Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / webcrypto / algorithms / ecdh_unittest.cc
blobc84143cc268e10313899c0ae9e505a05d52ec96f
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"
15 namespace webcrypto {
17 namespace {
19 bool SupportsEcdh() {
20 #if defined(USE_OPENSSL)
21 return true;
22 #else
23 LOG(ERROR) << "Skipping ECDH test because unsupported";
24 return false;
25 #endif
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,
78 private_key);
79 EXPECT_EQ(expected_private_key_error, StatusToString(status));
80 return status.IsSuccess();
83 class WebCryptoEcdhTest : public WebCryptoTestBase {};
85 TEST_F(WebCryptoEcdhTest, DeriveBitsKnownAnswer) {
86 if (!SupportsEcdh())
87 return;
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));
98 // Import the keys.
99 blink::WebCryptoKey public_key;
100 blink::WebCryptoKey private_key;
101 if (!ImportKeysFromTest(test, &public_key, &private_key))
102 continue;
104 // Now try to derive bytes.
105 std::vector<uint8_t> derived_bytes;
106 int length_bits = 0;
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())
117 continue;
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
128 // 528 bits.
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);
141 if (valid_p521_keys)
142 break;
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) {
159 if (!SupportsEcdh())
160 return;
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) {
177 if (!SupportsEcdh())
178 return;
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) {
195 if (!SupportsEcdh())
196 return;
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) {
215 if (!SupportsEcdh())
216 return;
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) {
248 if (!SupportsEcdh())
249 return;
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
285 // key.
286 TEST_F(WebCryptoEcdhTest, DeriveKeyHmacSha512NoLength) {
287 if (!SupportsEcdh())
288 return;
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) {
307 if (!SupportsEcdh())
308 return;
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) {
333 if (!SupportsEcdh())
334 return;
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));
365 } // namespace
367 } // namespace webcrypto