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 f30c8041f64a84b2d66e9aefefeb8a4c4929187c..bacb0c49455931b00114750bd08c3d08f15f7d94 100644 |
--- a/content/renderer/webcrypto/webcrypto_impl_nss.cc |
+++ b/content/renderer/webcrypto/webcrypto_impl_nss.cc |
@@ -372,6 +372,125 @@ bool AesGcmEncryptDecrypt( |
return true; |
} |
+// Constants for RFC 3394 AES Key Wrap / Unwrap. |
+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.
|
+// The Default IV. 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}; |
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
|
+ |
+// Performs RFC 3394 AES Key Wrap (encryption) of the input data. |
+bool AesKwEncrypt( |
+ const blink::WebCryptoKey& key, |
+ const unsigned char* data, |
+ unsigned data_size, |
+ blink::WebArrayBuffer* buffer) { |
Ryan Sleevi
2014/01/16 23:41:15
From an API design perspective, having the AES-KW
|
+ 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. |
+ if (data_size < 16 || data_size % 8) |
+ return false; |
+ 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
|
+ |
+ SECItem iv_item = {siBuffer, const_cast<unsigned char*>(kAesIv), |
+ arraysize(kAesIv)}; |
+ crypto::ScopedSECItem param_item(PK11_ParamFromIV(kAesKwMechanism, &iv_item)); |
+ DCHECK(param_item); |
+ |
+ // Turn the data to be wrapped into a PK11SymKey in order to use the NSS |
+ // PK11_WrapSymKey() API. Create the PK11SymKey by importing the data as a |
+ // 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.
|
+ SECItem data_item = {siBuffer, const_cast<unsigned char*>(data), data_size}; |
+ 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 false; |
+ |
+ // AES Key Wrap always adds 8 bytes to the input data size. RFC 3394 does not |
+ // specify a maximum allowed data length, but since we are only wrapping keys, |
+ // which are usually small, a reasonable max size is whatever will fit into an |
+ // unsigned. |
+ if (data_size > UINT_MAX - 8) |
+ return false; |
+ 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}; |
+ |
+ SymKeyHandle* wrapping_key = reinterpret_cast<SymKeyHandle*>(key.handle()); |
+ |
+ if (SECSuccess != PK11_WrapSymKey(kAesKwMechanism, |
+ param_item.get(), |
+ wrapping_key->key(), |
+ key_to_be_wrapped.get(), |
+ &wrapped_key_item)) { |
+ return false; |
+ } |
+ if (output_length != wrapped_key_item.len) { |
+ buffer->reset(); |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+// Performs RFC 3394 AES Key Unwrap (decryption) of the input data. |
+bool 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 || data_size % 8 != 0) |
+ return false; |
+ DCHECK(data); |
+ |
+ SymKeyHandle* wrapping_key = reinterpret_cast<SymKeyHandle*>(key.handle()); |
+ |
+ SECItem iv_item = {siBuffer, const_cast<unsigned char*>(kAesIv), |
+ arraysize(kAesIv)}; |
+ crypto::ScopedSECItem param_item(PK11_ParamFromIV(kAesKwMechanism, &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(), |
+ kAesKwMechanism, |
+ param_item.get(), |
+ &cipher_text, |
+ CKK_GENERIC_SECRET, // Import the key material without knowing its kind. |
+ CKA_ENCRYPT, // A safe value since all we're doing is exporting the key. |
+ plaintext_length)); |
+ if (!unwrapped_key) |
+ return false; |
+ |
+ if (PK11_ExtractKeyValue(unwrapped_key.get()) != SECSuccess) |
+ return false; |
+ |
+ const SECItem* key_data = PK11_GetKeyData(unwrapped_key.get()); |
+ if (!key_data || key_data->len != plaintext_length) |
+ return false; |
+ |
+ *buffer = webcrypto::CreateArrayBuffer(key_data->data, key_data->len); |
+ |
+ return true; |
+} |
+ |
CK_MECHANISM_TYPE WebCryptoAlgorithmToGenMechanism( |
const blink::WebCryptoAlgorithm& algorithm) { |
switch (algorithm.id()) { |
@@ -710,6 +829,8 @@ bool WebCryptoImpl::EncryptInternal( |
} else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesGcm) { |
return AesGcmEncryptDecrypt( |
true, algorithm, key, data, data_size, buffer); |
+ } else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesKw) { |
+ return AesKwEncrypt(key, data, data_size, buffer); |
} else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) { |
// RSAES encryption does not support empty input |
@@ -766,6 +887,8 @@ bool WebCryptoImpl::DecryptInternal( |
} else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesGcm) { |
return AesGcmEncryptDecrypt( |
false, algorithm, key, data, data_size, buffer); |
+ } else if (algorithm.id() == blink::WebCryptoAlgorithmIdAesKw) { |
+ return AesKwDecrypt(key, data, data_size, buffer); |
} else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) { |
// RSAES decryption does not support empty input |