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