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