Index: content/child/webcrypto/shared_crypto_unittest.cc |
diff --git a/content/child/webcrypto/shared_crypto_unittest.cc b/content/child/webcrypto/shared_crypto_unittest.cc |
index 7613194c61ffae55c300d7809718b11436e44a81..2fdb271575d798273cc5c27d58c17117f5aeafeb 100644 |
--- a/content/child/webcrypto/shared_crypto_unittest.cc |
+++ b/content/child/webcrypto/shared_crypto_unittest.cc |
@@ -29,6 +29,13 @@ |
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" |
#include "third_party/re2/re2/re2.h" |
+#if !defined(USE_OPENSSL) |
+#include <nss.h> |
+#include <pk11pub.h> |
+ |
+#include "crypto/scoped_nss_types.h" |
+#endif |
+ |
// The OpenSSL implementation of WebCrypto is less complete, so don't run all of |
// the tests: http://crbug.com/267888 |
#if defined(USE_OPENSSL) |
@@ -106,6 +113,17 @@ bool SupportsAesGcm() { |
return status.IsSuccess(); |
} |
+bool SupportsRsaOaep() { |
+#if defined(USE_OPENSSL) |
+ return false; |
+#else |
+ if (!NSS_VersionCheck("3.16.2")) |
+ return false; |
+ crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); |
+ return !!PK11_DoesMechanism(slot.get(), CKM_RSA_PKCS_OAEP); |
+#endif |
+} |
+ |
blink::WebCryptoAlgorithm CreateRsaKeyGenAlgorithm( |
blink::WebCryptoAlgorithmId algorithm_id, |
unsigned int modulus_length, |
@@ -136,6 +154,15 @@ blink::WebCryptoAlgorithm CreateRsaHashedKeyGenAlgorithm( |
public_exponent.size())); |
} |
+// Creates an RSA-OAEP algorithm |
+blink::WebCryptoAlgorithm CreateRsaOaepAlgorithm( |
+ const std::vector<uint8>& label) { |
+ return blink::WebCryptoAlgorithm::adoptParamsAndCreate( |
+ blink::WebCryptoAlgorithmIdRsaOaep, |
+ new blink::WebCryptoRsaOaepParams( |
+ !label.empty(), Uint8VectorStart(label), label.size())); |
+} |
+ |
// Creates an AES-CBC algorithm. |
blink::WebCryptoAlgorithm CreateAesCbcAlgorithm(const std::vector<uint8>& iv) { |
return blink::WebCryptoAlgorithm::adoptParamsAndCreate( |
@@ -403,6 +430,13 @@ const char* const kPrivateKeyPkcs8DerHex = |
"156aba4126d24a81f3a528cbfb27f56886f840a9f6e86e17a44b94fe9319" |
"584b8e22fdde1e5a2e3bd8aa5ba8d8584194eb2190acf832b847f13a3d24" |
"a79f4d"; |
+// The modulus and exponent (in hex) of kPublicKeySpkiDerHex |
+const char* const kPublicKeyModulusHex = |
+ "A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056" |
+ "FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B" |
+ "8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E138" |
+ "6B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137"; |
+const char* const kPublicKeyExponentHex = "010001"; |
class SharedCryptoTest : public testing::Test { |
protected: |
@@ -439,7 +473,7 @@ void ImportRsaKeyPair(const std::vector<uint8>& spki_der, |
blink::WebCryptoKeyUsageMask usage_mask, |
blink::WebCryptoKey* public_key, |
blink::WebCryptoKey* private_key) { |
- EXPECT_EQ(Status::Success(), |
+ ASSERT_EQ(Status::Success(), |
ImportKey(blink::WebCryptoKeyFormatSpki, |
CryptoData(spki_der), |
algorithm, |
@@ -453,7 +487,7 @@ void ImportRsaKeyPair(const std::vector<uint8>& spki_der, |
EXPECT_EQ(extractable, extractable); |
EXPECT_EQ(usage_mask, public_key->usages()); |
- EXPECT_EQ(Status::Success(), |
+ ASSERT_EQ(Status::Success(), |
ImportKey(blink::WebCryptoKeyFormatPkcs8, |
CryptoData(pkcs8_der), |
algorithm, |
@@ -1439,15 +1473,11 @@ TEST_F(SharedCryptoTest, ImportJwkOctFailures) { |
} |
TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkRsaPublicKey)) { |
- // This test uses kPublicKeySpkiDerHex as the RSA key. The data below |
- // represents the modulus and public exponent extracted from this SPKI blob. |
- // These values appear explicitly in the JWK rendering of the key. |
- const std::string n_hex = |
- "A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056" |
- "FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B" |
- "8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E138" |
- "6B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137"; |
- const std::string e_hex = "010001"; |
+ const bool supports_rsa_oaep = SupportsRsaOaep(); |
+ if (!supports_rsa_oaep) { |
+ LOG(WARNING) << "RSA-OAEP not supported on this platform. Skipping some" |
+ << "tests."; |
+ } |
struct TestCase { |
const blink::WebCryptoAlgorithm algorithm; |
@@ -1477,12 +1507,32 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkRsaPublicKey)) { |
{CreateRsaHashedImportAlgorithm( |
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
blink::WebCryptoAlgorithmIdSha512), |
- blink::WebCryptoKeyUsageSign, "RS512"}}; |
+ blink::WebCryptoKeyUsageSign, "RS512"}, |
+ // RSA-OAEP with SHA-1 and MGF-1 / SHA-1 |
+ {CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha1), |
+ blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP"}, |
+ // RSA-OAEP with SHA-256 and MGF-1 / SHA-256 |
+ {CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha256), |
+ blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP-256"}, |
+ // RSA-OAEP with SHA-384 and MGF-1 / SHA-384 |
+ {CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha384), |
+ blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP-384"}, |
+ // RSA-OAEP with SHA-512 and MGF-1 / SHA-512 |
+ {CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha512), |
+ blink::WebCryptoKeyUsageEncrypt, "RSA-OAEP-512"}}; |
for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(kTests); |
++test_index) { |
SCOPED_TRACE(test_index); |
const TestCase& test = kTests[test_index]; |
+ if (!supports_rsa_oaep && |
+ test.algorithm.id() == blink::WebCryptoAlgorithmIdRsaOaep) { |
+ continue; |
+ } |
// Import the spki to create a public key |
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
@@ -1498,24 +1548,33 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkRsaPublicKey)) { |
std::vector<uint8> jwk; |
ASSERT_EQ(Status::Success(), |
ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk)); |
- EXPECT_TRUE(VerifyPublicJwk(jwk, test.jwk_alg, n_hex, e_hex, test.usage)); |
+ EXPECT_TRUE(VerifyPublicJwk(jwk, |
+ test.jwk_alg, |
+ kPublicKeyModulusHex, |
+ kPublicKeyExponentHex, |
+ test.usage)); |
// Import the JWK back in to create a new key |
blink::WebCryptoKey public_key2 = blink::WebCryptoKey::createNull(); |
- EXPECT_EQ( |
+ ASSERT_EQ( |
Status::Success(), |
ImportKeyJwk( |
CryptoData(jwk), test.algorithm, true, test.usage, &public_key2)); |
- EXPECT_TRUE(public_key2.handle()); |
+ ASSERT_TRUE(public_key2.handle()); |
EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type()); |
EXPECT_EQ(true, public_key2.extractable()); |
EXPECT_EQ(test.algorithm.id(), public_key2.algorithm().id()); |
- // Export the new key as spki and compare to the original. |
- std::vector<uint8> spki; |
- ASSERT_EQ(Status::Success(), |
- ExportKey(blink::WebCryptoKeyFormatSpki, public_key2, &spki)); |
- EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, CryptoData(spki)); |
+ // Only perform SPKI consistency test for RSA-ES and RSA-SSA, as their |
+ // export format is the same as kPublicKeySpkiDerHex |
+ if (test.algorithm.id() == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || |
+ test.algorithm.id() == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5) { |
+ // Export the new key as spki and compare to the original. |
+ std::vector<uint8> spki; |
+ ASSERT_EQ(Status::Success(), |
+ ExportKey(blink::WebCryptoKeyFormatSpki, public_key2, &spki)); |
+ EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, CryptoData(spki)); |
+ } |
} |
} |
@@ -1980,6 +2039,13 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportSpki)) { |
EXPECT_FALSE(key.extractable()); |
EXPECT_EQ(Status::ErrorKeyNotExtractable(), |
ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); |
+ |
+ // TODO(eroman): Failing test: Import a SPKI with an unrecognized hash OID |
+ // TODO(eroman): Failing test: Import a SPKI with invalid algorithm params |
+ // TODO(eroman): Failing test: Import a SPKI with inconsistent parameters |
+ // (e.g. SHA-1 in OID, SHA-256 in params) |
+ // TODO(eroman): Failing test: Import a SPKI for RSA-SSA, but with params |
+ // as OAEP/PSS |
} |
TEST_F(SharedCryptoTest, MAYBE(ImportExportPkcs8)) { |
@@ -2234,14 +2300,14 @@ TEST_F(SharedCryptoTest, MAYBE(RsaEsRoundTrip)) { |
CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
- ImportRsaKeyPair( |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
HexStringToBytes(kPublicKeySpkiDerHex), |
HexStringToBytes(kPrivateKeyPkcs8DerHex), |
algorithm, |
false, |
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, |
&public_key, |
- &private_key); |
+ &private_key)); |
// 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. |
@@ -2302,14 +2368,14 @@ TEST_F(SharedCryptoTest, MAYBE(RsaEsKnownAnswer)) { |
CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
- ImportRsaKeyPair( |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
rsa_spki_der, |
rsa_pkcs8_der, |
algorithm, |
false, |
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, |
&public_key, |
- &private_key); |
+ &private_key)); |
// Decrypt the known-good ciphertext with the private key. As a check we must |
// get the known original cleartext. |
@@ -2343,14 +2409,14 @@ TEST_F(SharedCryptoTest, MAYBE(RsaEsFailures)) { |
CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
- ImportRsaKeyPair( |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
HexStringToBytes(kPublicKeySpkiDerHex), |
HexStringToBytes(kPrivateKeyPkcs8DerHex), |
algorithm, |
false, |
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, |
&public_key, |
- &private_key); |
+ &private_key)); |
// Fail encrypt with a private key. |
std::vector<uint8> encrypted_data; |
@@ -2416,13 +2482,14 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { |
blink::WebCryptoAlgorithmIdSha1); |
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
- ImportRsaKeyPair(HexStringToBytes(kPublicKeySpkiDerHex), |
- HexStringToBytes(kPrivateKeyPkcs8DerHex), |
- importAlgorithm, |
- false, |
- usage_mask, |
- &public_key, |
- &private_key); |
+ ASSERT_NO_FATAL_FAILURE( |
+ ImportRsaKeyPair(HexStringToBytes(kPublicKeySpkiDerHex), |
+ HexStringToBytes(kPrivateKeyPkcs8DerHex), |
+ importAlgorithm, |
+ false, |
+ usage_mask, |
+ &public_key, |
+ &private_key)); |
blink::WebCryptoAlgorithm algorithm = |
CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); |
@@ -2559,14 +2626,14 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSignVerifyKnownAnswer)) { |
blink::WebCryptoAlgorithmIdSha1); |
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
- ImportRsaKeyPair( |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
HexStringToBytes(kPublicKeySpkiDerHex), |
HexStringToBytes(kPrivateKeyPkcs8DerHex), |
importAlgorithm, |
false, |
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, |
&public_key, |
- &private_key); |
+ &private_key)); |
blink::WebCryptoAlgorithm algorithm = |
CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); |
@@ -3140,14 +3207,14 @@ TEST_F(SharedCryptoTest, MAYBE(RsaEsRawSymkeyWrapUnwrapKnownAnswer)) { |
CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
- ImportRsaKeyPair( |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
rsa_spki_der, |
rsa_pkcs8_der, |
algorithm, |
false, |
blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, |
&public_key, |
- &private_key); |
+ &private_key)); |
// Import the symmetric key. |
blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); |
@@ -3217,14 +3284,14 @@ TEST_F(SharedCryptoTest, MAYBE(RsaEsRawSymkeyWrapUnwrapErrors)) { |
CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
- ImportRsaKeyPair( |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
HexStringToBytes(kPublicKeySpkiDerHex), |
HexStringToBytes(kPrivateKeyPkcs8DerHex), |
wrapping_algorithm, |
false, |
blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, |
&public_key, |
- &private_key); |
+ &private_key)); |
// Import the symmetric key. |
blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); |
@@ -3370,14 +3437,14 @@ TEST_F(SharedCryptoTest, MAYBE(RsaEsJwkSymkeyWrapUnwrapRoundTrip)) { |
CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
blink::WebCryptoKey public_wrapping_key = blink::WebCryptoKey::createNull(); |
blink::WebCryptoKey private_wrapping_key = blink::WebCryptoKey::createNull(); |
- ImportRsaKeyPair( |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
HexStringToBytes(kPublicKeySpkiDerHex), |
HexStringToBytes(kPrivateKeyPkcs8DerHex), |
wrapping_algorithm, |
false, |
blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, |
&public_wrapping_key, |
- &private_wrapping_key); |
+ &private_wrapping_key)); |
// Wrap the symkey in JWK format, using the public wrapping key. |
std::vector<uint8> wrapped_data; |
@@ -3479,6 +3546,551 @@ TEST_F(SharedCryptoTest, MAYBE(RsaEsJwkSymkeyWrapUnwrapErrors)) { |
&unwrapped_key)); |
} |
+class SharedCryptoRsaOaepTest : public ::testing::Test { |
+ public: |
+ SharedCryptoRsaOaepTest() { Init(); } |
+ |
+ scoped_ptr<base::DictionaryValue> CreatePublicKeyJwkDict() { |
+ scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue()); |
+ jwk->SetString("kty", "RSA"); |
+ jwk->SetString("n", |
+ Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyModulusHex))); |
+ jwk->SetString( |
+ "e", Base64EncodeUrlSafe(HexStringToBytes(kPublicKeyExponentHex))); |
+ return jwk.Pass(); |
+ } |
+}; |
+ |
+// Import a PKCS#8 private key that uses RSAPrivateKey with the |
+// id-rsaEncryption OID. |
+TEST_F(SharedCryptoRsaOaepTest, ImportPkcs8WithRsaEncryption) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::Success(), |
+ ImportKey(blink::WebCryptoKeyFormatPkcs8, |
+ CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
+ CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha1), |
+ true, |
+ blink::WebCryptoKeyUsageDecrypt, |
+ &private_key)); |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, ImportPublicJwkWithNoAlg) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); |
+ |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::Success(), |
+ ImportKeyJwkFromDict(*jwk.get(), |
+ CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha1), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &public_key)); |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, ImportPublicJwkWithMatchingAlg) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); |
+ jwk->SetString("alg", "RSA-OAEP"); |
+ |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::Success(), |
+ ImportKeyJwkFromDict(*jwk.get(), |
+ CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha1), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &public_key)); |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, ImportPublicJwkWithMismatchedAlgFails) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); |
+ jwk->SetString("alg", "RSA-OAEP-512"); |
+ |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::ErrorJwkAlgorithmInconsistent(), |
+ ImportKeyJwkFromDict(*jwk.get(), |
+ CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha1), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &public_key)); |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, ImportPublicJwkWithMismatchedTypFails) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); |
+ jwk->SetString("kty", "oct"); |
+ jwk->SetString("alg", "RSA-OAEP"); |
+ |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::ErrorJwkPropertyMissing("k"), |
+ ImportKeyJwkFromDict(*jwk.get(), |
+ CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha1), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &public_key)); |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, ExportPublicJwk) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ struct TestData { |
+ blink::WebCryptoAlgorithmId hash_alg; |
+ const char* expected_jwk_alg; |
+ } kTestData[] = {{blink::WebCryptoAlgorithmIdSha1, "RSA-OAEP"}, |
+ {blink::WebCryptoAlgorithmIdSha256, "RSA-OAEP-256"}, |
+ {blink::WebCryptoAlgorithmIdSha384, "RSA-OAEP-384"}, |
+ {blink::WebCryptoAlgorithmIdSha512, "RSA-OAEP-512"}}; |
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestData); ++i) { |
+ const TestData& test_data = kTestData[i]; |
+ SCOPED_TRACE(test_data.expected_jwk_alg); |
+ |
+ scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); |
+ jwk->SetString("alg", test_data.expected_jwk_alg); |
+ |
+ // Import the key in a known-good format |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::Success(), |
+ ImportKeyJwkFromDict( |
+ *jwk.get(), |
+ CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, test_data.hash_alg), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &public_key)); |
+ |
+ // Now export the key as JWK and verify its contents |
+ std::vector<uint8> jwk_data; |
+ ASSERT_EQ(Status::Success(), |
+ ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk_data)); |
+ EXPECT_TRUE(VerifyPublicJwk(jwk_data, |
+ test_data.expected_jwk_alg, |
+ kPublicKeyModulusHex, |
+ kPublicKeyExponentHex, |
+ blink::WebCryptoKeyUsageEncrypt)); |
+ } |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ scoped_ptr<base::ListValue> tests; |
+ ASSERT_TRUE(ReadJsonTestFileToList("rsa_oaep.json", &tests)); |
+ |
+ for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { |
+ SCOPED_TRACE(test_index); |
+ |
+ base::DictionaryValue* test = NULL; |
+ ASSERT_TRUE(tests->GetDictionary(test_index, &test)); |
+ |
+ blink::WebCryptoAlgorithm digest_algorithm = |
+ GetDigestAlgorithm(test, "hash"); |
+ ASSERT_FALSE(digest_algorithm.isNull()); |
+ std::vector<uint8> public_key_der = |
+ GetBytesFromHexString(test, "public_key"); |
+ std::vector<uint8> private_key_der = |
+ GetBytesFromHexString(test, "private_key"); |
+ std::vector<uint8> ciphertext = GetBytesFromHexString(test, "ciphertext"); |
+ std::vector<uint8> plaintext = GetBytesFromHexString(test, "plaintext"); |
+ std::vector<uint8> label = GetBytesFromHexString(test, "label"); |
+ |
+ blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, digest_algorithm.id()); |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
+ |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
+ public_key_der, |
+ private_key_der, |
+ import_algorithm, |
+ false, |
+ blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, |
+ &public_key, |
+ &private_key)); |
+ |
+ blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); |
+ std::vector<uint8> decrypted_data; |
+ ASSERT_EQ(Status::Success(), |
+ Decrypt(op_algorithm, |
+ private_key, |
+ CryptoData(ciphertext), |
+ &decrypted_data)); |
+ EXPECT_BYTES_EQ(plaintext, decrypted_data); |
+ std::vector<uint8> encrypted_data; |
+ ASSERT_EQ( |
+ Status::Success(), |
+ Encrypt( |
+ op_algorithm, public_key, CryptoData(plaintext), &encrypted_data)); |
+ std::vector<uint8> redecrypted_data; |
+ ASSERT_EQ(Status::Success(), |
+ Decrypt(op_algorithm, |
+ private_key, |
+ CryptoData(encrypted_data), |
+ &redecrypted_data)); |
+ EXPECT_BYTES_EQ(plaintext, redecrypted_data); |
+ } |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, EncryptWithLargeMessageFails) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ const blink::WebCryptoAlgorithmId kHash = blink::WebCryptoAlgorithmIdSha1; |
+ const size_t kHashSize = 20; |
+ |
+ scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); |
+ |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::Success(), |
+ ImportKeyJwkFromDict(*jwk.get(), |
+ CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, kHash), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &public_key)); |
+ |
+ // The maximum size of an encrypted message is: |
+ // modulus length |
+ // - 1 (leading octet) |
+ // - hash size (maskedSeed) |
+ // - hash size (lHash portion of maskedDB) |
+ // - 1 (at least one octet for the padding string) |
+ size_t kMaxMessageSize = (kModulusLengthBits / 8) - 2 - (2 * kHashSize); |
+ |
+ // The label has no influence on the maximum message size. For simplicity, |
+ // use the empty string. |
+ std::vector<uint8> label; |
+ blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); |
+ |
+ // Test that a message just before the boundary succeeds. |
+ std::string large_message; |
+ large_message.resize(kMaxMessageSize - 1, 'A'); |
+ |
+ std::vector<uint8> ciphertext; |
+ ASSERT_EQ( |
+ Status::Success(), |
+ Encrypt( |
+ op_algorithm, public_key, CryptoData(large_message), &ciphertext)); |
+ |
+ // Test that a message at the boundary succeeds. |
+ large_message.resize(kMaxMessageSize, 'A'); |
+ ciphertext.clear(); |
+ |
+ ASSERT_EQ( |
+ Status::Success(), |
+ Encrypt( |
+ op_algorithm, public_key, CryptoData(large_message), &ciphertext)); |
+ |
+ // Test that a message greater than the largest size fails. |
+ large_message.resize(kMaxMessageSize + 1, 'A'); |
+ ciphertext.clear(); |
+ |
+ ASSERT_EQ( |
+ Status::OperationError(), |
+ Encrypt( |
+ op_algorithm, public_key, CryptoData(large_message), &ciphertext)); |
+} |
+ |
+// Ensures that if the selected hash algorithm for the RSA-OAEP message is too |
+// large, then it is rejected, independent of the actual message to be |
+// encrypted. |
+// For example, a 1024-bit RSA key is too small to accomodate a message that |
+// uses OAEP with SHA-512, since it requires 1040 bits to encode |
+// (2 * hash size + 2 padding bytes). |
+TEST_F(SharedCryptoRsaOaepTest, EncryptWithLargeDigestFails) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ const blink::WebCryptoAlgorithmId kHash = blink::WebCryptoAlgorithmIdSha512; |
+ |
+ scoped_ptr<base::DictionaryValue> jwk(CreatePublicKeyJwkDict()); |
+ |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::Success(), |
+ ImportKeyJwkFromDict(*jwk.get(), |
+ CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, kHash), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &public_key)); |
+ |
+ // The label has no influence on the maximum message size. For simplicity, |
+ // use the empty string. |
+ std::vector<uint8> label; |
+ blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); |
+ |
+ std::string small_message("A"); |
+ std::vector<uint8> ciphertext; |
+ // This is an operation error, as the internal consistency checking of the |
+ // algorithm parameters is up to the implementation. |
+ ASSERT_EQ( |
+ Status::OperationError(), |
+ Encrypt( |
+ op_algorithm, public_key, CryptoData(small_message), &ciphertext)); |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, DecryptWithLargeMessageFails) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::Success(), |
+ ImportKey(blink::WebCryptoKeyFormatPkcs8, |
+ CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
+ CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, |
+ blink::WebCryptoAlgorithmIdSha1), |
+ true, |
+ blink::WebCryptoKeyUsageDecrypt, |
+ &private_key)); |
+ |
+ // The label has no influence on the maximum message size. For simplicity, |
+ // use the empty string. |
+ std::vector<uint8> label; |
+ blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); |
+ |
+ std::string large_dummy_message(kModulusLengthBits / 8, 'A'); |
+ std::vector<uint8> plaintext; |
+ |
+ ASSERT_EQ(Status::OperationError(), |
+ Decrypt(op_algorithm, |
+ private_key, |
+ CryptoData(large_dummy_message), |
+ &plaintext)); |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapRawKey) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, blink::WebCryptoAlgorithmIdSha1); |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
+ |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
+ HexStringToBytes(kPublicKeySpkiDerHex), |
+ HexStringToBytes(kPrivateKeyPkcs8DerHex), |
+ import_algorithm, |
+ false, |
+ blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
+ blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, |
+ &public_key, |
+ &private_key)); |
+ |
+ std::vector<uint8> label; |
+ blink::WebCryptoAlgorithm wrapping_algorithm = CreateRsaOaepAlgorithm(label); |
+ |
+ const std::string key_hex = "000102030405060708090A0B0C0D0E0F"; |
+ const blink::WebCryptoAlgorithm key_algorithm = |
+ webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); |
+ |
+ blink::WebCryptoKey key = |
+ ImportSecretKeyFromRaw(HexStringToBytes(key_hex), |
+ key_algorithm, |
+ blink::WebCryptoKeyUsageEncrypt); |
+ ASSERT_FALSE(key.isNull()); |
+ |
+ std::vector<uint8> wrapped_key; |
+ ASSERT_EQ(Status::Success(), |
+ WrapKey(blink::WebCryptoKeyFormatRaw, |
+ key, |
+ public_key, |
+ wrapping_algorithm, |
+ &wrapped_key)); |
+ |
+ // Verify that |wrapped_key| can be decrypted and yields the key data. |
+ // Because |private_key| supports both decrypt and unwrap, this is valid. |
+ std::vector<uint8> decrypted_key; |
+ ASSERT_EQ(Status::Success(), |
+ Decrypt(wrapping_algorithm, |
+ private_key, |
+ CryptoData(wrapped_key), |
+ &decrypted_key)); |
+ EXPECT_BYTES_EQ_HEX(key_hex, decrypted_key); |
+ |
+ // Now attempt to unwrap the key, which should also decrypt the data. |
+ blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::Success(), |
+ UnwrapKey(blink::WebCryptoKeyFormatRaw, |
+ CryptoData(wrapped_key), |
+ private_key, |
+ wrapping_algorithm, |
+ key_algorithm, |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &unwrapped_key)); |
+ ASSERT_FALSE(unwrapped_key.isNull()); |
+ |
+ std::vector<uint8> raw_key; |
+ ASSERT_EQ(Status::Success(), |
+ ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); |
+ EXPECT_BYTES_EQ_HEX(key_hex, raw_key); |
+} |
+ |
+TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapJwkSymKey) { |
+ if (!SupportsRsaOaep()) { |
+ LOG(WARNING) << "RSA-OAEP support not present; skipping."; |
+ return; |
+ } |
+ |
+ // The public and private portions of a 2048-bit RSA key with the |
+ // id-rsaEncryption OID |
+ const char kPublicKey2048SpkiDerHex[] = |
+ "30820122300d06092a864886f70d01010105000382010f003082010a0282010100c5d8ce" |
+ "137a38168c8ab70229cfa5accc640567159750a312ce2e7d54b6e2fdd59b300c6a6c9764" |
+ "f8de6f00519cdb90111453d273a967462786480621f9e7cee5b73d63358448e7183a3a68" |
+ "e991186359f26aa88fbca5f53e673e502e4c5a2ba5068aeba60c9d0c44d872458d1b1e2f" |
+ "7f339f986076d516e93dc750f0b7680b6f5f02bc0d5590495be04c4ae59d34ba17bc5d08" |
+ "a93c75cfda2828f4a55b153af912038438276cb4a14f8116ca94db0ea9893652d02fc606" |
+ "36f19975e3d79a4d8ea8bfed6f8e0a24b63d243b08ea70a086ad56dd6341d733711c89ca" |
+ "749d4a80b3e6ecd2f8e53731eadeac2ea77788ee55d7b4b47c0f2523fbd61b557c16615d" |
+ "5d0203010001"; |
+ const char kPrivateKey2048Pkcs8DerHex[] = |
+ "308204bd020100300d06092a864886f70d0101010500048204a7308204a3020100028201" |
+ "0100c5d8ce137a38168c8ab70229cfa5accc640567159750a312ce2e7d54b6e2fdd59b30" |
+ "0c6a6c9764f8de6f00519cdb90111453d273a967462786480621f9e7cee5b73d63358448" |
+ "e7183a3a68e991186359f26aa88fbca5f53e673e502e4c5a2ba5068aeba60c9d0c44d872" |
+ "458d1b1e2f7f339f986076d516e93dc750f0b7680b6f5f02bc0d5590495be04c4ae59d34" |
+ "ba17bc5d08a93c75cfda2828f4a55b153af912038438276cb4a14f8116ca94db0ea98936" |
+ "52d02fc60636f19975e3d79a4d8ea8bfed6f8e0a24b63d243b08ea70a086ad56dd6341d7" |
+ "33711c89ca749d4a80b3e6ecd2f8e53731eadeac2ea77788ee55d7b4b47c0f2523fbd61b" |
+ "557c16615d5d02030100010282010074b70feb41a0b0fcbc207670400556c9450042ede3" |
+ "d4383fb1ce8f3558a6d4641d26dd4c333fa4db842d2b9cf9d2354d3e16ad027a9f682d8c" |
+ "f4145a1ad97b9edcd8a41c402bd9d8db10f62f43df854cdccbbb2100834f083f53ed6d42" |
+ "b1b729a59072b004a4e945fc027db15e9c121d1251464d320d4774d5732df6b3dbf751f4" |
+ "9b19c9db201e19989c883bbaad5333db47f64f6f7a95b8d4936b10d945aa3f794cfaab62" |
+ "e7d47686129358914f3b8085f03698a650ab5b8c7e45813f2b0515ec05b6e5195b6a7c2a" |
+ "0d36969745f431ded4fd059f6aa361a4649541016d356297362b778e90f077d48815b339" |
+ "ec6f43aba345df93e67fcb6c2cb5b4544e9be902818100e9c90abe5f9f32468c5b6d630c" |
+ "54a4d7d75e29a72cf792f21e242aac78fd7995c42dfd4ae871d2619ff7096cb05baa78e3" |
+ "23ecab338401a8059adf7a0d8be3b21edc9a9c82c5605634a2ec81ec053271721351868a" |
+ "4c2e50c689d7cef94e31ff23658af5843366e2b289c5bf81d72756a7b93487dd8770d69c" |
+ "1f4e089d6d89f302818100d8a58a727c4e209132afd9933b98c89aca862a01cc0be74133" |
+ "bee517909e5c379e526895ac4af11780c1fe91194c777c9670b6423f0f5a32fd7691a622" |
+ "113eef4bed2ef863363a335fd55b0e75088c582437237d7f3ed3f0a643950237bc6e6277" |
+ "ccd0d0a1b4170aa1047aa7ffa7c8c54be10e8c7327ae2e0885663963817f6f02818100e5" |
+ "aed9ba4d71b7502e6748a1ce247ecb7bd10c352d6d9256031cdf3c11a65e44b0b7ca2945" |
+ "134671195af84c6b3bb3d10ebf65ae916f38bd5dbc59a0ad1c69b8beaf57cb3a8335f19b" |
+ "c7117b576987b48331cd9fd3d1a293436b7bb5e1a35c6560de4b5688ea834367cb0997eb" |
+ "b578f59ed4cb724c47dba94d3b484c1876dcd70281807f15bc7d2406007cac2b138a96af" |
+ "2d1e00276b84da593132c253fcb73212732dfd25824c2a615bc3d9b7f2c8d2fa542d3562" |
+ "b0c7738e61eeff580a6056239fb367ea9e5efe73d4f846033602e90c36a78db6fa8ea792" |
+ "0769675ec58e237bd994d189c8045a96f5dd3a4f12547257ce224e3c9af830a4da3c0eab" |
+ "9227a0035ae9028180067caea877e0b23090fc689322b71fbcce63d6596e66ab5fcdbaa0" |
+ "0d49e93aba8effb4518c2da637f209028401a68f344865b4956b032c69acde51d29177ca" |
+ "3db99fdbf5e74848ed4fa7bdfc2ebb60e2aaa5354770a763e1399ab7a2099762d525fea0" |
+ "37f3e1972c45a477e66db95c9609bb27f862700ef93379930786cf751b"; |
+ blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( |
+ blink::WebCryptoAlgorithmIdRsaOaep, blink::WebCryptoAlgorithmIdSha1); |
+ blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
+ blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); |
+ |
+ ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
+ HexStringToBytes(kPublicKey2048SpkiDerHex), |
+ HexStringToBytes(kPrivateKey2048Pkcs8DerHex), |
+ import_algorithm, |
+ false, |
+ blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
+ blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, |
+ &public_key, |
+ &private_key)); |
+ |
+ std::vector<uint8> label; |
+ blink::WebCryptoAlgorithm wrapping_algorithm = CreateRsaOaepAlgorithm(label); |
+ |
+ const std::string key_hex = "000102030405060708090a0b0c0d0e0f"; |
+ const blink::WebCryptoAlgorithm key_algorithm = |
+ webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc); |
+ |
+ blink::WebCryptoKey key = |
+ ImportSecretKeyFromRaw(HexStringToBytes(key_hex), |
+ key_algorithm, |
+ blink::WebCryptoKeyUsageEncrypt); |
+ ASSERT_FALSE(key.isNull()); |
+ |
+ std::vector<uint8> wrapped_key; |
+ ASSERT_EQ(Status::Success(), |
+ WrapKey(blink::WebCryptoKeyFormatJwk, |
+ key, |
+ public_key, |
+ wrapping_algorithm, |
+ &wrapped_key)); |
+ |
+ // Verify that |wrapped_key| can be decrypted and yields a valid JWK object. |
+ // Because |private_key| supports both decrypt and unwrap, this is valid. |
+ std::vector<uint8> decrypted_jwk; |
+ ASSERT_EQ(Status::Success(), |
+ Decrypt(wrapping_algorithm, |
+ private_key, |
+ CryptoData(wrapped_key), |
+ &decrypted_jwk)); |
+ EXPECT_TRUE(VerifySecretJwk( |
+ decrypted_jwk, "A128CBC", key_hex, blink::WebCryptoKeyUsageEncrypt)); |
+ |
+ // Now attempt to unwrap the key, which should also decrypt the data. |
+ blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_EQ(Status::Success(), |
+ UnwrapKey(blink::WebCryptoKeyFormatJwk, |
+ CryptoData(wrapped_key), |
+ private_key, |
+ wrapping_algorithm, |
+ key_algorithm, |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &unwrapped_key)); |
+ ASSERT_FALSE(unwrapped_key.isNull()); |
+ |
+ std::vector<uint8> raw_key; |
+ ASSERT_EQ(Status::Success(), |
+ ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); |
+ EXPECT_BYTES_EQ_HEX(key_hex, raw_key); |
+} |
+ |
} // namespace webcrypto |
} // namespace content |