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