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

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: rebase Created 6 years, 10 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 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 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();
}

Powered by Google App Engine
This is Rietveld 408576698