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/algorithms/test_helpers.h"
8 #include "components/webcrypto/crypto_data.h"
9 #include "components/webcrypto/jwk.h"
10 #include "components/webcrypto/status.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"
20 #if defined(USE_OPENSSL)
23 LOG(ERROR
) << "Skipping ECDH test because unsupported";
28 // TODO(eroman): Test passing an RSA public key instead of ECDH key.
29 // TODO(eroman): Test passing an ECDSA public key
31 blink::WebCryptoAlgorithm
CreateEcdhImportAlgorithm(
32 blink::WebCryptoNamedCurve named_curve
) {
33 return CreateEcImportAlgorithm(blink::WebCryptoAlgorithmIdEcdh
, named_curve
);
36 blink::WebCryptoAlgorithm
CreateEcdhDeriveParams(
37 const blink::WebCryptoKey
& public_key
) {
38 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
39 blink::WebCryptoAlgorithmIdEcdh
,
40 new blink::WebCryptoEcdhKeyDeriveParams(public_key
));
43 blink::WebCryptoAlgorithm
CreateAesGcmDerivedKeyParams(
44 unsigned short length_bits
) {
45 return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
46 blink::WebCryptoAlgorithmIdAesGcm
,
47 new blink::WebCryptoAesDerivedKeyParams(length_bits
));
50 // Helper that loads a "public_key" and "private_key" from the test data.
51 bool ImportKeysFromTest(const base::DictionaryValue
* test
,
52 blink::WebCryptoKey
* public_key
,
53 blink::WebCryptoKey
* private_key
) {
54 // Import the public key.
55 const base::DictionaryValue
* public_key_json
= NULL
;
56 EXPECT_TRUE(test
->GetDictionary("public_key", &public_key_json
));
57 blink::WebCryptoNamedCurve curve
=
58 GetCurveNameFromDictionary(public_key_json
);
59 EXPECT_EQ(Status::Success(),
60 ImportKey(blink::WebCryptoKeyFormatJwk
,
61 CryptoData(MakeJsonVector(*public_key_json
)),
62 CreateEcdhImportAlgorithm(curve
), true, 0, public_key
));
64 // If the test didn't specify an error for private key import, that implies
65 // it expects success.
66 std::string expected_private_key_error
= "Success";
67 test
->GetString("private_key_error", &expected_private_key_error
);
69 // Import the private key.
70 const base::DictionaryValue
* private_key_json
= NULL
;
71 EXPECT_TRUE(test
->GetDictionary("private_key", &private_key_json
));
72 curve
= GetCurveNameFromDictionary(private_key_json
);
73 Status status
= ImportKey(
74 blink::WebCryptoKeyFormatJwk
,
75 CryptoData(MakeJsonVector(*private_key_json
)),
76 CreateEcdhImportAlgorithm(curve
), true,
77 blink::WebCryptoKeyUsageDeriveBits
| blink::WebCryptoKeyUsageDeriveKey
,
79 EXPECT_EQ(expected_private_key_error
, StatusToString(status
));
80 return status
.IsSuccess();
83 class WebCryptoEcdhTest
: public WebCryptoTestBase
{};
85 TEST_F(WebCryptoEcdhTest
, DeriveBitsKnownAnswer
) {
89 scoped_ptr
<base::ListValue
> tests
;
90 ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests
));
92 for (size_t test_index
= 0; test_index
< tests
->GetSize(); ++test_index
) {
93 SCOPED_TRACE(test_index
);
95 const base::DictionaryValue
* test
;
96 ASSERT_TRUE(tests
->GetDictionary(test_index
, &test
));
99 blink::WebCryptoKey public_key
;
100 blink::WebCryptoKey private_key
;
101 if (!ImportKeysFromTest(test
, &public_key
, &private_key
))
104 // Now try to derive bytes.
105 std::vector
<uint8_t> derived_bytes
;
107 ASSERT_TRUE(test
->GetInteger("length_bits", &length_bits
));
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
);
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())
119 std::vector
<uint8_t> expected_bytes
=
120 GetBytesFromHexString(test
, "derived_bytes");
122 EXPECT_EQ(CryptoData(expected_bytes
), CryptoData(derived_bytes
));
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
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";
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
);
144 if (!valid_p521_keys
) {
145 return ::testing::AssertionFailure()
146 << "The P-521 test are missing in ecdh.json";
149 ImportKeysFromTest(test
, public_key
, private_key
);
151 EXPECT_EQ(blink::WebCryptoNamedCurveP521
,
152 public_key
->algorithm().ecParams()->namedCurve());
154 return ::testing::AssertionSuccess();
157 // Try deriving an AES key of length 129 bits.
158 TEST_F(WebCryptoEcdhTest
, DeriveKeyBadAesLength
) {
162 blink::WebCryptoKey public_key
;
163 blink::WebCryptoKey base_key
;
164 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
166 blink::WebCryptoKey derived_key
;
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
));
175 // Try deriving an AES key of length 192 bits.
176 TEST_F(WebCryptoEcdhTest
, DeriveKeyUnsupportedAesLength
) {
180 blink::WebCryptoKey public_key
;
181 blink::WebCryptoKey base_key
;
182 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
184 blink::WebCryptoKey derived_key
;
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
));
193 // Try deriving an HMAC key of length 0 bits.
194 TEST_F(WebCryptoEcdhTest
, DeriveKeyZeroLengthHmac
) {
198 blink::WebCryptoKey public_key
;
199 blink::WebCryptoKey base_key
;
200 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
202 blink::WebCryptoKey derived_key
;
204 const blink::WebCryptoAlgorithm import_algorithm
=
205 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1
, 0);
207 ASSERT_EQ(Status::ErrorGetHmacKeyLengthZero(),
208 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
209 import_algorithm
, import_algorithm
, true,
210 blink::WebCryptoKeyUsageSign
, &derived_key
));
213 // Derive an HMAC key of length 19 bits.
214 TEST_F(WebCryptoEcdhTest
, DeriveKeyHmac19Bits
) {
218 blink::WebCryptoKey public_key
;
219 blink::WebCryptoKey base_key
;
220 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
222 blink::WebCryptoKey derived_key
;
224 const blink::WebCryptoAlgorithm import_algorithm
=
225 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1
, 19);
227 ASSERT_EQ(Status::Success(),
228 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
229 import_algorithm
, import_algorithm
, true,
230 blink::WebCryptoKeyUsageSign
, &derived_key
));
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());
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);
246 // Derive an HMAC key with no specified length (just the hash of SHA-256).
247 TEST_F(WebCryptoEcdhTest
, DeriveKeyHmacSha256NoLength
) {
251 blink::WebCryptoKey public_key
;
252 blink::WebCryptoKey base_key
;
253 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
255 blink::WebCryptoKey derived_key
;
257 const blink::WebCryptoAlgorithm import_algorithm
=
258 CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256
);
260 ASSERT_EQ(Status::Success(),
261 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
262 import_algorithm
, import_algorithm
, true,
263 blink::WebCryptoKeyUsageSign
, &derived_key
));
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());
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());
277 // Derive an HMAC key with no specified length (just the hash of SHA-512).
279 // This fails, because ECDH using P-521 can only generate 528 bits, however HMAC
280 // SHA-512 requires 1024 bits.
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
286 TEST_F(WebCryptoEcdhTest
, DeriveKeyHmacSha512NoLength
) {
290 blink::WebCryptoKey public_key
;
291 blink::WebCryptoKey base_key
;
292 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
294 blink::WebCryptoKey derived_key
;
296 const blink::WebCryptoAlgorithm import_algorithm
=
297 CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha512
);
299 ASSERT_EQ(Status::ErrorEcdhLengthTooBig(528),
300 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
301 import_algorithm
, import_algorithm
, true,
302 blink::WebCryptoKeyUsageSign
, &derived_key
));
305 // Try deriving an AES key of length 128 bits.
306 TEST_F(WebCryptoEcdhTest
, DeriveKeyAes128
) {
310 blink::WebCryptoKey public_key
;
311 blink::WebCryptoKey base_key
;
312 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
314 blink::WebCryptoKey derived_key
;
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
));
322 ASSERT_EQ(blink::WebCryptoAlgorithmIdAesGcm
, derived_key
.algorithm().id());
323 ASSERT_EQ(128, derived_key
.algorithm().aesParams()->lengthBits());
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());
332 TEST_F(WebCryptoEcdhTest
, ImportKeyEmptyUsage
) {
336 blink::WebCryptoKey key
;
338 scoped_ptr
<base::ListValue
> tests
;
339 ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests
));
341 const base::DictionaryValue
* test
;
342 ASSERT_TRUE(tests
->GetDictionary(0, &test
));
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());
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, 0, &key
));
367 } // namespace webcrypto