IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / renderer / webcrypto / webcrypto_impl_nss.cc
blob32660f5934b155f62e62d8518646b2bc98887f7b
1 // Copyright 2013 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.
5 #include "content/renderer/webcrypto/webcrypto_impl.h"
7 #include <cryptohi.h>
8 #include <pk11pub.h>
9 #include <sechash.h>
11 #include <vector>
13 #include "base/logging.h"
14 #include "content/renderer/webcrypto/webcrypto_util.h"
15 #include "crypto/nss_util.h"
16 #include "crypto/scoped_nss_types.h"
17 #include "crypto/secure_util.h"
18 #include "third_party/WebKit/public/platform/WebArrayBuffer.h"
19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
20 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
22 namespace content {
24 namespace {
26 class SymKeyHandle : public blink::WebCryptoKeyHandle {
27 public:
28 explicit SymKeyHandle(crypto::ScopedPK11SymKey key) : key_(key.Pass()) {}
30 PK11SymKey* key() { return key_.get(); }
32 private:
33 crypto::ScopedPK11SymKey key_;
35 DISALLOW_COPY_AND_ASSIGN(SymKeyHandle);
38 class PublicKeyHandle : public blink::WebCryptoKeyHandle {
39 public:
40 explicit PublicKeyHandle(crypto::ScopedSECKEYPublicKey key)
41 : key_(key.Pass()) {}
43 SECKEYPublicKey* key() { return key_.get(); }
45 private:
46 crypto::ScopedSECKEYPublicKey key_;
48 DISALLOW_COPY_AND_ASSIGN(PublicKeyHandle);
51 class PrivateKeyHandle : public blink::WebCryptoKeyHandle {
52 public:
53 explicit PrivateKeyHandle(crypto::ScopedSECKEYPrivateKey key)
54 : key_(key.Pass()) {}
56 SECKEYPrivateKey* key() { return key_.get(); }
58 private:
59 crypto::ScopedSECKEYPrivateKey key_;
61 DISALLOW_COPY_AND_ASSIGN(PrivateKeyHandle);
64 HASH_HashType WebCryptoAlgorithmToNSSHashType(
65 const blink::WebCryptoAlgorithm& algorithm) {
66 switch (algorithm.id()) {
67 case blink::WebCryptoAlgorithmIdSha1:
68 return HASH_AlgSHA1;
69 case blink::WebCryptoAlgorithmIdSha224:
70 return HASH_AlgSHA224;
71 case blink::WebCryptoAlgorithmIdSha256:
72 return HASH_AlgSHA256;
73 case blink::WebCryptoAlgorithmIdSha384:
74 return HASH_AlgSHA384;
75 case blink::WebCryptoAlgorithmIdSha512:
76 return HASH_AlgSHA512;
77 default:
78 // Not a digest algorithm.
79 return HASH_AlgNULL;
83 CK_MECHANISM_TYPE WebCryptoHashToHMACMechanism(
84 const blink::WebCryptoAlgorithm& algorithm) {
85 switch (algorithm.id()) {
86 case blink::WebCryptoAlgorithmIdSha1:
87 return CKM_SHA_1_HMAC;
88 case blink::WebCryptoAlgorithmIdSha224:
89 return CKM_SHA224_HMAC;
90 case blink::WebCryptoAlgorithmIdSha256:
91 return CKM_SHA256_HMAC;
92 case blink::WebCryptoAlgorithmIdSha384:
93 return CKM_SHA384_HMAC;
94 case blink::WebCryptoAlgorithmIdSha512:
95 return CKM_SHA512_HMAC;
96 default:
97 // Not a supported algorithm.
98 return CKM_INVALID_MECHANISM;
102 bool AesCbcEncryptDecrypt(
103 CK_ATTRIBUTE_TYPE operation,
104 const blink::WebCryptoAlgorithm& algorithm,
105 const blink::WebCryptoKey& key,
106 const unsigned char* data,
107 unsigned data_size,
108 blink::WebArrayBuffer* buffer) {
109 DCHECK_EQ(blink::WebCryptoAlgorithmIdAesCbc, algorithm.id());
110 DCHECK_EQ(algorithm.id(), key.algorithm().id());
111 DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type());
112 DCHECK(operation == CKA_ENCRYPT || operation == CKA_DECRYPT);
114 SymKeyHandle* sym_key = reinterpret_cast<SymKeyHandle*>(key.handle());
116 const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams();
117 if (params->iv().size() != AES_BLOCK_SIZE)
118 return false;
120 SECItem iv_item;
121 iv_item.type = siBuffer;
122 iv_item.data = const_cast<unsigned char*>(params->iv().data());
123 iv_item.len = params->iv().size();
125 crypto::ScopedSECItem param(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item));
126 if (!param)
127 return false;
129 crypto::ScopedPK11Context context(PK11_CreateContextBySymKey(
130 CKM_AES_CBC_PAD, operation, sym_key->key(), param.get()));
132 if (!context.get())
133 return false;
135 // Oddly PK11_CipherOp takes input and output lengths as "int" rather than
136 // "unsigned". Do some checks now to avoid integer overflowing.
137 if (data_size >= INT_MAX - AES_BLOCK_SIZE) {
138 // TODO(eroman): Handle this by chunking the input fed into NSS. Right now
139 // it doesn't make much difference since the one-shot API would end up
140 // blowing out the memory and crashing anyway. However a newer version of
141 // the spec allows for a sequence<CryptoData> so this will be relevant.
142 return false;
145 // PK11_CipherOp does an invalid memory access when given empty decryption
146 // input, or input which is not a multiple of the block size. See also
147 // https://bugzilla.mozilla.com/show_bug.cgi?id=921687.
148 if (operation == CKA_DECRYPT &&
149 (data_size == 0 || (data_size % AES_BLOCK_SIZE != 0))) {
150 return false;
153 // TODO(eroman): Refine the output buffer size. It can be computed exactly for
154 // encryption, and can be smaller for decryption.
155 unsigned output_max_len = data_size + AES_BLOCK_SIZE;
156 CHECK_GT(output_max_len, data_size);
158 *buffer = blink::WebArrayBuffer::create(output_max_len, 1);
160 unsigned char* buffer_data = reinterpret_cast<unsigned char*>(buffer->data());
162 int output_len;
163 if (SECSuccess != PK11_CipherOp(context.get(),
164 buffer_data,
165 &output_len,
166 buffer->byteLength(),
167 data,
168 data_size)) {
169 return false;
172 unsigned int final_output_chunk_len;
173 if (SECSuccess != PK11_DigestFinal(context.get(),
174 buffer_data + output_len,
175 &final_output_chunk_len,
176 output_max_len - output_len)) {
177 return false;
180 webcrypto::ShrinkBuffer(buffer, final_output_chunk_len + output_len);
181 return true;
184 CK_MECHANISM_TYPE WebCryptoAlgorithmToGenMechanism(
185 const blink::WebCryptoAlgorithm& algorithm) {
186 switch (algorithm.id()) {
187 case blink::WebCryptoAlgorithmIdAesCbc:
188 case blink::WebCryptoAlgorithmIdAesGcm:
189 case blink::WebCryptoAlgorithmIdAesKw:
190 return CKM_AES_KEY_GEN;
191 case blink::WebCryptoAlgorithmIdHmac:
192 return WebCryptoHashToHMACMechanism(algorithm.hmacKeyParams()->hash());
193 default:
194 return CKM_INVALID_MECHANISM;
198 // Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros,
199 // to unsigned long.
200 bool BigIntegerToLong(const uint8* data,
201 unsigned data_size,
202 unsigned long* result) {
203 // TODO(padolph): Is it correct to say that empty data is an error, or does it
204 // mean value 0? See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23655
205 if (data_size == 0)
206 return false;
208 *result = 0;
209 for (size_t i = 0; i < data_size; ++i) {
210 size_t reverse_i = data_size - i - 1;
212 if (reverse_i >= sizeof(unsigned long) && data[i])
213 return false; // Too large for a long.
215 *result |= data[i] << 8 * reverse_i;
217 return true;
220 bool IsAlgorithmRsa(const blink::WebCryptoAlgorithm& algorithm) {
221 return algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 ||
222 algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep ||
223 algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5;
226 bool ImportKeyInternalRaw(
227 const unsigned char* key_data,
228 unsigned key_data_size,
229 const blink::WebCryptoAlgorithm& algorithm,
230 bool extractable,
231 blink::WebCryptoKeyUsageMask usage_mask,
232 blink::WebCryptoKey* key) {
234 DCHECK(!algorithm.isNull());
236 blink::WebCryptoKeyType type;
237 switch (algorithm.id()) {
238 case blink::WebCryptoAlgorithmIdHmac:
239 case blink::WebCryptoAlgorithmIdAesCbc:
240 case blink::WebCryptoAlgorithmIdAesKw:
241 type = blink::WebCryptoKeyTypeSecret;
242 break;
243 // TODO(bryaneyler): Support more key types.
244 default:
245 return false;
248 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM;
249 // Flags are verified at the Blink layer; here the flags are set to all
250 // possible operations for this key type.
251 CK_FLAGS flags = 0;
253 switch (algorithm.id()) {
254 case blink::WebCryptoAlgorithmIdHmac: {
255 const blink::WebCryptoHmacParams* params = algorithm.hmacParams();
256 if (!params) {
257 return false;
260 mechanism = WebCryptoHashToHMACMechanism(params->hash());
261 if (mechanism == CKM_INVALID_MECHANISM) {
262 return false;
265 flags |= CKF_SIGN | CKF_VERIFY;
267 break;
269 case blink::WebCryptoAlgorithmIdAesCbc: {
270 mechanism = CKM_AES_CBC;
271 flags |= CKF_ENCRYPT | CKF_DECRYPT;
272 break;
274 case blink::WebCryptoAlgorithmIdAesKw: {
275 mechanism = CKM_NSS_AES_KEY_WRAP;
276 flags |= CKF_WRAP | CKF_WRAP;
277 break;
279 default:
280 return false;
283 DCHECK_NE(CKM_INVALID_MECHANISM, mechanism);
284 DCHECK_NE(0ul, flags);
286 SECItem key_item = {
287 siBuffer,
288 const_cast<unsigned char*>(key_data),
289 key_data_size
292 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot());
293 crypto::ScopedPK11SymKey pk11_sym_key(
294 PK11_ImportSymKeyWithFlags(slot.get(),
295 mechanism,
296 PK11_OriginUnwrap,
297 CKA_FLAGS_ONLY,
298 &key_item,
299 flags,
300 false,
301 NULL));
302 if (!pk11_sym_key.get()) {
303 return false;
306 *key = blink::WebCryptoKey::create(new SymKeyHandle(pk11_sym_key.Pass()),
307 type, extractable, algorithm, usage_mask);
308 return true;
311 bool ExportKeyInternalRaw(
312 const blink::WebCryptoKey& key,
313 blink::WebArrayBuffer* buffer) {
315 DCHECK(key.handle());
316 DCHECK(buffer);
318 if (key.type() != blink::WebCryptoKeyTypeSecret || !key.extractable())
319 return false;
321 SymKeyHandle* sym_key = reinterpret_cast<SymKeyHandle*>(key.handle());
323 if (PK11_ExtractKeyValue(sym_key->key()) != SECSuccess)
324 return false;
326 const SECItem* key_data = PK11_GetKeyData(sym_key->key());
327 if (!key_data)
328 return false;
330 *buffer = webcrypto::CreateArrayBuffer(key_data->data, key_data->len);
332 return true;
335 typedef scoped_ptr<CERTSubjectPublicKeyInfo,
336 crypto::NSSDestroyer<CERTSubjectPublicKeyInfo,
337 SECKEY_DestroySubjectPublicKeyInfo> >
338 ScopedCERTSubjectPublicKeyInfo;
340 // Validates an NSS KeyType against a WebCrypto algorithm. Some NSS KeyTypes
341 // contain enough information to fabricate a Web Crypto algorithm, which is
342 // returned if the input algorithm isNull(). This function indicates failure by
343 // returning a Null algorithm.
344 blink::WebCryptoAlgorithm ResolveNssKeyTypeWithInputAlgorithm(
345 KeyType key_type,
346 const blink::WebCryptoAlgorithm& algorithm_or_null) {
347 switch (key_type) {
348 case rsaKey:
349 // NSS's rsaKey KeyType maps to keys with SEC_OID_PKCS1_RSA_ENCRYPTION and
350 // according to RFCs 4055/5756 this can be used for both encryption and
351 // signatures. However, this is not specific enough to build a compatible
352 // Web Crypto algorithm, since in Web Crypto, RSA encryption and signature
353 // algorithms are distinct. So if the input algorithm isNull() here, we
354 // have to fail.
355 if (!algorithm_or_null.isNull() && IsAlgorithmRsa(algorithm_or_null))
356 return algorithm_or_null;
357 break;
358 case dsaKey:
359 case ecKey:
360 case rsaPssKey:
361 case rsaOaepKey:
362 // TODO(padolph): Handle other key types.
363 break;
364 default:
365 break;
367 return blink::WebCryptoAlgorithm::createNull();
370 bool ImportKeyInternalSpki(
371 const unsigned char* key_data,
372 unsigned key_data_size,
373 const blink::WebCryptoAlgorithm& algorithm_or_null,
374 bool extractable,
375 blink::WebCryptoKeyUsageMask usage_mask,
376 blink::WebCryptoKey* key) {
378 DCHECK(key);
380 if (!key_data_size)
381 return false;
382 DCHECK(key_data);
384 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject
385 // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo.
386 SECItem spki_item = {siBuffer, const_cast<uint8*>(key_data), key_data_size};
387 const ScopedCERTSubjectPublicKeyInfo spki(
388 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
389 if (!spki)
390 return false;
392 crypto::ScopedSECKEYPublicKey sec_public_key(
393 SECKEY_ExtractPublicKey(spki.get()));
394 if (!sec_public_key)
395 return false;
397 const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get());
398 blink::WebCryptoAlgorithm algorithm =
399 ResolveNssKeyTypeWithInputAlgorithm(sec_key_type, algorithm_or_null);
400 if (algorithm.isNull())
401 return false;
403 *key = blink::WebCryptoKey::create(
404 new PublicKeyHandle(sec_public_key.Pass()),
405 blink::WebCryptoKeyTypePublic,
406 extractable,
407 algorithm,
408 usage_mask);
410 return true;
413 bool ExportKeyInternalSpki(
414 const blink::WebCryptoKey& key,
415 blink::WebArrayBuffer* buffer) {
417 DCHECK(key.handle());
418 DCHECK(buffer);
420 if (key.type() != blink::WebCryptoKeyTypePublic || !key.extractable())
421 return false;
423 PublicKeyHandle* const pub_key =
424 reinterpret_cast<PublicKeyHandle*>(key.handle());
426 const crypto::ScopedSECItem spki_der(
427 SECKEY_EncodeDERSubjectPublicKeyInfo(pub_key->key()));
428 if (!spki_der)
429 return false;
431 DCHECK(spki_der->data);
432 DCHECK(spki_der->len);
434 *buffer = webcrypto::CreateArrayBuffer(spki_der->data, spki_der->len);
436 return true;
439 bool ImportKeyInternalPkcs8(
440 const unsigned char* key_data,
441 unsigned key_data_size,
442 const blink::WebCryptoAlgorithm& algorithm_or_null,
443 bool extractable,
444 blink::WebCryptoKeyUsageMask usage_mask,
445 blink::WebCryptoKey* key) {
447 DCHECK(key);
449 if (!key_data_size)
450 return false;
451 DCHECK(key_data);
453 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8
454 // private key info object.
455 SECItem pki_der = {siBuffer, const_cast<uint8*>(key_data), key_data_size};
457 SECKEYPrivateKey* seckey_private_key = NULL;
458 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot());
459 if (PK11_ImportDERPrivateKeyInfoAndReturnKey(
460 slot.get(),
461 &pki_der,
462 NULL, // nickname
463 NULL, // publicValue
464 false, // isPerm
465 false, // isPrivate
466 KU_ALL, // usage
467 &seckey_private_key,
468 NULL) != SECSuccess) {
469 return false;
471 DCHECK(seckey_private_key);
472 crypto::ScopedSECKEYPrivateKey private_key(seckey_private_key);
474 const KeyType sec_key_type = SECKEY_GetPrivateKeyType(private_key.get());
475 blink::WebCryptoAlgorithm algorithm =
476 ResolveNssKeyTypeWithInputAlgorithm(sec_key_type, algorithm_or_null);
477 if (algorithm.isNull())
478 return false;
480 *key = blink::WebCryptoKey::create(
481 new PrivateKeyHandle(private_key.Pass()),
482 blink::WebCryptoKeyTypePrivate,
483 extractable,
484 algorithm,
485 usage_mask);
487 return true;
490 } // namespace
492 void WebCryptoImpl::Init() {
493 crypto::EnsureNSSInit();
496 bool WebCryptoImpl::EncryptInternal(
497 const blink::WebCryptoAlgorithm& algorithm,
498 const blink::WebCryptoKey& key,
499 const unsigned char* data,
500 unsigned data_size,
501 blink::WebArrayBuffer* buffer) {
503 DCHECK_EQ(algorithm.id(), key.algorithm().id());
504 DCHECK(key.handle());
505 DCHECK(buffer);
507 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) {
508 return AesCbcEncryptDecrypt(
509 CKA_ENCRYPT, algorithm, key, data, data_size, buffer);
510 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) {
512 // RSAES encryption does not support empty input
513 if (!data_size)
514 return false;
515 DCHECK(data);
517 if (key.type() != blink::WebCryptoKeyTypePublic)
518 return false;
520 PublicKeyHandle* const public_key =
521 reinterpret_cast<PublicKeyHandle*>(key.handle());
523 const unsigned encrypted_length_bytes =
524 SECKEY_PublicKeyStrength(public_key->key());
526 // RSAES can operate on messages up to a length of k - 11, where k is the
527 // octet length of the RSA modulus.
528 if (encrypted_length_bytes < 11 || encrypted_length_bytes - 11 < data_size)
529 return false;
531 *buffer = blink::WebArrayBuffer::create(encrypted_length_bytes, 1);
532 unsigned char* const buffer_data =
533 reinterpret_cast<unsigned char*>(buffer->data());
535 if (PK11_PubEncryptPKCS1(public_key->key(),
536 buffer_data,
537 const_cast<unsigned char*>(data),
538 data_size,
539 NULL) != SECSuccess) {
540 return false;
542 return true;
545 return false;
548 bool WebCryptoImpl::DecryptInternal(
549 const blink::WebCryptoAlgorithm& algorithm,
550 const blink::WebCryptoKey& key,
551 const unsigned char* data,
552 unsigned data_size,
553 blink::WebArrayBuffer* buffer) {
555 DCHECK_EQ(algorithm.id(), key.algorithm().id());
556 DCHECK(key.handle());
557 DCHECK(buffer);
559 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) {
560 return AesCbcEncryptDecrypt(
561 CKA_DECRYPT, algorithm, key, data, data_size, buffer);
562 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) {
564 // RSAES decryption does not support empty input
565 if (!data_size)
566 return false;
567 DCHECK(data);
569 if (key.type() != blink::WebCryptoKeyTypePrivate)
570 return false;
572 PrivateKeyHandle* const private_key =
573 reinterpret_cast<PrivateKeyHandle*>(key.handle());
575 const int modulus_length_bytes =
576 PK11_GetPrivateModulusLen(private_key->key());
577 if (modulus_length_bytes <= 0)
578 return false;
579 const unsigned max_output_length_bytes = modulus_length_bytes;
581 *buffer = blink::WebArrayBuffer::create(max_output_length_bytes, 1);
582 unsigned char* const buffer_data =
583 reinterpret_cast<unsigned char*>(buffer->data());
585 unsigned output_length_bytes = 0;
586 if (PK11_PrivDecryptPKCS1(private_key->key(),
587 buffer_data,
588 &output_length_bytes,
589 max_output_length_bytes,
590 const_cast<unsigned char*>(data),
591 data_size) != SECSuccess) {
592 return false;
594 DCHECK_LE(output_length_bytes, max_output_length_bytes);
595 webcrypto::ShrinkBuffer(buffer, output_length_bytes);
596 return true;
599 return false;
602 bool WebCryptoImpl::DigestInternal(
603 const blink::WebCryptoAlgorithm& algorithm,
604 const unsigned char* data,
605 unsigned data_size,
606 blink::WebArrayBuffer* buffer) {
607 HASH_HashType hash_type = WebCryptoAlgorithmToNSSHashType(algorithm);
608 if (hash_type == HASH_AlgNULL) {
609 return false;
612 HASHContext* context = HASH_Create(hash_type);
613 if (!context) {
614 return false;
617 HASH_Begin(context);
619 HASH_Update(context, data, data_size);
621 unsigned hash_result_length = HASH_ResultLenContext(context);
622 DCHECK_LE(hash_result_length, static_cast<size_t>(HASH_LENGTH_MAX));
624 *buffer = blink::WebArrayBuffer::create(hash_result_length, 1);
626 unsigned char* digest = reinterpret_cast<unsigned char*>(buffer->data());
628 unsigned result_length = 0;
629 HASH_End(context, digest, &result_length, hash_result_length);
631 HASH_Destroy(context);
633 return result_length == hash_result_length;
636 bool WebCryptoImpl::GenerateKeyInternal(
637 const blink::WebCryptoAlgorithm& algorithm,
638 bool extractable,
639 blink::WebCryptoKeyUsageMask usage_mask,
640 blink::WebCryptoKey* key) {
642 CK_MECHANISM_TYPE mech = WebCryptoAlgorithmToGenMechanism(algorithm);
643 unsigned int keylen_bytes = 0;
644 blink::WebCryptoKeyType key_type = blink::WebCryptoKeyTypeSecret;
646 if (mech == CKM_INVALID_MECHANISM) {
647 return false;
650 switch (algorithm.id()) {
651 case blink::WebCryptoAlgorithmIdAesCbc:
652 case blink::WebCryptoAlgorithmIdAesGcm:
653 case blink::WebCryptoAlgorithmIdAesKw: {
654 const blink::WebCryptoAesKeyGenParams* params =
655 algorithm.aesKeyGenParams();
656 DCHECK(params);
657 // Ensure the key length is a multiple of 8 bits. Let NSS verify further
658 // algorithm-specific length restrictions.
659 if (params->lengthBits() % 8)
660 return false;
661 keylen_bytes = params->lengthBits() / 8;
662 key_type = blink::WebCryptoKeyTypeSecret;
663 break;
665 case blink::WebCryptoAlgorithmIdHmac: {
666 const blink::WebCryptoHmacKeyParams* params = algorithm.hmacKeyParams();
667 DCHECK(params);
668 if (params->hasLengthBytes()) {
669 keylen_bytes = params->optionalLengthBytes();
670 } else {
671 keylen_bytes = webcrypto::ShaBlockSizeBytes(params->hash().id());
674 key_type = blink::WebCryptoKeyTypeSecret;
675 break;
678 default: {
679 return false;
683 if (keylen_bytes == 0) {
684 return false;
687 crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot());
688 if (!slot) {
689 return false;
692 crypto::ScopedPK11SymKey pk11_key(
693 PK11_KeyGen(slot.get(), mech, NULL, keylen_bytes, NULL));
695 if (!pk11_key) {
696 return false;
699 *key = blink::WebCryptoKey::create(
700 new SymKeyHandle(pk11_key.Pass()),
701 key_type, extractable, algorithm, usage_mask);
702 return true;
705 bool WebCryptoImpl::GenerateKeyPairInternal(
706 const blink::WebCryptoAlgorithm& algorithm,
707 bool extractable,
708 blink::WebCryptoKeyUsageMask usage_mask,
709 blink::WebCryptoKey* public_key,
710 blink::WebCryptoKey* private_key) {
712 // TODO(padolph): Handle other asymmetric algorithm key generation.
713 switch (algorithm.id()) {
714 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
715 case blink::WebCryptoAlgorithmIdRsaOaep:
716 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: {
717 const blink::WebCryptoRsaKeyGenParams* const params =
718 algorithm.rsaKeyGenParams();
719 DCHECK(params);
721 crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot());
722 unsigned long public_exponent;
723 if (!slot || !params->modulusLengthBits() ||
724 !BigIntegerToLong(params->publicExponent().data(),
725 params->publicExponent().size(),
726 &public_exponent) ||
727 !public_exponent) {
728 return false;
731 PK11RSAGenParams rsa_gen_params;
732 rsa_gen_params.keySizeInBits = params->modulusLengthBits();
733 rsa_gen_params.pe = public_exponent;
735 // Flags are verified at the Blink layer; here the flags are set to all
736 // possible operations for the given key type.
737 CK_FLAGS operation_flags;
738 switch (algorithm.id()) {
739 case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
740 case blink::WebCryptoAlgorithmIdRsaOaep:
741 operation_flags = CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP;
742 break;
743 case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
744 operation_flags = CKF_SIGN | CKF_VERIFY;
745 break;
746 default:
747 NOTREACHED();
748 return false;
750 const CK_FLAGS operation_flags_mask = CKF_ENCRYPT | CKF_DECRYPT |
751 CKF_SIGN | CKF_VERIFY | CKF_WRAP |
752 CKF_UNWRAP;
753 const PK11AttrFlags attribute_flags = 0; // Default all PK11_ATTR_ flags.
755 // Note: NSS does not generate an sec_public_key if the call below fails,
756 // so there is no danger of a leaked sec_public_key.
757 SECKEYPublicKey* sec_public_key;
758 crypto::ScopedSECKEYPrivateKey scoped_sec_private_key(
759 PK11_GenerateKeyPairWithOpFlags(slot.get(),
760 CKM_RSA_PKCS_KEY_PAIR_GEN,
761 &rsa_gen_params,
762 &sec_public_key,
763 attribute_flags,
764 operation_flags,
765 operation_flags_mask,
766 NULL));
767 if (!private_key) {
768 return false;
771 *public_key = blink::WebCryptoKey::create(
772 new PublicKeyHandle(crypto::ScopedSECKEYPublicKey(sec_public_key)),
773 blink::WebCryptoKeyTypePublic,
774 true,
775 algorithm,
776 usage_mask);
777 *private_key = blink::WebCryptoKey::create(
778 new PrivateKeyHandle(scoped_sec_private_key.Pass()),
779 blink::WebCryptoKeyTypePrivate,
780 extractable,
781 algorithm,
782 usage_mask);
784 return true;
786 default:
787 return false;
791 bool WebCryptoImpl::ImportKeyInternal(
792 blink::WebCryptoKeyFormat format,
793 const unsigned char* key_data,
794 unsigned key_data_size,
795 const blink::WebCryptoAlgorithm& algorithm_or_null,
796 bool extractable,
797 blink::WebCryptoKeyUsageMask usage_mask,
798 blink::WebCryptoKey* key) {
800 switch (format) {
801 case blink::WebCryptoKeyFormatRaw:
802 // A 'raw'-formatted key import requires an input algorithm.
803 if (algorithm_or_null.isNull())
804 return false;
805 return ImportKeyInternalRaw(key_data,
806 key_data_size,
807 algorithm_or_null,
808 extractable,
809 usage_mask,
810 key);
811 case blink::WebCryptoKeyFormatSpki:
812 return ImportKeyInternalSpki(key_data,
813 key_data_size,
814 algorithm_or_null,
815 extractable,
816 usage_mask,
817 key);
818 case blink::WebCryptoKeyFormatPkcs8:
819 return ImportKeyInternalPkcs8(key_data,
820 key_data_size,
821 algorithm_or_null,
822 extractable,
823 usage_mask,
824 key);
825 default:
826 // NOTE: blink::WebCryptoKeyFormatJwk is handled one level above.
827 return false;
831 bool WebCryptoImpl::ExportKeyInternal(
832 blink::WebCryptoKeyFormat format,
833 const blink::WebCryptoKey& key,
834 blink::WebArrayBuffer* buffer) {
835 switch (format) {
836 case blink::WebCryptoKeyFormatRaw:
837 return ExportKeyInternalRaw(key, buffer);
838 case blink::WebCryptoKeyFormatSpki:
839 return ExportKeyInternalSpki(key, buffer);
840 case blink::WebCryptoKeyFormatPkcs8:
841 // TODO(padolph): Implement pkcs8 export
842 return false;
843 default:
844 return false;
848 bool WebCryptoImpl::SignInternal(
849 const blink::WebCryptoAlgorithm& algorithm,
850 const blink::WebCryptoKey& key,
851 const unsigned char* data,
852 unsigned data_size,
853 blink::WebArrayBuffer* buffer) {
854 blink::WebArrayBuffer result;
856 switch (algorithm.id()) {
857 case blink::WebCryptoAlgorithmIdHmac: {
858 const blink::WebCryptoHmacParams* params = algorithm.hmacParams();
859 if (!params) {
860 return false;
863 SymKeyHandle* sym_key = reinterpret_cast<SymKeyHandle*>(key.handle());
865 DCHECK_EQ(PK11_GetMechanism(sym_key->key()),
866 WebCryptoHashToHMACMechanism(params->hash()));
867 DCHECK_NE(0, key.usages() & blink::WebCryptoKeyUsageSign);
869 SECItem param_item = { siBuffer, NULL, 0 };
870 SECItem data_item = {
871 siBuffer,
872 const_cast<unsigned char*>(data),
873 data_size
875 // First call is to figure out the length.
876 SECItem signature_item = { siBuffer, NULL, 0 };
878 if (PK11_SignWithSymKey(sym_key->key(),
879 PK11_GetMechanism(sym_key->key()),
880 &param_item,
881 &signature_item,
882 &data_item) != SECSuccess) {
883 NOTREACHED();
884 return false;
887 DCHECK_NE(0u, signature_item.len);
889 result = blink::WebArrayBuffer::create(signature_item.len, 1);
890 signature_item.data = reinterpret_cast<unsigned char*>(result.data());
892 if (PK11_SignWithSymKey(sym_key->key(),
893 PK11_GetMechanism(sym_key->key()),
894 &param_item,
895 &signature_item,
896 &data_item) != SECSuccess) {
897 NOTREACHED();
898 return false;
901 DCHECK_EQ(result.byteLength(), signature_item.len);
903 break;
905 default:
906 return false;
909 *buffer = result;
910 return true;
913 bool WebCryptoImpl::VerifySignatureInternal(
914 const blink::WebCryptoAlgorithm& algorithm,
915 const blink::WebCryptoKey& key,
916 const unsigned char* signature,
917 unsigned signature_size,
918 const unsigned char* data,
919 unsigned data_size,
920 bool* signature_match) {
921 switch (algorithm.id()) {
922 case blink::WebCryptoAlgorithmIdHmac: {
923 blink::WebArrayBuffer result;
924 if (!SignInternal(algorithm, key, data, data_size, &result)) {
925 return false;
928 // Handling of truncated signatures is underspecified in the WebCrypto
929 // spec, so here we fail verification if a truncated signature is being
930 // verified.
931 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23097
932 *signature_match =
933 result.byteLength() == signature_size &&
934 crypto::SecureMemEqual(result.data(), signature, signature_size);
936 break;
938 default:
939 return false;
942 return true;
945 bool WebCryptoImpl::ImportRsaPublicKeyInternal(
946 const unsigned char* modulus_data,
947 unsigned modulus_size,
948 const unsigned char* exponent_data,
949 unsigned exponent_size,
950 const blink::WebCryptoAlgorithm& algorithm,
951 bool extractable,
952 blink::WebCryptoKeyUsageMask usage_mask,
953 blink::WebCryptoKey* key) {
955 if (!modulus_size || !exponent_size)
956 return false;
957 DCHECK(modulus_data);
958 DCHECK(exponent_data);
960 // NSS does not provide a way to create an RSA public key directly from the
961 // modulus and exponent values, but it can import an DER-encoded ASN.1 blob
962 // with these values and create the public key from that. The code below
963 // follows the recommendation described in
964 // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7
966 // Pack the input values into a struct compatible with NSS ASN.1 encoding, and
967 // set up an ASN.1 encoder template for it.
968 struct RsaPublicKeyData {
969 SECItem modulus;
970 SECItem exponent;
972 const RsaPublicKeyData pubkey_in = {
973 {siUnsignedInteger, const_cast<unsigned char*>(modulus_data),
974 modulus_size},
975 {siUnsignedInteger, const_cast<unsigned char*>(exponent_data),
976 exponent_size}};
977 const SEC_ASN1Template rsa_public_key_template[] = {
978 {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RsaPublicKeyData)},
979 {SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, modulus), },
980 {SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, exponent), },
981 {0, }};
983 // DER-encode the public key.
984 crypto::ScopedSECItem pubkey_der(SEC_ASN1EncodeItem(
985 NULL, NULL, &pubkey_in, rsa_public_key_template));
986 if (!pubkey_der)
987 return false;
989 // Import the DER-encoded public key to create an RSA SECKEYPublicKey.
990 crypto::ScopedSECKEYPublicKey pubkey(
991 SECKEY_ImportDERPublicKey(pubkey_der.get(), CKK_RSA));
992 if (!pubkey)
993 return false;
995 *key = blink::WebCryptoKey::create(new PublicKeyHandle(pubkey.Pass()),
996 blink::WebCryptoKeyTypePublic,
997 extractable,
998 algorithm,
999 usage_mask);
1000 return true;
1003 } // namespace content