diff options
author | padolph@netflix.com <padolph@netflix.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-05 22:22:54 +0000 |
---|---|---|
committer | padolph@netflix.com <padolph@netflix.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-05 22:22:54 +0000 |
commit | 9c2d1244b9092ddfc1923a585e343bb98c8168b4 (patch) | |
tree | 5b88cc038ccc8ecd38b5eb9a5fdcf42e32169b2d | |
parent | 7407bf3baf6cd3c425a31d52ee3630350c1cd5f5 (diff) | |
download | chromium_src-9c2d1244b9092ddfc1923a585e343bb98c8168b4.zip chromium_src-9c2d1244b9092ddfc1923a585e343bb98c8168b4.tar.gz chromium_src-9c2d1244b9092ddfc1923a585e343bb98c8168b4.tar.bz2 |
[webcrypto] Add symmetric key export for NSS and OpenSSL.
Also add test to check randomness of generated symmetric keys.
BUG=245025
TEST=content_unittests --gtest_filter="WebCryptoImpl*"
Review URL: https://codereview.chromium.org/100593002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@239049 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/renderer/webcrypto/webcrypto_impl_nss.cc | 30 | ||||
-rw-r--r-- | content/renderer/webcrypto/webcrypto_impl_openssl.cc | 34 | ||||
-rw-r--r-- | content/renderer/webcrypto/webcrypto_impl_unittest.cc | 91 | ||||
-rw-r--r-- | content/renderer/webcrypto/webcrypto_util.cc | 8 | ||||
-rw-r--r-- | content/renderer/webcrypto/webcrypto_util.h | 3 |
5 files changed, 141 insertions, 25 deletions
diff --git a/content/renderer/webcrypto/webcrypto_impl_nss.cc b/content/renderer/webcrypto/webcrypto_impl_nss.cc index 73319df..29dc04d 100644 --- a/content/renderer/webcrypto/webcrypto_impl_nss.cc +++ b/content/renderer/webcrypto/webcrypto_impl_nss.cc @@ -325,6 +325,30 @@ bool ImportKeyInternalRaw( return true; } +bool ExportKeyInternalRaw( + const blink::WebCryptoKey& key, + blink::WebArrayBuffer* buffer) { + + DCHECK(key.handle()); + DCHECK(buffer); + + if (key.type() != blink::WebCryptoKeyTypeSecret || !key.extractable()) + return false; + + SymKeyHandle* sym_key = reinterpret_cast<SymKeyHandle*>(key.handle()); + + if (PK11_ExtractKeyValue(sym_key->key()) != SECSuccess) + return false; + + const SECItem* key_data = PK11_GetKeyData(sym_key->key()); + if (!key_data) + return false; + + *buffer = webcrypto::CreateArrayBuffer(key_data->data, key_data->len); + + return true; +} + typedef scoped_ptr<CERTSubjectPublicKeyInfo, crypto::NSSDestroyer<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo> > @@ -424,8 +448,7 @@ bool ExportKeyInternalSpki( DCHECK(spki_der->data); DCHECK(spki_der->len); - *buffer = blink::WebArrayBuffer::create(spki_der->len, 1); - memcpy(buffer->data(), spki_der->data, spki_der->len); + *buffer = webcrypto::CreateArrayBuffer(spki_der->data, spki_der->len); return true; } @@ -827,8 +850,7 @@ bool WebCryptoImpl::ExportKeyInternal( blink::WebArrayBuffer* buffer) { switch (format) { case blink::WebCryptoKeyFormatRaw: - // TODO(padolph): Implement raw export - return false; + return ExportKeyInternalRaw(key, buffer); case blink::WebCryptoKeyFormatSpki: return ExportKeyInternalSpki(key, buffer); case blink::WebCryptoKeyFormatPkcs8: diff --git a/content/renderer/webcrypto/webcrypto_impl_openssl.cc b/content/renderer/webcrypto/webcrypto_impl_openssl.cc index 3170b28..9faf51d 100644 --- a/content/renderer/webcrypto/webcrypto_impl_openssl.cc +++ b/content/renderer/webcrypto/webcrypto_impl_openssl.cc @@ -157,6 +157,24 @@ bool AesCbcEncryptDecrypt(CipherOperation cipher_operation, return true; } +bool ExportKeyInternalRaw( + const blink::WebCryptoKey& key, + blink::WebArrayBuffer* buffer) { + + DCHECK(key.handle()); + DCHECK(buffer); + + if (key.type() != blink::WebCryptoKeyTypeSecret || !key.extractable()) + return false; + + const SymKeyHandle* sym_key = reinterpret_cast<SymKeyHandle*>(key.handle()); + + *buffer = webcrypto::CreateArrayBuffer( + webcrypto::Uint8VectorStart(sym_key->key()), sym_key->key().size()); + + return true; +} + } // namespace void WebCryptoImpl::Init() { crypto::EnsureOpenSSLInit(); } @@ -370,10 +388,18 @@ bool WebCryptoImpl::ExportKeyInternal( blink::WebCryptoKeyFormat format, const blink::WebCryptoKey& key, blink::WebArrayBuffer* buffer) { - // TODO(padolph): Implement raw export - // TODO(padolph): Implement spki export - // TODO(padolph): Implement pkcs8 export - // TODO(padolph): Implement jwk export + switch (format) { + case blink::WebCryptoKeyFormatRaw: + return ExportKeyInternalRaw(key, buffer); + case blink::WebCryptoKeyFormatSpki: + // TODO(padolph): Implement spki export + return false; + case blink::WebCryptoKeyFormatPkcs8: + // TODO(padolph): Implement pkcs8 export + return false; + default: + return false; + } return false; } diff --git a/content/renderer/webcrypto/webcrypto_impl_unittest.cc b/content/renderer/webcrypto/webcrypto_impl_unittest.cc index 48552b7..9317c11 100644 --- a/content/renderer/webcrypto/webcrypto_impl_unittest.cc +++ b/content/renderer/webcrypto/webcrypto_impl_unittest.cc @@ -92,6 +92,25 @@ blink::WebCryptoAlgorithm CreateRsaKeyGenAlgorithm( public_exponent.size())); } +// Determines if two ArrayBuffers have identical content. +bool ArrayBuffersEqual( + const blink::WebArrayBuffer& a, + const blink::WebArrayBuffer& b) { + return a.byteLength() == b.byteLength() && + memcmp(a.data(), b.data(), a.byteLength()) == 0; +} + +// Given a vector of WebArrayBuffers, determines if there are any copies. +bool CopiesExist(std::vector<blink::WebArrayBuffer> bufs) { + for (size_t i = 0; i < bufs.size(); ++i) { + for (size_t j = i + 1; j < bufs.size(); ++j) { + if (ArrayBuffersEqual(bufs[i], bufs[j])) + return true; + } + } + return false; +} + #endif // #if !defined(USE_OPENSSL) } // namespace @@ -432,6 +451,11 @@ TEST_F(WebCryptoImplTest, HMACSampleSets) { blink::WebCryptoKey key = ImportSecretKeyFromRawHexString( test.key, algorithm, blink::WebCryptoKeyUsageSign); + // Verify exported raw key is identical to the imported data + blink::WebArrayBuffer raw_key; + EXPECT_TRUE(ExportKeyInternal(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + ExpectArrayBufferMatchesHex(test.key, raw_key); + std::vector<uint8> message_raw = HexStringToBytes(test.message); blink::WebArrayBuffer output; @@ -476,11 +500,17 @@ TEST_F(WebCryptoImplTest, HMACSampleSets) { #if !defined(USE_OPENSSL) TEST_F(WebCryptoImplTest, AesCbcFailures) { + const std::string key_hex = "2b7e151628aed2a6abf7158809cf4f3c"; blink::WebCryptoKey key = ImportSecretKeyFromRawHexString( - "2b7e151628aed2a6abf7158809cf4f3c", + key_hex, webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); + // Verify exported raw key is identical to the imported data + blink::WebArrayBuffer raw_key; + EXPECT_TRUE(ExportKeyInternal(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + ExpectArrayBufferMatchesHex(key_hex, raw_key); + blink::WebArrayBuffer output; // Use an invalid |iv| (fewer than 16 bytes) @@ -534,9 +564,10 @@ TEST_F(WebCryptoImplTest, AesCbcFailures) { &key)); } - // Fail exporting the key in SPKI format (SPKI export not allowed for secret - // keys) + // Fail exporting the key in SPKI and PKCS#8 formats (not allowed for secret + // keys). EXPECT_FALSE(ExportKeyInternal(blink::WebCryptoKeyFormatSpki, key, &output)); + EXPECT_FALSE(ExportKeyInternal(blink::WebCryptoKeyFormatPkcs8, key, &output)); } TEST_F(WebCryptoImplTest, AesCbcSampleSets) { @@ -626,6 +657,11 @@ TEST_F(WebCryptoImplTest, AesCbcSampleSets) { webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt); + // Verify exported raw key is identical to the imported data + blink::WebArrayBuffer raw_key; + EXPECT_TRUE(ExportKeyInternal(blink::WebCryptoKeyFormatRaw, key, &raw_key)); + ExpectArrayBufferMatchesHex(test.key, raw_key); + std::vector<uint8> plain_text = HexStringToBytes(test.plain_text); std::vector<uint8> iv = HexStringToBytes(test.iv); @@ -670,14 +706,23 @@ TEST_F(WebCryptoImplTest, AesCbcSampleSets) { } } -// TODO(padolph): Add test to verify generated symmetric keys appear random. - TEST_F(WebCryptoImplTest, GenerateKeyAes) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - ASSERT_TRUE( - GenerateKeyInternal(webcrypto::CreateAesCbcKeyGenAlgorithm(128), &key)); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + // Generate a small sample of AES keys. + std::vector<blink::WebArrayBuffer> keys; + blink::WebArrayBuffer key_bytes; + for (int i = 0; i < 16; ++i) { + blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); + ASSERT_TRUE( + GenerateKeyInternal(webcrypto::CreateAesCbcKeyGenAlgorithm(128), &key)); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + ASSERT_TRUE( + ExportKeyInternal(blink::WebCryptoKeyFormatRaw, key, &key_bytes)); + keys.push_back(key_bytes); + } + // Ensure all entries in the key sample set are unique. This is a simplistic + // estimate of whether the generated keys appear random. + EXPECT_FALSE(CopiesExist(keys)); } TEST_F(WebCryptoImplTest, GenerateKeyAesBadLength) { @@ -691,13 +736,21 @@ TEST_F(WebCryptoImplTest, GenerateKeyAesBadLength) { } TEST_F(WebCryptoImplTest, GenerateKeyHmac) { - blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); - blink::WebCryptoAlgorithm algorithm = webcrypto::CreateHmacKeyGenAlgorithm( - blink::WebCryptoAlgorithmIdSha1, 128); - ASSERT_TRUE(GenerateKeyInternal(algorithm, &key)); - EXPECT_FALSE(key.isNull()); - EXPECT_TRUE(key.handle()); - EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + // Generate a small sample of HMAC keys. + std::vector<blink::WebArrayBuffer> keys; + for (int i = 0; i < 16; ++i) { + blink::WebArrayBuffer key_bytes; + blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); + blink::WebCryptoAlgorithm algorithm = webcrypto::CreateHmacKeyGenAlgorithm( + blink::WebCryptoAlgorithmIdSha1, 128); + ASSERT_TRUE(GenerateKeyInternal(algorithm, &key)); + EXPECT_FALSE(key.isNull()); + EXPECT_TRUE(key.handle()); + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); + } + // Ensure all entries in the key sample set are unique. This is a simplistic + // estimate of whether the generated keys appear random. + EXPECT_FALSE(CopiesExist(keys)); } TEST_F(WebCryptoImplTest, GenerateKeyHmacNoLength) { @@ -1119,6 +1172,10 @@ TEST_F(WebCryptoImplTest, ImportExportSpki) { ASSERT_TRUE(ExportKeyInternal(blink::WebCryptoKeyFormatSpki, key, &output)); ExpectArrayBufferMatchesHex(hex_rsa_spki_der, output); + // Failing case: Try to export a previously imported RSA public key in raw + // format (not allowed for a public key). + EXPECT_FALSE(ExportKeyInternal(blink::WebCryptoKeyFormatRaw, key, &output)); + // Failing case: Try to export a non-extractable key ASSERT_TRUE(ImportKeyInternal( blink::WebCryptoKeyFormatSpki, diff --git a/content/renderer/webcrypto/webcrypto_util.cc b/content/renderer/webcrypto/webcrypto_util.cc index cae4e0f..b5a318a 100644 --- a/content/renderer/webcrypto/webcrypto_util.cc +++ b/content/renderer/webcrypto/webcrypto_util.cc @@ -50,6 +50,14 @@ void ShrinkBuffer(blink::WebArrayBuffer* buffer, unsigned new_size) { *buffer = new_buffer; } +blink::WebArrayBuffer CreateArrayBuffer(const uint8* data, unsigned data_size) { + blink::WebArrayBuffer buffer = blink::WebArrayBuffer::create(data_size, 1); + DCHECK(!buffer.isNull()); + if (data_size) // data_size == 0 might mean the data pointer is invalid + memcpy(buffer.data(), data, data_size); + return buffer; +} + // This function decodes unpadded 'base64url' encoded data, as described in // RFC4648 (http://www.ietf.org/rfc/rfc4648.txt) Section 5. To do this, first // change the incoming data to 'base64' encoding by applying the appropriate diff --git a/content/renderer/webcrypto/webcrypto_util.h b/content/renderer/webcrypto/webcrypto_util.h index 94159e0..022b3c2 100644 --- a/content/renderer/webcrypto/webcrypto_util.h +++ b/content/renderer/webcrypto/webcrypto_util.h @@ -27,6 +27,9 @@ CONTENT_EXPORT const uint8* Uint8VectorStart(const std::vector<uint8>& data); // the WebArrayBuffer could just be truncated instead. void ShrinkBuffer(blink::WebArrayBuffer* buffer, unsigned new_size); +// Creates a WebArrayBuffer from a uint8 byte array +blink::WebArrayBuffer CreateArrayBuffer(const uint8* data, unsigned data_size); + // This function decodes unpadded 'base64url' encoded data, as described in // RFC4648 (http://www.ietf.org/rfc/rfc4648.txt) Section 5. // In Web Crypto, this type of encoding is only used inside JWK. |