Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(675)

Side by Side Diff: content/child/webcrypto/test/ecdsa_unittest.cc

Issue 698363002: webcrypto: Add ECDSA algorithm (chromium-side) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@extract_more
Patch Set: rebase + add another test Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 SupportsEcdsa() {
22 #if defined(USE_OPENSSL)
23 return true;
24 #else
25 LOG(ERROR) << "Skipping ECDSA test because unsupported";
26 return false;
27 #endif
28 }
29
30 // This is essentially a duplication of the curve name parsing done by Blink,
31 // so tests can use the same names in data files.
32 blink::WebCryptoNamedCurve GetCurveNameFromJsonTest(
33 const base::DictionaryValue* test) {
34 std::string curve_str;
35 if (!test->GetString("curve", &curve_str)) {
36 EXPECT_TRUE(false) << "Missing \"curve\" parameter";
37 }
38
39 if (curve_str == "P-256")
40 return blink::WebCryptoNamedCurveP256;
41 if (curve_str == "P-384")
42 return blink::WebCryptoNamedCurveP384;
43 if (curve_str == "P-521")
44 return blink::WebCryptoNamedCurveP521;
45 else
46 EXPECT_TRUE(false) << "Unrecognized curve name: " << curve_str;
davidben 2014/11/07 00:17:21 Probably ADD_FAILURE() instead of EXPECT_TRUE(fals
eroman 2014/11/11 01:26:27 Done.
47
48 return blink::WebCryptoNamedCurveP384;
49 }
50
51 blink::WebCryptoAlgorithm CreateEcdsaKeyGenAlgorithm(
52 blink::WebCryptoNamedCurve named_curve) {
53 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
54 blink::WebCryptoAlgorithmIdEcdsa,
55 new blink::WebCryptoEcKeyGenParams(named_curve));
56 }
57
58 blink::WebCryptoAlgorithm CreateEcdsaImportAlgorithm(
59 blink::WebCryptoNamedCurve named_curve) {
60 return CreateEcImportAlgorithm(blink::WebCryptoAlgorithmIdEcdsa, named_curve);
61 }
62
63 blink::WebCryptoAlgorithm CreateEcdsaAlgorithm(
64 blink::WebCryptoAlgorithmId hash_id) {
65 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
66 blink::WebCryptoAlgorithmIdEcdsa,
67 new blink::WebCryptoEcdsaParams(CreateAlgorithm(hash_id)));
68 }
69
70 // Generates some ECDSA key pairs. Validates basic properties on the keys, and
71 // ensures the serialized key (as JWK) is unique. This test does nothing to
72 // ensure that the keys are otherwise usable (by trying to sign/verify with
73 // them).
74 TEST(WebCryptoEcdsaTest, GenerateKeyIsRandom) {
75 if (!SupportsEcdsa())
76 return;
77
78 blink::WebCryptoNamedCurve named_curve = blink::WebCryptoNamedCurveP256;
79
80 std::vector<std::vector<uint8_t>> serialized_keys;
81
82 // Generate a small sample of keys.
83 for (int j = 0; j < 4; ++j) {
84 blink::WebCryptoKey public_key;
85 blink::WebCryptoKey private_key;
86
87 ASSERT_EQ(Status::Success(),
88 GenerateKeyPair(CreateEcdsaKeyGenAlgorithm(named_curve), true, 0,
89 &public_key, &private_key));
90
91 // Basic sanity checks on the generated key pair.
92 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type());
93 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type());
94 EXPECT_EQ(named_curve, public_key.algorithm().ecParams()->namedCurve());
95 EXPECT_EQ(named_curve, private_key.algorithm().ecParams()->namedCurve());
96
97 // Export the key pair to JWK.
98 std::vector<uint8_t> key_bytes;
99 ASSERT_EQ(Status::Success(),
100 ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &key_bytes));
101 serialized_keys.push_back(key_bytes);
102
103 ASSERT_EQ(Status::Success(),
104 ExportKey(blink::WebCryptoKeyFormatJwk, private_key, &key_bytes));
105 serialized_keys.push_back(key_bytes);
106 }
107
108 // Ensure all entries in the key sample set are unique. This is a simplistic
109 // estimate of whether the generated keys appear random.
110 EXPECT_FALSE(CopiesExist(serialized_keys));
111 }
112
113 // Verify that ECDSA signatures are probabilistic. Signing the same message two
114 // times should yield different signatures. However both signatures should
115 // verify correctly.
116 TEST(WebCryptoEcdsaTest, SignatureIsRandom) {
117 if (!SupportsEcdsa())
118 return;
119
120 // Import a public and private keypair from "ec_private_keys.json". It doesn't
121 // really matter which one is used since they are all valid. In this case
122 // using the first one.
123 scoped_ptr<base::ListValue> private_keys;
124 ASSERT_TRUE(ReadJsonTestFileToList("ec_private_keys.json", &private_keys));
125 const base::DictionaryValue* key_dict;
126 ASSERT_TRUE(private_keys->GetDictionary(0, &key_dict));
127 blink::WebCryptoNamedCurve curve = GetCurveNameFromJsonTest(key_dict);
128 const base::DictionaryValue* key_jwk;
129 ASSERT_TRUE(key_dict->GetDictionary("jwk", &key_jwk));
130
131 blink::WebCryptoKey private_key;
132 ASSERT_EQ(
133 Status::Success(),
134 ImportKeyJwkFromDict(*key_jwk, CreateEcdsaImportAlgorithm(curve), true,
135 blink::WebCryptoKeyUsageSign, &private_key));
136
137 // Erase the "d" member so the private key JWK can be used to import the
138 // public key (WebCrypto doesn't provide a mechanism for importing a public
139 // key given a private key).
140 scoped_ptr<base::DictionaryValue> key_jwk_copy(key_jwk->DeepCopy());
141 key_jwk_copy->Remove("d", NULL);
142 blink::WebCryptoKey public_key;
143 ASSERT_EQ(Status::Success(),
144 ImportKeyJwkFromDict(*key_jwk_copy.get(),
145 CreateEcdsaImportAlgorithm(curve), true,
146 blink::WebCryptoKeyUsageVerify, &public_key));
147
148 // Sign twice
149 std::vector<uint8_t> message(10);
150 blink::WebCryptoAlgorithm algorithm =
151 CreateEcdsaAlgorithm(blink::WebCryptoAlgorithmIdSha1);
152
153 std::vector<uint8_t> signature1;
154 std::vector<uint8_t> signature2;
155 ASSERT_EQ(Status::Success(),
156 Sign(algorithm, private_key, CryptoData(message), &signature1));
157 ASSERT_EQ(Status::Success(),
158 Sign(algorithm, private_key, CryptoData(message), &signature2));
159
160 // The two signatures should be different.
161 EXPECT_NE(CryptoData(signature1), CryptoData(signature2));
162
163 // And both should be valid signatures which can be verified.
164 bool signature_matches;
165 ASSERT_EQ(Status::Success(),
166 Verify(algorithm, public_key, CryptoData(signature1),
167 CryptoData(message), &signature_matches));
168 EXPECT_TRUE(signature_matches);
169 ASSERT_EQ(Status::Success(),
170 Verify(algorithm, public_key, CryptoData(signature2),
171 CryptoData(message), &signature_matches));
172 EXPECT_TRUE(signature_matches);
173 }
174
175 // Tests verify() for ECDSA using an assortment of keys, curves and hashes.
176 // These tests also include expected failures for bad signatures and keys.
177 TEST(WebCryptoEcdsaTest, VerifyKnownAnswer) {
178 if (!SupportsEcdsa())
179 return;
180
181 scoped_ptr<base::ListValue> tests;
182 ASSERT_TRUE(ReadJsonTestFileToList("ecdsa.json", &tests));
183
184 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) {
185 SCOPED_TRACE(test_index);
186
187 const base::DictionaryValue* test;
188 ASSERT_TRUE(tests->GetDictionary(test_index, &test));
189
190 // Import the public key.
191 blink::WebCryptoNamedCurve curve = GetCurveNameFromJsonTest(test);
192 blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test);
193 std::vector<uint8_t> key_data =
194 GetKeyDataFromJsonTestCase(test, key_format);
195
196 // If the test didn't specify an error, that implies it expects success.
197 std::string expected_error = "Success";
198 test->GetString("error", &expected_error);
199
200 // Import the public key.
201 blink::WebCryptoKey key;
202 Status status = ImportKey(key_format, CryptoData(key_data),
203 CreateEcdsaImportAlgorithm(curve), true,
204 blink::WebCryptoKeyUsageVerify, &key);
205 ASSERT_EQ(expected_error, StatusToString(status));
206 if (status.IsError())
207 continue;
208
209 // Basic sanity checks on the imported public key.
210 EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type());
211 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages());
212 EXPECT_EQ(curve, key.algorithm().ecParams()->namedCurve());
213
214 // Now try to verify the given message and signature.
215 std::vector<uint8_t> message = GetBytesFromHexString(test, "msg");
216 std::vector<uint8_t> signature = GetBytesFromHexString(test, "sig");
217 blink::WebCryptoAlgorithm hash = GetDigestAlgorithm(test, "hash");
218
219 bool verify_result;
220 status = Verify(CreateEcdsaAlgorithm(hash.id()), key, CryptoData(signature),
221 CryptoData(message), &verify_result);
222 ASSERT_EQ(expected_error, StatusToString(status));
223 if (status.IsError())
224 continue;
225
226 // If no error was expected, the verification's boolean must match
227 // "verify_result" for the test.
228 bool expected_result = false;
229 ASSERT_TRUE(test->GetBoolean("verify_result", &expected_result));
230 EXPECT_EQ(expected_result, verify_result);
231 }
232 }
233
234 // Tests importing and exporting of EC private keys, using both JWK and PKCS8
235 // formats.
236 //
237 // The test imports a key first using JWK, and then exporting it to JWK and
238 // PKCS8. It does the same thing using PKCS8 as the original source of truth.
239 TEST(WebCryptoEcdsaTest, ImportExportPrivateKey) {
240 if (!SupportsEcdsa())
241 return;
242
243 scoped_ptr<base::ListValue> tests;
244 ASSERT_TRUE(ReadJsonTestFileToList("ec_private_keys.json", &tests));
245
246 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) {
247 SCOPED_TRACE(test_index);
248
249 const base::DictionaryValue* test;
250 ASSERT_TRUE(tests->GetDictionary(test_index, &test));
251
252 blink::WebCryptoNamedCurve curve = GetCurveNameFromJsonTest(test);
253 const base::DictionaryValue* jwk_dict;
254 EXPECT_TRUE(test->GetDictionary("jwk", &jwk_dict));
255 std::vector<uint8_t> jwk_bytes = MakeJsonVector(*jwk_dict);
256 std::vector<uint8_t> pkcs8_bytes = GetBytesFromHexString(test, "pkcs8");
257
258 // -------------------------------------------------
259 // Test from JWK, and then export to {JWK, PKCS8}
260 // -------------------------------------------------
261
262 // Import the key using JWK
263 blink::WebCryptoKey key;
264 ASSERT_EQ(Status::Success(),
265 ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(jwk_bytes),
266 CreateEcdsaImportAlgorithm(curve), true,
267 blink::WebCryptoKeyUsageSign, &key));
268
269 // Export the key as JWK
270 std::vector<uint8_t> exported_bytes;
271 ASSERT_EQ(Status::Success(),
272 ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_bytes));
273
274 // NOTE: The exported bytes can't be directly compared to jwk_bytes because
275 // the exported JWK differs from the inported one. In particular it contains
davidben 2014/11/07 00:17:21 s/inports/imported/
eroman 2014/11/11 01:26:27 Done.
276 // extra properties for extractability and key_ops.
277 //
278 // Verification is instead done by using the first exported JWK bytes as the
279 // expectation.
280 jwk_bytes = exported_bytes;
281 ASSERT_EQ(Status::Success(),
282 ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(jwk_bytes),
283 CreateEcdsaImportAlgorithm(curve), true,
284 blink::WebCryptoKeyUsageSign, &key));
285
286 // Export the key as JWK (again)
287 ASSERT_EQ(Status::Success(),
288 ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_bytes));
289 EXPECT_EQ(CryptoData(jwk_bytes), CryptoData(exported_bytes));
290
291 // Export the key as PKCS8
292 ASSERT_EQ(Status::Success(),
293 ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_bytes));
294 EXPECT_EQ(CryptoData(pkcs8_bytes), CryptoData(exported_bytes));
295
296 // -------------------------------------------------
297 // Test from PKCS8, and then export to {JWK, PKCS8}
298 // -------------------------------------------------
299
300 // Import the key using PKCS8
301 ASSERT_EQ(Status::Success(),
302 ImportKey(blink::WebCryptoKeyFormatPkcs8, CryptoData(pkcs8_bytes),
303 CreateEcdsaImportAlgorithm(curve), true,
304 blink::WebCryptoKeyUsageSign, &key));
305
306 // Export the key as PKCS8
307 ASSERT_EQ(Status::Success(),
308 ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_bytes));
309 EXPECT_EQ(CryptoData(pkcs8_bytes), CryptoData(exported_bytes));
310
311 // Export the key as JWK
312 ASSERT_EQ(Status::Success(),
313 ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_bytes));
314 EXPECT_EQ(CryptoData(jwk_bytes), CryptoData(exported_bytes));
315 }
316 }
317
318 } // namespace
319
320 } // namespace webcrypto
321
322 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698