summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpadolph@netflix.com <padolph@netflix.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-05 22:22:54 +0000
committerpadolph@netflix.com <padolph@netflix.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-05 22:22:54 +0000
commit9c2d1244b9092ddfc1923a585e343bb98c8168b4 (patch)
tree5b88cc038ccc8ecd38b5eb9a5fdcf42e32169b2d
parent7407bf3baf6cd3c425a31d52ee3630350c1cd5f5 (diff)
downloadchromium_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.cc30
-rw-r--r--content/renderer/webcrypto/webcrypto_impl_openssl.cc34
-rw-r--r--content/renderer/webcrypto/webcrypto_impl_unittest.cc91
-rw-r--r--content/renderer/webcrypto/webcrypto_util.cc8
-rw-r--r--content/renderer/webcrypto/webcrypto_util.h3
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.