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/stl_util.h" | |
6 #include "content/child/webcrypto/algorithm_dispatch.h" | |
7 #include "content/child/webcrypto/crypto_data.h" | |
8 #include "content/child/webcrypto/jwk.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 "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | |
13 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | |
14 | |
15 namespace content { | |
16 | |
17 namespace webcrypto { | |
18 | |
19 namespace { | |
20 | |
21 bool SupportsEcdh() { | |
22 #if defined(USE_OPENSSL) | |
23 return true; | |
24 #else | |
25 LOG(ERROR) << "Skipping ECDH test because unsupported"; | |
26 return false; | |
27 #endif | |
28 } | |
29 | |
30 // TODO(eroman): Test passing an RSA public key instead of ECDH key. | |
31 // TODO(eroman): Test passing an ECDSA public key | |
32 | |
33 blink::WebCryptoAlgorithm CreateEcdhImportAlgorithm( | |
34 blink::WebCryptoNamedCurve named_curve) { | |
35 return CreateEcImportAlgorithm(blink::WebCryptoAlgorithmIdEcdh, named_curve); | |
36 } | |
37 | |
38 blink::WebCryptoAlgorithm CreateEcdhDeriveParams( | |
39 const blink::WebCryptoKey& public_key) { | |
40 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | |
41 blink::WebCryptoAlgorithmIdEcdh, | |
42 new blink::WebCryptoEcdhKeyDeriveParams(public_key)); | |
43 } | |
44 | |
45 blink::WebCryptoAlgorithm CreateAesGcmDerivedKeyParams( | |
46 unsigned short length_bits) { | |
47 return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | |
48 blink::WebCryptoAlgorithmIdAesGcm, | |
49 new blink::WebCryptoAesDerivedKeyParams(length_bits)); | |
50 } | |
51 | |
52 // Helper that loads a "public_key" and "private_key" from the test data. | |
53 bool ImportKeysFromTest(const base::DictionaryValue* test, | |
54 blink::WebCryptoKey* public_key, | |
55 blink::WebCryptoKey* private_key) { | |
56 // Import the public key. | |
57 const base::DictionaryValue* public_key_json = NULL; | |
58 EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json)); | |
59 blink::WebCryptoNamedCurve curve = | |
60 GetCurveNameFromDictionary(public_key_json); | |
61 EXPECT_EQ(Status::Success(), | |
62 ImportKey(blink::WebCryptoKeyFormatJwk, | |
63 CryptoData(MakeJsonVector(*public_key_json)), | |
64 CreateEcdhImportAlgorithm(curve), true, 0, public_key)); | |
65 | |
66 // If the test didn't specify an error for private key import, that implies | |
67 // it expects success. | |
68 std::string expected_private_key_error = "Success"; | |
69 test->GetString("private_key_error", &expected_private_key_error); | |
70 | |
71 // Import the private key. | |
72 const base::DictionaryValue* private_key_json = NULL; | |
73 EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json)); | |
74 curve = GetCurveNameFromDictionary(private_key_json); | |
75 Status status = ImportKey( | |
76 blink::WebCryptoKeyFormatJwk, | |
77 CryptoData(MakeJsonVector(*private_key_json)), | |
78 CreateEcdhImportAlgorithm(curve), true, | |
79 blink::WebCryptoKeyUsageDeriveBits | blink::WebCryptoKeyUsageDeriveKey, | |
80 private_key); | |
81 EXPECT_EQ(expected_private_key_error, StatusToString(status)); | |
82 return status.IsSuccess(); | |
83 } | |
84 | |
85 TEST(WebCryptoEcdhTest, DeriveBitsKnownAnswer) { | |
86 if (!SupportsEcdh()) | |
87 return; | |
88 | |
89 scoped_ptr<base::ListValue> tests; | |
90 ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests)); | |
91 | |
92 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
93 SCOPED_TRACE(test_index); | |
94 | |
95 const base::DictionaryValue* test; | |
96 ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | |
97 | |
98 // Import the keys. | |
99 blink::WebCryptoKey public_key; | |
100 blink::WebCryptoKey private_key; | |
101 if (!ImportKeysFromTest(test, &public_key, &private_key)) | |
102 continue; | |
103 | |
104 // Now try to derive bytes. | |
105 std::vector<uint8_t> derived_bytes; | |
106 int length_bits = 0; | |
107 ASSERT_TRUE(test->GetInteger("length_bits", &length_bits)); | |
108 | |
109 // If the test didn't specify an error, that implies it expects success. | |
110 std::string expected_error = "Success"; | |
111 test->GetString("error", &expected_error); | |
112 | |
113 Status status = DeriveBits(CreateEcdhDeriveParams(public_key), private_key, | |
114 length_bits, &derived_bytes); | |
115 ASSERT_EQ(expected_error, StatusToString(status)); | |
116 if (status.IsError()) | |
117 continue; | |
118 | |
119 std::vector<uint8_t> expected_bytes = | |
120 GetBytesFromHexString(test, "derived_bytes"); | |
121 | |
122 EXPECT_EQ(CryptoData(expected_bytes), CryptoData(derived_bytes)); | |
123 } | |
124 } | |
125 | |
126 // Loads up a test ECDH public and private key for P-521. The keys | |
127 // come from different key pairs, and can be used for key derivation of up to | |
128 // 528 bits. | |
129 ::testing::AssertionResult LoadTestKeys(blink::WebCryptoKey* public_key, | |
130 blink::WebCryptoKey* private_key) { | |
131 scoped_ptr<base::ListValue> tests; | |
132 if (!ReadJsonTestFileToList("ecdh.json", &tests)) | |
133 return ::testing::AssertionFailure() << "Failed loading ecdh.json"; | |
134 | |
135 const base::DictionaryValue* test = NULL; | |
136 bool valid_p521_keys = false; | |
137 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | |
138 SCOPED_TRACE(test_index); | |
139 EXPECT_TRUE(tests->GetDictionary(test_index, &test)); | |
140 test->GetBoolean("valid_p521_keys", &valid_p521_keys); | |
141 if (valid_p521_keys) | |
142 break; | |
143 } | |
144 if (!valid_p521_keys) { | |
145 return ::testing::AssertionFailure() | |
146 << "The P-521 test are missing in ecdh.json"; | |
147 } | |
148 | |
149 ImportKeysFromTest(test, public_key, private_key); | |
150 | |
151 EXPECT_EQ(blink::WebCryptoNamedCurveP521, | |
152 public_key->algorithm().ecParams()->namedCurve()); | |
153 | |
154 return ::testing::AssertionSuccess(); | |
155 } | |
156 | |
157 // Try deriving an AES key of length 129 bits. | |
158 TEST(WebCryptoEcdhTest, DeriveKeyBadAesLength) { | |
159 if (!SupportsEcdh()) | |
160 return; | |
161 | |
162 blink::WebCryptoKey public_key; | |
163 blink::WebCryptoKey base_key; | |
164 ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); | |
165 | |
166 blink::WebCryptoKey derived_key; | |
167 | |
168 ASSERT_EQ(Status::ErrorGetAesKeyLength(), | |
169 DeriveKey(CreateEcdhDeriveParams(public_key), base_key, | |
170 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), | |
171 CreateAesGcmDerivedKeyParams(129), true, | |
172 blink::WebCryptoKeyUsageEncrypt, &derived_key)); | |
173 } | |
174 | |
175 // Try deriving an AES key of length 192 bits. | |
176 TEST(WebCryptoEcdhTest, DeriveKeyUnsupportedAesLength) { | |
177 if (!SupportsEcdh()) | |
178 return; | |
179 | |
180 blink::WebCryptoKey public_key; | |
181 blink::WebCryptoKey base_key; | |
182 ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); | |
183 | |
184 blink::WebCryptoKey derived_key; | |
185 | |
186 ASSERT_EQ(Status::ErrorAes192BitUnsupported(), | |
187 DeriveKey(CreateEcdhDeriveParams(public_key), base_key, | |
188 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), | |
189 CreateAesGcmDerivedKeyParams(192), true, | |
190 blink::WebCryptoKeyUsageEncrypt, &derived_key)); | |
191 } | |
192 | |
193 // Try deriving an HMAC key of length 0 bits. | |
194 TEST(WebCryptoEcdhTest, DeriveKeyZeroLengthHmac) { | |
195 if (!SupportsEcdh()) | |
196 return; | |
197 | |
198 blink::WebCryptoKey public_key; | |
199 blink::WebCryptoKey base_key; | |
200 ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); | |
201 | |
202 blink::WebCryptoKey derived_key; | |
203 | |
204 const blink::WebCryptoAlgorithm import_algorithm = | |
205 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1, 0); | |
206 | |
207 ASSERT_EQ(Status::ErrorGetHmacKeyLengthZero(), | |
208 DeriveKey(CreateEcdhDeriveParams(public_key), base_key, | |
209 import_algorithm, import_algorithm, true, | |
210 blink::WebCryptoKeyUsageSign, &derived_key)); | |
211 } | |
212 | |
213 // Derive an HMAC key of length 19 bits. | |
214 TEST(WebCryptoEcdhTest, DeriveKeyHmac19Bits) { | |
215 if (!SupportsEcdh()) | |
216 return; | |
217 | |
218 blink::WebCryptoKey public_key; | |
219 blink::WebCryptoKey base_key; | |
220 ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); | |
221 | |
222 blink::WebCryptoKey derived_key; | |
223 | |
224 const blink::WebCryptoAlgorithm import_algorithm = | |
225 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1, 19); | |
226 | |
227 ASSERT_EQ(Status::Success(), | |
228 DeriveKey(CreateEcdhDeriveParams(public_key), base_key, | |
229 import_algorithm, import_algorithm, true, | |
230 blink::WebCryptoKeyUsageSign, &derived_key)); | |
231 | |
232 ASSERT_EQ(blink::WebCryptoAlgorithmIdHmac, derived_key.algorithm().id()); | |
233 ASSERT_EQ(blink::WebCryptoAlgorithmIdSha1, | |
234 derived_key.algorithm().hmacParams()->hash().id()); | |
235 ASSERT_EQ(19u, derived_key.algorithm().hmacParams()->lengthBits()); | |
236 | |
237 // Export the key and verify its contents. | |
238 std::vector<uint8_t> raw_key; | |
239 EXPECT_EQ(Status::Success(), | |
240 ExportKey(blink::WebCryptoKeyFormatRaw, derived_key, &raw_key)); | |
241 EXPECT_EQ(3u, raw_key.size()); | |
242 // The last 7 bits of the key should be zero. | |
243 EXPECT_EQ(0, raw_key[raw_key.size() - 1] & 0x1f); | |
244 } | |
245 | |
246 // Derive an HMAC key with no specified length (just the hash of SHA-256). | |
247 TEST(WebCryptoEcdhTest, DeriveKeyHmacSha256NoLength) { | |
248 if (!SupportsEcdh()) | |
249 return; | |
250 | |
251 blink::WebCryptoKey public_key; | |
252 blink::WebCryptoKey base_key; | |
253 ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); | |
254 | |
255 blink::WebCryptoKey derived_key; | |
256 | |
257 const blink::WebCryptoAlgorithm import_algorithm = | |
258 CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256); | |
259 | |
260 ASSERT_EQ(Status::Success(), | |
261 DeriveKey(CreateEcdhDeriveParams(public_key), base_key, | |
262 import_algorithm, import_algorithm, true, | |
263 blink::WebCryptoKeyUsageSign, &derived_key)); | |
264 | |
265 ASSERT_EQ(blink::WebCryptoAlgorithmIdHmac, derived_key.algorithm().id()); | |
266 ASSERT_EQ(blink::WebCryptoAlgorithmIdSha256, | |
267 derived_key.algorithm().hmacParams()->hash().id()); | |
268 ASSERT_EQ(512u, derived_key.algorithm().hmacParams()->lengthBits()); | |
269 | |
270 // Export the key and verify its contents. | |
271 std::vector<uint8_t> raw_key; | |
272 EXPECT_EQ(Status::Success(), | |
273 ExportKey(blink::WebCryptoKeyFormatRaw, derived_key, &raw_key)); | |
274 EXPECT_EQ(64u, raw_key.size()); | |
275 } | |
276 | |
277 // Derive an HMAC key with no specified length (just the hash of SHA-512). | |
278 // | |
279 // This fails, because ECDH using P-521 can only generate 528 bits, however HMAC | |
280 // SHA-512 requires 1024 bits. | |
281 // | |
282 // In practice, authors won't be directly generating keys from key agreement | |
283 // schemes, as that is frequently insecure, and instead be using KDFs to expand | |
284 // and generate keys. For simplicity of testing, however, test using an HMAC | |
285 // key. | |
286 TEST(WebCryptoEcdhTest, DeriveKeyHmacSha512NoLength) { | |
287 if (!SupportsEcdh()) | |
288 return; | |
289 | |
290 blink::WebCryptoKey public_key; | |
291 blink::WebCryptoKey base_key; | |
292 ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); | |
293 | |
294 blink::WebCryptoKey derived_key; | |
295 | |
296 const blink::WebCryptoAlgorithm import_algorithm = | |
297 CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha512); | |
298 | |
299 ASSERT_EQ(Status::ErrorEcdhLengthTooBig(528), | |
300 DeriveKey(CreateEcdhDeriveParams(public_key), base_key, | |
301 import_algorithm, import_algorithm, true, | |
302 blink::WebCryptoKeyUsageSign, &derived_key)); | |
303 } | |
304 | |
305 // Try deriving an AES key of length 128 bits. | |
306 TEST(WebCryptoEcdhTest, DeriveKeyAes128) { | |
307 if (!SupportsEcdh()) | |
308 return; | |
309 | |
310 blink::WebCryptoKey public_key; | |
311 blink::WebCryptoKey base_key; | |
312 ASSERT_TRUE(LoadTestKeys(&public_key, &base_key)); | |
313 | |
314 blink::WebCryptoKey derived_key; | |
315 | |
316 ASSERT_EQ(Status::Success(), | |
317 DeriveKey(CreateEcdhDeriveParams(public_key), base_key, | |
318 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm), | |
319 CreateAesGcmDerivedKeyParams(128), true, | |
320 blink::WebCryptoKeyUsageEncrypt, &derived_key)); | |
321 | |
322 ASSERT_EQ(blink::WebCryptoAlgorithmIdAesGcm, derived_key.algorithm().id()); | |
323 ASSERT_EQ(128, derived_key.algorithm().aesParams()->lengthBits()); | |
324 | |
325 // Export the key and verify its contents. | |
326 std::vector<uint8_t> raw_key; | |
327 EXPECT_EQ(Status::Success(), | |
328 ExportKey(blink::WebCryptoKeyFormatRaw, derived_key, &raw_key)); | |
329 EXPECT_EQ(16u, raw_key.size()); | |
330 } | |
331 | |
332 TEST(WebCryptoEcdhTest, ImportKeyEmptyUsage) { | |
333 if (!SupportsEcdh()) | |
334 return; | |
335 | |
336 blink::WebCryptoKey key; | |
337 | |
338 scoped_ptr<base::ListValue> tests; | |
339 ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests)); | |
340 | |
341 const base::DictionaryValue* test; | |
342 ASSERT_TRUE(tests->GetDictionary(0, &test)); | |
343 | |
344 // Import the public key. | |
345 const base::DictionaryValue* public_key_json = NULL; | |
346 EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json)); | |
347 blink::WebCryptoNamedCurve curve = | |
348 GetCurveNameFromDictionary(public_key_json); | |
349 ASSERT_EQ(Status::Success(), | |
350 ImportKey(blink::WebCryptoKeyFormatJwk, | |
351 CryptoData(MakeJsonVector(*public_key_json)), | |
352 CreateEcdhImportAlgorithm(curve), true, 0, &key)); | |
353 EXPECT_EQ(0, key.usages()); | |
354 | |
355 // Import the private key. | |
356 const base::DictionaryValue* private_key_json = NULL; | |
357 EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json)); | |
358 curve = GetCurveNameFromDictionary(private_key_json); | |
359 ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(), | |
360 ImportKey(blink::WebCryptoKeyFormatJwk, | |
361 CryptoData(MakeJsonVector(*private_key_json)), | |
362 CreateEcdhImportAlgorithm(curve), true, | |
363 0, &key)); | |
364 } | |
365 | |
366 } // namespace | |
367 | |
368 } // namespace webcrypto | |
369 | |
370 } // namespace content | |
OLD | NEW |