Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1103)

Side by Side Diff: content/renderer/webcrypto/webcrypto_impl_nss.cc

Issue 118623002: [webcrypto] Add raw symmetric key AES-KW wrap/unwrap for NSS. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase and add large data test Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | content/renderer/webcrypto/webcrypto_impl_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/webcrypto/webcrypto_impl.h" 5 #include "content/renderer/webcrypto/webcrypto_impl.h"
6 6
7 #include <cryptohi.h> 7 #include <cryptohi.h>
8 #include <pk11pub.h> 8 #include <pk11pub.h>
9 #include <sechash.h> 9 #include <sechash.h>
10 10
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after
365 if (result != SECSuccess) 365 if (result != SECSuccess)
366 return false; 366 return false;
367 367
368 // Unfortunately the buffer needs to be shrunk for decryption (see the NSS bug 368 // Unfortunately the buffer needs to be shrunk for decryption (see the NSS bug
369 // above). 369 // above).
370 webcrypto::ShrinkBuffer(buffer, output_len); 370 webcrypto::ShrinkBuffer(buffer, output_len);
371 371
372 return true; 372 return true;
373 } 373 }
374 374
375 // Constants for RFC 3394 AES Key Wrap / Unwrap.
376 const CK_MECHANISM_TYPE kAesKwMechanism = CKM_NSS_AES_KEY_WRAP;
Ryan Sleevi 2014/01/16 23:41:15 I don't see there being any benefit from adding th
padolph 2014/02/19 03:55:43 Done.
377 // The Default IV. See http://www.ietf.org/rfc/rfc3394.txt Section 2.2.3.1.
378 const unsigned char kAesIv[] = {0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6};
Ryan Sleevi 2014/01/16 23:41:15 You can make this a function-local const.
padolph 2014/02/19 03:55:43 This is used in two separate functions. I don't wa
379
380 // Performs RFC 3394 AES Key Wrap (encryption) of the input data.
381 bool AesKwEncrypt(
382 const blink::WebCryptoKey& key,
383 const unsigned char* data,
384 unsigned data_size,
385 blink::WebArrayBuffer* buffer) {
Ryan Sleevi 2014/01/16 23:41:15 From an API design perspective, having the AES-KW
386 DCHECK_EQ(blink::WebCryptoAlgorithmIdAesKw, key.algorithm().id());
387 DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type());
388 DCHECK(blink::WebCryptoKeyUsageWrapKey & key.usages());
389
390 // The data size must be at least 16 bytes and a multiple of 8 bytes.
391 if (data_size < 16 || data_size % 8)
392 return false;
393 DCHECK(data);
Ryan Sleevi 2014/01/16 23:41:15 Why not DCHECK on line 389?
padolph 2014/02/19 03:55:43 Eric prefers this pattern. You won't hit the check
394
395 SECItem iv_item = {siBuffer, const_cast<unsigned char*>(kAesIv),
396 arraysize(kAesIv)};
397 crypto::ScopedSECItem param_item(PK11_ParamFromIV(kAesKwMechanism, &iv_item));
398 DCHECK(param_item);
399
400 // Turn the data to be wrapped into a PK11SymKey in order to use the NSS
401 // PK11_WrapSymKey() API. Create the PK11SymKey by importing the data as a
402 // generic secret blob, since we can't be certain what it is at this point.
Ryan Sleevi 2014/01/16 23:41:15 Pronouns considered harmful: https://groups.google
padolph 2014/02/19 03:55:43 Done.
403 SECItem data_item = {siBuffer, const_cast<unsigned char*>(data), data_size};
404 crypto::ScopedPK11SymKey key_to_be_wrapped(
405 PK11_ImportSymKey(PK11_GetInternalSlot(),
406 CKK_GENERIC_SECRET,
407 PK11_OriginGenerated,
408 CKA_ENCRYPT,
409 &data_item,
410 0));
411 if (!key_to_be_wrapped)
412 return false;
413
414 // AES Key Wrap always adds 8 bytes to the input data size. RFC 3394 does not
415 // specify a maximum allowed data length, but since we are only wrapping keys,
416 // which are usually small, a reasonable max size is whatever will fit into an
417 // unsigned.
418 if (data_size > UINT_MAX - 8)
419 return false;
420 const unsigned int output_length = data_size + 8;
421
422 *buffer = blink::WebArrayBuffer::create(output_length, 1);
423 unsigned char* buffer_data = reinterpret_cast<unsigned char*>(buffer->data());
424 SECItem wrapped_key_item = {siBuffer, buffer_data, output_length};
425
426 SymKeyHandle* wrapping_key = reinterpret_cast<SymKeyHandle*>(key.handle());
427
428 if (SECSuccess != PK11_WrapSymKey(kAesKwMechanism,
429 param_item.get(),
430 wrapping_key->key(),
431 key_to_be_wrapped.get(),
432 &wrapped_key_item)) {
433 return false;
434 }
435 if (output_length != wrapped_key_item.len) {
436 buffer->reset();
437 return false;
438 }
439
440 return true;
441 }
442
443 // Performs RFC 3394 AES Key Unwrap (decryption) of the input data.
444 bool AesKwDecrypt(
445 const blink::WebCryptoKey& key,
446 const unsigned char* data,
447 unsigned data_size,
448 blink::WebArrayBuffer* buffer) {
449 DCHECK_EQ(blink::WebCryptoAlgorithmIdAesKw, key.algorithm().id());
450 DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type());
451 DCHECK(blink::WebCryptoKeyUsageUnwrapKey & key.usages());
452
453 // The ciphertext data size must be at least 24 bytes and a multiple of
454 // 8 bytes.
455 if (data_size < 24 || data_size % 8 != 0)
456 return false;
457 DCHECK(data);
458
459 SymKeyHandle* wrapping_key = reinterpret_cast<SymKeyHandle*>(key.handle());
460
461 SECItem iv_item = {siBuffer, const_cast<unsigned char*>(kAesIv),
462 arraysize(kAesIv)};
463 crypto::ScopedSECItem param_item(PK11_ParamFromIV(kAesKwMechanism, &iv_item));
464 DCHECK(param_item);
465
466 SECItem cipher_text = {siBuffer, const_cast<unsigned char*>(data), data_size};
467
468 // The plaintext length is always 64 bits less than the data size.
469 const unsigned int plaintext_length = data_size - 8;
470
471 crypto::ScopedPK11SymKey unwrapped_key(PK11_UnwrapSymKey(
472 wrapping_key->key(),
473 kAesKwMechanism,
474 param_item.get(),
475 &cipher_text,
476 CKK_GENERIC_SECRET, // Import the key material without knowing its kind.
477 CKA_ENCRYPT, // A safe value since all we're doing is exporting the key.
478 plaintext_length));
479 if (!unwrapped_key)
480 return false;
481
482 if (PK11_ExtractKeyValue(unwrapped_key.get()) != SECSuccess)
483 return false;
484
485 const SECItem* key_data = PK11_GetKeyData(unwrapped_key.get());
486 if (!key_data || key_data->len != plaintext_length)
487 return false;
488
489 *buffer = webcrypto::CreateArrayBuffer(key_data->data, key_data->len);
490
491 return true;
492 }
493
375 CK_MECHANISM_TYPE WebCryptoAlgorithmToGenMechanism( 494 CK_MECHANISM_TYPE WebCryptoAlgorithmToGenMechanism(
376 const blink::WebCryptoAlgorithm& algorithm) { 495 const blink::WebCryptoAlgorithm& algorithm) {
377 switch (algorithm.id()) { 496 switch (algorithm.id()) {
378 case blink::WebCryptoAlgorithmIdAesCbc: 497 case blink::WebCryptoAlgorithmIdAesCbc:
379 case blink::WebCryptoAlgorithmIdAesGcm: 498 case blink::WebCryptoAlgorithmIdAesGcm:
380 case blink::WebCryptoAlgorithmIdAesKw: 499 case blink::WebCryptoAlgorithmIdAesKw:
381 return CKM_AES_KEY_GEN; 500 return CKM_AES_KEY_GEN;
382 case blink::WebCryptoAlgorithmIdHmac: 501 case blink::WebCryptoAlgorithmIdHmac:
383 return WebCryptoHashToHMACMechanism(algorithm.hmacKeyParams()->hash()); 502 return WebCryptoHashToHMACMechanism(algorithm.hmacKeyParams()->hash());
384 default: 503 default:
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after
703 DCHECK(key.handle()); 822 DCHECK(key.handle());
704 DCHECK(buffer); 823 DCHECK(buffer);
705 824
706 // TODO(eroman): Use a switch() statement. 825 // TODO(eroman): Use a switch() statement.
707 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { 826 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) {
708 return AesCbcEncryptDecrypt( 827 return AesCbcEncryptDecrypt(
709 CKA_ENCRYPT, algorithm, key, data, data_size, buffer); 828 CKA_ENCRYPT, algorithm, key, data, data_size, buffer);
710 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesGcm) { 829 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesGcm) {
711 return AesGcmEncryptDecrypt( 830 return AesGcmEncryptDecrypt(
712 true, algorithm, key, data, data_size, buffer); 831 true, algorithm, key, data, data_size, buffer);
832 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesKw) {
833 return AesKwEncrypt(key, data, data_size, buffer);
713 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) { 834 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) {
714 835
715 // RSAES encryption does not support empty input 836 // RSAES encryption does not support empty input
716 if (!data_size) 837 if (!data_size)
717 return false; 838 return false;
718 DCHECK(data); 839 DCHECK(data);
719 840
720 if (key.type() != blink::WebCryptoKeyTypePublic) 841 if (key.type() != blink::WebCryptoKeyTypePublic)
721 return false; 842 return false;
722 843
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
759 DCHECK(key.handle()); 880 DCHECK(key.handle());
760 DCHECK(buffer); 881 DCHECK(buffer);
761 882
762 // TODO(eroman): Use a switch() statement. 883 // TODO(eroman): Use a switch() statement.
763 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { 884 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) {
764 return AesCbcEncryptDecrypt( 885 return AesCbcEncryptDecrypt(
765 CKA_DECRYPT, algorithm, key, data, data_size, buffer); 886 CKA_DECRYPT, algorithm, key, data, data_size, buffer);
766 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesGcm) { 887 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesGcm) {
767 return AesGcmEncryptDecrypt( 888 return AesGcmEncryptDecrypt(
768 false, algorithm, key, data, data_size, buffer); 889 false, algorithm, key, data, data_size, buffer);
890 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesKw) {
891 return AesKwDecrypt(key, data, data_size, buffer);
769 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) { 892 } else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) {
770 893
771 // RSAES decryption does not support empty input 894 // RSAES decryption does not support empty input
772 if (!data_size) 895 if (!data_size)
773 return false; 896 return false;
774 DCHECK(data); 897 DCHECK(data);
775 898
776 if (key.type() != blink::WebCryptoKeyTypePrivate) 899 if (key.type() != blink::WebCryptoKeyTypePrivate)
777 return false; 900 return false;
778 901
(...skipping 422 matching lines...) Expand 10 before | Expand all | Expand 10 after
1201 1324
1202 *key = blink::WebCryptoKey::create(new PublicKeyHandle(pubkey.Pass()), 1325 *key = blink::WebCryptoKey::create(new PublicKeyHandle(pubkey.Pass()),
1203 blink::WebCryptoKeyTypePublic, 1326 blink::WebCryptoKeyTypePublic,
1204 extractable, 1327 extractable,
1205 algorithm, 1328 algorithm,
1206 usage_mask); 1329 usage_mask);
1207 return true; 1330 return true;
1208 } 1331 }
1209 1332
1210 } // namespace content 1333 } // namespace content
OLDNEW
« no previous file with comments | « no previous file | content/renderer/webcrypto/webcrypto_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698