| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/renderer/webcrypto/platform_crypto.h" | |
| 6 | |
| 7 #include <cryptohi.h> | |
| 8 #include <pk11pub.h> | |
| 9 #include <sechash.h> | |
| 10 | |
| 11 #include <vector> | |
| 12 | |
| 13 #include "base/lazy_instance.h" | |
| 14 #include "base/logging.h" | |
| 15 #include "content/renderer/webcrypto/crypto_data.h" | |
| 16 #include "content/renderer/webcrypto/webcrypto_util.h" | |
| 17 #include "crypto/nss_util.h" | |
| 18 #include "crypto/scoped_nss_types.h" | |
| 19 #include "third_party/WebKit/public/platform/WebArrayBuffer.h" | |
| 20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" | |
| 21 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | |
| 22 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | |
| 23 | |
| 24 #if defined(USE_NSS) | |
| 25 #include <dlfcn.h> | |
| 26 #endif | |
| 27 | |
| 28 // At the time of this writing: | |
| 29 // * Windows and Mac builds ship with their own copy of NSS (3.15+) | |
| 30 // * Linux builds use the system's libnss, which is 3.14 on Debian (but 3.15+ | |
| 31 // on other distros). | |
| 32 // | |
| 33 // Since NSS provides AES-GCM support starting in version 3.15, it may be | |
| 34 // unavailable for Linux Chrome users. | |
| 35 // | |
| 36 // * !defined(CKM_AES_GCM) | |
| 37 // | |
| 38 // This means that at build time, the NSS header pkcs11t.h is older than | |
| 39 // 3.15. However at runtime support may be present. | |
| 40 // | |
| 41 // * !defined(USE_NSS) | |
| 42 // | |
| 43 // This means that Chrome is being built with an embedded copy of NSS, | |
| 44 // which can be assumed to be >= 3.15. On the other hand if USE_NSS is | |
| 45 // defined, it also implies running on Linux. | |
| 46 // | |
| 47 // TODO(eroman): Simplify this once 3.15+ is required by Linux builds. | |
| 48 #if !defined(CKM_AES_GCM) | |
| 49 #define CKM_AES_GCM 0x00001087 | |
| 50 | |
| 51 struct CK_GCM_PARAMS { | |
| 52 CK_BYTE_PTR pIv; | |
| 53 CK_ULONG ulIvLen; | |
| 54 CK_BYTE_PTR pAAD; | |
| 55 CK_ULONG ulAADLen; | |
| 56 CK_ULONG ulTagBits; | |
| 57 }; | |
| 58 #endif // !defined(CKM_AES_GCM) | |
| 59 | |
| 60 // Signature for PK11_Encrypt and PK11_Decrypt. | |
| 61 typedef SECStatus (*PK11_EncryptDecryptFunction)(PK11SymKey*, | |
| 62 CK_MECHANISM_TYPE, | |
| 63 SECItem*, | |
| 64 unsigned char*, | |
| 65 unsigned int*, | |
| 66 unsigned int, | |
| 67 const unsigned char*, | |
| 68 unsigned int); | |
| 69 | |
| 70 // Singleton to abstract away dynamically loading libnss3.so | |
| 71 class AesGcmSupport { | |
| 72 public: | |
| 73 bool IsSupported() const { return pk11_encrypt_func_ && pk11_decrypt_func_; } | |
| 74 | |
| 75 // Returns NULL if unsupported. | |
| 76 PK11_EncryptDecryptFunction pk11_encrypt_func() const { | |
| 77 return pk11_encrypt_func_; | |
| 78 } | |
| 79 | |
| 80 // Returns NULL if unsupported. | |
| 81 PK11_EncryptDecryptFunction pk11_decrypt_func() const { | |
| 82 return pk11_decrypt_func_; | |
| 83 } | |
| 84 | |
| 85 private: | |
| 86 friend struct base::DefaultLazyInstanceTraits<AesGcmSupport>; | |
| 87 | |
| 88 AesGcmSupport() { | |
| 89 #if !defined(USE_NSS) | |
| 90 // Using a bundled version of NSS that is guaranteed to have this symbol. | |
| 91 pk11_encrypt_func_ = PK11_Encrypt; | |
| 92 pk11_decrypt_func_ = PK11_Decrypt; | |
| 93 #else | |
| 94 // Using system NSS libraries and PCKS #11 modules, which may not have the | |
| 95 // necessary function (PK11_Encrypt) or mechanism support (CKM_AES_GCM). | |
| 96 | |
| 97 // If PK11_Encrypt() was successfully resolved, then NSS will support | |
| 98 // AES-GCM directly. This was introduced in NSS 3.15. | |
| 99 pk11_encrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>( | |
| 100 dlsym(RTLD_DEFAULT, "PK11_Encrypt")); | |
| 101 pk11_decrypt_func_ = reinterpret_cast<PK11_EncryptDecryptFunction>( | |
| 102 dlsym(RTLD_DEFAULT, "PK11_Decrypt")); | |
| 103 #endif | |
| 104 } | |
| 105 | |
| 106 PK11_EncryptDecryptFunction pk11_encrypt_func_; | |
| 107 PK11_EncryptDecryptFunction pk11_decrypt_func_; | |
| 108 }; | |
| 109 | |
| 110 base::LazyInstance<AesGcmSupport>::Leaky g_aes_gcm_support = | |
| 111 LAZY_INSTANCE_INITIALIZER; | |
| 112 | |
| 113 namespace content { | |
| 114 | |
| 115 namespace webcrypto { | |
| 116 | |
| 117 namespace platform { | |
| 118 | |
| 119 class SymKey : public Key { | |
| 120 public: | |
| 121 explicit SymKey(crypto::ScopedPK11SymKey key) : key_(key.Pass()) {} | |
| 122 | |
| 123 PK11SymKey* key() { return key_.get(); } | |
| 124 | |
| 125 virtual SymKey* AsSymKey() OVERRIDE { return this; } | |
| 126 virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; } | |
| 127 virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; } | |
| 128 | |
| 129 private: | |
| 130 crypto::ScopedPK11SymKey key_; | |
| 131 | |
| 132 DISALLOW_COPY_AND_ASSIGN(SymKey); | |
| 133 }; | |
| 134 | |
| 135 class PublicKey : public Key { | |
| 136 public: | |
| 137 explicit PublicKey(crypto::ScopedSECKEYPublicKey key) : key_(key.Pass()) {} | |
| 138 | |
| 139 SECKEYPublicKey* key() { return key_.get(); } | |
| 140 | |
| 141 virtual SymKey* AsSymKey() OVERRIDE { return NULL; } | |
| 142 virtual PublicKey* AsPublicKey() OVERRIDE { return this; } | |
| 143 virtual PrivateKey* AsPrivateKey() OVERRIDE { return NULL; } | |
| 144 | |
| 145 private: | |
| 146 crypto::ScopedSECKEYPublicKey key_; | |
| 147 | |
| 148 DISALLOW_COPY_AND_ASSIGN(PublicKey); | |
| 149 }; | |
| 150 | |
| 151 class PrivateKey : public Key { | |
| 152 public: | |
| 153 explicit PrivateKey(crypto::ScopedSECKEYPrivateKey key) : key_(key.Pass()) {} | |
| 154 | |
| 155 SECKEYPrivateKey* key() { return key_.get(); } | |
| 156 | |
| 157 virtual SymKey* AsSymKey() OVERRIDE { return NULL; } | |
| 158 virtual PublicKey* AsPublicKey() OVERRIDE { return NULL; } | |
| 159 virtual PrivateKey* AsPrivateKey() OVERRIDE { return this; } | |
| 160 | |
| 161 private: | |
| 162 crypto::ScopedSECKEYPrivateKey key_; | |
| 163 | |
| 164 DISALLOW_COPY_AND_ASSIGN(PrivateKey); | |
| 165 }; | |
| 166 | |
| 167 namespace { | |
| 168 | |
| 169 // Creates a SECItem for the data in |buffer|. This does NOT make a copy, so | |
| 170 // |buffer| should outlive the SECItem. | |
| 171 SECItem MakeSECItemForBuffer(const CryptoData& buffer) { | |
| 172 SECItem item = { | |
| 173 siBuffer, | |
| 174 // NSS requires non-const data even though it is just for input. | |
| 175 const_cast<unsigned char*>(buffer.bytes()), buffer.byte_length()}; | |
| 176 return item; | |
| 177 } | |
| 178 | |
| 179 HASH_HashType WebCryptoAlgorithmToNSSHashType( | |
| 180 blink::WebCryptoAlgorithmId algorithm) { | |
| 181 switch (algorithm) { | |
| 182 case blink::WebCryptoAlgorithmIdSha1: | |
| 183 return HASH_AlgSHA1; | |
| 184 case blink::WebCryptoAlgorithmIdSha224: | |
| 185 return HASH_AlgSHA224; | |
| 186 case blink::WebCryptoAlgorithmIdSha256: | |
| 187 return HASH_AlgSHA256; | |
| 188 case blink::WebCryptoAlgorithmIdSha384: | |
| 189 return HASH_AlgSHA384; | |
| 190 case blink::WebCryptoAlgorithmIdSha512: | |
| 191 return HASH_AlgSHA512; | |
| 192 default: | |
| 193 // Not a digest algorithm. | |
| 194 return HASH_AlgNULL; | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 CK_MECHANISM_TYPE WebCryptoHashToHMACMechanism( | |
| 199 const blink::WebCryptoAlgorithm& algorithm) { | |
| 200 switch (algorithm.id()) { | |
| 201 case blink::WebCryptoAlgorithmIdSha1: | |
| 202 return CKM_SHA_1_HMAC; | |
| 203 case blink::WebCryptoAlgorithmIdSha224: | |
| 204 return CKM_SHA224_HMAC; | |
| 205 case blink::WebCryptoAlgorithmIdSha256: | |
| 206 return CKM_SHA256_HMAC; | |
| 207 case blink::WebCryptoAlgorithmIdSha384: | |
| 208 return CKM_SHA384_HMAC; | |
| 209 case blink::WebCryptoAlgorithmIdSha512: | |
| 210 return CKM_SHA512_HMAC; | |
| 211 default: | |
| 212 // Not a supported algorithm. | |
| 213 return CKM_INVALID_MECHANISM; | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 Status AesCbcEncryptDecrypt(EncryptOrDecrypt mode, | |
| 218 SymKey* key, | |
| 219 const CryptoData& iv, | |
| 220 const CryptoData& data, | |
| 221 blink::WebArrayBuffer* buffer) { | |
| 222 CK_ATTRIBUTE_TYPE operation = (mode == ENCRYPT) ? CKA_ENCRYPT : CKA_DECRYPT; | |
| 223 | |
| 224 SECItem iv_item = MakeSECItemForBuffer(iv); | |
| 225 | |
| 226 crypto::ScopedSECItem param(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item)); | |
| 227 if (!param) | |
| 228 return Status::Error(); | |
| 229 | |
| 230 crypto::ScopedPK11Context context(PK11_CreateContextBySymKey( | |
| 231 CKM_AES_CBC_PAD, operation, key->key(), param.get())); | |
| 232 | |
| 233 if (!context.get()) | |
| 234 return Status::Error(); | |
| 235 | |
| 236 // Oddly PK11_CipherOp takes input and output lengths as "int" rather than | |
| 237 // "unsigned int". Do some checks now to avoid integer overflowing. | |
| 238 if (data.byte_length() >= INT_MAX - AES_BLOCK_SIZE) { | |
| 239 // TODO(eroman): Handle this by chunking the input fed into NSS. Right now | |
| 240 // it doesn't make much difference since the one-shot API would end up | |
| 241 // blowing out the memory and crashing anyway. | |
| 242 return Status::ErrorDataTooLarge(); | |
| 243 } | |
| 244 | |
| 245 // PK11_CipherOp does an invalid memory access when given empty decryption | |
| 246 // input, or input which is not a multiple of the block size. See also | |
| 247 // https://bugzilla.mozilla.com/show_bug.cgi?id=921687. | |
| 248 if (operation == CKA_DECRYPT && | |
| 249 (data.byte_length() == 0 || (data.byte_length() % AES_BLOCK_SIZE != 0))) { | |
| 250 return Status::Error(); | |
| 251 } | |
| 252 | |
| 253 // TODO(eroman): Refine the output buffer size. It can be computed exactly for | |
| 254 // encryption, and can be smaller for decryption. | |
| 255 unsigned int output_max_len = data.byte_length() + AES_BLOCK_SIZE; | |
| 256 CHECK_GT(output_max_len, data.byte_length()); | |
| 257 | |
| 258 *buffer = blink::WebArrayBuffer::create(output_max_len, 1); | |
| 259 | |
| 260 unsigned char* buffer_data = reinterpret_cast<unsigned char*>(buffer->data()); | |
| 261 | |
| 262 int output_len; | |
| 263 if (SECSuccess != PK11_CipherOp(context.get(), | |
| 264 buffer_data, | |
| 265 &output_len, | |
| 266 buffer->byteLength(), | |
| 267 data.bytes(), | |
| 268 data.byte_length())) { | |
| 269 return Status::Error(); | |
| 270 } | |
| 271 | |
| 272 unsigned int final_output_chunk_len; | |
| 273 if (SECSuccess != PK11_DigestFinal(context.get(), | |
| 274 buffer_data + output_len, | |
| 275 &final_output_chunk_len, | |
| 276 output_max_len - output_len)) { | |
| 277 return Status::Error(); | |
| 278 } | |
| 279 | |
| 280 ShrinkBuffer(buffer, final_output_chunk_len + output_len); | |
| 281 return Status::Success(); | |
| 282 } | |
| 283 | |
| 284 // Helper to either encrypt or decrypt for AES-GCM. The result of encryption is | |
| 285 // the concatenation of the ciphertext and the authentication tag. Similarly, | |
| 286 // this is the expectation for the input to decryption. | |
| 287 Status AesGcmEncryptDecrypt(EncryptOrDecrypt mode, | |
| 288 SymKey* key, | |
| 289 const CryptoData& data, | |
| 290 const CryptoData& iv, | |
| 291 const CryptoData& additional_data, | |
| 292 unsigned int tag_length_bits, | |
| 293 blink::WebArrayBuffer* buffer) { | |
| 294 if (!g_aes_gcm_support.Get().IsSupported()) | |
| 295 return Status::ErrorUnsupported(); | |
| 296 | |
| 297 unsigned int tag_length_bytes = tag_length_bits / 8; | |
| 298 | |
| 299 CK_GCM_PARAMS gcm_params = {0}; | |
| 300 gcm_params.pIv = const_cast<unsigned char*>(iv.bytes()); | |
| 301 gcm_params.ulIvLen = iv.byte_length(); | |
| 302 | |
| 303 gcm_params.pAAD = const_cast<unsigned char*>(additional_data.bytes()); | |
| 304 gcm_params.ulAADLen = additional_data.byte_length(); | |
| 305 | |
| 306 gcm_params.ulTagBits = tag_length_bits; | |
| 307 | |
| 308 SECItem param; | |
| 309 param.type = siBuffer; | |
| 310 param.data = reinterpret_cast<unsigned char*>(&gcm_params); | |
| 311 param.len = sizeof(gcm_params); | |
| 312 | |
| 313 unsigned int buffer_size = 0; | |
| 314 | |
| 315 // Calculate the output buffer size. | |
| 316 if (mode == ENCRYPT) { | |
| 317 // TODO(eroman): This is ugly, abstract away the safe integer arithmetic. | |
| 318 if (data.byte_length() > (UINT_MAX - tag_length_bytes)) | |
| 319 return Status::ErrorDataTooLarge(); | |
| 320 buffer_size = data.byte_length() + tag_length_bytes; | |
| 321 } else { | |
| 322 // TODO(eroman): In theory the buffer allocated for the plain text should be | |
| 323 // sized as |data.byte_length() - tag_length_bytes|. | |
| 324 // | |
| 325 // However NSS has a bug whereby it will fail if the output buffer size is | |
| 326 // not at least as large as the ciphertext: | |
| 327 // | |
| 328 // https://bugzilla.mozilla.org/show_bug.cgi?id=%20853674 | |
| 329 // | |
| 330 // From the analysis of that bug it looks like it might be safe to pass a | |
| 331 // correctly sized buffer but lie about its size. Since resizing the | |
| 332 // WebCryptoArrayBuffer is expensive that hack may be worth looking into. | |
| 333 buffer_size = data.byte_length(); | |
| 334 } | |
| 335 | |
| 336 *buffer = blink::WebArrayBuffer::create(buffer_size, 1); | |
| 337 unsigned char* buffer_data = reinterpret_cast<unsigned char*>(buffer->data()); | |
| 338 | |
| 339 PK11_EncryptDecryptFunction func = | |
| 340 (mode == ENCRYPT) ? g_aes_gcm_support.Get().pk11_encrypt_func() | |
| 341 : g_aes_gcm_support.Get().pk11_decrypt_func(); | |
| 342 | |
| 343 unsigned int output_len = 0; | |
| 344 SECStatus result = func(key->key(), | |
| 345 CKM_AES_GCM, | |
| 346 ¶m, | |
| 347 buffer_data, | |
| 348 &output_len, | |
| 349 buffer->byteLength(), | |
| 350 data.bytes(), | |
| 351 data.byte_length()); | |
| 352 | |
| 353 if (result != SECSuccess) | |
| 354 return Status::Error(); | |
| 355 | |
| 356 // Unfortunately the buffer needs to be shrunk for decryption (see the NSS bug | |
| 357 // above). | |
| 358 ShrinkBuffer(buffer, output_len); | |
| 359 | |
| 360 return Status::Success(); | |
| 361 } | |
| 362 | |
| 363 CK_MECHANISM_TYPE WebCryptoAlgorithmToGenMechanism( | |
| 364 const blink::WebCryptoAlgorithm& algorithm) { | |
| 365 switch (algorithm.id()) { | |
| 366 case blink::WebCryptoAlgorithmIdAesCbc: | |
| 367 case blink::WebCryptoAlgorithmIdAesGcm: | |
| 368 case blink::WebCryptoAlgorithmIdAesKw: | |
| 369 return CKM_AES_KEY_GEN; | |
| 370 case blink::WebCryptoAlgorithmIdHmac: | |
| 371 return WebCryptoHashToHMACMechanism(algorithm.hmacKeyGenParams()->hash()); | |
| 372 default: | |
| 373 return CKM_INVALID_MECHANISM; | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, | |
| 378 // to unsigned long. | |
| 379 bool BigIntegerToLong(const uint8* data, | |
| 380 unsigned int data_size, | |
| 381 unsigned long* result) { | |
| 382 // TODO(padolph): Is it correct to say that empty data is an error, or does it | |
| 383 // mean value 0? See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23655 | |
| 384 if (data_size == 0) | |
| 385 return false; | |
| 386 | |
| 387 *result = 0; | |
| 388 for (size_t i = 0; i < data_size; ++i) { | |
| 389 size_t reverse_i = data_size - i - 1; | |
| 390 | |
| 391 if (reverse_i >= sizeof(unsigned long) && data[i]) | |
| 392 return false; // Too large for a long. | |
| 393 | |
| 394 *result |= data[i] << 8 * reverse_i; | |
| 395 } | |
| 396 return true; | |
| 397 } | |
| 398 | |
| 399 bool IsAlgorithmRsa(const blink::WebCryptoAlgorithm& algorithm) { | |
| 400 return algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 || | |
| 401 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep || | |
| 402 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5; | |
| 403 } | |
| 404 | |
| 405 bool CreatePublicKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, | |
| 406 SECKEYPublicKey* key, | |
| 407 blink::WebCryptoKeyAlgorithm* key_algorithm) { | |
| 408 // TODO(eroman): What about other key types rsaPss, rsaOaep. | |
| 409 if (!key || key->keyType != rsaKey) | |
| 410 return false; | |
| 411 | |
| 412 unsigned int modulus_length_bits = SECKEY_PublicKeyStrength(key) * 8; | |
| 413 CryptoData public_exponent(key->u.rsa.publicExponent.data, | |
| 414 key->u.rsa.publicExponent.len); | |
| 415 | |
| 416 switch (algorithm.paramsType()) { | |
| 417 case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams: | |
| 418 case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: | |
| 419 *key_algorithm = blink::WebCryptoKeyAlgorithm::adoptParamsAndCreate( | |
| 420 algorithm.id(), | |
| 421 new blink::WebCryptoRsaHashedKeyAlgorithmParams( | |
| 422 modulus_length_bits, | |
| 423 public_exponent.bytes(), | |
| 424 public_exponent.byte_length(), | |
| 425 GetInnerHashAlgorithm(algorithm))); | |
| 426 return true; | |
| 427 case blink::WebCryptoAlgorithmParamsTypeRsaKeyGenParams: | |
| 428 case blink::WebCryptoAlgorithmParamsTypeNone: | |
| 429 *key_algorithm = blink::WebCryptoKeyAlgorithm::adoptParamsAndCreate( | |
| 430 algorithm.id(), | |
| 431 new blink::WebCryptoRsaKeyAlgorithmParams( | |
| 432 modulus_length_bits, | |
| 433 public_exponent.bytes(), | |
| 434 public_exponent.byte_length())); | |
| 435 return true; | |
| 436 default: | |
| 437 return false; | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 bool CreatePrivateKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, | |
| 442 SECKEYPrivateKey* key, | |
| 443 blink::WebCryptoKeyAlgorithm* key_algorithm) { | |
| 444 crypto::ScopedSECKEYPublicKey public_key(SECKEY_ConvertToPublicKey(key)); | |
| 445 return CreatePublicKeyAlgorithm(algorithm, public_key.get(), key_algorithm); | |
| 446 } | |
| 447 | |
| 448 } // namespace | |
| 449 | |
| 450 Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, | |
| 451 const CryptoData& key_data, | |
| 452 bool extractable, | |
| 453 blink::WebCryptoKeyUsageMask usage_mask, | |
| 454 blink::WebCryptoKey* key) { | |
| 455 | |
| 456 DCHECK(!algorithm.isNull()); | |
| 457 | |
| 458 // TODO(bryaneyler): Need to split handling for symmetric and asymmetric keys. | |
| 459 // Currently only supporting symmetric. | |
| 460 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; | |
| 461 // Flags are verified at the Blink layer; here the flags are set to all | |
| 462 // possible operations for this key type. | |
| 463 CK_FLAGS flags = 0; | |
| 464 | |
| 465 switch (algorithm.id()) { | |
| 466 case blink::WebCryptoAlgorithmIdHmac: { | |
| 467 const blink::WebCryptoAlgorithm& hash = GetInnerHashAlgorithm(algorithm); | |
| 468 | |
| 469 mechanism = WebCryptoHashToHMACMechanism(hash); | |
| 470 if (mechanism == CKM_INVALID_MECHANISM) | |
| 471 return Status::ErrorUnsupported(); | |
| 472 | |
| 473 flags |= CKF_SIGN | CKF_VERIFY; | |
| 474 break; | |
| 475 } | |
| 476 case blink::WebCryptoAlgorithmIdAesCbc: { | |
| 477 mechanism = CKM_AES_CBC; | |
| 478 flags |= CKF_ENCRYPT | CKF_DECRYPT; | |
| 479 break; | |
| 480 } | |
| 481 case blink::WebCryptoAlgorithmIdAesKw: { | |
| 482 mechanism = CKM_NSS_AES_KEY_WRAP; | |
| 483 flags |= CKF_WRAP | CKF_WRAP; | |
| 484 break; | |
| 485 } | |
| 486 case blink::WebCryptoAlgorithmIdAesGcm: { | |
| 487 if (!g_aes_gcm_support.Get().IsSupported()) | |
| 488 return Status::ErrorUnsupported(); | |
| 489 mechanism = CKM_AES_GCM; | |
| 490 flags |= CKF_ENCRYPT | CKF_DECRYPT; | |
| 491 break; | |
| 492 } | |
| 493 default: | |
| 494 return Status::ErrorUnsupported(); | |
| 495 } | |
| 496 | |
| 497 DCHECK_NE(CKM_INVALID_MECHANISM, mechanism); | |
| 498 DCHECK_NE(0ul, flags); | |
| 499 | |
| 500 SECItem key_item = MakeSECItemForBuffer(key_data); | |
| 501 | |
| 502 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); | |
| 503 crypto::ScopedPK11SymKey pk11_sym_key( | |
| 504 PK11_ImportSymKeyWithFlags(slot.get(), | |
| 505 mechanism, | |
| 506 PK11_OriginUnwrap, | |
| 507 CKA_FLAGS_ONLY, | |
| 508 &key_item, | |
| 509 flags, | |
| 510 false, | |
| 511 NULL)); | |
| 512 if (!pk11_sym_key.get()) | |
| 513 return Status::Error(); | |
| 514 | |
| 515 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 516 if (!CreateSecretKeyAlgorithm( | |
| 517 algorithm, key_data.byte_length(), &key_algorithm)) | |
| 518 return Status::ErrorUnexpected(); | |
| 519 | |
| 520 *key = blink::WebCryptoKey::create(new SymKey(pk11_sym_key.Pass()), | |
| 521 blink::WebCryptoKeyTypeSecret, | |
| 522 extractable, | |
| 523 key_algorithm, | |
| 524 usage_mask); | |
| 525 return Status::Success(); | |
| 526 } | |
| 527 | |
| 528 Status ExportKeyRaw(SymKey* key, blink::WebArrayBuffer* buffer) { | |
| 529 if (PK11_ExtractKeyValue(key->key()) != SECSuccess) | |
| 530 return Status::Error(); | |
| 531 | |
| 532 const SECItem* key_data = PK11_GetKeyData(key->key()); | |
| 533 if (!key_data) | |
| 534 return Status::Error(); | |
| 535 | |
| 536 *buffer = CreateArrayBuffer(key_data->data, key_data->len); | |
| 537 | |
| 538 return Status::Success(); | |
| 539 } | |
| 540 | |
| 541 namespace { | |
| 542 | |
| 543 typedef scoped_ptr<CERTSubjectPublicKeyInfo, | |
| 544 crypto::NSSDestroyer<CERTSubjectPublicKeyInfo, | |
| 545 SECKEY_DestroySubjectPublicKeyInfo> > | |
| 546 ScopedCERTSubjectPublicKeyInfo; | |
| 547 | |
| 548 // Validates an NSS KeyType against a WebCrypto algorithm. Some NSS KeyTypes | |
| 549 // contain enough information to fabricate a Web Crypto algorithm, which is | |
| 550 // returned if the input algorithm isNull(). This function indicates failure by | |
| 551 // returning a Null algorithm. | |
| 552 blink::WebCryptoAlgorithm ResolveNssKeyTypeWithInputAlgorithm( | |
| 553 KeyType key_type, | |
| 554 const blink::WebCryptoAlgorithm& algorithm_or_null) { | |
| 555 switch (key_type) { | |
| 556 case rsaKey: | |
| 557 // NSS's rsaKey KeyType maps to keys with SEC_OID_PKCS1_RSA_ENCRYPTION and | |
| 558 // according to RFCs 4055/5756 this can be used for both encryption and | |
| 559 // signatures. However, this is not specific enough to build a compatible | |
| 560 // Web Crypto algorithm, since in Web Crypto, RSA encryption and signature | |
| 561 // algorithms are distinct. So if the input algorithm isNull() here, we | |
| 562 // have to fail. | |
| 563 if (!algorithm_or_null.isNull() && IsAlgorithmRsa(algorithm_or_null)) | |
| 564 return algorithm_or_null; | |
| 565 break; | |
| 566 case dsaKey: | |
| 567 case ecKey: | |
| 568 case rsaPssKey: | |
| 569 case rsaOaepKey: | |
| 570 // TODO(padolph): Handle other key types. | |
| 571 break; | |
| 572 default: | |
| 573 break; | |
| 574 } | |
| 575 return blink::WebCryptoAlgorithm::createNull(); | |
| 576 } | |
| 577 | |
| 578 } // namespace | |
| 579 | |
| 580 Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm_or_null, | |
| 581 const CryptoData& key_data, | |
| 582 bool extractable, | |
| 583 blink::WebCryptoKeyUsageMask usage_mask, | |
| 584 blink::WebCryptoKey* key) { | |
| 585 | |
| 586 DCHECK(key); | |
| 587 | |
| 588 if (!key_data.byte_length()) | |
| 589 return Status::ErrorImportEmptyKeyData(); | |
| 590 DCHECK(key_data.bytes()); | |
| 591 | |
| 592 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject | |
| 593 // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo. | |
| 594 SECItem spki_item = MakeSECItemForBuffer(key_data); | |
| 595 const ScopedCERTSubjectPublicKeyInfo spki( | |
| 596 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); | |
| 597 if (!spki) | |
| 598 return Status::Error(); | |
| 599 | |
| 600 crypto::ScopedSECKEYPublicKey sec_public_key( | |
| 601 SECKEY_ExtractPublicKey(spki.get())); | |
| 602 if (!sec_public_key) | |
| 603 return Status::Error(); | |
| 604 | |
| 605 const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get()); | |
| 606 blink::WebCryptoAlgorithm algorithm = | |
| 607 ResolveNssKeyTypeWithInputAlgorithm(sec_key_type, algorithm_or_null); | |
| 608 if (algorithm.isNull()) | |
| 609 return Status::Error(); | |
| 610 | |
| 611 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 612 if (!CreatePublicKeyAlgorithm( | |
| 613 algorithm, sec_public_key.get(), &key_algorithm)) | |
| 614 return Status::ErrorUnexpected(); | |
| 615 | |
| 616 *key = blink::WebCryptoKey::create(new PublicKey(sec_public_key.Pass()), | |
| 617 blink::WebCryptoKeyTypePublic, | |
| 618 extractable, | |
| 619 key_algorithm, | |
| 620 usage_mask); | |
| 621 | |
| 622 return Status::Success(); | |
| 623 } | |
| 624 | |
| 625 Status ExportKeySpki(PublicKey* key, blink::WebArrayBuffer* buffer) { | |
| 626 const crypto::ScopedSECItem spki_der( | |
| 627 SECKEY_EncodeDERSubjectPublicKeyInfo(key->key())); | |
| 628 if (!spki_der) | |
| 629 return Status::Error(); | |
| 630 | |
| 631 DCHECK(spki_der->data); | |
| 632 DCHECK(spki_der->len); | |
| 633 | |
| 634 *buffer = CreateArrayBuffer(spki_der->data, spki_der->len); | |
| 635 | |
| 636 return Status::Success(); | |
| 637 } | |
| 638 | |
| 639 Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm_or_null, | |
| 640 const CryptoData& key_data, | |
| 641 bool extractable, | |
| 642 blink::WebCryptoKeyUsageMask usage_mask, | |
| 643 blink::WebCryptoKey* key) { | |
| 644 | |
| 645 DCHECK(key); | |
| 646 | |
| 647 if (!key_data.byte_length()) | |
| 648 return Status::ErrorImportEmptyKeyData(); | |
| 649 DCHECK(key_data.bytes()); | |
| 650 | |
| 651 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8 | |
| 652 // private key info object. | |
| 653 SECItem pki_der = MakeSECItemForBuffer(key_data); | |
| 654 | |
| 655 SECKEYPrivateKey* seckey_private_key = NULL; | |
| 656 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); | |
| 657 if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot.get(), | |
| 658 &pki_der, | |
| 659 NULL, // nickname | |
| 660 NULL, // publicValue | |
| 661 false, // isPerm | |
| 662 false, // isPrivate | |
| 663 KU_ALL, // usage | |
| 664 &seckey_private_key, | |
| 665 NULL) != SECSuccess) { | |
| 666 return Status::Error(); | |
| 667 } | |
| 668 DCHECK(seckey_private_key); | |
| 669 crypto::ScopedSECKEYPrivateKey private_key(seckey_private_key); | |
| 670 | |
| 671 const KeyType sec_key_type = SECKEY_GetPrivateKeyType(private_key.get()); | |
| 672 blink::WebCryptoAlgorithm algorithm = | |
| 673 ResolveNssKeyTypeWithInputAlgorithm(sec_key_type, algorithm_or_null); | |
| 674 if (algorithm.isNull()) | |
| 675 return Status::Error(); | |
| 676 | |
| 677 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 678 if (!CreatePrivateKeyAlgorithm(algorithm, private_key.get(), &key_algorithm)) | |
| 679 return Status::ErrorUnexpected(); | |
| 680 | |
| 681 *key = blink::WebCryptoKey::create(new PrivateKey(private_key.Pass()), | |
| 682 blink::WebCryptoKeyTypePrivate, | |
| 683 extractable, | |
| 684 key_algorithm, | |
| 685 usage_mask); | |
| 686 | |
| 687 return Status::Success(); | |
| 688 } | |
| 689 | |
| 690 // ----------------------------------- | |
| 691 // Hmac | |
| 692 // ----------------------------------- | |
| 693 | |
| 694 Status SignHmac(SymKey* key, | |
| 695 const blink::WebCryptoAlgorithm& hash, | |
| 696 const CryptoData& data, | |
| 697 blink::WebArrayBuffer* buffer) { | |
| 698 DCHECK_EQ(PK11_GetMechanism(key->key()), WebCryptoHashToHMACMechanism(hash)); | |
| 699 | |
| 700 SECItem param_item = {siBuffer, NULL, 0}; | |
| 701 SECItem data_item = MakeSECItemForBuffer(data); | |
| 702 // First call is to figure out the length. | |
| 703 SECItem signature_item = {siBuffer, NULL, 0}; | |
| 704 | |
| 705 if (PK11_SignWithSymKey(key->key(), | |
| 706 PK11_GetMechanism(key->key()), | |
| 707 ¶m_item, | |
| 708 &signature_item, | |
| 709 &data_item) != SECSuccess) { | |
| 710 return Status::Error(); | |
| 711 } | |
| 712 | |
| 713 DCHECK_NE(0u, signature_item.len); | |
| 714 | |
| 715 *buffer = blink::WebArrayBuffer::create(signature_item.len, 1); | |
| 716 signature_item.data = reinterpret_cast<unsigned char*>(buffer->data()); | |
| 717 | |
| 718 if (PK11_SignWithSymKey(key->key(), | |
| 719 PK11_GetMechanism(key->key()), | |
| 720 ¶m_item, | |
| 721 &signature_item, | |
| 722 &data_item) != SECSuccess) { | |
| 723 return Status::Error(); | |
| 724 } | |
| 725 | |
| 726 DCHECK_EQ(buffer->byteLength(), signature_item.len); | |
| 727 return Status::Success(); | |
| 728 } | |
| 729 | |
| 730 // ----------------------------------- | |
| 731 // RsaEsPkcs1v1_5 | |
| 732 // ----------------------------------- | |
| 733 | |
| 734 Status EncryptRsaEsPkcs1v1_5(PublicKey* key, | |
| 735 const CryptoData& data, | |
| 736 blink::WebArrayBuffer* buffer) { | |
| 737 const unsigned int encrypted_length_bytes = | |
| 738 SECKEY_PublicKeyStrength(key->key()); | |
| 739 | |
| 740 // RSAES can operate on messages up to a length of k - 11, where k is the | |
| 741 // octet length of the RSA modulus. | |
| 742 if (encrypted_length_bytes < 11 || | |
| 743 encrypted_length_bytes - 11 < data.byte_length()) | |
| 744 return Status::ErrorDataTooLarge(); | |
| 745 | |
| 746 *buffer = blink::WebArrayBuffer::create(encrypted_length_bytes, 1); | |
| 747 unsigned char* const buffer_data = | |
| 748 reinterpret_cast<unsigned char*>(buffer->data()); | |
| 749 | |
| 750 if (PK11_PubEncryptPKCS1(key->key(), | |
| 751 buffer_data, | |
| 752 const_cast<unsigned char*>(data.bytes()), | |
| 753 data.byte_length(), | |
| 754 NULL) != SECSuccess) { | |
| 755 return Status::Error(); | |
| 756 } | |
| 757 return Status::Success(); | |
| 758 } | |
| 759 | |
| 760 Status DecryptRsaEsPkcs1v1_5(PrivateKey* key, | |
| 761 const CryptoData& data, | |
| 762 blink::WebArrayBuffer* buffer) { | |
| 763 const int modulus_length_bytes = PK11_GetPrivateModulusLen(key->key()); | |
| 764 if (modulus_length_bytes <= 0) | |
| 765 return Status::ErrorUnexpected(); | |
| 766 const unsigned int max_output_length_bytes = modulus_length_bytes; | |
| 767 | |
| 768 *buffer = blink::WebArrayBuffer::create(max_output_length_bytes, 1); | |
| 769 unsigned char* const buffer_data = | |
| 770 reinterpret_cast<unsigned char*>(buffer->data()); | |
| 771 | |
| 772 unsigned int output_length_bytes = 0; | |
| 773 if (PK11_PrivDecryptPKCS1(key->key(), | |
| 774 buffer_data, | |
| 775 &output_length_bytes, | |
| 776 max_output_length_bytes, | |
| 777 const_cast<unsigned char*>(data.bytes()), | |
| 778 data.byte_length()) != SECSuccess) { | |
| 779 return Status::Error(); | |
| 780 } | |
| 781 DCHECK_LE(output_length_bytes, max_output_length_bytes); | |
| 782 ShrinkBuffer(buffer, output_length_bytes); | |
| 783 return Status::Success(); | |
| 784 } | |
| 785 | |
| 786 // ----------------------------------- | |
| 787 // RsaSsaPkcs1v1_5 | |
| 788 // ----------------------------------- | |
| 789 | |
| 790 Status SignRsaSsaPkcs1v1_5(PrivateKey* key, | |
| 791 const blink::WebCryptoAlgorithm& hash, | |
| 792 const CryptoData& data, | |
| 793 blink::WebArrayBuffer* buffer) { | |
| 794 // Pick the NSS signing algorithm by combining RSA-SSA (RSA PKCS1) and the | |
| 795 // inner hash of the input Web Crypto algorithm. | |
| 796 SECOidTag sign_alg_tag; | |
| 797 switch (hash.id()) { | |
| 798 case blink::WebCryptoAlgorithmIdSha1: | |
| 799 sign_alg_tag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; | |
| 800 break; | |
| 801 case blink::WebCryptoAlgorithmIdSha224: | |
| 802 sign_alg_tag = SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION; | |
| 803 break; | |
| 804 case blink::WebCryptoAlgorithmIdSha256: | |
| 805 sign_alg_tag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; | |
| 806 break; | |
| 807 case blink::WebCryptoAlgorithmIdSha384: | |
| 808 sign_alg_tag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; | |
| 809 break; | |
| 810 case blink::WebCryptoAlgorithmIdSha512: | |
| 811 sign_alg_tag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; | |
| 812 break; | |
| 813 default: | |
| 814 return Status::ErrorUnsupported(); | |
| 815 } | |
| 816 | |
| 817 crypto::ScopedSECItem signature_item(SECITEM_AllocItem(NULL, NULL, 0)); | |
| 818 if (SEC_SignData(signature_item.get(), | |
| 819 data.bytes(), | |
| 820 data.byte_length(), | |
| 821 key->key(), | |
| 822 sign_alg_tag) != SECSuccess) { | |
| 823 return Status::Error(); | |
| 824 } | |
| 825 | |
| 826 *buffer = CreateArrayBuffer(signature_item->data, signature_item->len); | |
| 827 return Status::Success(); | |
| 828 } | |
| 829 | |
| 830 Status VerifyRsaSsaPkcs1v1_5(PublicKey* key, | |
| 831 const blink::WebCryptoAlgorithm& hash, | |
| 832 const CryptoData& signature, | |
| 833 const CryptoData& data, | |
| 834 bool* signature_match) { | |
| 835 const SECItem signature_item = MakeSECItemForBuffer(signature); | |
| 836 | |
| 837 SECOidTag hash_alg_tag; | |
| 838 switch (hash.id()) { | |
| 839 case blink::WebCryptoAlgorithmIdSha1: | |
| 840 hash_alg_tag = SEC_OID_SHA1; | |
| 841 break; | |
| 842 case blink::WebCryptoAlgorithmIdSha224: | |
| 843 hash_alg_tag = SEC_OID_SHA224; | |
| 844 break; | |
| 845 case blink::WebCryptoAlgorithmIdSha256: | |
| 846 hash_alg_tag = SEC_OID_SHA256; | |
| 847 break; | |
| 848 case blink::WebCryptoAlgorithmIdSha384: | |
| 849 hash_alg_tag = SEC_OID_SHA384; | |
| 850 break; | |
| 851 case blink::WebCryptoAlgorithmIdSha512: | |
| 852 hash_alg_tag = SEC_OID_SHA512; | |
| 853 break; | |
| 854 default: | |
| 855 return Status::ErrorUnsupported(); | |
| 856 } | |
| 857 | |
| 858 *signature_match = | |
| 859 SECSuccess == VFY_VerifyDataDirect(data.bytes(), | |
| 860 data.byte_length(), | |
| 861 key->key(), | |
| 862 &signature_item, | |
| 863 SEC_OID_PKCS1_RSA_ENCRYPTION, | |
| 864 hash_alg_tag, | |
| 865 NULL, | |
| 866 NULL); | |
| 867 return Status::Success(); | |
| 868 } | |
| 869 | |
| 870 Status EncryptDecryptAesCbc(EncryptOrDecrypt mode, | |
| 871 SymKey* key, | |
| 872 const CryptoData& data, | |
| 873 const CryptoData& iv, | |
| 874 blink::WebArrayBuffer* buffer) { | |
| 875 // TODO(eroman): Inline. | |
| 876 return AesCbcEncryptDecrypt(mode, key, iv, data, buffer); | |
| 877 } | |
| 878 | |
| 879 Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, | |
| 880 SymKey* key, | |
| 881 const CryptoData& data, | |
| 882 const CryptoData& iv, | |
| 883 const CryptoData& additional_data, | |
| 884 unsigned int tag_length_bits, | |
| 885 blink::WebArrayBuffer* buffer) { | |
| 886 // TODO(eroman): Inline. | |
| 887 return AesGcmEncryptDecrypt( | |
| 888 mode, key, data, iv, additional_data, tag_length_bits, buffer); | |
| 889 } | |
| 890 | |
| 891 // ----------------------------------- | |
| 892 // Key generation | |
| 893 // ----------------------------------- | |
| 894 | |
| 895 Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, | |
| 896 bool extractable, | |
| 897 blink::WebCryptoKeyUsageMask usage_mask, | |
| 898 unsigned int modulus_length_bits, | |
| 899 const CryptoData& public_exponent, | |
| 900 const blink::WebCryptoAlgorithm& hash_or_null, | |
| 901 blink::WebCryptoKey* public_key, | |
| 902 blink::WebCryptoKey* private_key) { | |
| 903 crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); | |
| 904 if (!slot) | |
| 905 return Status::Error(); | |
| 906 | |
| 907 unsigned long public_exponent_long; | |
| 908 if (!BigIntegerToLong(public_exponent.bytes(), | |
| 909 public_exponent.byte_length(), | |
| 910 &public_exponent_long) || | |
| 911 !public_exponent_long) { | |
| 912 return Status::ErrorGenerateKeyPublicExponent(); | |
| 913 } | |
| 914 | |
| 915 PK11RSAGenParams rsa_gen_params; | |
| 916 rsa_gen_params.keySizeInBits = modulus_length_bits; | |
| 917 rsa_gen_params.pe = public_exponent_long; | |
| 918 | |
| 919 // Flags are verified at the Blink layer; here the flags are set to all | |
| 920 // possible operations for the given key type. | |
| 921 CK_FLAGS operation_flags; | |
| 922 switch (algorithm.id()) { | |
| 923 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: | |
| 924 case blink::WebCryptoAlgorithmIdRsaOaep: | |
| 925 operation_flags = CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP; | |
| 926 break; | |
| 927 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: | |
| 928 operation_flags = CKF_SIGN | CKF_VERIFY; | |
| 929 break; | |
| 930 default: | |
| 931 NOTREACHED(); | |
| 932 return Status::ErrorUnexpected(); | |
| 933 } | |
| 934 const CK_FLAGS operation_flags_mask = | |
| 935 CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY | CKF_WRAP | CKF_UNWRAP; | |
| 936 const PK11AttrFlags attribute_flags = 0; // Default all PK11_ATTR_ flags. | |
| 937 | |
| 938 // Note: NSS does not generate an sec_public_key if the call below fails, | |
| 939 // so there is no danger of a leaked sec_public_key. | |
| 940 SECKEYPublicKey* sec_public_key; | |
| 941 crypto::ScopedSECKEYPrivateKey scoped_sec_private_key( | |
| 942 PK11_GenerateKeyPairWithOpFlags(slot.get(), | |
| 943 CKM_RSA_PKCS_KEY_PAIR_GEN, | |
| 944 &rsa_gen_params, | |
| 945 &sec_public_key, | |
| 946 attribute_flags, | |
| 947 operation_flags, | |
| 948 operation_flags_mask, | |
| 949 NULL)); | |
| 950 if (!private_key) | |
| 951 return Status::Error(); | |
| 952 | |
| 953 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 954 if (!CreatePublicKeyAlgorithm(algorithm, sec_public_key, &key_algorithm)) | |
| 955 return Status::ErrorUnexpected(); | |
| 956 | |
| 957 *public_key = blink::WebCryptoKey::create( | |
| 958 new PublicKey(crypto::ScopedSECKEYPublicKey(sec_public_key)), | |
| 959 blink::WebCryptoKeyTypePublic, | |
| 960 true, | |
| 961 key_algorithm, | |
| 962 usage_mask); | |
| 963 *private_key = | |
| 964 blink::WebCryptoKey::create(new PrivateKey(scoped_sec_private_key.Pass()), | |
| 965 blink::WebCryptoKeyTypePrivate, | |
| 966 extractable, | |
| 967 key_algorithm, | |
| 968 usage_mask); | |
| 969 | |
| 970 return Status::Success(); | |
| 971 } | |
| 972 | |
| 973 void Init() { crypto::EnsureNSSInit(); } | |
| 974 | |
| 975 Status DigestSha(blink::WebCryptoAlgorithmId algorithm, | |
| 976 const CryptoData& data, | |
| 977 blink::WebArrayBuffer* buffer) { | |
| 978 HASH_HashType hash_type = WebCryptoAlgorithmToNSSHashType(algorithm); | |
| 979 if (hash_type == HASH_AlgNULL) | |
| 980 return Status::ErrorUnsupported(); | |
| 981 | |
| 982 HASHContext* context = HASH_Create(hash_type); | |
| 983 if (!context) | |
| 984 return Status::Error(); | |
| 985 | |
| 986 HASH_Begin(context); | |
| 987 | |
| 988 HASH_Update(context, data.bytes(), data.byte_length()); | |
| 989 | |
| 990 unsigned int hash_result_length = HASH_ResultLenContext(context); | |
| 991 DCHECK_LE(hash_result_length, static_cast<size_t>(HASH_LENGTH_MAX)); | |
| 992 | |
| 993 *buffer = blink::WebArrayBuffer::create(hash_result_length, 1); | |
| 994 | |
| 995 unsigned char* digest = reinterpret_cast<unsigned char*>(buffer->data()); | |
| 996 | |
| 997 unsigned int result_length = 0; | |
| 998 HASH_End(context, digest, &result_length, hash_result_length); | |
| 999 | |
| 1000 HASH_Destroy(context); | |
| 1001 | |
| 1002 if (result_length != hash_result_length) | |
| 1003 return Status::ErrorUnexpected(); | |
| 1004 return Status::Success(); | |
| 1005 } | |
| 1006 | |
| 1007 Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, | |
| 1008 bool extractable, | |
| 1009 blink::WebCryptoKeyUsageMask usage_mask, | |
| 1010 unsigned keylen_bytes, | |
| 1011 blink::WebCryptoKey* key) { | |
| 1012 CK_MECHANISM_TYPE mech = WebCryptoAlgorithmToGenMechanism(algorithm); | |
| 1013 blink::WebCryptoKeyType key_type = blink::WebCryptoKeyTypeSecret; | |
| 1014 | |
| 1015 if (mech == CKM_INVALID_MECHANISM) | |
| 1016 return Status::ErrorUnsupported(); | |
| 1017 | |
| 1018 crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); | |
| 1019 if (!slot) | |
| 1020 return Status::Error(); | |
| 1021 | |
| 1022 crypto::ScopedPK11SymKey pk11_key( | |
| 1023 PK11_KeyGen(slot.get(), mech, NULL, keylen_bytes, NULL)); | |
| 1024 | |
| 1025 if (!pk11_key) | |
| 1026 return Status::Error(); | |
| 1027 | |
| 1028 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 1029 if (!CreateSecretKeyAlgorithm(algorithm, keylen_bytes, &key_algorithm)) | |
| 1030 return Status::ErrorUnexpected(); | |
| 1031 | |
| 1032 *key = blink::WebCryptoKey::create(new SymKey(pk11_key.Pass()), | |
| 1033 key_type, | |
| 1034 extractable, | |
| 1035 key_algorithm, | |
| 1036 usage_mask); | |
| 1037 return Status::Success(); | |
| 1038 } | |
| 1039 | |
| 1040 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, | |
| 1041 bool extractable, | |
| 1042 blink::WebCryptoKeyUsageMask usage_mask, | |
| 1043 const CryptoData& modulus_data, | |
| 1044 const CryptoData& exponent_data, | |
| 1045 blink::WebCryptoKey* key) { | |
| 1046 | |
| 1047 if (!modulus_data.byte_length()) | |
| 1048 return Status::ErrorImportRsaEmptyModulus(); | |
| 1049 | |
| 1050 if (!exponent_data.byte_length()) | |
| 1051 return Status::ErrorImportRsaEmptyExponent(); | |
| 1052 | |
| 1053 DCHECK(modulus_data.bytes()); | |
| 1054 DCHECK(exponent_data.bytes()); | |
| 1055 | |
| 1056 // NSS does not provide a way to create an RSA public key directly from the | |
| 1057 // modulus and exponent values, but it can import an DER-encoded ASN.1 blob | |
| 1058 // with these values and create the public key from that. The code below | |
| 1059 // follows the recommendation described in | |
| 1060 // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7 | |
| 1061 | |
| 1062 // Pack the input values into a struct compatible with NSS ASN.1 encoding, and | |
| 1063 // set up an ASN.1 encoder template for it. | |
| 1064 struct RsaPublicKeyData { | |
| 1065 SECItem modulus; | |
| 1066 SECItem exponent; | |
| 1067 }; | |
| 1068 const RsaPublicKeyData pubkey_in = { | |
| 1069 {siUnsignedInteger, const_cast<unsigned char*>(modulus_data.bytes()), | |
| 1070 modulus_data.byte_length()}, | |
| 1071 {siUnsignedInteger, const_cast<unsigned char*>(exponent_data.bytes()), | |
| 1072 exponent_data.byte_length()}}; | |
| 1073 const SEC_ASN1Template rsa_public_key_template[] = { | |
| 1074 {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RsaPublicKeyData)}, | |
| 1075 {SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, modulus), }, | |
| 1076 {SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, exponent), }, | |
| 1077 {0, }}; | |
| 1078 | |
| 1079 // DER-encode the public key. | |
| 1080 crypto::ScopedSECItem pubkey_der( | |
| 1081 SEC_ASN1EncodeItem(NULL, NULL, &pubkey_in, rsa_public_key_template)); | |
| 1082 if (!pubkey_der) | |
| 1083 return Status::Error(); | |
| 1084 | |
| 1085 // Import the DER-encoded public key to create an RSA SECKEYPublicKey. | |
| 1086 crypto::ScopedSECKEYPublicKey pubkey( | |
| 1087 SECKEY_ImportDERPublicKey(pubkey_der.get(), CKK_RSA)); | |
| 1088 if (!pubkey) | |
| 1089 return Status::Error(); | |
| 1090 | |
| 1091 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 1092 if (!CreatePublicKeyAlgorithm(algorithm, pubkey.get(), &key_algorithm)) | |
| 1093 return Status::ErrorUnexpected(); | |
| 1094 | |
| 1095 *key = blink::WebCryptoKey::create(new PublicKey(pubkey.Pass()), | |
| 1096 blink::WebCryptoKeyTypePublic, | |
| 1097 extractable, | |
| 1098 key_algorithm, | |
| 1099 usage_mask); | |
| 1100 return Status::Success(); | |
| 1101 } | |
| 1102 | |
| 1103 } // namespace platform | |
| 1104 | |
| 1105 } // namespace webcrypto | |
| 1106 | |
| 1107 } // namespace content | |
| OLD | NEW |