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 814a26fb45ff31d140754e9e2ac4a3504cf9fdab..0f1422be584f3758926f60279540039ff7a2d6a7 100644 |
--- a/content/child/webcrypto/shared_crypto_unittest.cc |
+++ b/content/child/webcrypto/shared_crypto_unittest.cc |
@@ -2650,62 +2650,6 @@ TEST_F(SharedCryptoTest, MAYBE(AesKwJwkSymkeyUnwrapKnownData)) { |
EXPECT_TRUE(ArrayBufferMatches(key_data, raw_key)); |
} |
-TEST_F(SharedCryptoTest, MAYBE(AesKwJwkSymkeyUnwrapErrors)) { |
- // Unwrap data that can be successfully decrypted, but contains an error in |
- // the plaintext JWK, and ensure that a generic error is returned instead of |
- // some other more specific error, to show that information about the |
- // plaintext JWK inside the encrypted data is not leaked. |
- // Specifically, wrapped_key_data below is an AES-KW encrypted version of the |
- // plaintext JWK |
- // { |
- // "alg":"HS256", |
- // "ext":true, |
- // "k":"AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8", |
- // "key_ops":["verify"], |
- // "kty":"foo" <-- Invalid kty value |
- // } |
- // Recall that unwrapKey = decrypt followed by import. The wrapped_key_data |
- // will decrypt successfully, but the import step will fail because of the bad |
- // kty value. But unlike the standalone ImportKey() method which returns |
- // ErrorJwkUnrecognizedKty in this case, the error returned must be just |
- // Error::Status(). |
- // Note that it is sufficient to consider just one JWK import failure mode |
- // here; others are validated in the ImportJwkFailures Test. |
- const std::vector<uint8> wrapped_key_data = HexStringToBytes( |
- "8d5ad45f5be6195a7a5944f0cf521bbae255daea140d4712985bb63ca1de1a318fbc49ff" |
- "307bd91bfafd7e9ea2057a2ddabb42ba94e319465972d165e5cc42785ad5cfa36159d5cc" |
- "50084133eae85a22bf8f7cb35f3c07b7c06480dec745d9ce4d4bfce45a6cbc2d39263ab7" |
- "073fc346724841f872f7148d"); |
- const std::vector<uint8> wrapping_key_data = |
- HexStringToBytes("000102030405060708090A0B0C0D0E0F"); |
- const blink::WebCryptoAlgorithm wrapping_algorithm = |
- webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw); |
- |
- // Import the wrapping key. |
- blink::WebCryptoKey wrapping_key = ImportSecretKeyFromRaw( |
- wrapping_key_data, wrapping_algorithm, blink::WebCryptoKeyUsageUnwrapKey); |
- |
- // Unwrap and ensure a generic error is received. |
- blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); |
- EXPECT_STATUS(Status::Error(), |
- UnwrapKey(blink::WebCryptoKeyFormatJwk, |
- CryptoData(wrapped_key_data), |
- wrapping_key, |
- wrapping_algorithm, |
- blink::WebCryptoAlgorithm::createNull(), |
- true, |
- blink::WebCryptoKeyUsageVerify, |
- &unwrapped_key)); |
- |
- // FIXME(padolph): The check above can fail if the AES-KW decryption step |
- // failed, which masks the test result desired here. For now we have to just |
- // trust the result because I say so. |
- // Once RSA-ES unwrapping is implemented, port this test to use that wrapping |
- // algorithm instead of AES-KW. Unlike AES-KW, RSA-ES supports both the |
- // decrypt and unwrapKey usages, so we can validate successful decryption of |
- // wrapped_key_data prior to seeing the unwrapKey (import) failure. |
-} |
- |
// TODO(eroman): |
// * Test decryption when the tag length exceeds input size |
// * Test decryption with empty input |
@@ -3000,6 +2944,179 @@ TEST_F(SharedCryptoTest, MAYBE(RsaEsRawSymkeyWrapUnwrapErrors)) { |
&unwrapped_key)); |
} |
+TEST_F(SharedCryptoTest, MAYBE(RsaEsJwkSymkeyUnwrapKnownAnswer)) { |
+ // The following data lists a known 128-bit AES-CBC key, then a JWK |
+ // representation of this key that was encrypted ("wrapped") using |
+ // RSAES-PKCS1-v1_5 and kPublicKeySpkiDerHex as the wrapping key. |
+ // For reference, the intermediate clear JWK is |
+ // {"alg":"A128CBC","ext":true,"k":<b64url>,"key_ops":["encrypt"],"kty":"oct"} |
+ const std::vector<uint8> key_data = |
+ HexStringToBytes("8f56a26e7e8b77dca15ed54339724bf5"); |
+ const std::vector<uint8> wrapped_key_data = HexStringToBytes( |
+ "9debcabd9c731d6a779622dbef38635419c409b3077af67b3cf0601b2da7054f2ec26156" |
+ "06bb764e4986f45dd09ce660432a7abbac48b5249924f12dea52275b6d67d8b8a2f63525" |
+ "fbbf67d61244c1afa9e30857b87b7a48cdc0b3196dc1477738cbf9e42ea65d5e0edc3b05" |
+ "afafadc7d7400e26a51270d251040d51ce46cecc"); |
+ const blink::WebCryptoAlgorithm wrapping_algorithm = |
+ webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
+ |
+ // Import the private wrapping key. |
+ blink::WebCryptoKey private_wrapping_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_STATUS_SUCCESS(ImportKey( |
+ blink::WebCryptoKeyFormatPkcs8, |
+ CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
+ wrapping_algorithm, |
+ false, |
+ blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageUnwrapKey, |
+ &private_wrapping_key)); |
+ |
+ // Unwrap the key. |
+ blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); |
+ EXPECT_STATUS_SUCCESS( |
+ UnwrapKey(blink::WebCryptoKeyFormatJwk, |
+ CryptoData(wrapped_key_data), |
+ private_wrapping_key, |
+ wrapping_algorithm, |
+ CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &unwrapped_key)); |
+ EXPECT_FALSE(unwrapped_key.isNull()); |
+ EXPECT_TRUE(unwrapped_key.handle()); |
+ EXPECT_EQ(blink::WebCryptoKeyTypeSecret, unwrapped_key.type()); |
+ EXPECT_EQ(blink::WebCryptoAlgorithmIdAesCbc, unwrapped_key.algorithm().id()); |
+ EXPECT_EQ(true, unwrapped_key.extractable()); |
+ EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt, unwrapped_key.usages()); |
+ |
+ // Export the unwrapped key and compare to the original. |
+ blink::WebArrayBuffer raw_key; |
+ EXPECT_STATUS_SUCCESS( |
+ ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key)); |
+ EXPECT_TRUE(ArrayBufferMatches(key_data, raw_key)); |
+} |
+ |
+TEST_F(SharedCryptoTest, MAYBE(RsaEsJwkSymkeyWrapUnwrapRoundTrip)) { |
+ // Generate the symkey to be wrapped (256-bit AES-CBC key). |
+ const blink::WebCryptoAlgorithm gen_algorithm = |
+ CreateAesCbcKeyGenAlgorithm(256); |
+ blink::WebCryptoKey key_to_wrap = blink::WebCryptoKey::createNull(); |
+ ASSERT_STATUS_SUCCESS(GenerateSecretKey( |
+ gen_algorithm, true, blink::WebCryptoKeyUsageEncrypt, &key_to_wrap)); |
+ |
+ // Import the wrapping key pair. |
+ const blink::WebCryptoAlgorithm wrapping_algorithm = |
+ CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
+ blink::WebCryptoKey public_wrapping_key = blink::WebCryptoKey::createNull(); |
+ blink::WebCryptoKey private_wrapping_key = blink::WebCryptoKey::createNull(); |
+ ImportRsaKeyPair( |
+ HexStringToBytes(kPublicKeySpkiDerHex), |
+ HexStringToBytes(kPrivateKeyPkcs8DerHex), |
+ wrapping_algorithm, |
+ false, |
+ blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, |
+ &public_wrapping_key, |
+ &private_wrapping_key); |
+ |
+ // Wrap the symkey in JWK format, using the public wrapping key. |
+ blink::WebArrayBuffer wrapped_data; |
+ ASSERT_STATUS_SUCCESS(WrapKey(blink::WebCryptoKeyFormatJwk, |
+ public_wrapping_key, |
+ key_to_wrap, |
+ wrapping_algorithm, |
+ &wrapped_data)); |
+ |
+ // Unwrap the key using the private wrapping key. |
+ blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_STATUS_SUCCESS( |
+ UnwrapKey(blink::WebCryptoKeyFormatJwk, |
+ CryptoData(wrapped_data), |
+ private_wrapping_key, |
+ wrapping_algorithm, |
+ CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &unwrapped_key)); |
+ |
+ // Export the original symkey and the unwrapped key and compare. |
+ blink::WebArrayBuffer raw_key1, raw_key2; |
+ EXPECT_STATUS_SUCCESS( |
+ ExportKey(blink::WebCryptoKeyFormatRaw, key_to_wrap, &raw_key1)); |
+ EXPECT_STATUS_SUCCESS( |
+ ExportKey(blink::WebCryptoKeyFormatRaw, unwrapped_key, &raw_key2)); |
+ EXPECT_TRUE(ArrayBuffersEqual(raw_key1, raw_key2)); |
+} |
+ |
+TEST_F(SharedCryptoTest, MAYBE(RsaEsJwkSymkeyWrapUnwrapErrors)) { |
+ // Unwrap JWK-formatted data that can be successfully decrypted, but contains |
+ // an error in the plaintext JWK so it cannot be subsequently imported, and |
+ // ensure that a generic error is returned instead of some other more specific |
+ // error. This shows that information about the plaintext JWK inside the |
+ // encrypted data is not leaked. |
+ // Note that it is sufficient to consider just one JWK import failure mode |
+ // here; others are validated in the ImportJwkFailures Test. The specific |
+ // error in the cleartext data below is kty = "foo", which is an invalid kty |
+ // value. |
+ const std::string cleartext = |
+ "{\"alg\":\"A128CBC\",\"ext\":true,\"k\":" |
+ "\"j1aibn6Ld9yhXtVDOXJL9Q\",\"key_ops\":[\"encrypt\"],\"kty\":\"foo\"}"; |
+ // ciphertext is the cleartext above encrypted with kPublicKeySpkiDerHex, and |
+ // can be decrypted with kPrivateKeyPkcs8DerHex |
+ const std::vector<uint8> ciphertext = HexStringToBytes( |
+ "93bc7bb2ca8502fcf3224e19b12ba455ac32d01695611022c76d3dbdd797c044de047d44" |
+ "6c5ed5de5b8f79147ffe1df8da9c894b58881b238d39bd24cecd5c1a98a7c0b07354aee6" |
+ "24791b2d549b7ecf1219c49513a1bcbb0fac5c6b59d350b564c44dc3678dadf84b4ea3d1" |
+ "32e576e88f8d4a2d27c173e033a97bbda7e47bb9"); |
+ |
+ // Import the private decryption key. |
+ const blink::WebCryptoAlgorithm algorithm = |
+ CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
+ blink::WebCryptoKey private_decryption_key = |
+ blink::WebCryptoKey::createNull(); |
+ ASSERT_STATUS_SUCCESS( |
+ ImportKey(blink::WebCryptoKeyFormatPkcs8, |
+ CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
+ algorithm, |
+ false, |
+ blink::WebCryptoKeyUsageDecrypt, |
+ &private_decryption_key)); |
+ |
+ // Decrypt the ciphertext and validate the result, to prove that decryption is |
+ // successful. |
+ blink::WebArrayBuffer decrypted_data; |
+ ASSERT_STATUS_SUCCESS(Decrypt(algorithm, |
+ private_decryption_key, |
+ CryptoData(ciphertext), |
+ &decrypted_data)); |
+ const std::string decrypted(static_cast<const char*>(decrypted_data.data()), |
+ decrypted_data.byteLength()); |
+ EXPECT_EQ(cleartext, decrypted); |
+ |
+ // Import the private wrapping key. Note this is the same underlying keying |
+ // material used for private_decryption_key above. The only difference is that |
+ // it has unwrap rather than decrypt usage. |
+ blink::WebCryptoKey private_wrapping_key = blink::WebCryptoKey::createNull(); |
+ ASSERT_STATUS_SUCCESS( |
+ ImportKey(blink::WebCryptoKeyFormatPkcs8, |
+ CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
+ algorithm, |
+ false, |
+ blink::WebCryptoKeyUsageUnwrapKey, |
+ &private_wrapping_key)); |
+ |
+ // Treat the ciphertext as a wrapped key and try to unwrap it. Ensure a |
+ // generic error is received. |
+ blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); |
+ EXPECT_STATUS(Status::Error(), |
+ UnwrapKey(blink::WebCryptoKeyFormatJwk, |
+ CryptoData(ciphertext), |
+ private_wrapping_key, |
+ algorithm, |
+ CreateAesCbcAlgorithm(std::vector<uint8>(0, 16)), |
+ true, |
+ blink::WebCryptoKeyUsageEncrypt, |
+ &unwrapped_key)); |
+} |
+ |
} // namespace webcrypto |
} // namespace content |