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

Unified 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: minor refactoring Created 7 years 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 side-by-side diff with in-line comments
Download patch
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(

Powered by Google App Engine
This is Rietveld 408576698