| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/values.h" |
| #include "components/webcrypto/algorithm_dispatch.h" |
| #include "components/webcrypto/algorithms/test_helpers.h" |
| #include "components/webcrypto/crypto_data.h" |
| #include "components/webcrypto/status.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" |
| #include "third_party/WebKit/public/platform/WebCryptoKey.h" |
| #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" |
| |
| namespace webcrypto { |
| |
| namespace { |
| |
| // Helper for ImportJwkRsaFailures. Restores the JWK JSON |
| // dictionary to a good state |
| void RestoreJwkRsaDictionary(base::DictionaryValue* dict) { |
| dict->Clear(); |
| dict->SetString("kty", "RSA"); |
| dict->SetString("alg", "RS256"); |
| dict->SetString("use", "sig"); |
| dict->SetBoolean("ext", false); |
| dict->SetString( |
| "n", |
| "qLOyhK-OtQs4cDSoYPFGxJGfMYdjzWxVmMiuSBGh4KvEx-CwgtaTpef87Wdc9GaFEncsDLxk" |
| "p0LGxjD1M8jMcvYq6DPEC_JYQumEu3i9v5fAEH1VvbZi9cTg-rmEXLUUjvc5LdOq_5OuHmtm" |
| "e7PUJHYW1PW6ENTP0ibeiNOfFvs"); |
| dict->SetString("e", "AQAB"); |
| } |
| |
| class WebCryptoRsaSsaTest : public WebCryptoTestBase {}; |
| |
| TEST_F(WebCryptoRsaSsaTest, ImportExportSpki) { |
| // Passing case: Import a valid RSA key in SPKI format. |
| blink::WebCryptoKey key; |
| ASSERT_EQ(Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatSpki, |
| CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, blink::kWebCryptoKeyUsageVerify, &key)); |
| EXPECT_TRUE(key.Handle()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePublic, key.GetType()); |
| EXPECT_TRUE(key.Extractable()); |
| EXPECT_EQ(blink::kWebCryptoKeyUsageVerify, key.Usages()); |
| EXPECT_EQ(kModulusLengthBits, |
| key.Algorithm().RsaHashedParams()->ModulusLengthBits()); |
| EXPECT_BYTES_EQ_HEX( |
| "010001", |
| CryptoData(key.Algorithm().RsaHashedParams()->PublicExponent())); |
| |
| // Failing case: Import RSA key but provide an inconsistent input algorithm. |
| EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), |
| ImportKey(blink::kWebCryptoKeyFormatSpki, |
| CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), |
| CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true, |
| blink::kWebCryptoKeyUsageEncrypt, &key)); |
| |
| // Passing case: Export a previously imported RSA public key in SPKI format |
| // and compare to original data. |
| std::vector<uint8_t> output; |
| ASSERT_EQ(Status::Success(), |
| ExportKey(blink::kWebCryptoKeyFormatSpki, key, &output)); |
| EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, output); |
| |
| // Failing case: Try to export a previously imported RSA public key in raw |
| // format (not allowed for a public key). |
| EXPECT_EQ(Status::ErrorUnsupportedExportKeyFormat(), |
| ExportKey(blink::kWebCryptoKeyFormatRaw, key, &output)); |
| |
| // Failing case: Try to export a non-extractable key |
| ASSERT_EQ(Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatSpki, |
| CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| false, blink::kWebCryptoKeyUsageVerify, &key)); |
| EXPECT_TRUE(key.Handle()); |
| EXPECT_FALSE(key.Extractable()); |
| EXPECT_EQ(Status::ErrorKeyNotExtractable(), |
| ExportKey(blink::kWebCryptoKeyFormatSpki, 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(WebCryptoRsaSsaTest, ImportExportPkcs8) { |
| // Passing case: Import a valid RSA key in PKCS#8 format. |
| blink::WebCryptoKey key; |
| ASSERT_EQ(Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatPkcs8, |
| CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha1), |
| true, blink::kWebCryptoKeyUsageSign, &key)); |
| EXPECT_TRUE(key.Handle()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePrivate, key.GetType()); |
| EXPECT_TRUE(key.Extractable()); |
| EXPECT_EQ(blink::kWebCryptoKeyUsageSign, key.Usages()); |
| EXPECT_EQ(blink::kWebCryptoAlgorithmIdSha1, |
| key.Algorithm().RsaHashedParams()->GetHash().Id()); |
| EXPECT_EQ(kModulusLengthBits, |
| key.Algorithm().RsaHashedParams()->ModulusLengthBits()); |
| EXPECT_BYTES_EQ_HEX( |
| "010001", |
| CryptoData(key.Algorithm().RsaHashedParams()->PublicExponent())); |
| |
| std::vector<uint8_t> exported_key; |
| ASSERT_EQ(Status::Success(), |
| ExportKey(blink::kWebCryptoKeyFormatPkcs8, key, &exported_key)); |
| EXPECT_BYTES_EQ_HEX(kPrivateKeyPkcs8DerHex, exported_key); |
| |
| // Failing case: Import RSA key but provide an inconsistent input algorithm |
| // and usage. Several issues here: |
| // * AES-CBC doesn't support PKCS8 key format |
| // * AES-CBC doesn't support "sign" usage |
| EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), |
| ImportKey(blink::kWebCryptoKeyFormatPkcs8, |
| CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
| CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true, |
| blink::kWebCryptoKeyUsageSign, &key)); |
| } |
| |
| // Tests JWK import and export by doing a roundtrip key conversion and ensuring |
| // it was lossless: |
| // |
| // PKCS8 --> JWK --> PKCS8 |
| TEST_F(WebCryptoRsaSsaTest, ImportRsaPrivateKeyJwkToPkcs8RoundTrip) { |
| blink::WebCryptoKey key; |
| ASSERT_EQ(Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatPkcs8, |
| CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha1), |
| true, blink::kWebCryptoKeyUsageSign, &key)); |
| |
| std::vector<uint8_t> exported_key_jwk; |
| ASSERT_EQ(Status::Success(), |
| ExportKey(blink::kWebCryptoKeyFormatJwk, key, &exported_key_jwk)); |
| |
| // All of the optional parameters (p, q, dp, dq, qi) should be present in the |
| // output. |
| const char* expected_jwk = |
| "{\"alg\":\"RS1\",\"d\":\"M6UEKpCyfU9UUcqbu9C0R3GhAa-IQ0Cu-YhfKku-" |
| "kuiUpySsPFaMj5eFOtB8AmbIxqPKCSnx6PESMYhEKfxNmuVf7olqEM5wfD7X5zTkRyejlXRQ" |
| "GlMmgxCcKrrKuig8MbS9L1PD7jfjUs7jT55QO9gMBiKtecbc7og1R8ajsyU\",\"dp\":" |
| "\"KPoTk4ZVvh-" |
| "KFZy6ylpy6hkMMAieGc0nSlVvNsT24Z9VSzTAd3kEJ7vdjdPt4kSDKPOF2Bsw6OQ7L_-" |
| "gJ4YZeQ\",\"dq\":\"Gos485j6cSBJiY1_t57gp3ZoeRKZzfoJ78DlB6yyHtdDAe9b_Ui-" |
| "RV6utuFnglWCdYCo5OjhQVHRUQqCo_LnKQ\",\"e\":\"AQAB\",\"ext\":true,\"key_" |
| "ops\":[\"sign\"],\"kty\":\"RSA\",\"n\":" |
| "\"pW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h962ZHYxRBW_-2xYrTA8oOhKoijlN_" |
| "1JqtykcuzB86r_OCx39XNlQgJbVsri2311nHvY3fAkhyyPCcKcOJZjm_4nRnxBazC0_" |
| "DLNfKSgOE4a29kxO8i4eHyDQzoz_siSb2aITc\",\"p\":\"5-" |
| "iUJyCod1Fyc6NWBT6iobwMlKpy1VxuhilrLfyWeUjApyy8zKfqyzVwbgmh31WhU1vZs8w0Fg" |
| "s7bc0-2o5kQw\",\"q\":\"tp3KHPfU1-yB51uQ_MqHSrzeEj_" |
| "ScAGAqpBHm25I3o1n7ST58Z2FuidYdPVCzSDccj5pYzZKH5QlRSsmmmeZ_Q\",\"qi\":" |
| "\"JxVqukEm0kqB86Uoy_sn9WiG-" |
| "ECp9uhuF6RLlP6TGVhLjiL93h5aLjvYqluo2FhBlOshkKz4MrhH8To9JKefTQ\"}"; |
| |
| ASSERT_EQ(CryptoData(std::string(expected_jwk)), |
| CryptoData(exported_key_jwk)); |
| |
| ASSERT_EQ( |
| Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(exported_key_jwk), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha1), |
| true, blink::kWebCryptoKeyUsageSign, &key)); |
| |
| std::vector<uint8_t> exported_key_pkcs8; |
| ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatPkcs8, key, |
| &exported_key_pkcs8)); |
| |
| ASSERT_EQ(CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
| CryptoData(exported_key_pkcs8)); |
| } |
| |
| // Tests importing multiple RSA private keys from JWK, and then exporting to |
| // PKCS8. |
| // |
| // This is a regression test for http://crbug.com/378315, for which importing |
| // a sequence of keys from JWK could yield the wrong key. The first key would |
| // be imported correctly, however every key after that would actually import |
| // the first key. |
| TEST_F(WebCryptoRsaSsaTest, ImportMultipleRSAPrivateKeysJwk) { |
| std::unique_ptr<base::ListValue> key_list; |
| ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); |
| |
| // For this test to be meaningful the keys MUST be kept alive before importing |
| // new keys. |
| std::vector<blink::WebCryptoKey> live_keys; |
| |
| for (size_t key_index = 0; key_index < key_list->GetSize(); ++key_index) { |
| SCOPED_TRACE(key_index); |
| |
| base::DictionaryValue* key_values; |
| ASSERT_TRUE(key_list->GetDictionary(key_index, &key_values)); |
| |
| // Get the JWK representation of the key. |
| base::DictionaryValue* key_jwk; |
| ASSERT_TRUE(key_values->GetDictionary("jwk", &key_jwk)); |
| |
| // Get the PKCS8 representation of the key. |
| std::string pkcs8_hex_string; |
| ASSERT_TRUE(key_values->GetString("pkcs8", &pkcs8_hex_string)); |
| std::vector<uint8_t> pkcs8_bytes = HexStringToBytes(pkcs8_hex_string); |
| |
| // Get the modulus length for the key. |
| int modulus_length_bits = 0; |
| ASSERT_TRUE(key_values->GetInteger("modulusLength", &modulus_length_bits)); |
| |
| blink::WebCryptoKey private_key; |
| |
| // Import the key from JWK. |
| ASSERT_EQ(Status::Success(), |
| ImportKeyJwkFromDict( |
| *key_jwk, |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, blink::kWebCryptoKeyUsageSign, &private_key)); |
| |
| live_keys.push_back(private_key); |
| |
| EXPECT_EQ( |
| modulus_length_bits, |
| static_cast<int>( |
| private_key.Algorithm().RsaHashedParams()->ModulusLengthBits())); |
| |
| // Export to PKCS8 and verify that it matches expectation. |
| std::vector<uint8_t> exported_key_pkcs8; |
| ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatPkcs8, |
| private_key, &exported_key_pkcs8)); |
| |
| EXPECT_BYTES_EQ(pkcs8_bytes, exported_key_pkcs8); |
| } |
| } |
| |
| // Import an RSA private key using JWK. Next import a JWK containing the same |
| // modulus, but mismatched parameters for the rest. It should NOT be possible |
| // that the second import retrieves the first key. See http://crbug.com/378315 |
| // for how that could happen. |
| TEST_F(WebCryptoRsaSsaTest, ImportJwkExistingModulusAndInvalid) { |
| std::unique_ptr<base::ListValue> key_list; |
| ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); |
| |
| // Import a 1024-bit private key. |
| base::DictionaryValue* key1_props; |
| ASSERT_TRUE(key_list->GetDictionary(1, &key1_props)); |
| base::DictionaryValue* key1_jwk; |
| ASSERT_TRUE(key1_props->GetDictionary("jwk", &key1_jwk)); |
| |
| blink::WebCryptoKey key1; |
| ASSERT_EQ( |
| Status::Success(), |
| ImportKeyJwkFromDict(*key1_jwk, |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, blink::kWebCryptoKeyUsageSign, &key1)); |
| |
| ASSERT_EQ(1024u, key1.Algorithm().RsaHashedParams()->ModulusLengthBits()); |
| |
| // Construct a JWK using the modulus of key1, but all the other fields from |
| // another key (also a 1024-bit private key). |
| base::DictionaryValue* key2_props; |
| ASSERT_TRUE(key_list->GetDictionary(5, &key2_props)); |
| base::DictionaryValue* key2_jwk; |
| ASSERT_TRUE(key2_props->GetDictionary("jwk", &key2_jwk)); |
| std::string modulus; |
| key1_jwk->GetString("n", &modulus); |
| key2_jwk->SetString("n", modulus); |
| |
| // This should fail, as the n,e,d parameters are not consistent. It MUST NOT |
| // somehow return the key created earlier. |
| blink::WebCryptoKey key2; |
| ASSERT_EQ( |
| Status::OperationError(), |
| ImportKeyJwkFromDict(*key2_jwk, |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, blink::kWebCryptoKeyUsageSign, &key2)); |
| } |
| |
| TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsa) { |
| // Note: using unrealistic short key lengths here to avoid bogging down tests. |
| |
| // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha256) |
| const unsigned int modulus_length = 256; |
| const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); |
| blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, modulus_length, public_exponent); |
| bool extractable = true; |
| const blink::WebCryptoKeyUsageMask public_usages = |
| blink::kWebCryptoKeyUsageVerify; |
| const blink::WebCryptoKeyUsageMask private_usages = |
| blink::kWebCryptoKeyUsageSign; |
| const blink::WebCryptoKeyUsageMask usages = public_usages | private_usages; |
| blink::WebCryptoKey public_key; |
| blink::WebCryptoKey private_key; |
| |
| EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, extractable, usages, |
| &public_key, &private_key)); |
| ASSERT_FALSE(public_key.IsNull()); |
| ASSERT_FALSE(private_key.IsNull()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePublic, public_key.GetType()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePrivate, private_key.GetType()); |
| EXPECT_EQ(modulus_length, |
| public_key.Algorithm().RsaHashedParams()->ModulusLengthBits()); |
| EXPECT_EQ(modulus_length, |
| private_key.Algorithm().RsaHashedParams()->ModulusLengthBits()); |
| EXPECT_EQ(blink::kWebCryptoAlgorithmIdSha256, |
| public_key.Algorithm().RsaHashedParams()->GetHash().Id()); |
| EXPECT_EQ(blink::kWebCryptoAlgorithmIdSha256, |
| private_key.Algorithm().RsaHashedParams()->GetHash().Id()); |
| EXPECT_TRUE(public_key.Extractable()); |
| EXPECT_EQ(extractable, private_key.Extractable()); |
| EXPECT_EQ(public_usages, public_key.Usages()); |
| EXPECT_EQ(private_usages, private_key.Usages()); |
| |
| // Try exporting the generated key pair, and then re-importing to verify that |
| // the exported data was valid. |
| std::vector<uint8_t> public_key_spki; |
| EXPECT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatSpki, |
| public_key, &public_key_spki)); |
| |
| public_key = blink::WebCryptoKey::CreateNull(); |
| ASSERT_EQ( |
| Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatSpki, CryptoData(public_key_spki), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, public_usages, &public_key)); |
| EXPECT_EQ(modulus_length, |
| public_key.Algorithm().RsaHashedParams()->ModulusLengthBits()); |
| |
| std::vector<uint8_t> private_key_pkcs8; |
| EXPECT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatPkcs8, |
| private_key, &private_key_pkcs8)); |
| private_key = blink::WebCryptoKey::CreateNull(); |
| ASSERT_EQ( |
| Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatPkcs8, CryptoData(private_key_pkcs8), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, private_usages, &private_key)); |
| EXPECT_EQ(modulus_length, |
| private_key.Algorithm().RsaHashedParams()->ModulusLengthBits()); |
| |
| // Fail with bad modulus. |
| algorithm = CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, 0, public_exponent); |
| EXPECT_EQ(Status::ErrorGenerateRsaUnsupportedModulus(), |
| GenerateKeyPair(algorithm, extractable, usages, &public_key, |
| &private_key)); |
| |
| // Fail with bad exponent: larger than unsigned long. |
| unsigned int exponent_length = sizeof(unsigned long) + 1; // NOLINT |
| const std::vector<uint8_t> long_exponent(exponent_length, 0x01); |
| algorithm = CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, modulus_length, long_exponent); |
| EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), |
| GenerateKeyPair(algorithm, extractable, usages, &public_key, |
| &private_key)); |
| |
| // Fail with bad exponent: empty. |
| const std::vector<uint8_t> empty_exponent; |
| algorithm = CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, modulus_length, empty_exponent); |
| EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), |
| GenerateKeyPair(algorithm, extractable, usages, &public_key, |
| &private_key)); |
| |
| // Fail with bad exponent: all zeros. |
| std::vector<uint8_t> exponent_with_leading_zeros(15, 0x00); |
| algorithm = CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, modulus_length, |
| exponent_with_leading_zeros); |
| EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), |
| GenerateKeyPair(algorithm, extractable, usages, &public_key, |
| &private_key)); |
| |
| // Key generation success using exponent with leading zeros. |
| exponent_with_leading_zeros.insert(exponent_with_leading_zeros.end(), |
| public_exponent.begin(), |
| public_exponent.end()); |
| algorithm = CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, modulus_length, |
| exponent_with_leading_zeros); |
| EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, extractable, usages, |
| &public_key, &private_key)); |
| EXPECT_FALSE(public_key.IsNull()); |
| EXPECT_FALSE(private_key.IsNull()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePublic, public_key.GetType()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePrivate, private_key.GetType()); |
| EXPECT_TRUE(public_key.Extractable()); |
| EXPECT_EQ(extractable, private_key.Extractable()); |
| EXPECT_EQ(public_usages, public_key.Usages()); |
| EXPECT_EQ(private_usages, private_key.Usages()); |
| |
| // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha1) |
| algorithm = CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha1, modulus_length, public_exponent); |
| EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, false, usages, |
| &public_key, &private_key)); |
| EXPECT_FALSE(public_key.IsNull()); |
| EXPECT_FALSE(private_key.IsNull()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePublic, public_key.GetType()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePrivate, private_key.GetType()); |
| EXPECT_EQ(modulus_length, |
| public_key.Algorithm().RsaHashedParams()->ModulusLengthBits()); |
| EXPECT_EQ(modulus_length, |
| private_key.Algorithm().RsaHashedParams()->ModulusLengthBits()); |
| EXPECT_EQ(blink::kWebCryptoAlgorithmIdSha1, |
| public_key.Algorithm().RsaHashedParams()->GetHash().Id()); |
| EXPECT_EQ(blink::kWebCryptoAlgorithmIdSha1, |
| private_key.Algorithm().RsaHashedParams()->GetHash().Id()); |
| // Even though "extractable" was set to false, the public key remains |
| // extractable. |
| EXPECT_TRUE(public_key.Extractable()); |
| EXPECT_FALSE(private_key.Extractable()); |
| EXPECT_EQ(public_usages, public_key.Usages()); |
| EXPECT_EQ(private_usages, private_key.Usages()); |
| |
| // Exporting a private key as SPKI format doesn't make sense. However this |
| // will first fail because the key is not extractable. |
| std::vector<uint8_t> output; |
| EXPECT_EQ(Status::ErrorKeyNotExtractable(), |
| ExportKey(blink::kWebCryptoKeyFormatSpki, private_key, &output)); |
| |
| // Re-generate an extractable private_key and try to export it as SPKI format. |
| // This should fail since spki is for public keys. |
| EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, true, usages, |
| &public_key, &private_key)); |
| EXPECT_EQ(Status::ErrorUnexpectedKeyType(), |
| ExportKey(blink::kWebCryptoKeyFormatSpki, private_key, &output)); |
| } |
| |
| TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsaBadModulusLength) { |
| const unsigned int kBadModulusBits[] = { |
| 0, |
| 248, // Too small. |
| 257, // Not a multiple of 8. |
| 1023, // Not a multiple of 8. |
| 0xFFFFFFFF, // Too big. |
| 16384 + 8, // 16384 is the maxmimum length that NSS succeeds for. |
| }; |
| |
| const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); |
| |
| for (size_t i = 0; i < arraysize(kBadModulusBits); ++i) { |
| const unsigned int modulus_length_bits = kBadModulusBits[i]; |
| blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, modulus_length_bits, |
| public_exponent); |
| bool extractable = true; |
| const blink::WebCryptoKeyUsageMask usages = blink::kWebCryptoKeyUsageSign; |
| blink::WebCryptoKey public_key; |
| blink::WebCryptoKey private_key; |
| |
| EXPECT_EQ(Status::ErrorGenerateRsaUnsupportedModulus(), |
| GenerateKeyPair(algorithm, extractable, usages, &public_key, |
| &private_key)); |
| } |
| } |
| |
| // Try generating RSA key pairs using unsupported public exponents. Only |
| // exponents of 3 and 65537 are supported. Although OpenSSL can support other |
| // values, it can also hang when given invalid exponents. To avoid hanging, use |
| // a whitelist of known safe exponents. |
| TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsaBadExponent) { |
| const unsigned int modulus_length = 1024; |
| |
| const char* const kPublicExponents[] = { |
| "11", // 17 - This is a valid public exponent, but currently disallowed. |
| "00", |
| "01", |
| "02", |
| "010000", // 65536 |
| }; |
| |
| for (size_t i = 0; i < arraysize(kPublicExponents); ++i) { |
| SCOPED_TRACE(i); |
| blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, modulus_length, |
| HexStringToBytes(kPublicExponents[i])); |
| |
| blink::WebCryptoKey public_key; |
| blink::WebCryptoKey private_key; |
| |
| EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), |
| GenerateKeyPair(algorithm, true, blink::kWebCryptoKeyUsageSign, |
| &public_key, &private_key)); |
| } |
| } |
| |
| TEST_F(WebCryptoRsaSsaTest, SignVerifyFailures) { |
| // Import a key pair. |
| blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha1); |
| blink::WebCryptoKey public_key; |
| blink::WebCryptoKey private_key; |
| ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
| HexStringToBytes(kPublicKeySpkiDerHex), |
| HexStringToBytes(kPrivateKeyPkcs8DerHex), import_algorithm, false, |
| blink::kWebCryptoKeyUsageVerify, blink::kWebCryptoKeyUsageSign, |
| &public_key, &private_key)); |
| |
| blink::WebCryptoAlgorithm algorithm = |
| CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5); |
| |
| std::vector<uint8_t> signature; |
| bool signature_match; |
| |
| // Compute a signature. |
| const std::vector<uint8_t> data = HexStringToBytes("010203040506070809"); |
| ASSERT_EQ(Status::Success(), |
| Sign(algorithm, private_key, CryptoData(data), &signature)); |
| |
| // Ensure truncated signature does not verify by passing one less byte. |
| EXPECT_EQ(Status::Success(), |
| Verify(algorithm, public_key, |
| CryptoData(signature.data(), |
| static_cast<unsigned int>(signature.size()) - 1), |
| CryptoData(data), &signature_match)); |
| EXPECT_FALSE(signature_match); |
| |
| // Ensure truncated signature does not verify by passing no bytes. |
| EXPECT_EQ(Status::Success(), Verify(algorithm, public_key, CryptoData(), |
| CryptoData(data), &signature_match)); |
| EXPECT_FALSE(signature_match); |
| |
| // Ensure corrupted signature does not verify. |
| std::vector<uint8_t> corrupt_sig = signature; |
| corrupt_sig[corrupt_sig.size() / 2] ^= 0x1; |
| EXPECT_EQ(Status::Success(), |
| Verify(algorithm, public_key, CryptoData(corrupt_sig), |
| CryptoData(data), &signature_match)); |
| EXPECT_FALSE(signature_match); |
| |
| // Ensure signatures that are greater than the modulus size fail. |
| const unsigned int long_message_size_bytes = 1024; |
| DCHECK_GT(long_message_size_bytes, kModulusLengthBits / 8); |
| const unsigned char kLongSignature[long_message_size_bytes] = {0}; |
| EXPECT_EQ(Status::Success(), |
| Verify(algorithm, public_key, |
| CryptoData(kLongSignature, sizeof(kLongSignature)), |
| CryptoData(data), &signature_match)); |
| EXPECT_FALSE(signature_match); |
| |
| // Ensure that signing and verifying with an incompatible algorithm fails. |
| algorithm = CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaOaep); |
| |
| EXPECT_EQ(Status::ErrorUnexpected(), |
| Sign(algorithm, private_key, CryptoData(data), &signature)); |
| EXPECT_EQ(Status::ErrorUnexpected(), |
| Verify(algorithm, public_key, CryptoData(signature), |
| CryptoData(data), &signature_match)); |
| |
| // Some crypto libraries (NSS) can automatically select the RSA SSA inner hash |
| // based solely on the contents of the input signature data. In the Web Crypto |
| // implementation, the inner hash should be specified uniquely by the key |
| // algorithm parameter. To validate this behavior, call Verify with a computed |
| // signature that used one hash type (SHA-1), but pass in a key with a |
| // different inner hash type (SHA-256). If the hash type is determined by the |
| // signature itself (undesired), the verify will pass, while if the hash type |
| // is specified by the key algorithm (desired), the verify will fail. |
| |
| // Compute a signature using SHA-1 as the inner hash. |
| EXPECT_EQ(Status::Success(), |
| Sign(CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5), |
| private_key, CryptoData(data), &signature)); |
| |
| blink::WebCryptoKey public_key_256; |
| EXPECT_EQ(Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatSpki, |
| CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, blink::kWebCryptoKeyUsageVerify, &public_key_256)); |
| |
| // Now verify using an algorithm whose inner hash is SHA-256, not SHA-1. The |
| // signature should not verify. |
| // NOTE: public_key was produced by generateKey, and so its associated |
| // algorithm has WebCryptoRsaKeyGenParams and not WebCryptoRsaSsaParams. Thus |
| // it has no inner hash to conflict with the input algorithm. |
| EXPECT_EQ(blink::kWebCryptoAlgorithmIdSha1, |
| private_key.Algorithm().RsaHashedParams()->GetHash().Id()); |
| EXPECT_EQ(blink::kWebCryptoAlgorithmIdSha256, |
| public_key_256.Algorithm().RsaHashedParams()->GetHash().Id()); |
| |
| bool is_match; |
| EXPECT_EQ(Status::Success(), |
| Verify(CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5), |
| public_key_256, CryptoData(signature), CryptoData(data), |
| &is_match)); |
| EXPECT_FALSE(is_match); |
| } |
| |
| TEST_F(WebCryptoRsaSsaTest, SignVerifyKnownAnswer) { |
| std::unique_ptr<base::ListValue> tests; |
| ASSERT_TRUE(ReadJsonTestFileToList("pkcs1v15_sign.json", &tests)); |
| |
| // Import the key pair. |
| blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha1); |
| blink::WebCryptoKey public_key; |
| blink::WebCryptoKey private_key; |
| ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( |
| HexStringToBytes(kPublicKeySpkiDerHex), |
| HexStringToBytes(kPrivateKeyPkcs8DerHex), import_algorithm, false, |
| blink::kWebCryptoKeyUsageVerify, blink::kWebCryptoKeyUsageSign, |
| &public_key, &private_key)); |
| |
| blink::WebCryptoAlgorithm algorithm = |
| CreateAlgorithm(blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5); |
| |
| // Validate the signatures are computed and verified as expected. |
| std::vector<uint8_t> signature; |
| for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { |
| SCOPED_TRACE(test_index); |
| |
| base::DictionaryValue* test; |
| ASSERT_TRUE(tests->GetDictionary(test_index, &test)); |
| |
| std::vector<uint8_t> test_message = |
| GetBytesFromHexString(test, "message_hex"); |
| std::vector<uint8_t> test_signature = |
| GetBytesFromHexString(test, "signature_hex"); |
| |
| signature.clear(); |
| ASSERT_EQ(Status::Success(), Sign(algorithm, private_key, |
| CryptoData(test_message), &signature)); |
| EXPECT_BYTES_EQ(test_signature, signature); |
| |
| bool is_match = false; |
| ASSERT_EQ(Status::Success(), |
| Verify(algorithm, public_key, CryptoData(test_signature), |
| CryptoData(test_message), &is_match)); |
| EXPECT_TRUE(is_match); |
| } |
| } |
| |
| // Try importing an RSA-SSA public key with unsupported key usages using SPKI |
| // format. RSA-SSA public keys only support the 'verify' usage. |
| TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_SPKI) { |
| const blink::WebCryptoAlgorithm algorithm = CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256); |
| |
| blink::WebCryptoKeyUsageMask bad_usages[] = { |
| blink::kWebCryptoKeyUsageSign, |
| blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify, |
| blink::kWebCryptoKeyUsageEncrypt, |
| blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt, |
| }; |
| |
| for (size_t i = 0; i < arraysize(bad_usages); ++i) { |
| SCOPED_TRACE(i); |
| |
| blink::WebCryptoKey public_key; |
| ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), |
| ImportKey(blink::kWebCryptoKeyFormatSpki, |
| CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), |
| algorithm, false, bad_usages[i], &public_key)); |
| } |
| } |
| |
| // Try importing an RSA-SSA public key with unsupported key usages using JWK |
| // format. RSA-SSA public keys only support the 'verify' usage. |
| TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_JWK) { |
| const blink::WebCryptoAlgorithm algorithm = CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256); |
| |
| blink::WebCryptoKeyUsageMask bad_usages[] = { |
| blink::kWebCryptoKeyUsageSign, |
| blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify, |
| blink::kWebCryptoKeyUsageEncrypt, |
| blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt, |
| }; |
| |
| base::DictionaryValue dict; |
| RestoreJwkRsaDictionary(&dict); |
| dict.Remove("use", nullptr); |
| dict.SetString("alg", "RS256"); |
| |
| for (size_t i = 0; i < arraysize(bad_usages); ++i) { |
| SCOPED_TRACE(i); |
| |
| blink::WebCryptoKey public_key; |
| ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), |
| ImportKeyJwkFromDict(dict, algorithm, false, bad_usages[i], |
| &public_key)); |
| } |
| } |
| |
| // Generate an RSA-SSA key pair with invalid usages. RSA-SSA supports: |
| // 'sign', 'verify' |
| TEST_F(WebCryptoRsaSsaTest, GenerateKeyBadUsages) { |
| blink::WebCryptoKeyUsageMask bad_usages[] = { |
| blink::kWebCryptoKeyUsageDecrypt, |
| blink::kWebCryptoKeyUsageVerify | blink::kWebCryptoKeyUsageDecrypt, |
| blink::kWebCryptoKeyUsageWrapKey, |
| }; |
| |
| const unsigned int modulus_length = 256; |
| const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); |
| |
| for (size_t i = 0; i < arraysize(bad_usages); ++i) { |
| SCOPED_TRACE(i); |
| |
| blink::WebCryptoKey public_key; |
| blink::WebCryptoKey private_key; |
| |
| ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), |
| GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, |
| modulus_length, public_exponent), |
| true, bad_usages[i], &public_key, &private_key)); |
| } |
| } |
| |
| // Generate an RSA-SSA key pair. The public and private keys should select the |
| // key usages which are applicable, and not have the exact same usages as was |
| // specified to GenerateKey |
| TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairIntersectUsages) { |
| const unsigned int modulus_length = 256; |
| const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); |
| |
| blink::WebCryptoKey public_key; |
| blink::WebCryptoKey private_key; |
| |
| ASSERT_EQ( |
| Status::Success(), |
| GenerateKeyPair( |
| CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, modulus_length, |
| public_exponent), |
| true, blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify, |
| &public_key, &private_key)); |
| |
| EXPECT_EQ(blink::kWebCryptoKeyUsageVerify, public_key.Usages()); |
| EXPECT_EQ(blink::kWebCryptoKeyUsageSign, private_key.Usages()); |
| |
| // Try again but this time without the Verify usages. |
| ASSERT_EQ(Status::Success(), |
| GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, |
| modulus_length, public_exponent), |
| true, blink::kWebCryptoKeyUsageSign, &public_key, |
| &private_key)); |
| |
| EXPECT_EQ(0, public_key.Usages()); |
| EXPECT_EQ(blink::kWebCryptoKeyUsageSign, private_key.Usages()); |
| } |
| |
| TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairEmptyUsages) { |
| const unsigned int modulus_length = 256; |
| const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); |
| |
| blink::WebCryptoKey public_key; |
| blink::WebCryptoKey private_key; |
| |
| ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), |
| GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256, |
| modulus_length, public_exponent), |
| true, 0, &public_key, &private_key)); |
| } |
| |
| TEST_F(WebCryptoRsaSsaTest, ImportKeyEmptyUsages) { |
| blink::WebCryptoKey public_key; |
| blink::WebCryptoKey private_key; |
| |
| // Public without usage does not throw an error. |
| ASSERT_EQ(Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatSpki, |
| CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, 0, &public_key)); |
| EXPECT_EQ(0, public_key.Usages()); |
| |
| // Private empty usage will throw an error. |
| ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), |
| ImportKey(blink::kWebCryptoKeyFormatPkcs8, |
| CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha1), |
| true, 0, &private_key)); |
| |
| std::vector<uint8_t> public_jwk; |
| ASSERT_EQ(Status::Success(), |
| ExportKey(blink::kWebCryptoKeyFormatJwk, public_key, &public_jwk)); |
| |
| ASSERT_EQ(Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(public_jwk), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, 0, &public_key)); |
| EXPECT_EQ(0, public_key.Usages()); |
| |
| // With correct usage to get correct imported private_key |
| std::vector<uint8_t> private_jwk; |
| ImportKey(blink::kWebCryptoKeyFormatPkcs8, |
| CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha1), |
| true, blink::kWebCryptoKeyUsageSign, &private_key); |
| |
| ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatJwk, |
| private_key, &private_jwk)); |
| |
| ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), |
| ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(private_jwk), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha1), |
| true, 0, &private_key)); |
| } |
| |
| TEST_F(WebCryptoRsaSsaTest, ImportExportJwkRsaPublicKey) { |
| struct TestCase { |
| const blink::WebCryptoAlgorithmId hash; |
| const blink::WebCryptoKeyUsageMask usage; |
| const char* const jwk_alg; |
| }; |
| const TestCase kTests[] = {{blink::kWebCryptoAlgorithmIdSha1, |
| blink::kWebCryptoKeyUsageVerify, "RS1"}, |
| {blink::kWebCryptoAlgorithmIdSha256, |
| blink::kWebCryptoKeyUsageVerify, "RS256"}, |
| {blink::kWebCryptoAlgorithmIdSha384, |
| blink::kWebCryptoKeyUsageVerify, "RS384"}, |
| {blink::kWebCryptoAlgorithmIdSha512, |
| blink::kWebCryptoKeyUsageVerify, "RS512"}}; |
| |
| for (size_t test_index = 0; test_index < arraysize(kTests); ++test_index) { |
| SCOPED_TRACE(test_index); |
| const TestCase& test = kTests[test_index]; |
| |
| const blink::WebCryptoAlgorithm import_algorithm = |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, test.hash); |
| |
| // Import the spki to create a public key |
| blink::WebCryptoKey public_key; |
| ASSERT_EQ(Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatSpki, |
| CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), |
| import_algorithm, true, test.usage, &public_key)); |
| |
| // Export the public key as JWK and verify its contents |
| std::vector<uint8_t> jwk; |
| ASSERT_EQ(Status::Success(), |
| ExportKey(blink::kWebCryptoKeyFormatJwk, public_key, &jwk)); |
| 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; |
| ASSERT_EQ(Status::Success(), |
| ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(jwk), |
| import_algorithm, true, test.usage, &public_key2)); |
| ASSERT_TRUE(public_key2.Handle()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePublic, public_key2.GetType()); |
| EXPECT_TRUE(public_key2.Extractable()); |
| EXPECT_EQ(import_algorithm.Id(), public_key2.Algorithm().Id()); |
| |
| // Export the new key as spki and compare to the original. |
| std::vector<uint8_t> spki; |
| ASSERT_EQ(Status::Success(), |
| ExportKey(blink::kWebCryptoKeyFormatSpki, public_key2, &spki)); |
| EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, CryptoData(spki)); |
| } |
| } |
| |
| TEST_F(WebCryptoRsaSsaTest, ImportJwkRsaFailures) { |
| base::DictionaryValue dict; |
| RestoreJwkRsaDictionary(&dict); |
| blink::WebCryptoAlgorithm algorithm = CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256); |
| blink::WebCryptoKeyUsageMask usages = blink::kWebCryptoKeyUsageVerify; |
| blink::WebCryptoKey key; |
| |
| // An RSA public key JWK _must_ have an "n" (modulus) and an "e" (exponent) |
| // entry, while an RSA private key must have those plus at least a "d" |
| // (private exponent) entry. |
| // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, |
| // section 6.3. |
| |
| // Baseline pass. |
| EXPECT_EQ(Status::Success(), |
| ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); |
| EXPECT_EQ(algorithm.Id(), key.Algorithm().Id()); |
| EXPECT_FALSE(key.Extractable()); |
| EXPECT_EQ(blink::kWebCryptoKeyUsageVerify, key.Usages()); |
| EXPECT_EQ(blink::kWebCryptoKeyTypePublic, key.GetType()); |
| |
| // The following are specific failure cases for when kty = "RSA". |
| |
| // Fail if either "n" or "e" is not present or malformed. |
| const std::string kKtyParmName[] = {"n", "e"}; |
| for (size_t idx = 0; idx < arraysize(kKtyParmName); ++idx) { |
| // Fail on missing parameter. |
| dict.Remove(kKtyParmName[idx], nullptr); |
| EXPECT_NE(Status::Success(), |
| ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); |
| RestoreJwkRsaDictionary(&dict); |
| |
| // Fail on bad b64 parameter encoding. |
| dict.SetString(kKtyParmName[idx], "Qk3f0DsytU8lfza2au #$% Htaw2xpop9yTuH0"); |
| EXPECT_NE(Status::Success(), |
| ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); |
| RestoreJwkRsaDictionary(&dict); |
| |
| // Fail on empty parameter. |
| dict.SetString(kKtyParmName[idx], ""); |
| EXPECT_EQ(Status::ErrorJwkEmptyBigInteger(kKtyParmName[idx]), |
| ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); |
| RestoreJwkRsaDictionary(&dict); |
| } |
| } |
| |
| // Try importing an RSA-SSA key from JWK format, having specified both Sign and |
| // Verify usage, AND an invalid JWK. |
| // |
| // Parsing the invalid JWK will fail before the usage check is done. |
| TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaJwkBadUsageAndData) { |
| std::string bad_data = "hello"; |
| |
| blink::WebCryptoKey key; |
| ASSERT_EQ( |
| Status::ErrorJwkNotDictionary(), |
| ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(bad_data), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, |
| blink::kWebCryptoKeyUsageVerify | blink::kWebCryptoKeyUsageSign, |
| &key)); |
| } |
| |
| // Imports invalid JWK/SPKI/PKCS8 data and verifies that it fails as expected. |
| TEST_F(WebCryptoRsaSsaTest, ImportInvalidKeyData) { |
| std::unique_ptr<base::ListValue> tests; |
| ASSERT_TRUE(ReadJsonTestFileToList("bad_rsa_keys.json", &tests)); |
| |
| for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { |
| SCOPED_TRACE(test_index); |
| |
| const base::DictionaryValue* test; |
| ASSERT_TRUE(tests->GetDictionary(test_index, &test)); |
| |
| blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test); |
| std::vector<uint8_t> key_data = |
| GetKeyDataFromJsonTestCase(test, key_format); |
| std::string test_error; |
| ASSERT_TRUE(test->GetString("error", &test_error)); |
| |
| blink::WebCryptoKeyUsageMask usages = blink::kWebCryptoKeyUsageSign; |
| if (key_format == blink::kWebCryptoKeyFormatSpki) |
| usages = blink::kWebCryptoKeyUsageVerify; |
| blink::WebCryptoKey key; |
| Status status = ImportKey(key_format, CryptoData(key_data), |
| CreateRsaHashedImportAlgorithm( |
| blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| blink::kWebCryptoAlgorithmIdSha256), |
| true, usages, &key); |
| EXPECT_EQ(test_error, StatusToString(status)); |
| } |
| } |
| |
| } // namespace |
| |
| } // namespace webcrypto |