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"
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 TEST(WebCryptoEcdhTest
, DeriveBitsKnownAnswer
) {
87 scoped_ptr
<base::ListValue
> tests
;
88 ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests
));
90 for (size_t test_index
= 0; test_index
< tests
->GetSize(); ++test_index
) {
91 SCOPED_TRACE(test_index
);
93 const base::DictionaryValue
* test
;
94 ASSERT_TRUE(tests
->GetDictionary(test_index
, &test
));
97 blink::WebCryptoKey public_key
;
98 blink::WebCryptoKey private_key
;
99 if (!ImportKeysFromTest(test
, &public_key
, &private_key
))
102 // Now try to derive bytes.
103 std::vector
<uint8_t> derived_bytes
;
105 ASSERT_TRUE(test
->GetInteger("length_bits", &length_bits
));
107 // If the test didn't specify an error, that implies it expects success.
108 std::string expected_error
= "Success";
109 test
->GetString("error", &expected_error
);
111 Status status
= DeriveBits(CreateEcdhDeriveParams(public_key
), private_key
,
112 length_bits
, &derived_bytes
);
113 ASSERT_EQ(expected_error
, StatusToString(status
));
114 if (status
.IsError())
117 std::vector
<uint8_t> expected_bytes
=
118 GetBytesFromHexString(test
, "derived_bytes");
120 EXPECT_EQ(CryptoData(expected_bytes
), CryptoData(derived_bytes
));
124 // Loads up a test ECDH public and private key for P-521. The keys
125 // come from different key pairs, and can be used for key derivation of up to
127 ::testing::AssertionResult
LoadTestKeys(blink::WebCryptoKey
* public_key
,
128 blink::WebCryptoKey
* private_key
) {
129 scoped_ptr
<base::ListValue
> tests
;
130 if (!ReadJsonTestFileToList("ecdh.json", &tests
))
131 return ::testing::AssertionFailure() << "Failed loading ecdh.json";
133 const base::DictionaryValue
* test
= NULL
;
134 bool valid_p521_keys
= false;
135 for (size_t test_index
= 0; test_index
< tests
->GetSize(); ++test_index
) {
136 SCOPED_TRACE(test_index
);
137 EXPECT_TRUE(tests
->GetDictionary(test_index
, &test
));
138 test
->GetBoolean("valid_p521_keys", &valid_p521_keys
);
142 if (!valid_p521_keys
) {
143 return ::testing::AssertionFailure()
144 << "The P-521 test are missing in ecdh.json";
147 ImportKeysFromTest(test
, public_key
, private_key
);
149 EXPECT_EQ(blink::WebCryptoNamedCurveP521
,
150 public_key
->algorithm().ecParams()->namedCurve());
152 return ::testing::AssertionSuccess();
155 // Try deriving an AES key of length 129 bits.
156 TEST(WebCryptoEcdhTest
, DeriveKeyBadAesLength
) {
160 blink::WebCryptoKey public_key
;
161 blink::WebCryptoKey base_key
;
162 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
164 blink::WebCryptoKey derived_key
;
166 ASSERT_EQ(Status::ErrorGetAesKeyLength(),
167 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
168 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm
),
169 CreateAesGcmDerivedKeyParams(129), true,
170 blink::WebCryptoKeyUsageEncrypt
, &derived_key
));
173 // Try deriving an AES key of length 192 bits.
174 TEST(WebCryptoEcdhTest
, DeriveKeyUnsupportedAesLength
) {
178 blink::WebCryptoKey public_key
;
179 blink::WebCryptoKey base_key
;
180 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
182 blink::WebCryptoKey derived_key
;
184 ASSERT_EQ(Status::ErrorAes192BitUnsupported(),
185 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
186 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm
),
187 CreateAesGcmDerivedKeyParams(192), true,
188 blink::WebCryptoKeyUsageEncrypt
, &derived_key
));
191 // Try deriving an HMAC key of length 0 bits.
192 TEST(WebCryptoEcdhTest
, DeriveKeyZeroLengthHmac
) {
196 blink::WebCryptoKey public_key
;
197 blink::WebCryptoKey base_key
;
198 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
200 blink::WebCryptoKey derived_key
;
202 const blink::WebCryptoAlgorithm import_algorithm
=
203 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1
, 0);
205 ASSERT_EQ(Status::ErrorGetHmacKeyLengthZero(),
206 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
207 import_algorithm
, import_algorithm
, true,
208 blink::WebCryptoKeyUsageSign
, &derived_key
));
211 // Derive an HMAC key of length 19 bits.
212 TEST(WebCryptoEcdhTest
, DeriveKeyHmac19Bits
) {
216 blink::WebCryptoKey public_key
;
217 blink::WebCryptoKey base_key
;
218 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
220 blink::WebCryptoKey derived_key
;
222 const blink::WebCryptoAlgorithm import_algorithm
=
223 CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1
, 19);
225 ASSERT_EQ(Status::Success(),
226 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
227 import_algorithm
, import_algorithm
, true,
228 blink::WebCryptoKeyUsageSign
, &derived_key
));
230 ASSERT_EQ(blink::WebCryptoAlgorithmIdHmac
, derived_key
.algorithm().id());
231 ASSERT_EQ(blink::WebCryptoAlgorithmIdSha1
,
232 derived_key
.algorithm().hmacParams()->hash().id());
233 ASSERT_EQ(19u, derived_key
.algorithm().hmacParams()->lengthBits());
235 // Export the key and verify its contents.
236 std::vector
<uint8_t> raw_key
;
237 EXPECT_EQ(Status::Success(),
238 ExportKey(blink::WebCryptoKeyFormatRaw
, derived_key
, &raw_key
));
239 EXPECT_EQ(3u, raw_key
.size());
240 // The last 7 bits of the key should be zero.
241 EXPECT_EQ(0, raw_key
[raw_key
.size() - 1] & 0x1f);
244 // Derive an HMAC key with no specified length (just the hash of SHA-256).
245 TEST(WebCryptoEcdhTest
, DeriveKeyHmacSha256NoLength
) {
249 blink::WebCryptoKey public_key
;
250 blink::WebCryptoKey base_key
;
251 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
253 blink::WebCryptoKey derived_key
;
255 const blink::WebCryptoAlgorithm import_algorithm
=
256 CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha256
);
258 ASSERT_EQ(Status::Success(),
259 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
260 import_algorithm
, import_algorithm
, true,
261 blink::WebCryptoKeyUsageSign
, &derived_key
));
263 ASSERT_EQ(blink::WebCryptoAlgorithmIdHmac
, derived_key
.algorithm().id());
264 ASSERT_EQ(blink::WebCryptoAlgorithmIdSha256
,
265 derived_key
.algorithm().hmacParams()->hash().id());
266 ASSERT_EQ(512u, derived_key
.algorithm().hmacParams()->lengthBits());
268 // Export the key and verify its contents.
269 std::vector
<uint8_t> raw_key
;
270 EXPECT_EQ(Status::Success(),
271 ExportKey(blink::WebCryptoKeyFormatRaw
, derived_key
, &raw_key
));
272 EXPECT_EQ(64u, raw_key
.size());
275 // Derive an HMAC key with no specified length (just the hash of SHA-512).
277 // This fails, because ECDH using P-521 can only generate 528 bits, however HMAC
278 // SHA-512 requires 1024 bits.
280 // In practice, authors won't be directly generating keys from key agreement
281 // schemes, as that is frequently insecure, and instead be using KDFs to expand
282 // and generate keys. For simplicity of testing, however, test using an HMAC
284 TEST(WebCryptoEcdhTest
, DeriveKeyHmacSha512NoLength
) {
288 blink::WebCryptoKey public_key
;
289 blink::WebCryptoKey base_key
;
290 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
292 blink::WebCryptoKey derived_key
;
294 const blink::WebCryptoAlgorithm import_algorithm
=
295 CreateHmacImportAlgorithmNoLength(blink::WebCryptoAlgorithmIdSha512
);
297 ASSERT_EQ(Status::ErrorEcdhLengthTooBig(528),
298 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
299 import_algorithm
, import_algorithm
, true,
300 blink::WebCryptoKeyUsageSign
, &derived_key
));
303 // Try deriving an AES key of length 128 bits.
304 TEST(WebCryptoEcdhTest
, DeriveKeyAes128
) {
308 blink::WebCryptoKey public_key
;
309 blink::WebCryptoKey base_key
;
310 ASSERT_TRUE(LoadTestKeys(&public_key
, &base_key
));
312 blink::WebCryptoKey derived_key
;
314 ASSERT_EQ(Status::Success(),
315 DeriveKey(CreateEcdhDeriveParams(public_key
), base_key
,
316 CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm
),
317 CreateAesGcmDerivedKeyParams(128), true,
318 blink::WebCryptoKeyUsageEncrypt
, &derived_key
));
320 ASSERT_EQ(blink::WebCryptoAlgorithmIdAesGcm
, derived_key
.algorithm().id());
321 ASSERT_EQ(128, derived_key
.algorithm().aesParams()->lengthBits());
323 // Export the key and verify its contents.
324 std::vector
<uint8_t> raw_key
;
325 EXPECT_EQ(Status::Success(),
326 ExportKey(blink::WebCryptoKeyFormatRaw
, derived_key
, &raw_key
));
327 EXPECT_EQ(16u, raw_key
.size());
330 TEST(WebCryptoEcdhTest
, ImportKeyEmptyUsage
) {
334 blink::WebCryptoKey key
;
336 scoped_ptr
<base::ListValue
> tests
;
337 ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests
));
339 const base::DictionaryValue
* test
;
340 ASSERT_TRUE(tests
->GetDictionary(0, &test
));
342 // Import the public key.
343 const base::DictionaryValue
* public_key_json
= NULL
;
344 EXPECT_TRUE(test
->GetDictionary("public_key", &public_key_json
));
345 blink::WebCryptoNamedCurve curve
=
346 GetCurveNameFromDictionary(public_key_json
);
347 ASSERT_EQ(Status::Success(),
348 ImportKey(blink::WebCryptoKeyFormatJwk
,
349 CryptoData(MakeJsonVector(*public_key_json
)),
350 CreateEcdhImportAlgorithm(curve
), true, 0, &key
));
351 EXPECT_EQ(0, key
.usages());
353 // Import the private key.
354 const base::DictionaryValue
* private_key_json
= NULL
;
355 EXPECT_TRUE(test
->GetDictionary("private_key", &private_key_json
));
356 curve
= GetCurveNameFromDictionary(private_key_json
);
357 ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
358 ImportKey(blink::WebCryptoKeyFormatJwk
,
359 CryptoData(MakeJsonVector(*private_key_json
)),
360 CreateEcdhImportAlgorithm(curve
), true, 0, &key
));
365 } // namespace webcrypto