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"
19 bool SupportsEcdsa() {
20 #if defined(USE_OPENSSL)
23 LOG(ERROR
) << "Skipping ECDSA test because unsupported";
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
51 TEST(WebCryptoEcdsaTest
, GenerateKeyIsRandom
) {
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
,
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
) {
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
106 TEST(WebCryptoEcdsaTest
, SignatureIsRandom
) {
107 if (!SupportsEcdsa())
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
;
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
));
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())
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())
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");
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())
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
;
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
;
248 return kPrivateUsages
;
251 // Tests importing bad public/private keys in a variety of formats.
252 TEST(WebCryptoEcdsaTest
, ImportBadKeys
) {
253 if (!SupportsEcdsa())
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
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())
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
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
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
));
375 } // namespace webcrypto