Stack sampling profiler: add fire-and-forget interface
[chromium-blink-merge.git] / components / webcrypto / test / ecdsa_unittest.cc
blob89cb72cdba278b32e039ea7299e35a669b02a114
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.
5 #include "base/stl_util.h"
6 #include "components/webcrypto/algorithm_dispatch.h"
7 #include "components/webcrypto/crypto_data.h"
8 #include "components/webcrypto/jwk.h"
9 #include "components/webcrypto/status.h"
10 #include "components/webcrypto/test/test_helpers.h"
11 #include "components/webcrypto/webcrypto_util.h"
12 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
13 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
15 namespace webcrypto {
17 namespace {
19 bool SupportsEcdsa() {
20 #if defined(USE_OPENSSL)
21 return true;
22 #else
23 LOG(ERROR) << "Skipping ECDSA test because unsupported";
24 return false;
25 #endif
28 blink::WebCryptoAlgorithm CreateEcdsaKeyGenAlgorithm(
29 blink::WebCryptoNamedCurve named_curve) {
30 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
31 blink::WebCryptoAlgorithmIdEcdsa,
32 new blink::WebCryptoEcKeyGenParams(named_curve));
35 blink::WebCryptoAlgorithm CreateEcdsaImportAlgorithm(
36 blink::WebCryptoNamedCurve named_curve) {
37 return CreateEcImportAlgorithm(blink::WebCryptoAlgorithmIdEcdsa, named_curve);
40 blink::WebCryptoAlgorithm CreateEcdsaAlgorithm(
41 blink::WebCryptoAlgorithmId hash_id) {
42 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
43 blink::WebCryptoAlgorithmIdEcdsa,
44 new blink::WebCryptoEcdsaParams(CreateAlgorithm(hash_id)));
47 // Generates some ECDSA key pairs. Validates basic properties on the keys, and
48 // ensures the serialized key (as JWK) is unique. This test does nothing to
49 // ensure that the keys are otherwise usable (by trying to sign/verify with
50 // them).
51 TEST(WebCryptoEcdsaTest, GenerateKeyIsRandom) {
52 if (!SupportsEcdsa())
53 return;
55 blink::WebCryptoNamedCurve named_curve = blink::WebCryptoNamedCurveP256;
57 std::vector<std::vector<uint8_t>> serialized_keys;
59 // Generate a small sample of keys.
60 for (int j = 0; j < 4; ++j) {
61 blink::WebCryptoKey public_key;
62 blink::WebCryptoKey private_key;
64 ASSERT_EQ(Status::Success(),
65 GenerateKeyPair(CreateEcdsaKeyGenAlgorithm(named_curve), true,
66 blink::WebCryptoKeyUsageSign, &public_key,
67 &private_key));
69 // Basic sanity checks on the generated key pair.
70 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key.type());
71 EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key.type());
72 EXPECT_EQ(named_curve, public_key.algorithm().ecParams()->namedCurve());
73 EXPECT_EQ(named_curve, private_key.algorithm().ecParams()->namedCurve());
75 // Export the key pair to JWK.
76 std::vector<uint8_t> key_bytes;
77 ASSERT_EQ(Status::Success(),
78 ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &key_bytes));
79 serialized_keys.push_back(key_bytes);
81 ASSERT_EQ(Status::Success(),
82 ExportKey(blink::WebCryptoKeyFormatJwk, private_key, &key_bytes));
83 serialized_keys.push_back(key_bytes);
86 // Ensure all entries in the key sample set are unique. This is a simplistic
87 // estimate of whether the generated keys appear random.
88 EXPECT_FALSE(CopiesExist(serialized_keys));
91 TEST(WebCryptoEcdsaTest, GenerateKeyEmptyUsage) {
92 if (!SupportsEcdsa())
93 return;
95 blink::WebCryptoNamedCurve named_curve = blink::WebCryptoNamedCurveP256;
96 blink::WebCryptoKey public_key;
97 blink::WebCryptoKey private_key;
98 ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
99 GenerateKeyPair(CreateEcdsaKeyGenAlgorithm(named_curve), true, 0,
100 &public_key, &private_key));
103 // Verify that ECDSA signatures are probabilistic. Signing the same message two
104 // times should yield different signatures. However both signatures should
105 // verify correctly.
106 TEST(WebCryptoEcdsaTest, SignatureIsRandom) {
107 if (!SupportsEcdsa())
108 return;
110 // Import a public and private keypair from "ec_private_keys.json". It doesn't
111 // really matter which one is used since they are all valid. In this case
112 // using the first one.
113 scoped_ptr<base::ListValue> private_keys;
114 ASSERT_TRUE(ReadJsonTestFileToList("ec_private_keys.json", &private_keys));
115 const base::DictionaryValue* key_dict;
116 ASSERT_TRUE(private_keys->GetDictionary(0, &key_dict));
117 blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(key_dict);
118 const base::DictionaryValue* key_jwk;
119 ASSERT_TRUE(key_dict->GetDictionary("jwk", &key_jwk));
121 blink::WebCryptoKey private_key;
122 ASSERT_EQ(
123 Status::Success(),
124 ImportKeyJwkFromDict(*key_jwk, CreateEcdsaImportAlgorithm(curve), true,
125 blink::WebCryptoKeyUsageSign, &private_key));
127 // Erase the "d" member so the private key JWK can be used to import the
128 // public key (WebCrypto doesn't provide a mechanism for importing a public
129 // key given a private key).
130 scoped_ptr<base::DictionaryValue> key_jwk_copy(key_jwk->DeepCopy());
131 key_jwk_copy->Remove("d", NULL);
132 blink::WebCryptoKey public_key;
133 ASSERT_EQ(Status::Success(),
134 ImportKeyJwkFromDict(*key_jwk_copy.get(),
135 CreateEcdsaImportAlgorithm(curve), true,
136 blink::WebCryptoKeyUsageVerify, &public_key));
138 // Sign twice
139 std::vector<uint8_t> message(10);
140 blink::WebCryptoAlgorithm algorithm =
141 CreateEcdsaAlgorithm(blink::WebCryptoAlgorithmIdSha1);
143 std::vector<uint8_t> signature1;
144 std::vector<uint8_t> signature2;
145 ASSERT_EQ(Status::Success(),
146 Sign(algorithm, private_key, CryptoData(message), &signature1));
147 ASSERT_EQ(Status::Success(),
148 Sign(algorithm, private_key, CryptoData(message), &signature2));
150 // The two signatures should be different.
151 EXPECT_NE(CryptoData(signature1), CryptoData(signature2));
153 // And both should be valid signatures which can be verified.
154 bool signature_matches;
155 ASSERT_EQ(Status::Success(),
156 Verify(algorithm, public_key, CryptoData(signature1),
157 CryptoData(message), &signature_matches));
158 EXPECT_TRUE(signature_matches);
159 ASSERT_EQ(Status::Success(),
160 Verify(algorithm, public_key, CryptoData(signature2),
161 CryptoData(message), &signature_matches));
162 EXPECT_TRUE(signature_matches);
165 // Tests verify() for ECDSA using an assortment of keys, curves and hashes.
166 // These tests also include expected failures for bad signatures and keys.
167 TEST(WebCryptoEcdsaTest, VerifyKnownAnswer) {
168 if (!SupportsEcdsa())
169 return;
171 scoped_ptr<base::ListValue> tests;
172 ASSERT_TRUE(ReadJsonTestFileToList("ecdsa.json", &tests));
174 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) {
175 SCOPED_TRACE(test_index);
177 const base::DictionaryValue* test;
178 ASSERT_TRUE(tests->GetDictionary(test_index, &test));
180 blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test);
181 blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test);
182 std::vector<uint8_t> key_data =
183 GetKeyDataFromJsonTestCase(test, key_format);
185 // If the test didn't specify an error, that implies it expects success.
186 std::string expected_error = "Success";
187 test->GetString("error", &expected_error);
189 // Import the public key.
190 blink::WebCryptoKey key;
191 Status status = ImportKey(key_format, CryptoData(key_data),
192 CreateEcdsaImportAlgorithm(curve), true,
193 blink::WebCryptoKeyUsageVerify, &key);
194 ASSERT_EQ(expected_error, StatusToString(status));
195 if (status.IsError())
196 continue;
198 // Basic sanity checks on the imported public key.
199 EXPECT_EQ(blink::WebCryptoKeyTypePublic, key.type());
200 EXPECT_EQ(blink::WebCryptoKeyUsageVerify, key.usages());
201 EXPECT_EQ(curve, key.algorithm().ecParams()->namedCurve());
203 // Now try to verify the given message and signature.
204 std::vector<uint8_t> message = GetBytesFromHexString(test, "msg");
205 std::vector<uint8_t> signature = GetBytesFromHexString(test, "sig");
206 blink::WebCryptoAlgorithm hash = GetDigestAlgorithm(test, "hash");
208 bool verify_result;
209 status = Verify(CreateEcdsaAlgorithm(hash.id()), key, CryptoData(signature),
210 CryptoData(message), &verify_result);
211 ASSERT_EQ(expected_error, StatusToString(status));
212 if (status.IsError())
213 continue;
215 // If no error was expected, the verification's boolean must match
216 // "verify_result" for the test.
217 bool expected_result = false;
218 ASSERT_TRUE(test->GetBoolean("verify_result", &expected_result));
219 EXPECT_EQ(expected_result, verify_result);
223 // The test file may include either public or private keys. In order to import
224 // them successfully, the correct usages need to be specified. This function
225 // determines what usages to use for the key.
226 blink::WebCryptoKeyUsageMask GetExpectedUsagesForKeyImport(
227 blink::WebCryptoKeyFormat key_format,
228 const base::DictionaryValue* test) {
229 blink::WebCryptoKeyUsageMask kPublicUsages = blink::WebCryptoKeyUsageVerify;
230 blink::WebCryptoKeyUsageMask kPrivateUsages = blink::WebCryptoKeyUsageSign;
232 switch (key_format) {
233 case blink::WebCryptoKeyFormatRaw:
234 case blink::WebCryptoKeyFormatSpki:
235 return kPublicUsages;
236 case blink::WebCryptoKeyFormatPkcs8:
237 return kPrivateUsages;
238 break;
239 case blink::WebCryptoKeyFormatJwk: {
240 const base::DictionaryValue* key = NULL;
241 if (!test->GetDictionary("key", &key))
242 ADD_FAILURE() << "Missing key property";
243 return key->HasKey("d") ? kPrivateUsages : kPublicUsages;
247 // Appease compiler.
248 return kPrivateUsages;
251 // Tests importing bad public/private keys in a variety of formats.
252 TEST(WebCryptoEcdsaTest, ImportBadKeys) {
253 if (!SupportsEcdsa())
254 return;
256 scoped_ptr<base::ListValue> tests;
257 ASSERT_TRUE(ReadJsonTestFileToList("bad_ec_keys.json", &tests));
259 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) {
260 SCOPED_TRACE(test_index);
262 const base::DictionaryValue* test;
263 ASSERT_TRUE(tests->GetDictionary(test_index, &test));
265 blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test);
266 blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test);
267 std::vector<uint8_t> key_data =
268 GetKeyDataFromJsonTestCase(test, key_format);
269 std::string expected_error;
270 ASSERT_TRUE(test->GetString("error", &expected_error));
272 blink::WebCryptoKey key;
273 Status status = ImportKey(
274 key_format, CryptoData(key_data), CreateEcdsaImportAlgorithm(curve),
275 true, GetExpectedUsagesForKeyImport(key_format, test), &key);
276 ASSERT_EQ(expected_error, StatusToString(status));
280 // Tests importing and exporting of EC private keys, using both JWK and PKCS8
281 // formats.
283 // The test imports a key first using JWK, and then exporting it to JWK and
284 // PKCS8. It does the same thing using PKCS8 as the original source of truth.
285 TEST(WebCryptoEcdsaTest, ImportExportPrivateKey) {
286 if (!SupportsEcdsa())
287 return;
289 scoped_ptr<base::ListValue> tests;
290 ASSERT_TRUE(ReadJsonTestFileToList("ec_private_keys.json", &tests));
292 for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) {
293 SCOPED_TRACE(test_index);
295 const base::DictionaryValue* test;
296 ASSERT_TRUE(tests->GetDictionary(test_index, &test));
298 blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test);
299 const base::DictionaryValue* jwk_dict;
300 EXPECT_TRUE(test->GetDictionary("jwk", &jwk_dict));
301 std::vector<uint8_t> jwk_bytes = MakeJsonVector(*jwk_dict);
302 std::vector<uint8_t> pkcs8_bytes = GetBytesFromHexString(
303 test, test->HasKey("exported_pkcs8") ? "exported_pkcs8" : "pkcs8");
305 // -------------------------------------------------
306 // Test from JWK, and then export to {JWK, PKCS8}
307 // -------------------------------------------------
309 // Import the key using JWK
310 blink::WebCryptoKey key;
311 ASSERT_EQ(Status::Success(),
312 ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(jwk_bytes),
313 CreateEcdsaImportAlgorithm(curve), true,
314 blink::WebCryptoKeyUsageSign, &key));
316 // Export the key as JWK
317 std::vector<uint8_t> exported_bytes;
318 ASSERT_EQ(Status::Success(),
319 ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_bytes));
321 // NOTE: The exported bytes can't be directly compared to jwk_bytes because
322 // the exported JWK differs from the imported one. In particular it contains
323 // extra properties for extractability and key_ops.
325 // Verification is instead done by using the first exported JWK bytes as the
326 // expectation.
327 jwk_bytes = exported_bytes;
328 ASSERT_EQ(Status::Success(),
329 ImportKey(blink::WebCryptoKeyFormatJwk, CryptoData(jwk_bytes),
330 CreateEcdsaImportAlgorithm(curve), true,
331 blink::WebCryptoKeyUsageSign, &key));
333 // Export the key as JWK (again)
334 ASSERT_EQ(Status::Success(),
335 ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_bytes));
336 EXPECT_EQ(CryptoData(jwk_bytes), CryptoData(exported_bytes));
338 // Export the key as PKCS8
339 ASSERT_EQ(Status::Success(),
340 ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_bytes));
341 EXPECT_EQ(CryptoData(pkcs8_bytes), CryptoData(exported_bytes));
343 // -------------------------------------------------
344 // Test from PKCS8, and then export to {JWK, PKCS8}
345 // -------------------------------------------------
347 // The imported PKCS8 bytes may differ from the exported bytes (in the case
348 // where the publicKey was missing, it will be synthesized and written back
349 // during export).
350 std::vector<uint8_t> pkcs8_input_bytes = GetBytesFromHexString(
351 test, test->HasKey("original_pkcs8") ? "original_pkcs8" : "pkcs8");
352 CryptoData pkcs8_input_data(pkcs8_input_bytes.empty() ? pkcs8_bytes
353 : pkcs8_input_bytes);
355 // Import the key using PKCS8
356 ASSERT_EQ(Status::Success(),
357 ImportKey(blink::WebCryptoKeyFormatPkcs8, pkcs8_input_data,
358 CreateEcdsaImportAlgorithm(curve), true,
359 blink::WebCryptoKeyUsageSign, &key));
361 // Export the key as PKCS8
362 ASSERT_EQ(Status::Success(),
363 ExportKey(blink::WebCryptoKeyFormatPkcs8, key, &exported_bytes));
364 EXPECT_EQ(CryptoData(pkcs8_bytes), CryptoData(exported_bytes));
366 // Export the key as JWK
367 ASSERT_EQ(Status::Success(),
368 ExportKey(blink::WebCryptoKeyFormatJwk, key, &exported_bytes));
369 EXPECT_EQ(CryptoData(jwk_bytes), CryptoData(exported_bytes));
373 } // namespace
375 } // namespace webcrypto