Index: content/renderer/webcrypto/webcrypto_impl_unittest.cc |
diff --git a/content/renderer/webcrypto/webcrypto_impl_unittest.cc b/content/renderer/webcrypto/webcrypto_impl_unittest.cc |
index 9540292b51c101e5538118d2e86e79456ef25150..f9ff0d67f232b6d36e1fa38689175c289d2fa9f5 100644 |
--- a/content/renderer/webcrypto/webcrypto_impl_unittest.cc |
+++ b/content/renderer/webcrypto/webcrypto_impl_unittest.cc |
@@ -4,6 +4,7 @@ |
#include "content/renderer/webcrypto/webcrypto_impl.h" |
+#include <algorithm> |
#include <string> |
#include <vector> |
@@ -81,7 +82,8 @@ blink::WebCryptoAlgorithm CreateAesCbcAlgorithm( |
} |
#if !defined(USE_OPENSSL) |
-blink::WebCryptoAlgorithm CreateRsaAlgorithm( |
+ |
+blink::WebCryptoAlgorithm CreateRsaKeyGenAlgorithm( |
blink::WebCryptoAlgorithmId algorithm_id, |
unsigned modulus_length, |
const std::vector<uint8>& public_exponent) { |
@@ -93,7 +95,8 @@ blink::WebCryptoAlgorithm CreateRsaAlgorithm( |
new blink::WebCryptoRsaKeyGenParams( |
modulus_length, Start(public_exponent), public_exponent.size())); |
} |
-#endif // !defined(USE_OPENSSL) |
+ |
+#endif // #if !defined(USE_OPENSSL) |
} // namespace |
@@ -879,9 +882,9 @@ TEST_F(WebCryptoImplTest, GenerateKeyPairRsa) { |
const unsigned modulus_length = 256; |
const std::vector<uint8> public_exponent = HexStringToBytes("010001"); |
blink::WebCryptoAlgorithm algorithm = |
- CreateRsaAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
- modulus_length, |
- public_exponent); |
+ CreateRsaKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
+ modulus_length, |
+ public_exponent); |
bool extractable = true; |
const blink::WebCryptoKeyUsageMask usage_mask = 0; |
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
@@ -898,7 +901,7 @@ TEST_F(WebCryptoImplTest, GenerateKeyPairRsa) { |
EXPECT_EQ(usage_mask, private_key.usages()); |
// Fail with bad modulus. |
- algorithm = CreateRsaAlgorithm( |
+ algorithm = CreateRsaKeyGenAlgorithm( |
blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, 0, public_exponent); |
EXPECT_FALSE(GenerateKeyPairInternal( |
algorithm, extractable, usage_mask, &public_key, &private_key)); |
@@ -906,25 +909,26 @@ TEST_F(WebCryptoImplTest, GenerateKeyPairRsa) { |
// Fail with bad exponent: larger than unsigned long. |
unsigned exponent_length = sizeof(unsigned long) + 1; // NOLINT |
const std::vector<uint8> long_exponent(exponent_length, 0x01); |
- algorithm = CreateRsaAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
- modulus_length, |
- long_exponent); |
+ algorithm = CreateRsaKeyGenAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, modulus_length, long_exponent); |
EXPECT_FALSE(GenerateKeyPairInternal( |
algorithm, extractable, usage_mask, &public_key, &private_key)); |
// Fail with bad exponent: empty. |
const std::vector<uint8> empty_exponent; |
- algorithm = CreateRsaAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
- modulus_length, |
- empty_exponent); |
+ algorithm = |
+ CreateRsaKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
+ modulus_length, |
+ empty_exponent); |
EXPECT_FALSE(GenerateKeyPairInternal( |
algorithm, extractable, usage_mask, &public_key, &private_key)); |
// Fail with bad exponent: all zeros. |
std::vector<uint8> exponent_with_leading_zeros(15, 0x00); |
- algorithm = CreateRsaAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
- modulus_length, |
- exponent_with_leading_zeros); |
+ algorithm = |
+ CreateRsaKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
+ modulus_length, |
+ exponent_with_leading_zeros); |
EXPECT_FALSE(GenerateKeyPairInternal( |
algorithm, extractable, usage_mask, &public_key, &private_key)); |
@@ -932,9 +936,10 @@ TEST_F(WebCryptoImplTest, GenerateKeyPairRsa) { |
exponent_with_leading_zeros.insert(exponent_with_leading_zeros.end(), |
public_exponent.begin(), |
public_exponent.end()); |
- algorithm = CreateRsaAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
- modulus_length, |
- exponent_with_leading_zeros); |
+ algorithm = |
+ CreateRsaKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
+ modulus_length, |
+ exponent_with_leading_zeros); |
EXPECT_TRUE(GenerateKeyPairInternal( |
algorithm, extractable, usage_mask, &public_key, &private_key)); |
EXPECT_FALSE(public_key.isNull()); |
@@ -947,7 +952,7 @@ TEST_F(WebCryptoImplTest, GenerateKeyPairRsa) { |
EXPECT_EQ(usage_mask, private_key.usages()); |
// Successful WebCryptoAlgorithmIdRsaOaep key generation. |
- algorithm = CreateRsaAlgorithm( |
+ algorithm = CreateRsaKeyGenAlgorithm( |
blink::WebCryptoAlgorithmIdRsaOaep, modulus_length, public_exponent); |
EXPECT_TRUE(GenerateKeyPairInternal( |
algorithm, extractable, usage_mask, &public_key, &private_key)); |
@@ -961,9 +966,10 @@ TEST_F(WebCryptoImplTest, GenerateKeyPairRsa) { |
EXPECT_EQ(usage_mask, private_key.usages()); |
// Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation. |
- algorithm = CreateRsaAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
- modulus_length, |
- public_exponent); |
+ algorithm = |
+ CreateRsaKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
+ modulus_length, |
+ public_exponent); |
EXPECT_TRUE(GenerateKeyPairInternal( |
algorithm, extractable, usage_mask, &public_key, &private_key)); |
EXPECT_FALSE(public_key.isNull()); |
@@ -983,6 +989,259 @@ TEST_F(WebCryptoImplTest, GenerateKeyPairRsa) { |
ExportKeyInternal(blink::WebCryptoKeyFormatSpki, private_key, &output)); |
} |
+TEST_F(WebCryptoImplTest, RsaEsRoundTrip) { |
+ // Note: using unrealistic short key length here to avoid bogging down tests. |
+ |
+ // Create a key pair. |
+ const unsigned kModulusLength = 256; |
+ blink::WebCryptoAlgorithm algorithm = |
+ CreateRsaKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
+ kModulusLength, |
+ HexStringToBytes("010001")); |
+ const blink::WebCryptoKeyUsageMask usage_mask = |
+ blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt; |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
+ EXPECT_TRUE(GenerateKeyPairInternal( |
+ algorithm, false, usage_mask, &public_key, &private_key)); |
+ EXPECT_FALSE(public_key.isNull()); |
+ EXPECT_FALSE(private_key.isNull()); |
+ |
+ // Make a maximum-length data message. RSAES can operate on messages up to |
+ // length of k - 11 bytes, where k is the octet length of the RSA modulus. |
+ const unsigned kMaxMsgSizeBytes = kModulusLength / 8 - 11; |
+ // There are two hex chars for each byte. |
+ const unsigned kMsgHexSize = kMaxMsgSizeBytes * 2; |
+ char max_data_hex[kMsgHexSize+1]; |
+ std::fill(&max_data_hex[0], &max_data_hex[0] + kMsgHexSize, 'a'); |
+ max_data_hex[kMsgHexSize] = '\0'; |
+ |
+ // Verify encrypt / decrypt round trip on a few messages. Note that RSA |
+ // encryption does not support empty input. |
+ algorithm = CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
+ const char* const kTestDataHex[] = { |
+ "ff", |
+ "0102030405060708090a0b0c0d0e0f", |
+ max_data_hex |
+ }; |
+ blink::WebArrayBuffer encrypted_data; |
+ blink::WebArrayBuffer decrypted_data; |
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestDataHex); ++i) { |
+ SCOPED_TRACE(i); |
+ ASSERT_TRUE(EncryptInternal( |
+ algorithm, |
+ public_key, |
+ HexStringToBytes(kTestDataHex[i]), |
+ &encrypted_data)); |
+ EXPECT_EQ(kModulusLength/8, encrypted_data.byteLength()); |
+ ASSERT_TRUE(DecryptInternal( |
+ algorithm, |
+ private_key, |
+ reinterpret_cast<const unsigned char*>(encrypted_data.data()), |
+ encrypted_data.byteLength(), |
+ &decrypted_data)); |
+ ExpectArrayBufferMatchesHex(kTestDataHex[i], decrypted_data); |
+ } |
+} |
+ |
+TEST_F(WebCryptoImplTest, RsaEsKnownAnswer) { |
+ // Because the random data in PKCS1.5 padding makes the encryption output non- |
+ // deterministic, we cannot easily do a typical known-answer test for RSA |
+ // encryption / decryption. Instead we will take a known-good encrypted |
+ // message, decrypt it, re-encrypt it, then decrypt again, verifying that the |
+ // original known cleartext is the result. |
+ |
+ // The RSA public and private keys used for this test are produced by the |
+ // openssl command line: |
+ // % openssl genrsa -out pair.pem 1024 |
+ // % openssl rsa -in pair.pem -out spki.der -outform DER -pubout |
+ // % openssl pkcs8 -topk8 -inform PEM -outform DER -in pair.pem -out |
+ // pkcs8.der -nocrypt |
+ // % xxd -p spki.der |
+ // % xxd -p pkcs8.der |
+ const std::string rsa_spki_der_hex = |
+ "30819f300d06092a864886f70d010101050003818d0030818902818100a8" |
+ "d30894b93f376f7822229bfd2483e50da944c4ab803ca31979e0f47e70bf" |
+ "683c687c6b3e80f280a237cea3643fd1f7f10f7cc664dbc2ecd45be53e1c" |
+ "9b15a53c37dbdad846c0f8340c472abc7821e4aa7df185867bf38228ac3e" |
+ "cc1d97d3c8b57e21ea6ba57b2bc3814a436e910ee8ab64a0b7743a927e94" |
+ "4d3420401f7dd50203010001"; |
+ const std::string rsa_pkcs8_der_hex = |
+ "30820276020100300d06092a864886f70d0101010500048202603082025c" |
+ "02010002818100a8d30894b93f376f7822229bfd2483e50da944c4ab803c" |
+ "a31979e0f47e70bf683c687c6b3e80f280a237cea3643fd1f7f10f7cc664" |
+ "dbc2ecd45be53e1c9b15a53c37dbdad846c0f8340c472abc7821e4aa7df1" |
+ "85867bf38228ac3ecc1d97d3c8b57e21ea6ba57b2bc3814a436e910ee8ab" |
+ "64a0b7743a927e944d3420401f7dd5020301000102818100896cdffb50a0" |
+ "691bd00ad9696933243a7c5861a64684e8d74b91aed0d76c28234da9303e" |
+ "8c6ea2f89b141a9d5ea9a4ddd3d8eb9503dcf05ba0b1fd76060b281e3ae4" |
+ "b9d497fb5519bdf1127db8ad412d6a722686c78df3e3002acca960c6b2a2" |
+ "42a83ace5410693c03ce3d74cb9c9a7bacc8e271812920d1f53fee9312ef" |
+ "4eb1024100d09c14418ce92af7cc62f7cdc79836d8c6e3d0d33e7229cc11" |
+ "d732cbac75aa4c56c92e409a3ccbe75d4ce63ac5adca33080690782c6371" |
+ "e3628134c3534ca603024100cf2d3206f6deea2f39b70351c51f85436200" |
+ "5aa8f643e49e22486736d536e040dc30a2b4f9be3ab212a88d1891280874" |
+ "b9a170cdeb22eaf61c27c4b082c7d1470240638411a5b3b307ec6e744802" |
+ "c2d4ba556f8bfe72c7b76e790b89bd91ac13f5c9b51d04138d80b3450c1d" |
+ "4337865601bf96748b36c8f627be719f71ac3c70b441024065ce92cfe34e" |
+ "a58bf173a2b8f3024b4d5282540ac581957db3e11a7f528535ec098808dc" |
+ "a0013ffcb3b88a25716757c86c540e07d2ad8502cdd129118822c30f0240" |
+ "420a4983040e9db46eb29f1315a0d7b41cf60428f7460fce748e9a1a7d22" |
+ "d7390fa328948e7e9d1724401374e99d45eb41474781201378a4330e8e80" |
+ "8ce63551"; |
+ |
+ // Similarly, the cleartext and public key encrypted ciphertext for this test |
+ // are also produced by openssl. Note that since we are using a 1024-bit key, |
+ // the cleartext size must be less than or equal to 117 bytes (modulusLength / |
+ // 8 - 11). |
+ // % openssl rand -out cleartext.bin 64 |
+ // % openssl rsautl -encrypt -inkey spki.der -keyform DER -pubin -in |
+ // cleartext.bin -out ciphertext.bin |
+ // % xxd -p cleartext.bin |
+ // % xxd -p ciphertext.bin |
+ const std::string cleartext_hex = |
+ "ec358ed141c45d7e03d4c6338aebad718e8bcbbf8f8ee6f8d9f4b9ef06d8" |
+ "84739a398c6bcbc688418b2ff64761dc0ccd40e7d52bed03e06946d0957a" |
+ "eef9e822"; |
+ const std::string ciphertext_hex = |
+ "6106441c2b7a4b1a16260ed1ae4fe6135247345dc8e674754bbda6588c6c" |
+ "0d95a3d4d26bb34cdbcbe327723e80343bd7a15cd4c91c3a44e6cb9c6cd6" |
+ "7ad2e8bf41523188d9b36dc364a838642dcbc2c25e85dfb2106ba47578ca" |
+ "3bbf8915055aea4fa7c3cbfdfbcc163f04c234fb6d847f39bab9612ecbee" |
+ "04626e945c3ccf42"; |
+ |
+ // Import the public key. |
+ const blink::WebCryptoAlgorithm algorithm = |
+ CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_TRUE(ImportKeyInternal( |
+ blink::WebCryptoKeyFormatSpki, |
+ HexStringToBytes(rsa_spki_der_hex), |
+ algorithm, |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &public_key)); |
+ EXPECT_FALSE(public_key.isNull()); |
+ EXPECT_TRUE(public_key.handle()); |
+ |
+ // Import the private key. |
+ blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_TRUE(ImportKeyInternal( |
+ blink::WebCryptoKeyFormatPkcs8, |
+ HexStringToBytes(rsa_pkcs8_der_hex), |
+ algorithm, |
+ true, |
+ blink::WebCryptoKeyUsageDecrypt, |
+ &private_key)); |
+ EXPECT_FALSE(private_key.isNull()); |
+ EXPECT_TRUE(private_key.handle()); |
+ |
+ // Decrypt the known-good ciphertext with the private key. As a check we must |
+ // get the known original cleartext. |
+ blink::WebArrayBuffer decrypted_data; |
+ ASSERT_TRUE(DecryptInternal( |
+ algorithm, |
+ private_key, |
+ HexStringToBytes(ciphertext_hex), |
+ &decrypted_data)); |
+ EXPECT_FALSE(decrypted_data.isNull()); |
+ ExpectArrayBufferMatchesHex(cleartext_hex, decrypted_data); |
+ |
+ // Encrypt this decrypted data with the public key. |
+ blink::WebArrayBuffer encrypted_data; |
+ ASSERT_TRUE(EncryptInternal( |
+ algorithm, |
+ public_key, |
+ reinterpret_cast<const unsigned char*>(decrypted_data.data()), |
+ decrypted_data.byteLength(), |
+ &encrypted_data)); |
+ EXPECT_EQ(128u, encrypted_data.byteLength()); |
+ |
+ // Finally, decrypt the newly encrypted result with the private key, and |
+ // compare to the known original cleartext. |
+ decrypted_data.reset(); |
+ ASSERT_TRUE(DecryptInternal( |
+ algorithm, |
+ private_key, |
+ reinterpret_cast<const unsigned char*>(encrypted_data.data()), |
+ encrypted_data.byteLength(), |
+ &decrypted_data)); |
+ EXPECT_FALSE(decrypted_data.isNull()); |
+ ExpectArrayBufferMatchesHex(cleartext_hex, decrypted_data); |
+} |
+ |
+TEST_F(WebCryptoImplTest, RsaEsFailures) { |
+ // Note: using unrealistic short key length here to avoid bogging down tests. |
+ |
+ // Create a key pair. |
+ const unsigned kModulusLength = 256; |
+ blink::WebCryptoAlgorithm algorithm = |
+ CreateRsaKeyGenAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5, |
+ kModulusLength, |
+ HexStringToBytes("010001")); |
+ const blink::WebCryptoKeyUsageMask usage_mask = |
+ blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt; |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
+ EXPECT_TRUE(GenerateKeyPairInternal( |
+ algorithm, false, usage_mask, &public_key, &private_key)); |
+ EXPECT_FALSE(public_key.isNull()); |
+ EXPECT_FALSE(private_key.isNull()); |
+ |
+ // Fail encrypt with a private key. |
+ algorithm = CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
+ blink::WebArrayBuffer encrypted_data; |
+ const std::string message_hex_str("0102030405060708090a0b0c0d0e0f"); |
+ const std::vector<uint8> message_hex(HexStringToBytes(message_hex_str)); |
+ EXPECT_FALSE( |
+ EncryptInternal(algorithm, private_key, message_hex, &encrypted_data)); |
+ |
+ // Fail encrypt with empty message. |
+ EXPECT_FALSE(EncryptInternal( |
+ algorithm, public_key, std::vector<uint8>(), &encrypted_data)); |
+ |
+ // Fail encrypt with message too large. RSAES can operate on messages up to |
+ // length of k - 11 bytes, where k is the octet length of the RSA modulus. |
+ const unsigned kMaxMsgSizeBytes = kModulusLength / 8 - 11; |
+ EXPECT_FALSE(EncryptInternal(algorithm, |
+ public_key, |
+ std::vector<uint8>(kMaxMsgSizeBytes + 1, '0'), |
+ &encrypted_data)); |
+ |
+ // Generate encrypted data. |
+ EXPECT_TRUE( |
+ EncryptInternal(algorithm, public_key, message_hex, &encrypted_data)); |
+ |
+ // Fail decrypt with a public key. |
+ blink::WebArrayBuffer decrypted_data; |
+ EXPECT_FALSE(DecryptInternal( |
+ algorithm, |
+ public_key, |
+ reinterpret_cast<const unsigned char*>(encrypted_data.data()), |
+ encrypted_data.byteLength(), |
+ &decrypted_data)); |
+ |
+ // Corrupt encrypted data; ensure decrypt fails because padding was disrupted. |
+ std::vector<uint8> corrupted_data( |
+ static_cast<uint8*>(encrypted_data.data()), |
+ static_cast<uint8*>(encrypted_data.data()) + encrypted_data.byteLength()); |
+ corrupted_data[corrupted_data.size() / 2] ^= 0x01; |
+ EXPECT_FALSE( |
+ DecryptInternal(algorithm, private_key, corrupted_data, &decrypted_data)); |
+ |
+ // TODO(padolph): Are there other specific data corruption scenarios to |
+ // consider? |
+ |
+ // Do a successful decrypt with good data just for confirmation. |
+ EXPECT_TRUE(DecryptInternal( |
+ algorithm, |
+ private_key, |
+ reinterpret_cast<const unsigned char*>(encrypted_data.data()), |
+ encrypted_data.byteLength(), |
+ &decrypted_data)); |
+ ExpectArrayBufferMatchesHex(message_hex_str, decrypted_data); |
+} |
+ |
#endif // #if !defined(USE_OPENSSL) |
} // namespace content |