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