Chromium Code Reviews| Index: content/renderer/webcrypto/webcrypto_impl_nss.cc |
| diff --git a/content/renderer/webcrypto/webcrypto_impl_nss.cc b/content/renderer/webcrypto/webcrypto_impl_nss.cc |
| index e70aab82e2a10ed7a681911494a97be3cca29480..bfaccab1f9ffdf331ddd1d086804ba396c6ddb15 100644 |
| --- a/content/renderer/webcrypto/webcrypto_impl_nss.cc |
| +++ b/content/renderer/webcrypto/webcrypto_impl_nss.cc |
| @@ -369,6 +369,133 @@ Status AesGcmEncryptDecrypt( |
| return Status::Success(); |
| } |
| +// The Default IV for AES-KW. See http://www.ietf.org/rfc/rfc3394.txt |
| +// Section 2.2.3.1. |
| +const unsigned char kAesIv[] = {0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}; |
| + |
| +// Performs RFC 3394 AES Key Wrap (encryption) of the input data. |
| +Status AesKwEncrypt( |
| + const blink::WebCryptoKey& key, |
| + const unsigned char* data, |
| + unsigned data_size, |
| + blink::WebArrayBuffer* buffer) { |
| + DCHECK_EQ(blink::WebCryptoAlgorithmIdAesKw, key.algorithm().id()); |
| + DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type()); |
| + DCHECK(blink::WebCryptoKeyUsageWrapKey & key.usages()); |
| + |
| + // The data size must be at least 16 bytes and a multiple of 8 bytes. |
| + // RFC 3394 does not specify a maximum allowed data length, but since only |
| + // keys are being wrapped in this application (which are small), a reasonable |
| + // max limit is whatever will fit into an unsigned. For the max size test, |
| + // note that AES Key Wrap always adds 8 bytes to the input data size. |
| + if (data_size < 16) |
| + return Status::ErrorDataTooSmall(); |
| + else if (data_size > UINT_MAX - 8) |
| + return Status::ErrorDataTooLarge(); |
| + if (data_size % 8) |
| + return Status::ErrorInvalidAesKwDataLength(); |
| + DCHECK(data); |
| + |
| + SECItem iv_item = {siBuffer, const_cast<unsigned char*>(kAesIv), |
|
eroman
2014/02/26 23:40:22
there is a helper for this once you rebase
padolph
2014/02/28 23:28:40
Done.
|
| + arraysize(kAesIv)}; |
| + crypto::ScopedSECItem param_item(PK11_ParamFromIV( |
| + CKM_NSS_AES_KEY_WRAP, |
| + &iv_item)); |
| + DCHECK(param_item); |
|
eroman
2014/02/26 23:40:22
Not sure if this can be reached, perhaps make it a
padolph
2014/02/28 23:28:40
PK11_ParamFromIV can return NULL in the case of an
|
| + |
| + // Turn the data to be wrapped into a PK11SymKey in order to use the NSS |
|
eroman
2014/02/26 23:40:22
I chatted with Sleevi and he suggested just starti
padolph
2014/02/28 23:28:40
Ok, this is in line with my earlier question to Ry
|
| + // PK11_WrapSymKey() API. The data to be wrapped can be anything, so the best |
| + // choice when creating the PK11SymKey is to import the data as a generic |
| + // secret blob. |
| + SECItem data_item = {siBuffer, const_cast<unsigned char*>(data), data_size}; |
|
eroman
2014/02/26 23:40:22
there is a helper for this once you rebase
padolph
2014/02/28 23:28:40
Done.
|
| + crypto::ScopedPK11SymKey key_to_be_wrapped( |
| + PK11_ImportSymKey(PK11_GetInternalSlot(), |
| + CKK_GENERIC_SECRET, |
| + PK11_OriginGenerated, |
| + CKA_ENCRYPT, |
| + &data_item, |
| + 0)); |
| + if (!key_to_be_wrapped) |
| + return Status::Error(); |
| + |
| + const unsigned int output_length = data_size + 8; |
| + |
| + *buffer = blink::WebArrayBuffer::create(output_length, 1); |
| + unsigned char* buffer_data = reinterpret_cast<unsigned char*>(buffer->data()); |
| + SECItem wrapped_key_item = {siBuffer, buffer_data, output_length}; |
|
eroman
2014/02/26 23:40:22
there is a helper for this once you rebase
padolph
2014/02/28 23:28:40
Done.
|
| + |
| + SymKeyHandle* wrapping_key = reinterpret_cast<SymKeyHandle*>(key.handle()); |
|
eroman
2014/02/26 23:40:22
This has changed from my recent refactors. Instead
padolph
2014/02/28 23:28:40
Done.
|
| + |
| + if (SECSuccess != PK11_WrapSymKey(CKM_NSS_AES_KEY_WRAP, |
| + param_item.get(), |
| + wrapping_key->key(), |
| + key_to_be_wrapped.get(), |
| + &wrapped_key_item)) { |
| + return Status::Error(); |
| + } |
| + if (output_length != wrapped_key_item.len) { |
| + buffer->reset(); |
|
eroman
2014/02/26 23:40:22
I wouldn't worry about calling reset in the error
padolph
2014/02/28 23:28:40
Done.
|
| + return Status::ErrorUnexpected(); |
| + } |
| + |
| + return Status::Success(); |
| +} |
| + |
| +// Performs RFC 3394 AES Key Unwrap (decryption) of the input data. |
| +Status AesKwDecrypt( |
| + const blink::WebCryptoKey& key, |
| + const unsigned char* data, |
| + unsigned data_size, |
| + blink::WebArrayBuffer* buffer) { |
| + DCHECK_EQ(blink::WebCryptoAlgorithmIdAesKw, key.algorithm().id()); |
| + DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type()); |
| + DCHECK(blink::WebCryptoKeyUsageUnwrapKey & key.usages()); |
| + |
| + // The ciphertext data size must be at least 24 bytes and a multiple of |
| + // 8 bytes. |
| + if (data_size < 24) |
| + return Status::ErrorDataTooSmall(); |
| + else if (data_size % 8 != 0) |
|
eroman
2014/02/26 23:40:22
nit: no "else" after a return. Also above you omit
padolph
2014/02/28 23:28:40
Done.
|
| + return Status::ErrorInvalidAesKwDataLength(); |
| + DCHECK(data); |
| + |
| + SymKeyHandle* wrapping_key = reinterpret_cast<SymKeyHandle*>(key.handle()); |
|
eroman
2014/02/26 23:40:22
ToPlatformSymKey() after you rebase
padolph
2014/02/28 23:28:40
Done.
|
| + |
| + SECItem iv_item = {siBuffer, const_cast<unsigned char*>(kAesIv), |
|
eroman
2014/02/26 23:40:22
there is a helper for this once you rebase
padolph
2014/02/28 23:28:40
Done.
|
| + arraysize(kAesIv)}; |
| + crypto::ScopedSECItem param_item(PK11_ParamFromIV( |
| + CKM_NSS_AES_KEY_WRAP, |
| + &iv_item)); |
| + DCHECK(param_item); |
| + |
| + SECItem cipher_text = {siBuffer, const_cast<unsigned char*>(data), data_size}; |
| + |
| + // The plaintext length is always 64 bits less than the data size. |
| + const unsigned int plaintext_length = data_size - 8; |
| + |
| + crypto::ScopedPK11SymKey unwrapped_key(PK11_UnwrapSymKey( |
| + wrapping_key->key(), |
| + CKM_NSS_AES_KEY_WRAP, |
| + param_item.get(), |
| + &cipher_text, |
| + CKK_GENERIC_SECRET, // Import the key material without knowing its kind. |
| + CKA_ENCRYPT, // This is a safe value for a key export operation. |
| + plaintext_length)); |
| + if (!unwrapped_key) |
| + return Status::Error(); |
| + |
| + if (PK11_ExtractKeyValue(unwrapped_key.get()) != SECSuccess) |
|
eroman
2014/02/26 23:40:22
Per earlier comment, let's restrict this to just s
padolph
2014/02/28 23:28:40
This call is required even on a symkey. Otherwise
|
| + return Status::Error(); |
| + |
| + const SECItem* key_data = PK11_GetKeyData(unwrapped_key.get()); |
| + if (!key_data || key_data->len != plaintext_length) |
| + return Status::Error(); |
| + |
| + *buffer = webcrypto::CreateArrayBuffer(key_data->data, key_data->len); |
| + |
| + return Status::Success(); |
| +} |
| + |
| CK_MECHANISM_TYPE WebCryptoAlgorithmToGenMechanism( |
| const blink::WebCryptoAlgorithm& algorithm) { |
| switch (algorithm.id()) { |
| @@ -1121,6 +1248,8 @@ Status WebCryptoImpl::EncryptInternal( |
| true, algorithm, key, data, data_size, buffer); |
| case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: |
| return EncryptRsaEsPkcs1v1_5(algorithm, key, data, data_size, buffer); |
| + case blink::WebCryptoAlgorithmIdAesKw: |
| + return AesKwEncrypt(key, data, data_size, buffer); |
| default: |
| return Status::ErrorUnsupported(); |
| } |
| @@ -1146,6 +1275,8 @@ Status WebCryptoImpl::DecryptInternal( |
| false, algorithm, key, data, data_size, buffer); |
| case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: |
| return DecryptRsaEsPkcs1v1_5(algorithm, key, data, data_size, buffer); |
| + case blink::WebCryptoAlgorithmIdAesKw: |
| + return AesKwDecrypt(key, data, data_size, buffer); |
| default: |
| return Status::ErrorUnsupported(); |
| } |