| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "base/logging.h" | |
| 6 #include "base/stl_util.h" | |
| 7 #include "components/webcrypto/algorithm_dispatch.h" | |
| 8 #include "components/webcrypto/crypto_data.h" | |
| 9 #include "components/webcrypto/status.h" | |
| 10 #include "components/webcrypto/test/test_helpers.h" | |
| 11 #include "components/webcrypto/webcrypto_util.h" | |
| 12 #include "testing/gtest/include/gtest/gtest.h" | |
| 13 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | |
| 14 #include "third_party/WebKit/public/platform/WebCryptoKey.h" | |
| 15 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | |
| 16 | |
| 17 namespace webcrypto { | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 // Helper for ImportJwkRsaFailures. Restores the JWK JSON | |
| 22 // dictionary to a good state | |
| 23 void RestoreJwkRsaDictionary(base::DictionaryValue* dict) { | |
| 24 dict->Clear(); | |
| 25 dict->SetString("kty", "RSA"); | |
| 26 dict->SetString("alg", "RS256"); | |
| 27 dict->SetString("use", "sig"); | |
| 28 dict->SetBoolean("ext", false); | |
| 29 dict->SetString( | |
| 30 "n", | |
| 31 "qLOyhK-OtQs4cDSoYPFGxJGfMYdjzWxVmMiuSBGh4KvEx-CwgtaTpef87Wdc9GaFEncsDLxk" | |
| 32 "p0LGxjD1M8jMcvYq6DPEC_JYQumEu3i9v5fAEH1VvbZi9cTg-rmEXLUUjvc5LdOq_5OuHmtm" | |
| 33 "e7PUJHYW1PW6ENTP0ibeiNOfFvs"); | |
| 34 dict->SetString("e", "AQAB"); | |
| 35 } | |
| 36 | |
| 37 class WebCryptoRsaSsaTest : public WebCryptoTestBase {}; | |
| 38 | |
| 39 TEST_F(WebCryptoRsaSsaTest, ImportExportSpki) { | |
| 40 // Passing case: Import a valid RSA key in SPKI format. | |
| 41 blink::WebCryptoKey key; | |
| 42 ASSERT_EQ(Status::Success(), | |
| 43 ImportKey(blink::WebCryptoKeyFormatSpki, | |
| 44 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
| 45 CreateRsaHashedImportAlgorithm( | |
| 46 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 47 blink::WebCryptoAlgorithmIdSha256), | |
| 48 true, blink::WebCryptoKeyUsageVerify, &key)); | |
| 49 EXPECT_TRUE(key.handle()); | |
| 50 EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type()); | |
| 51 EXPECT_TRUE(key.extractable()); | |
| 52 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); | |
| 53 EXPECT_EQ(kModulusLengthBits, | |
| 54 key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
| 55 EXPECT_BYTES_EQ_HEX( | |
| 56 "010001", | |
| 57 CryptoData(key.algorithm().rsaHashedParams()->publicExponent())); | |
| 58 | |
| 59 // Failing case: Import RSA key but provide an inconsistent input algorithm. | |
| 60 EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), | |
| 61 ImportKey(blink::WebCryptoKeyFormatSpki, | |
| 62 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
| 63 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), true, | |
| 64 blink::WebCryptoKeyUsageEncrypt, &key)); | |
| 65 | |
| 66 // Passing case: Export a previously imported RSA public key in SPKI format | |
| 67 // and compare to original data. | |
| 68 std::vector<uint8_t> output; | |
| 69 ASSERT_EQ(Status::Success(), | |
| 70 ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); | |
| 71 EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, output); | |
| 72 | |
| 73 // Failing case: Try to export a previously imported RSA public key in raw | |
| 74 // format (not allowed for a public key). | |
| 75 EXPECT_EQ(Status::ErrorUnsupportedExportKeyFormat(), | |
| 76 ExportKey(blink::WebCryptoKeyFormatRaw, key, &output)); | |
| 77 | |
| 78 // Failing case: Try to export a non-extractable key | |
| 79 ASSERT_EQ(Status::Success(), | |
| 80 ImportKey(blink::WebCryptoKeyFormatSpki, | |
| 81 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
| 82 CreateRsaHashedImportAlgorithm( | |
| 83 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 84 blink::WebCryptoAlgorithmIdSha256), | |
| 85 false, blink::WebCryptoKeyUsageVerify, &key)); | |
| 86 EXPECT_TRUE(key.handle()); | |
| 87 EXPECT_FALSE(key.extractable()); | |
| 88 EXPECT_EQ(Status::ErrorKeyNotExtractable(), | |
| 89 ExportKey(blink::WebCryptoKeyFormatSpki, key, &output)); | |
| 90 | |
| 91 // TODO(eroman): Failing test: Import a SPKI with an unrecognized hash OID | |
| 92 // TODO(eroman): Failing test: Import a SPKI with invalid algorithm params | |
| 93 // TODO(eroman): Failing test: Import a SPKI with inconsistent parameters | |
| 94 // (e.g. SHA-1 in OID, SHA-256 in params) | |
| 95 // TODO(eroman): Failing test: Import a SPKI for RSA-SSA, but with params | |
| 96 // as OAEP/PSS | |
| 97 } | |
| 98 | |
| 99 TEST_F(WebCryptoRsaSsaTest, ImportExportPkcs8) { | |
| 100 // Passing case: Import a valid RSA key in PKCS#8 format. | |
| 101 blink::WebCryptoKey key; | |
| 102 ASSERT_EQ(Status::Success(), | |
| 103 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
| 104 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
| 105 CreateRsaHashedImportAlgorithm( | |
| 106 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 107 blink::WebCryptoAlgorithmIdSha1), | |
| 108 true, blink::WebCryptoKeyUsageSign, &key)); | |
| 109 EXPECT_TRUE(key.handle()); | |
| 110 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, key.type()); | |
| 111 EXPECT_TRUE(key.extractable()); | |
| 112 EXPECT_EQ(blink::WebCryptoKeyUsageSign, key.usages()); | |
| 113 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
| 114 key.algorithm().rsaHashedParams()->hash().id()); | |
| 115 EXPECT_EQ(kModulusLengthBits, | |
| 116 key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
| 117 EXPECT_BYTES_EQ_HEX( | |
| 118 "010001", | |
| 119 CryptoData(key.algorithm().rsaHashedParams()->publicExponent())); | |
| 120 | |
| 121 std::vector<uint8_t> exported_key; | |
| 122 ASSERT_EQ(Status::Success(), | |
| 123 ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_key)); | |
| 124 EXPECT_BYTES_EQ_HEX(kPrivateKeyPkcs8DerHex, exported_key); | |
| 125 | |
| 126 // Failing case: Import RSA key but provide an inconsistent input algorithm | |
| 127 // and usage. Several issues here: | |
| 128 // * AES-CBC doesn't support PKCS8 key format | |
| 129 // * AES-CBC doesn't support "sign" usage | |
| 130 EXPECT_EQ(Status::ErrorUnsupportedImportKeyFormat(), | |
| 131 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
| 132 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
| 133 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), true, | |
| 134 blink::WebCryptoKeyUsageSign, &key)); | |
| 135 } | |
| 136 | |
| 137 // Tests JWK import and export by doing a roundtrip key conversion and ensuring | |
| 138 // it was lossless: | |
| 139 // | |
| 140 // PKCS8 --> JWK --> PKCS8 | |
| 141 TEST_F(WebCryptoRsaSsaTest, ImportRsaPrivateKeyJwkToPkcs8RoundTrip) { | |
| 142 blink::WebCryptoKey key; | |
| 143 ASSERT_EQ(Status::Success(), | |
| 144 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
| 145 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
| 146 CreateRsaHashedImportAlgorithm( | |
| 147 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 148 blink::WebCryptoAlgorithmIdSha1), | |
| 149 true, blink::WebCryptoKeyUsageSign, &key)); | |
| 150 | |
| 151 std::vector<uint8_t> exported_key_jwk; | |
| 152 ASSERT_EQ(Status::Success(), | |
| 153 ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_key_jwk)); | |
| 154 | |
| 155 // All of the optional parameters (p, q, dp, dq, qi) should be present in the | |
| 156 // output. | |
| 157 const char* expected_jwk = | |
| 158 "{\"alg\":\"RS1\",\"d\":\"M6UEKpCyfU9UUcqbu9C0R3GhAa-IQ0Cu-YhfKku-" | |
| 159 "kuiUpySsPFaMj5eFOtB8AmbIxqPKCSnx6PESMYhEKfxNmuVf7olqEM5wfD7X5zTkRyejlXRQ" | |
| 160 "GlMmgxCcKrrKuig8MbS9L1PD7jfjUs7jT55QO9gMBiKtecbc7og1R8ajsyU\",\"dp\":" | |
| 161 "\"KPoTk4ZVvh-" | |
| 162 "KFZy6ylpy6hkMMAieGc0nSlVvNsT24Z9VSzTAd3kEJ7vdjdPt4kSDKPOF2Bsw6OQ7L_-" | |
| 163 "gJ4YZeQ\",\"dq\":\"Gos485j6cSBJiY1_t57gp3ZoeRKZzfoJ78DlB6yyHtdDAe9b_Ui-" | |
| 164 "RV6utuFnglWCdYCo5OjhQVHRUQqCo_LnKQ\",\"e\":\"AQAB\",\"ext\":true,\"key_" | |
| 165 "ops\":[\"sign\"],\"kty\":\"RSA\",\"n\":" | |
| 166 "\"pW5KDnAQF1iaUYfcfqhB0Vby7A42rVKkTf6x5h962ZHYxRBW_-2xYrTA8oOhKoijlN_" | |
| 167 "1JqtykcuzB86r_OCx39XNlQgJbVsri2311nHvY3fAkhyyPCcKcOJZjm_4nRnxBazC0_" | |
| 168 "DLNfKSgOE4a29kxO8i4eHyDQzoz_siSb2aITc\",\"p\":\"5-" | |
| 169 "iUJyCod1Fyc6NWBT6iobwMlKpy1VxuhilrLfyWeUjApyy8zKfqyzVwbgmh31WhU1vZs8w0Fg" | |
| 170 "s7bc0-2o5kQw\",\"q\":\"tp3KHPfU1-yB51uQ_MqHSrzeEj_" | |
| 171 "ScAGAqpBHm25I3o1n7ST58Z2FuidYdPVCzSDccj5pYzZKH5QlRSsmmmeZ_Q\",\"qi\":" | |
| 172 "\"JxVqukEm0kqB86Uoy_sn9WiG-" | |
| 173 "ECp9uhuF6RLlP6TGVhLjiL93h5aLjvYqluo2FhBlOshkKz4MrhH8To9JKefTQ\"}"; | |
| 174 | |
| 175 ASSERT_EQ(CryptoData(std::string(expected_jwk)), | |
| 176 CryptoData(exported_key_jwk)); | |
| 177 | |
| 178 ASSERT_EQ( | |
| 179 Status::Success(), | |
| 180 ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(exported_key_jwk), | |
| 181 CreateRsaHashedImportAlgorithm( | |
| 182 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 183 blink::WebCryptoAlgorithmIdSha1), | |
| 184 true, blink::WebCryptoKeyUsageSign, &key)); | |
| 185 | |
| 186 std::vector<uint8_t> exported_key_pkcs8; | |
| 187 ASSERT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatPkcs8, key, | |
| 188 &exported_key_pkcs8)); | |
| 189 | |
| 190 ASSERT_EQ(CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
| 191 CryptoData(exported_key_pkcs8)); | |
| 192 } | |
| 193 | |
| 194 // Tests importing multiple RSA private keys from JWK, and then exporting to | |
| 195 // PKCS8. | |
| 196 // | |
| 197 // This is a regression test for http://crbug.com/378315, for which importing | |
| 198 // a sequence of keys from JWK could yield the wrong key. The first key would | |
| 199 // be imported correctly, however every key after that would actually import | |
| 200 // the first key. | |
| 201 TEST_F(WebCryptoRsaSsaTest, ImportMultipleRSAPrivateKeysJwk) { | |
| 202 scoped_ptr<base::ListValue> key_list; | |
| 203 ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); | |
| 204 | |
| 205 // For this test to be meaningful the keys MUST be kept alive before importing | |
| 206 // new keys. | |
| 207 std::vector<blink::WebCryptoKey> live_keys; | |
| 208 | |
| 209 for (size_t key_index = 0; key_index < key_list->GetSize(); ++key_index) { | |
| 210 SCOPED_TRACE(key_index); | |
| 211 | |
| 212 base::DictionaryValue* key_values; | |
| 213 ASSERT_TRUE(key_list->GetDictionary(key_index, &key_values)); | |
| 214 | |
| 215 // Get the JWK representation of the key. | |
| 216 base::DictionaryValue* key_jwk; | |
| 217 ASSERT_TRUE(key_values->GetDictionary("jwk", &key_jwk)); | |
| 218 | |
| 219 // Get the PKCS8 representation of the key. | |
| 220 std::string pkcs8_hex_string; | |
| 221 ASSERT_TRUE(key_values->GetString("pkcs8", &pkcs8_hex_string)); | |
| 222 std::vector<uint8_t> pkcs8_bytes = HexStringToBytes(pkcs8_hex_string); | |
| 223 | |
| 224 // Get the modulus length for the key. | |
| 225 int modulus_length_bits = 0; | |
| 226 ASSERT_TRUE(key_values->GetInteger("modulusLength", &modulus_length_bits)); | |
| 227 | |
| 228 blink::WebCryptoKey private_key; | |
| 229 | |
| 230 // Import the key from JWK. | |
| 231 ASSERT_EQ(Status::Success(), | |
| 232 ImportKeyJwkFromDict( | |
| 233 *key_jwk, CreateRsaHashedImportAlgorithm( | |
| 234 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 235 blink::WebCryptoAlgorithmIdSha256), | |
| 236 true, blink::WebCryptoKeyUsageSign, &private_key)); | |
| 237 | |
| 238 live_keys.push_back(private_key); | |
| 239 | |
| 240 EXPECT_EQ( | |
| 241 modulus_length_bits, | |
| 242 static_cast<int>( | |
| 243 private_key.algorithm().rsaHashedParams()->modulusLengthBits())); | |
| 244 | |
| 245 // Export to PKCS8 and verify that it matches expectation. | |
| 246 std::vector<uint8_t> exported_key_pkcs8; | |
| 247 ASSERT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatPkcs8, | |
| 248 private_key, &exported_key_pkcs8)); | |
| 249 | |
| 250 EXPECT_BYTES_EQ(pkcs8_bytes, exported_key_pkcs8); | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 // Import an RSA private key using JWK. Next import a JWK containing the same | |
| 255 // modulus, but mismatched parameters for the rest. It should NOT be possible | |
| 256 // that the second import retrieves the first key. See http://crbug.com/378315 | |
| 257 // for how that could happen. | |
| 258 TEST_F(WebCryptoRsaSsaTest, ImportJwkExistingModulusAndInvalid) { | |
| 259 scoped_ptr<base::ListValue> key_list; | |
| 260 ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list)); | |
| 261 | |
| 262 // Import a 1024-bit private key. | |
| 263 base::DictionaryValue* key1_props; | |
| 264 ASSERT_TRUE(key_list->GetDictionary(1, &key1_props)); | |
| 265 base::DictionaryValue* key1_jwk; | |
| 266 ASSERT_TRUE(key1_props->GetDictionary("jwk", &key1_jwk)); | |
| 267 | |
| 268 blink::WebCryptoKey key1; | |
| 269 ASSERT_EQ(Status::Success(), | |
| 270 ImportKeyJwkFromDict(*key1_jwk, | |
| 271 CreateRsaHashedImportAlgorithm( | |
| 272 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 273 blink::WebCryptoAlgorithmIdSha256), | |
| 274 true, blink::WebCryptoKeyUsageSign, &key1)); | |
| 275 | |
| 276 ASSERT_EQ(1024u, key1.algorithm().rsaHashedParams()->modulusLengthBits()); | |
| 277 | |
| 278 // Construct a JWK using the modulus of key1, but all the other fields from | |
| 279 // another key (also a 1024-bit private key). | |
| 280 base::DictionaryValue* key2_props; | |
| 281 ASSERT_TRUE(key_list->GetDictionary(5, &key2_props)); | |
| 282 base::DictionaryValue* key2_jwk; | |
| 283 ASSERT_TRUE(key2_props->GetDictionary("jwk", &key2_jwk)); | |
| 284 std::string modulus; | |
| 285 key1_jwk->GetString("n", &modulus); | |
| 286 key2_jwk->SetString("n", modulus); | |
| 287 | |
| 288 // This should fail, as the n,e,d parameters are not consistent. It MUST NOT | |
| 289 // somehow return the key created earlier. | |
| 290 blink::WebCryptoKey key2; | |
| 291 ASSERT_EQ(Status::OperationError(), | |
| 292 ImportKeyJwkFromDict(*key2_jwk, | |
| 293 CreateRsaHashedImportAlgorithm( | |
| 294 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 295 blink::WebCryptoAlgorithmIdSha256), | |
| 296 true, blink::WebCryptoKeyUsageSign, &key2)); | |
| 297 } | |
| 298 | |
| 299 TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsa) { | |
| 300 // Note: using unrealistic short key lengths here to avoid bogging down tests. | |
| 301 | |
| 302 // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha256) | |
| 303 const unsigned int modulus_length = 256; | |
| 304 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
| 305 blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( | |
| 306 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 307 blink::WebCryptoAlgorithmIdSha256, modulus_length, public_exponent); | |
| 308 bool extractable = true; | |
| 309 const blink::WebCryptoKeyUsageMask public_usages = | |
| 310 blink::WebCryptoKeyUsageVerify; | |
| 311 const blink::WebCryptoKeyUsageMask private_usages = | |
| 312 blink::WebCryptoKeyUsageSign; | |
| 313 const blink::WebCryptoKeyUsageMask usages = public_usages | private_usages; | |
| 314 blink::WebCryptoKey public_key; | |
| 315 blink::WebCryptoKey private_key; | |
| 316 | |
| 317 EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, extractable, usages, | |
| 318 &public_key, &private_key)); | |
| 319 ASSERT_FALSE(public_key.isNull()); | |
| 320 ASSERT_FALSE(private_key.isNull()); | |
| 321 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); | |
| 322 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); | |
| 323 EXPECT_EQ(modulus_length, | |
| 324 public_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
| 325 EXPECT_EQ(modulus_length, | |
| 326 private_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
| 327 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
| 328 public_key.algorithm().rsaHashedParams()->hash().id()); | |
| 329 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
| 330 private_key.algorithm().rsaHashedParams()->hash().id()); | |
| 331 EXPECT_TRUE(public_key.extractable()); | |
| 332 EXPECT_EQ(extractable, private_key.extractable()); | |
| 333 EXPECT_EQ(public_usages, public_key.usages()); | |
| 334 EXPECT_EQ(private_usages, private_key.usages()); | |
| 335 | |
| 336 // Try exporting the generated key pair, and then re-importing to verify that | |
| 337 // the exported data was valid. | |
| 338 std::vector<uint8_t> public_key_spki; | |
| 339 EXPECT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatSpki, | |
| 340 public_key, &public_key_spki)); | |
| 341 | |
| 342 public_key = blink::WebCryptoKey::createNull(); | |
| 343 ASSERT_EQ( | |
| 344 Status::Success(), | |
| 345 ImportKey(blink::WebCryptoKeyFormatSpki, CryptoData(public_key_spki), | |
| 346 CreateRsaHashedImportAlgorithm( | |
| 347 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 348 blink::WebCryptoAlgorithmIdSha256), | |
| 349 true, public_usages, &public_key)); | |
| 350 EXPECT_EQ(modulus_length, | |
| 351 public_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
| 352 | |
| 353 std::vector<uint8_t> private_key_pkcs8; | |
| 354 EXPECT_EQ(Status::Success(), ExportKey(blink::WebCryptoKeyFormatPkcs8, | |
| 355 private_key, &private_key_pkcs8)); | |
| 356 private_key = blink::WebCryptoKey::createNull(); | |
| 357 ASSERT_EQ( | |
| 358 Status::Success(), | |
| 359 ImportKey(blink::WebCryptoKeyFormatPkcs8, CryptoData(private_key_pkcs8), | |
| 360 CreateRsaHashedImportAlgorithm( | |
| 361 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 362 blink::WebCryptoAlgorithmIdSha256), | |
| 363 true, private_usages, &private_key)); | |
| 364 EXPECT_EQ(modulus_length, | |
| 365 private_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
| 366 | |
| 367 // Fail with bad modulus. | |
| 368 algorithm = CreateRsaHashedKeyGenAlgorithm( | |
| 369 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 370 blink::WebCryptoAlgorithmIdSha256, 0, public_exponent); | |
| 371 EXPECT_EQ(Status::ErrorGenerateRsaUnsupportedModulus(), | |
| 372 GenerateKeyPair(algorithm, extractable, usages, &public_key, | |
| 373 &private_key)); | |
| 374 | |
| 375 // Fail with bad exponent: larger than unsigned long. | |
| 376 unsigned int exponent_length = sizeof(unsigned long) + 1; // NOLINT | |
| 377 const std::vector<uint8_t> long_exponent(exponent_length, 0x01); | |
| 378 algorithm = CreateRsaHashedKeyGenAlgorithm( | |
| 379 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 380 blink::WebCryptoAlgorithmIdSha256, modulus_length, long_exponent); | |
| 381 EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), | |
| 382 GenerateKeyPair(algorithm, extractable, usages, &public_key, | |
| 383 &private_key)); | |
| 384 | |
| 385 // Fail with bad exponent: empty. | |
| 386 const std::vector<uint8_t> empty_exponent; | |
| 387 algorithm = CreateRsaHashedKeyGenAlgorithm( | |
| 388 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 389 blink::WebCryptoAlgorithmIdSha256, modulus_length, empty_exponent); | |
| 390 EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), | |
| 391 GenerateKeyPair(algorithm, extractable, usages, &public_key, | |
| 392 &private_key)); | |
| 393 | |
| 394 // Fail with bad exponent: all zeros. | |
| 395 std::vector<uint8_t> exponent_with_leading_zeros(15, 0x00); | |
| 396 algorithm = CreateRsaHashedKeyGenAlgorithm( | |
| 397 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 398 blink::WebCryptoAlgorithmIdSha256, modulus_length, | |
| 399 exponent_with_leading_zeros); | |
| 400 EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), | |
| 401 GenerateKeyPair(algorithm, extractable, usages, &public_key, | |
| 402 &private_key)); | |
| 403 | |
| 404 // Key generation success using exponent with leading zeros. | |
| 405 exponent_with_leading_zeros.insert(exponent_with_leading_zeros.end(), | |
| 406 public_exponent.begin(), | |
| 407 public_exponent.end()); | |
| 408 algorithm = CreateRsaHashedKeyGenAlgorithm( | |
| 409 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 410 blink::WebCryptoAlgorithmIdSha256, modulus_length, | |
| 411 exponent_with_leading_zeros); | |
| 412 EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, extractable, usages, | |
| 413 &public_key, &private_key)); | |
| 414 EXPECT_FALSE(public_key.isNull()); | |
| 415 EXPECT_FALSE(private_key.isNull()); | |
| 416 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); | |
| 417 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); | |
| 418 EXPECT_TRUE(public_key.extractable()); | |
| 419 EXPECT_EQ(extractable, private_key.extractable()); | |
| 420 EXPECT_EQ(public_usages, public_key.usages()); | |
| 421 EXPECT_EQ(private_usages, private_key.usages()); | |
| 422 | |
| 423 // Successful WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 key generation (sha1) | |
| 424 algorithm = CreateRsaHashedKeyGenAlgorithm( | |
| 425 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 426 blink::WebCryptoAlgorithmIdSha1, modulus_length, public_exponent); | |
| 427 EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, false, usages, | |
| 428 &public_key, &private_key)); | |
| 429 EXPECT_FALSE(public_key.isNull()); | |
| 430 EXPECT_FALSE(private_key.isNull()); | |
| 431 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type()); | |
| 432 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type()); | |
| 433 EXPECT_EQ(modulus_length, | |
| 434 public_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
| 435 EXPECT_EQ(modulus_length, | |
| 436 private_key.algorithm().rsaHashedParams()->modulusLengthBits()); | |
| 437 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
| 438 public_key.algorithm().rsaHashedParams()->hash().id()); | |
| 439 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
| 440 private_key.algorithm().rsaHashedParams()->hash().id()); | |
| 441 // Even though "extractable" was set to false, the public key remains | |
| 442 // extractable. | |
| 443 EXPECT_TRUE(public_key.extractable()); | |
| 444 EXPECT_FALSE(private_key.extractable()); | |
| 445 EXPECT_EQ(public_usages, public_key.usages()); | |
| 446 EXPECT_EQ(private_usages, private_key.usages()); | |
| 447 | |
| 448 // Exporting a private key as SPKI format doesn't make sense. However this | |
| 449 // will first fail because the key is not extractable. | |
| 450 std::vector<uint8_t> output; | |
| 451 EXPECT_EQ(Status::ErrorKeyNotExtractable(), | |
| 452 ExportKey(blink::WebCryptoKeyFormatSpki, private_key, &output)); | |
| 453 | |
| 454 // Re-generate an extractable private_key and try to export it as SPKI format. | |
| 455 // This should fail since spki is for public keys. | |
| 456 EXPECT_EQ(Status::Success(), GenerateKeyPair(algorithm, true, usages, | |
| 457 &public_key, &private_key)); | |
| 458 EXPECT_EQ(Status::ErrorUnexpectedKeyType(), | |
| 459 ExportKey(blink::WebCryptoKeyFormatSpki, private_key, &output)); | |
| 460 } | |
| 461 | |
| 462 TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsaBadModulusLength) { | |
| 463 const unsigned int kBadModulusBits[] = { | |
| 464 0, | |
| 465 248, // Too small. | |
| 466 257, // Not a multiple of 8. | |
| 467 1023, // Not a multiple of 8. | |
| 468 0xFFFFFFFF, // Too big. | |
| 469 16384 + 8, // 16384 is the maxmimum length that NSS succeeds for. | |
| 470 }; | |
| 471 | |
| 472 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
| 473 | |
| 474 for (size_t i = 0; i < arraysize(kBadModulusBits); ++i) { | |
| 475 const unsigned int modulus_length_bits = kBadModulusBits[i]; | |
| 476 blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( | |
| 477 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 478 blink::WebCryptoAlgorithmIdSha256, modulus_length_bits, | |
| 479 public_exponent); | |
| 480 bool extractable = true; | |
| 481 const blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; | |
| 482 blink::WebCryptoKey public_key; | |
| 483 blink::WebCryptoKey private_key; | |
| 484 | |
| 485 EXPECT_EQ(Status::ErrorGenerateRsaUnsupportedModulus(), | |
| 486 GenerateKeyPair(algorithm, extractable, usages, &public_key, | |
| 487 &private_key)); | |
| 488 } | |
| 489 } | |
| 490 | |
| 491 // Try generating RSA key pairs using unsupported public exponents. Only | |
| 492 // exponents of 3 and 65537 are supported. Although OpenSSL can support other | |
| 493 // values, it can also hang when given invalid exponents. To avoid hanging, use | |
| 494 // a whitelist of known safe exponents. | |
| 495 TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairRsaBadExponent) { | |
| 496 const unsigned int modulus_length = 1024; | |
| 497 | |
| 498 const char* const kPublicExponents[] = { | |
| 499 "11", // 17 - This is a valid public exponent, but currently disallowed. | |
| 500 "00", | |
| 501 "01", | |
| 502 "02", | |
| 503 "010000", // 65536 | |
| 504 }; | |
| 505 | |
| 506 for (size_t i = 0; i < arraysize(kPublicExponents); ++i) { | |
| 507 SCOPED_TRACE(i); | |
| 508 blink::WebCryptoAlgorithm algorithm = CreateRsaHashedKeyGenAlgorithm( | |
| 509 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 510 blink::WebCryptoAlgorithmIdSha256, modulus_length, | |
| 511 HexStringToBytes(kPublicExponents[i])); | |
| 512 | |
| 513 blink::WebCryptoKey public_key; | |
| 514 blink::WebCryptoKey private_key; | |
| 515 | |
| 516 EXPECT_EQ(Status::ErrorGenerateKeyPublicExponent(), | |
| 517 GenerateKeyPair(algorithm, true, blink::WebCryptoKeyUsageSign, | |
| 518 &public_key, &private_key)); | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 TEST_F(WebCryptoRsaSsaTest, SignVerifyFailures) { | |
| 523 // Import a key pair. | |
| 524 blink::WebCryptoAlgorithm import_algorithm = | |
| 525 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 526 blink::WebCryptoAlgorithmIdSha1); | |
| 527 blink::WebCryptoKey public_key; | |
| 528 blink::WebCryptoKey private_key; | |
| 529 ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( | |
| 530 HexStringToBytes(kPublicKeySpkiDerHex), | |
| 531 HexStringToBytes(kPrivateKeyPkcs8DerHex), import_algorithm, false, | |
| 532 blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign, &public_key, | |
| 533 &private_key)); | |
| 534 | |
| 535 blink::WebCryptoAlgorithm algorithm = | |
| 536 CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); | |
| 537 | |
| 538 std::vector<uint8_t> signature; | |
| 539 bool signature_match; | |
| 540 | |
| 541 // Compute a signature. | |
| 542 const std::vector<uint8_t> data = HexStringToBytes("010203040506070809"); | |
| 543 ASSERT_EQ(Status::Success(), | |
| 544 Sign(algorithm, private_key, CryptoData(data), &signature)); | |
| 545 | |
| 546 // Ensure truncated signature does not verify by passing one less byte. | |
| 547 EXPECT_EQ(Status::Success(), | |
| 548 Verify(algorithm, public_key, | |
| 549 CryptoData(vector_as_array(&signature), | |
| 550 static_cast<unsigned int>(signature.size()) - 1), | |
| 551 CryptoData(data), &signature_match)); | |
| 552 EXPECT_FALSE(signature_match); | |
| 553 | |
| 554 // Ensure truncated signature does not verify by passing no bytes. | |
| 555 EXPECT_EQ(Status::Success(), Verify(algorithm, public_key, CryptoData(), | |
| 556 CryptoData(data), &signature_match)); | |
| 557 EXPECT_FALSE(signature_match); | |
| 558 | |
| 559 // Ensure corrupted signature does not verify. | |
| 560 std::vector<uint8_t> corrupt_sig = signature; | |
| 561 corrupt_sig[corrupt_sig.size() / 2] ^= 0x1; | |
| 562 EXPECT_EQ(Status::Success(), | |
| 563 Verify(algorithm, public_key, CryptoData(corrupt_sig), | |
| 564 CryptoData(data), &signature_match)); | |
| 565 EXPECT_FALSE(signature_match); | |
| 566 | |
| 567 // Ensure signatures that are greater than the modulus size fail. | |
| 568 const unsigned int long_message_size_bytes = 1024; | |
| 569 DCHECK_GT(long_message_size_bytes, kModulusLengthBits / 8); | |
| 570 const unsigned char kLongSignature[long_message_size_bytes] = {0}; | |
| 571 EXPECT_EQ(Status::Success(), | |
| 572 Verify(algorithm, public_key, | |
| 573 CryptoData(kLongSignature, sizeof(kLongSignature)), | |
| 574 CryptoData(data), &signature_match)); | |
| 575 EXPECT_FALSE(signature_match); | |
| 576 | |
| 577 // Ensure that signing and verifying with an incompatible algorithm fails. | |
| 578 algorithm = CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep); | |
| 579 | |
| 580 EXPECT_EQ(Status::ErrorUnexpected(), | |
| 581 Sign(algorithm, private_key, CryptoData(data), &signature)); | |
| 582 EXPECT_EQ(Status::ErrorUnexpected(), | |
| 583 Verify(algorithm, public_key, CryptoData(signature), | |
| 584 CryptoData(data), &signature_match)); | |
| 585 | |
| 586 // Some crypto libraries (NSS) can automatically select the RSA SSA inner hash | |
| 587 // based solely on the contents of the input signature data. In the Web Crypto | |
| 588 // implementation, the inner hash should be specified uniquely by the key | |
| 589 // algorithm parameter. To validate this behavior, call Verify with a computed | |
| 590 // signature that used one hash type (SHA-1), but pass in a key with a | |
| 591 // different inner hash type (SHA-256). If the hash type is determined by the | |
| 592 // signature itself (undesired), the verify will pass, while if the hash type | |
| 593 // is specified by the key algorithm (desired), the verify will fail. | |
| 594 | |
| 595 // Compute a signature using SHA-1 as the inner hash. | |
| 596 EXPECT_EQ(Status::Success(), | |
| 597 Sign(CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), | |
| 598 private_key, CryptoData(data), &signature)); | |
| 599 | |
| 600 blink::WebCryptoKey public_key_256; | |
| 601 EXPECT_EQ(Status::Success(), | |
| 602 ImportKey(blink::WebCryptoKeyFormatSpki, | |
| 603 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
| 604 CreateRsaHashedImportAlgorithm( | |
| 605 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 606 blink::WebCryptoAlgorithmIdSha256), | |
| 607 true, blink::WebCryptoKeyUsageVerify, &public_key_256)); | |
| 608 | |
| 609 // Now verify using an algorithm whose inner hash is SHA-256, not SHA-1. The | |
| 610 // signature should not verify. | |
| 611 // NOTE: public_key was produced by generateKey, and so its associated | |
| 612 // algorithm has WebCryptoRsaKeyGenParams and not WebCryptoRsaSsaParams. Thus | |
| 613 // it has no inner hash to conflict with the input algorithm. | |
| 614 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
| 615 private_key.algorithm().rsaHashedParams()->hash().id()); | |
| 616 EXPECT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
| 617 public_key_256.algorithm().rsaHashedParams()->hash().id()); | |
| 618 | |
| 619 bool is_match; | |
| 620 EXPECT_EQ(Status::Success(), | |
| 621 Verify(CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5), | |
| 622 public_key_256, CryptoData(signature), CryptoData(data), | |
| 623 &is_match)); | |
| 624 EXPECT_FALSE(is_match); | |
| 625 } | |
| 626 | |
| 627 TEST_F(WebCryptoRsaSsaTest, SignVerifyKnownAnswer) { | |
| 628 scoped_ptr<base::ListValue> tests; | |
| 629 ASSERT_TRUE(ReadJsonTestFileToList("pkcs1v15_sign.json", &tests)); | |
| 630 | |
| 631 // Import the key pair. | |
| 632 blink::WebCryptoAlgorithm import_algorithm = | |
| 633 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 634 blink::WebCryptoAlgorithmIdSha1); | |
| 635 blink::WebCryptoKey public_key; | |
| 636 blink::WebCryptoKey private_key; | |
| 637 ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( | |
| 638 HexStringToBytes(kPublicKeySpkiDerHex), | |
| 639 HexStringToBytes(kPrivateKeyPkcs8DerHex), import_algorithm, false, | |
| 640 blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign, &public_key, | |
| 641 &private_key)); | |
| 642 | |
| 643 blink::WebCryptoAlgorithm algorithm = | |
| 644 CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); | |
| 645 | |
| 646 // Validate the signatures are computed and verified as expected. | |
| 647 std::vector<uint8_t> signature; | |
| 648 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
| 649 SCOPED_TRACE(test_index); | |
| 650 | |
| 651 base::DictionaryValue* test; | |
| 652 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
| 653 | |
| 654 std::vector<uint8_t> test_message = | |
| 655 GetBytesFromHexString(test, "message_hex"); | |
| 656 std::vector<uint8_t> test_signature = | |
| 657 GetBytesFromHexString(test, "signature_hex"); | |
| 658 | |
| 659 signature.clear(); | |
| 660 ASSERT_EQ(Status::Success(), Sign(algorithm, private_key, | |
| 661 CryptoData(test_message), &signature)); | |
| 662 EXPECT_BYTES_EQ(test_signature, signature); | |
| 663 | |
| 664 bool is_match = false; | |
| 665 ASSERT_EQ(Status::Success(), | |
| 666 Verify(algorithm, public_key, CryptoData(test_signature), | |
| 667 CryptoData(test_message), &is_match)); | |
| 668 EXPECT_TRUE(is_match); | |
| 669 } | |
| 670 } | |
| 671 | |
| 672 // Try importing an RSA-SSA public key with unsupported key usages using SPKI | |
| 673 // format. RSA-SSA public keys only support the 'verify' usage. | |
| 674 TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_SPKI) { | |
| 675 const blink::WebCryptoAlgorithm algorithm = | |
| 676 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 677 blink::WebCryptoAlgorithmIdSha256); | |
| 678 | |
| 679 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
| 680 blink::WebCryptoKeyUsageSign, | |
| 681 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
| 682 blink::WebCryptoKeyUsageEncrypt, | |
| 683 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, | |
| 684 }; | |
| 685 | |
| 686 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
| 687 SCOPED_TRACE(i); | |
| 688 | |
| 689 blink::WebCryptoKey public_key; | |
| 690 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
| 691 ImportKey(blink::WebCryptoKeyFormatSpki, | |
| 692 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
| 693 algorithm, false, bad_usages[i], &public_key)); | |
| 694 } | |
| 695 } | |
| 696 | |
| 697 // Try importing an RSA-SSA public key with unsupported key usages using JWK | |
| 698 // format. RSA-SSA public keys only support the 'verify' usage. | |
| 699 TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaPublicKeyBadUsage_JWK) { | |
| 700 const blink::WebCryptoAlgorithm algorithm = | |
| 701 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 702 blink::WebCryptoAlgorithmIdSha256); | |
| 703 | |
| 704 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
| 705 blink::WebCryptoKeyUsageSign, | |
| 706 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, | |
| 707 blink::WebCryptoKeyUsageEncrypt, | |
| 708 blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, | |
| 709 }; | |
| 710 | |
| 711 base::DictionaryValue dict; | |
| 712 RestoreJwkRsaDictionary(&dict); | |
| 713 dict.Remove("use", NULL); | |
| 714 dict.SetString("alg", "RS256"); | |
| 715 | |
| 716 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
| 717 SCOPED_TRACE(i); | |
| 718 | |
| 719 blink::WebCryptoKey public_key; | |
| 720 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
| 721 ImportKeyJwkFromDict(dict, algorithm, false, bad_usages[i], | |
| 722 &public_key)); | |
| 723 } | |
| 724 } | |
| 725 | |
| 726 // Generate an RSA-SSA key pair with invalid usages. RSA-SSA supports: | |
| 727 // 'sign', 'verify' | |
| 728 TEST_F(WebCryptoRsaSsaTest, GenerateKeyBadUsages) { | |
| 729 blink::WebCryptoKeyUsageMask bad_usages[] = { | |
| 730 blink::WebCryptoKeyUsageDecrypt, | |
| 731 blink::WebCryptoKeyUsageVerify | blink::WebCryptoKeyUsageDecrypt, | |
| 732 blink::WebCryptoKeyUsageWrapKey, | |
| 733 }; | |
| 734 | |
| 735 const unsigned int modulus_length = 256; | |
| 736 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
| 737 | |
| 738 for (size_t i = 0; i < arraysize(bad_usages); ++i) { | |
| 739 SCOPED_TRACE(i); | |
| 740 | |
| 741 blink::WebCryptoKey public_key; | |
| 742 blink::WebCryptoKey private_key; | |
| 743 | |
| 744 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
| 745 GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( | |
| 746 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 747 blink::WebCryptoAlgorithmIdSha256, | |
| 748 modulus_length, public_exponent), | |
| 749 true, bad_usages[i], &public_key, &private_key)); | |
| 750 } | |
| 751 } | |
| 752 | |
| 753 // Generate an RSA-SSA key pair. The public and private keys should select the | |
| 754 // key usages which are applicable, and not have the exact same usages as was | |
| 755 // specified to GenerateKey | |
| 756 TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairIntersectUsages) { | |
| 757 const unsigned int modulus_length = 256; | |
| 758 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
| 759 | |
| 760 blink::WebCryptoKey public_key; | |
| 761 blink::WebCryptoKey private_key; | |
| 762 | |
| 763 ASSERT_EQ(Status::Success(), | |
| 764 GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( | |
| 765 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 766 blink::WebCryptoAlgorithmIdSha256, | |
| 767 modulus_length, public_exponent), | |
| 768 true, blink::WebCryptoKeyUsageSign | | |
| 769 blink::WebCryptoKeyUsageVerify, | |
| 770 &public_key, &private_key)); | |
| 771 | |
| 772 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, public_key.usages()); | |
| 773 EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages()); | |
| 774 | |
| 775 // Try again but this time without the Verify usages. | |
| 776 ASSERT_EQ(Status::Success(), | |
| 777 GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( | |
| 778 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 779 blink::WebCryptoAlgorithmIdSha256, | |
| 780 modulus_length, public_exponent), | |
| 781 true, blink::WebCryptoKeyUsageSign, &public_key, | |
| 782 &private_key)); | |
| 783 | |
| 784 EXPECT_EQ(0, public_key.usages()); | |
| 785 EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages()); | |
| 786 } | |
| 787 | |
| 788 TEST_F(WebCryptoRsaSsaTest, GenerateKeyPairEmptyUsages) { | |
| 789 const unsigned int modulus_length = 256; | |
| 790 const std::vector<uint8_t> public_exponent = HexStringToBytes("010001"); | |
| 791 | |
| 792 blink::WebCryptoKey public_key; | |
| 793 blink::WebCryptoKey private_key; | |
| 794 | |
| 795 ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), | |
| 796 GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm( | |
| 797 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 798 blink::WebCryptoAlgorithmIdSha256, | |
| 799 modulus_length, public_exponent), | |
| 800 true, 0, &public_key, &private_key)); | |
| 801 } | |
| 802 | |
| 803 TEST_F(WebCryptoRsaSsaTest, ImportKeyEmptyUsages) { | |
| 804 blink::WebCryptoKey public_key; | |
| 805 blink::WebCryptoKey private_key; | |
| 806 | |
| 807 // Public without usage does not throw an error. | |
| 808 ASSERT_EQ(Status::Success(), | |
| 809 ImportKey(blink::WebCryptoKeyFormatSpki, | |
| 810 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
| 811 CreateRsaHashedImportAlgorithm( | |
| 812 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 813 blink::WebCryptoAlgorithmIdSha256), | |
| 814 true, 0, &public_key)); | |
| 815 EXPECT_EQ(0, public_key.usages()); | |
| 816 | |
| 817 // Private empty usage will throw an error. | |
| 818 ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), | |
| 819 ImportKey(blink::WebCryptoKeyFormatPkcs8, | |
| 820 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
| 821 CreateRsaHashedImportAlgorithm( | |
| 822 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 823 blink::WebCryptoAlgorithmIdSha1), | |
| 824 true, 0, &private_key)); | |
| 825 | |
| 826 std::vector<uint8_t> public_jwk; | |
| 827 ASSERT_EQ(Status::Success(), | |
| 828 ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &public_jwk)); | |
| 829 | |
| 830 ASSERT_EQ(Status::Success(), | |
| 831 ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(public_jwk), | |
| 832 CreateRsaHashedImportAlgorithm( | |
| 833 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 834 blink::WebCryptoAlgorithmIdSha256), | |
| 835 true, 0, &public_key)); | |
| 836 EXPECT_EQ(0, public_key.usages()); | |
| 837 | |
| 838 // With correct usage to get correct imported private_key | |
| 839 std::vector<uint8_t> private_jwk; | |
| 840 ImportKey( | |
| 841 blink::WebCryptoKeyFormatPkcs8, | |
| 842 CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), | |
| 843 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 844 blink::WebCryptoAlgorithmIdSha1), | |
| 845 true, blink::WebCryptoKeyUsageSign, &private_key); | |
| 846 | |
| 847 ASSERT_EQ(Status::Success(), | |
| 848 ExportKey(blink::WebCryptoKeyFormatJwk, private_key, &private_jwk)); | |
| 849 | |
| 850 ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), | |
| 851 ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(private_jwk), | |
| 852 CreateRsaHashedImportAlgorithm( | |
| 853 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 854 blink::WebCryptoAlgorithmIdSha1), | |
| 855 true, 0, &private_key)); | |
| 856 } | |
| 857 | |
| 858 TEST_F(WebCryptoRsaSsaTest, ImportExportJwkRsaPublicKey) { | |
| 859 struct TestCase { | |
| 860 const blink::WebCryptoAlgorithmId hash; | |
| 861 const blink::WebCryptoKeyUsageMask usage; | |
| 862 const char* const jwk_alg; | |
| 863 }; | |
| 864 const TestCase kTests[] = { | |
| 865 {blink::WebCryptoAlgorithmIdSha1, blink::WebCryptoKeyUsageVerify, "RS1"}, | |
| 866 {blink::WebCryptoAlgorithmIdSha256, | |
| 867 blink::WebCryptoKeyUsageVerify, | |
| 868 "RS256"}, | |
| 869 {blink::WebCryptoAlgorithmIdSha384, | |
| 870 blink::WebCryptoKeyUsageVerify, | |
| 871 "RS384"}, | |
| 872 {blink::WebCryptoAlgorithmIdSha512, | |
| 873 blink::WebCryptoKeyUsageVerify, | |
| 874 "RS512"}}; | |
| 875 | |
| 876 for (size_t test_index = 0; test_index < arraysize(kTests); ++test_index) { | |
| 877 SCOPED_TRACE(test_index); | |
| 878 const TestCase& test = kTests[test_index]; | |
| 879 | |
| 880 const blink::WebCryptoAlgorithm import_algorithm = | |
| 881 CreateRsaHashedImportAlgorithm( | |
| 882 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, test.hash); | |
| 883 | |
| 884 // Import the spki to create a public key | |
| 885 blink::WebCryptoKey public_key; | |
| 886 ASSERT_EQ(Status::Success(), | |
| 887 ImportKey(blink::WebCryptoKeyFormatSpki, | |
| 888 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), | |
| 889 import_algorithm, true, test.usage, &public_key)); | |
| 890 | |
| 891 // Export the public key as JWK and verify its contents | |
| 892 std::vector<uint8_t> jwk; | |
| 893 ASSERT_EQ(Status::Success(), | |
| 894 ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk)); | |
| 895 EXPECT_TRUE(VerifyPublicJwk(jwk, test.jwk_alg, kPublicKeyModulusHex, | |
| 896 kPublicKeyExponentHex, test.usage)); | |
| 897 | |
| 898 // Import the JWK back in to create a new key | |
| 899 blink::WebCryptoKey public_key2; | |
| 900 ASSERT_EQ(Status::Success(), | |
| 901 ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(jwk), | |
| 902 import_algorithm, true, test.usage, &public_key2)); | |
| 903 ASSERT_TRUE(public_key2.handle()); | |
| 904 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type()); | |
| 905 EXPECT_TRUE(public_key2.extractable()); | |
| 906 EXPECT_EQ(import_algorithm.id(), public_key2.algorithm().id()); | |
| 907 | |
| 908 // Export the new key as spki and compare to the original. | |
| 909 std::vector<uint8_t> spki; | |
| 910 ASSERT_EQ(Status::Success(), | |
| 911 ExportKey(blink::WebCryptoKeyFormatSpki, public_key2, &spki)); | |
| 912 EXPECT_BYTES_EQ_HEX(kPublicKeySpkiDerHex, CryptoData(spki)); | |
| 913 } | |
| 914 } | |
| 915 | |
| 916 TEST_F(WebCryptoRsaSsaTest, ImportJwkRsaFailures) { | |
| 917 base::DictionaryValue dict; | |
| 918 RestoreJwkRsaDictionary(&dict); | |
| 919 blink::WebCryptoAlgorithm algorithm = | |
| 920 CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 921 blink::WebCryptoAlgorithmIdSha256); | |
| 922 blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageVerify; | |
| 923 blink::WebCryptoKey key; | |
| 924 | |
| 925 // An RSA public key JWK _must_ have an "n" (modulus) and an "e" (exponent) | |
| 926 // entry, while an RSA private key must have those plus at least a "d" | |
| 927 // (private exponent) entry. | |
| 928 // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, | |
| 929 // section 6.3. | |
| 930 | |
| 931 // Baseline pass. | |
| 932 EXPECT_EQ(Status::Success(), | |
| 933 ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); | |
| 934 EXPECT_EQ(algorithm.id(), key.algorithm().id()); | |
| 935 EXPECT_FALSE(key.extractable()); | |
| 936 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages()); | |
| 937 EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type()); | |
| 938 | |
| 939 // The following are specific failure cases for when kty = "RSA". | |
| 940 | |
| 941 // Fail if either "n" or "e" is not present or malformed. | |
| 942 const std::string kKtyParmName[] = {"n", "e"}; | |
| 943 for (size_t idx = 0; idx < arraysize(kKtyParmName); ++idx) { | |
| 944 // Fail on missing parameter. | |
| 945 dict.Remove(kKtyParmName[idx], NULL); | |
| 946 EXPECT_NE(Status::Success(), | |
| 947 ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); | |
| 948 RestoreJwkRsaDictionary(&dict); | |
| 949 | |
| 950 // Fail on bad b64 parameter encoding. | |
| 951 dict.SetString(kKtyParmName[idx], "Qk3f0DsytU8lfza2au #$% Htaw2xpop9yTuH0"); | |
| 952 EXPECT_NE(Status::Success(), | |
| 953 ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); | |
| 954 RestoreJwkRsaDictionary(&dict); | |
| 955 | |
| 956 // Fail on empty parameter. | |
| 957 dict.SetString(kKtyParmName[idx], ""); | |
| 958 EXPECT_EQ(Status::ErrorJwkEmptyBigInteger(kKtyParmName[idx]), | |
| 959 ImportKeyJwkFromDict(dict, algorithm, false, usages, &key)); | |
| 960 RestoreJwkRsaDictionary(&dict); | |
| 961 } | |
| 962 } | |
| 963 | |
| 964 // Try importing an RSA-SSA key from JWK format, having specified both Sign and | |
| 965 // Verify usage, and an invalid JWK. | |
| 966 // | |
| 967 // The test must fail with a usage error BEFORE attempting to read the JWK data. | |
| 968 // Although both Sign and Verify are valid usages for RSA-SSA keys, it is | |
| 969 // invalid to have them both at the same time for one key (since Sign applies to | |
| 970 // private keys, whereas Verify applies to public keys). | |
| 971 // | |
| 972 // If the implementation does not fail fast, this test will crash dereferencing | |
| 973 // invalid memory. | |
| 974 TEST_F(WebCryptoRsaSsaTest, ImportRsaSsaJwkBadUsageFailFast) { | |
| 975 CryptoData bad_data(NULL, 128); // Invalid buffer of length 128. | |
| 976 | |
| 977 blink::WebCryptoKey key; | |
| 978 ASSERT_EQ(Status::ErrorCreateKeyBadUsages(), | |
| 979 ImportKey(blink::WebCryptoKeyFormatJwk, bad_data, | |
| 980 CreateRsaHashedImportAlgorithm( | |
| 981 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 982 blink::WebCryptoAlgorithmIdSha256), | |
| 983 true, blink::WebCryptoKeyUsageVerify | | |
| 984 blink::WebCryptoKeyUsageSign, | |
| 985 &key)); | |
| 986 } | |
| 987 | |
| 988 // Imports invalid JWK/SPKI/PKCS8 data and verifies that it fails as expected. | |
| 989 TEST_F(WebCryptoRsaSsaTest, ImportInvalidKeyData) { | |
| 990 scoped_ptr<base::ListValue> tests; | |
| 991 ASSERT_TRUE(ReadJsonTestFileToList("bad_rsa_keys.json", &tests)); | |
| 992 | |
| 993 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
| 994 SCOPED_TRACE(test_index); | |
| 995 | |
| 996 const base::DictionaryValue* test; | |
| 997 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
| 998 | |
| 999 blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test); | |
| 1000 std::vector<uint8_t> key_data = | |
| 1001 GetKeyDataFromJsonTestCase(test, key_format); | |
| 1002 std::string test_error; | |
| 1003 ASSERT_TRUE(test->GetString("error", &test_error)); | |
| 1004 | |
| 1005 blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageSign; | |
| 1006 if (key_format == blink::WebCryptoKeyFormatSpki) | |
| 1007 usages = blink::WebCryptoKeyUsageVerify; | |
| 1008 blink::WebCryptoKey key; | |
| 1009 Status status = ImportKey(key_format, CryptoData(key_data), | |
| 1010 CreateRsaHashedImportAlgorithm( | |
| 1011 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, | |
| 1012 blink::WebCryptoAlgorithmIdSha256), | |
| 1013 true, usages, &key); | |
| 1014 EXPECT_EQ(test_error, StatusToString(status)); | |
| 1015 } | |
| 1016 } | |
| 1017 | |
| 1018 } // namespace | |
| 1019 | |
| 1020 } // namespace webcrypto | |
| OLD | NEW |