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 1df84289b243df2018f9de3bcf4e1a7d15d523d5..8dfa2c2d8b48fc6ad35e7926434402047e5bd9aa 100644 |
--- a/content/renderer/webcrypto/webcrypto_impl_nss.cc |
+++ b/content/renderer/webcrypto/webcrypto_impl_nss.cc |
@@ -175,6 +175,123 @@ bool AesCbcEncryptDecrypt( |
return true; |
} |
+// The RFC 3394 AES Key Wrap / Unwrap Mechanism. |
+const CK_MECHANISM_TYPE kAesKwMechanism = CKM_NSS_AES_KEY_WRAP; |
+ |
+// Creates an NSS SECItem representing the Default IV specified for AES Key |
+// Wrap. See http://www.ietf.org/rfc/rfc3394.txt Section 2.2.3.1. |
+crypto::ScopedSECItem CreateAesKwIvItem() { |
+ const unsigned int kAesKwIvLength = 8; |
+ const unsigned char kAesIv[kAesKwIvLength] = {0xA6, 0xA6, 0xA6, 0xA6, |
+ 0xA6, 0xA6, 0xA6, 0xA6}; |
+ SECItem iv_item = |
+ {siBuffer, const_cast<unsigned char*>(kAesIv), kAesKwIvLength}; |
+ SECItem* param_item = PK11_ParamFromIV(kAesKwMechanism, &iv_item); |
+ DCHECK(param_item); |
+ return crypto::ScopedSECItem(param_item); |
+} |
+ |
+// Performs RFC 3394 AES Key Wrap (encryption) of the input data. |
+bool AesKwEncrypt( |
+ const blink::WebCryptoAlgorithm& algorithm, |
+ const blink::WebCryptoKey& key, |
+ const unsigned char* data, |
+ unsigned data_size, |
+ blink::WebArrayBuffer* buffer) { |
+ DCHECK_EQ(blink::WebCryptoAlgorithmIdAesKw, algorithm.id()); |
+ DCHECK_EQ(algorithm.id(), key.algorithm().id()); |
Bryan Eyler
2013/12/18 23:30:32
Is this right? Isn't algorithm the key wrap algor
padolph
2013/12/19 00:07:15
In key wrapping terms that is correct. But this fu
|
+ DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type()); |
+ |
+ // The data size must be at least 16 bytes and a multiple of 8 bytes. |
+ // TODO(padolph): Should this be a DCHECK() instead? |
+ if (data_size < 16 || data_size % 8) |
+ return false; |
+ DCHECK(data); |
+ |
+ // 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. |
+ 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. |
+ 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, |
+ CreateAesKwIvItem().get(), |
+ wrapping_key->key(), |
+ key_to_be_wrapped.get(), |
+ &wrapped_key_item)) { |
+ return false; |
+ } |
+ DCHECK_EQ(output_length, wrapped_key_item.len); |
+ |
+ return true; |
+} |
+ |
+// Performs RFC 3394 AES Key Unwrap (decryption) of the input data. |
+bool AesKwDecrypt( |
+ const blink::WebCryptoAlgorithm& algorithm, |
+ const blink::WebCryptoKey& key, |
+ const unsigned char* data, |
+ unsigned data_size, |
+ blink::WebArrayBuffer* buffer) { |
+ DCHECK_EQ(blink::WebCryptoAlgorithmIdAesKw, algorithm.id()); |
+ DCHECK_EQ(algorithm.id(), key.algorithm().id()); |
Bryan Eyler
2013/12/18 23:30:32
Same here.
padolph
2013/12/19 00:07:15
See above.
|
+ DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type()); |
+ |
+ // The ciphertext data size must be at least 24 bytes and a multiple of |
+ // 8 bytes. This is a DCHECK() because this function is not callable with |
+ // arbitrary data: it only receives data representing an assumed-good internal |
+ // key to be wrapped. |
+ DCHECK(data_size >= 24 && data_size % 8 == 0); |
+ DCHECK(data); |
+ |
+ SymKeyHandle* wrapping_key = reinterpret_cast<SymKeyHandle*>(key.handle()); |
+ |
+ 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, |
+ CreateAesKwIvItem().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) |
+ return false; |
+ DCHECK_EQ(plaintext_length, key_data->len); |
+ |
+ *buffer = webcrypto::CreateArrayBuffer(key_data->data, key_data->len); |
+ |
+ return true; |
+} |
+ |
CK_MECHANISM_TYPE HmacAlgorithmToGenMechanism( |
const blink::WebCryptoAlgorithm& algorithm) { |
DCHECK_EQ(algorithm.id(), blink::WebCryptoAlgorithmIdHmac); |
@@ -525,45 +642,53 @@ bool WebCryptoImpl::EncryptInternal( |
DCHECK(key.handle()); |
DCHECK(buffer); |
- if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { |
- return AesCbcEncryptDecrypt( |
- CKA_ENCRYPT, algorithm, key, data, data_size, buffer); |
- } else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) { |
- |
- // RSAES encryption does not support empty input |
- if (!data_size) |
- return false; |
- DCHECK(data); |
+ switch (algorithm.id()) { |
+ case blink::WebCryptoAlgorithmIdAesCbc: { |
+ return AesCbcEncryptDecrypt( |
+ CKA_ENCRYPT, algorithm, key, data, data_size, buffer); |
+ } |
+ case blink::WebCryptoAlgorithmIdAesKw: { |
Bryan Eyler
2013/12/18 23:30:32
According to the spec, the encrypt() function is n
padolph
2013/12/19 00:07:15
The spec indeed says that you cannot do AES-KW fro
|
+ return AesKwEncrypt(algorithm, key, data, data_size, buffer); |
+ } |
+ case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: { |
+ // RSAES encryption does not support empty input |
+ if (!data_size) |
+ return false; |
+ DCHECK(data); |
- if (key.type() != blink::WebCryptoKeyTypePublic) |
- return false; |
+ if (key.type() != blink::WebCryptoKeyTypePublic) |
+ return false; |
- PublicKeyHandle* const public_key = |
- reinterpret_cast<PublicKeyHandle*>(key.handle()); |
+ PublicKeyHandle* const public_key = |
+ reinterpret_cast<PublicKeyHandle*>(key.handle()); |
- const unsigned encrypted_length_bytes = |
- SECKEY_PublicKeyStrength(public_key->key()); |
+ const unsigned encrypted_length_bytes = |
+ SECKEY_PublicKeyStrength(public_key->key()); |
- // RSAES can operate on messages up to a length of k - 11, where k is the |
- // octet length of the RSA modulus. |
- if (encrypted_length_bytes < 11 || encrypted_length_bytes - 11 < data_size) |
- return false; |
+ // RSAES can operate on messages up to a length of k - 11, where k is the |
+ // octet length of the RSA modulus. |
+ if (encrypted_length_bytes < 11 || |
+ encrypted_length_bytes - 11 < data_size) { |
+ return false; |
+ } |
- *buffer = blink::WebArrayBuffer::create(encrypted_length_bytes, 1); |
- unsigned char* const buffer_data = |
- reinterpret_cast<unsigned char*>(buffer->data()); |
+ *buffer = blink::WebArrayBuffer::create(encrypted_length_bytes, 1); |
+ unsigned char* const buffer_data = |
+ reinterpret_cast<unsigned char*>(buffer->data()); |
- if (PK11_PubEncryptPKCS1(public_key->key(), |
- buffer_data, |
- const_cast<unsigned char*>(data), |
- data_size, |
- NULL) != SECSuccess) { |
+ if (PK11_PubEncryptPKCS1(public_key->key(), |
+ buffer_data, |
+ const_cast<unsigned char*>(data), |
+ data_size, |
+ NULL) != SECSuccess) { |
+ return false; |
+ } |
+ return true; |
+ } |
+ default: { |
return false; |
} |
- return true; |
} |
- |
- return false; |
} |
bool WebCryptoImpl::DecryptInternal( |
@@ -577,47 +702,53 @@ bool WebCryptoImpl::DecryptInternal( |
DCHECK(key.handle()); |
DCHECK(buffer); |
- if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { |
- return AesCbcEncryptDecrypt( |
- CKA_DECRYPT, algorithm, key, data, data_size, buffer); |
- } else if (algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) { |
- |
- // RSAES decryption does not support empty input |
- if (!data_size) |
- return false; |
- DCHECK(data); |
+ switch (algorithm.id()) { |
+ case blink::WebCryptoAlgorithmIdAesCbc: { |
+ return AesCbcEncryptDecrypt( |
+ CKA_DECRYPT, algorithm, key, data, data_size, buffer); |
+ } |
+ case blink::WebCryptoAlgorithmIdAesKw: { |
Bryan Eyler
2013/12/18 23:30:32
Same here.
padolph
2013/12/19 00:07:15
See above.
|
+ return AesKwDecrypt(algorithm, key, data, data_size, buffer); |
+ } |
+ case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: { |
+ // RSAES decryption does not support empty input |
+ if (!data_size) |
+ return false; |
+ DCHECK(data); |
- if (key.type() != blink::WebCryptoKeyTypePrivate) |
- return false; |
+ if (key.type() != blink::WebCryptoKeyTypePrivate) |
+ return false; |
- PrivateKeyHandle* const private_key = |
- reinterpret_cast<PrivateKeyHandle*>(key.handle()); |
+ PrivateKeyHandle* const private_key = |
+ reinterpret_cast<PrivateKeyHandle*>(key.handle()); |
- const int modulus_length_bytes = |
- PK11_GetPrivateModulusLen(private_key->key()); |
- if (modulus_length_bytes <= 0) |
- return false; |
- const unsigned max_output_length_bytes = modulus_length_bytes; |
- |
- *buffer = blink::WebArrayBuffer::create(max_output_length_bytes, 1); |
- unsigned char* const buffer_data = |
- reinterpret_cast<unsigned char*>(buffer->data()); |
- |
- unsigned output_length_bytes = 0; |
- if (PK11_PrivDecryptPKCS1(private_key->key(), |
- buffer_data, |
- &output_length_bytes, |
- max_output_length_bytes, |
- const_cast<unsigned char*>(data), |
- data_size) != SECSuccess) { |
+ const int modulus_length_bytes = |
+ PK11_GetPrivateModulusLen(private_key->key()); |
+ if (modulus_length_bytes <= 0) |
+ return false; |
+ const unsigned max_output_length_bytes = modulus_length_bytes; |
+ |
+ *buffer = blink::WebArrayBuffer::create(max_output_length_bytes, 1); |
+ unsigned char* const buffer_data = |
+ reinterpret_cast<unsigned char*>(buffer->data()); |
+ |
+ unsigned output_length_bytes = 0; |
+ if (PK11_PrivDecryptPKCS1(private_key->key(), |
+ buffer_data, |
+ &output_length_bytes, |
+ max_output_length_bytes, |
+ const_cast<unsigned char*>(data), |
+ data_size) != SECSuccess) { |
+ return false; |
+ } |
+ DCHECK_LE(output_length_bytes, max_output_length_bytes); |
+ webcrypto::ShrinkBuffer(buffer, output_length_bytes); |
+ return true; |
+ } |
+ default: { |
return false; |
} |
- DCHECK_LE(output_length_bytes, max_output_length_bytes); |
- webcrypto::ShrinkBuffer(buffer, output_length_bytes); |
- return true; |
} |
- |
- return false; |
} |
bool WebCryptoImpl::DigestInternal( |