ecdh_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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.
  4. #include <stddef.h>
  5. #include <stdint.h>
  6. #include "components/webcrypto/algorithm_dispatch.h"
  7. #include "components/webcrypto/algorithms/ec.h"
  8. #include "components/webcrypto/algorithms/test_helpers.h"
  9. #include "components/webcrypto/jwk.h"
  10. #include "components/webcrypto/status.h"
  11. #include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
  12. #include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
  13. namespace webcrypto {
  14. namespace {
  15. // TODO(eroman): Test passing an RSA public key instead of ECDH key.
  16. // TODO(eroman): Test passing an ECDSA public key
  17. blink::WebCryptoAlgorithm CreateEcdhImportAlgorithm(
  18. blink::WebCryptoNamedCurve named_curve) {
  19. return CreateEcImportAlgorithm(blink::kWebCryptoAlgorithmIdEcdh, named_curve);
  20. }
  21. blink::WebCryptoAlgorithm CreateEcdhDeriveParams(
  22. const blink::WebCryptoKey& public_key) {
  23. return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(
  24. blink::kWebCryptoAlgorithmIdEcdh,
  25. new blink::WebCryptoEcdhKeyDeriveParams(public_key));
  26. }
  27. blink::WebCryptoAlgorithm CreateAesGcmDerivedKeyParams(uint16_t length_bits) {
  28. return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(
  29. blink::kWebCryptoAlgorithmIdAesGcm,
  30. new blink::WebCryptoAesDerivedKeyParams(length_bits));
  31. }
  32. // Helper that loads a "public_key" and "private_key" from the test data.
  33. bool ImportKeysFromTest(const base::DictionaryValue* test,
  34. blink::WebCryptoKey* public_key,
  35. blink::WebCryptoKey* private_key) {
  36. // Import the public key.
  37. const base::DictionaryValue* public_key_json = nullptr;
  38. EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json));
  39. blink::WebCryptoNamedCurve curve =
  40. GetCurveNameFromDictionary(public_key_json);
  41. EXPECT_EQ(
  42. Status::Success(),
  43. ImportKey(blink::kWebCryptoKeyFormatJwk, MakeJsonVector(*public_key_json),
  44. CreateEcdhImportAlgorithm(curve), true, 0, public_key));
  45. // If the test didn't specify an error for private key import, that implies
  46. // it expects success.
  47. std::string expected_private_key_error = "Success";
  48. test->GetString("private_key_error", &expected_private_key_error);
  49. // Import the private key.
  50. const base::DictionaryValue* private_key_json = nullptr;
  51. EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json));
  52. curve = GetCurveNameFromDictionary(private_key_json);
  53. Status status = ImportKey(
  54. blink::kWebCryptoKeyFormatJwk, MakeJsonVector(*private_key_json),
  55. CreateEcdhImportAlgorithm(curve), true,
  56. blink::kWebCryptoKeyUsageDeriveBits | blink::kWebCryptoKeyUsageDeriveKey,
  57. private_key);
  58. EXPECT_EQ(expected_private_key_error, StatusToString(status));
  59. return status.IsSuccess();
  60. }
  61. class WebCryptoEcdhTest : public WebCryptoTestBase {};
  62. TEST_F(WebCryptoEcdhTest, DeriveBitsKnownAnswer) {
  63. base::Value::List tests = ReadJsonTestFileAsList("ecdh.json");
  64. for (const base::Value& test_value : tests) {
  65. SCOPED_TRACE(&test_value - &tests[0]);
  66. ASSERT_TRUE(test_value.is_dict());
  67. const base::DictionaryValue* test =
  68. &base::Value::AsDictionaryValue(test_value);
  69. // Import the keys.
  70. blink::WebCryptoKey public_key;
  71. blink::WebCryptoKey private_key;
  72. if (!ImportKeysFromTest(test, &public_key, &private_key))
  73. continue;
  74. // Now try to derive bytes.
  75. std::vector<uint8_t> derived_bytes;
  76. absl::optional<int> length_bits = test->FindIntKey("length_bits");
  77. ASSERT_TRUE(length_bits);
  78. // If the test didn't specify an error, that implies it expects success.
  79. std::string expected_error = "Success";
  80. test->GetString("error", &expected_error);
  81. Status status = DeriveBits(CreateEcdhDeriveParams(public_key), private_key,
  82. *length_bits, &derived_bytes);
  83. ASSERT_EQ(expected_error, StatusToString(status));
  84. if (status.IsError())
  85. continue;
  86. std::vector<uint8_t> expected_bytes =
  87. GetBytesFromHexString(test, "derived_bytes");
  88. EXPECT_EQ(expected_bytes, derived_bytes);
  89. }
  90. }
  91. // Loads up a test ECDH public and private key for P-521. The keys
  92. // come from different key pairs, and can be used for key derivation of up to
  93. // 528 bits.
  94. ::testing::AssertionResult LoadTestKeys(blink::WebCryptoKey* public_key,
  95. blink::WebCryptoKey* private_key) {
  96. base::Value::List tests = ReadJsonTestFileAsList("ecdh.json");
  97. const base::DictionaryValue* test = nullptr;
  98. bool valid_p521_keys = false;
  99. for (const base::Value& test_value : tests) {
  100. SCOPED_TRACE(&test_value - &tests[0]);
  101. EXPECT_TRUE(test_value.is_dict());
  102. test = &base::Value::AsDictionaryValue(test_value);
  103. absl::optional<bool> keys = test->FindBoolKey("valid_p521_keys");
  104. if (keys && keys.value()) {
  105. valid_p521_keys = true;
  106. break;
  107. }
  108. }
  109. if (!valid_p521_keys) {
  110. return ::testing::AssertionFailure()
  111. << "The P-521 test are missing in ecdh.json";
  112. }
  113. ImportKeysFromTest(test, public_key, private_key);
  114. EXPECT_EQ(blink::kWebCryptoNamedCurveP521,
  115. public_key->Algorithm().EcParams()->NamedCurve());
  116. return ::testing::AssertionSuccess();
  117. }
  118. // Try deriving an AES key of length 129 bits.
  119. TEST_F(WebCryptoEcdhTest, DeriveKeyBadAesLength) {
  120. blink::WebCryptoKey public_key;
  121. blink::WebCryptoKey base_key;
  122. ASSERT_TRUE(LoadTestKeys(&public_key, &base_key));
  123. blink::WebCryptoKey derived_key;
  124. ASSERT_EQ(Status::ErrorGetAesKeyLength(),
  125. DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
  126. CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesGcm),
  127. CreateAesGcmDerivedKeyParams(129), true,
  128. blink::kWebCryptoKeyUsageEncrypt, &derived_key));
  129. }
  130. // Try deriving an AES key of length 192 bits.
  131. TEST_F(WebCryptoEcdhTest, DeriveKeyUnsupportedAesLength) {
  132. blink::WebCryptoKey public_key;
  133. blink::WebCryptoKey base_key;
  134. ASSERT_TRUE(LoadTestKeys(&public_key, &base_key));
  135. blink::WebCryptoKey derived_key;
  136. ASSERT_EQ(Status::ErrorAes192BitUnsupported(),
  137. DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
  138. CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesGcm),
  139. CreateAesGcmDerivedKeyParams(192), true,
  140. blink::kWebCryptoKeyUsageEncrypt, &derived_key));
  141. }
  142. // Try deriving an HMAC key of length 0 bits.
  143. TEST_F(WebCryptoEcdhTest, DeriveKeyZeroLengthHmac) {
  144. blink::WebCryptoKey public_key;
  145. blink::WebCryptoKey base_key;
  146. ASSERT_TRUE(LoadTestKeys(&public_key, &base_key));
  147. blink::WebCryptoKey derived_key;
  148. const blink::WebCryptoAlgorithm import_algorithm =
  149. CreateHmacImportAlgorithm(blink::kWebCryptoAlgorithmIdSha1, 0);
  150. ASSERT_EQ(Status::ErrorGetHmacKeyLengthZero(),
  151. DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
  152. import_algorithm, import_algorithm, true,
  153. blink::kWebCryptoKeyUsageSign, &derived_key));
  154. }
  155. // Derive an HMAC key of length 19 bits.
  156. TEST_F(WebCryptoEcdhTest, DeriveKeyHmac19Bits) {
  157. blink::WebCryptoKey public_key;
  158. blink::WebCryptoKey base_key;
  159. ASSERT_TRUE(LoadTestKeys(&public_key, &base_key));
  160. blink::WebCryptoKey derived_key;
  161. const blink::WebCryptoAlgorithm import_algorithm =
  162. CreateHmacImportAlgorithm(blink::kWebCryptoAlgorithmIdSha1, 19);
  163. ASSERT_EQ(Status::Success(),
  164. DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
  165. import_algorithm, import_algorithm, true,
  166. blink::kWebCryptoKeyUsageSign, &derived_key));
  167. ASSERT_EQ(blink::kWebCryptoAlgorithmIdHmac, derived_key.Algorithm().Id());
  168. ASSERT_EQ(blink::kWebCryptoAlgorithmIdSha1,
  169. derived_key.Algorithm().HmacParams()->GetHash().Id());
  170. ASSERT_EQ(19u, derived_key.Algorithm().HmacParams()->LengthBits());
  171. // Export the key and verify its contents.
  172. std::vector<uint8_t> raw_key;
  173. EXPECT_EQ(Status::Success(),
  174. ExportKey(blink::kWebCryptoKeyFormatRaw, derived_key, &raw_key));
  175. EXPECT_EQ(3u, raw_key.size());
  176. // The last 7 bits of the key should be zero.
  177. EXPECT_EQ(0, raw_key.back() & 0x1f);
  178. }
  179. // Derive an HMAC key with no specified length (just the hash of SHA-256).
  180. TEST_F(WebCryptoEcdhTest, DeriveKeyHmacSha256NoLength) {
  181. blink::WebCryptoKey public_key;
  182. blink::WebCryptoKey base_key;
  183. ASSERT_TRUE(LoadTestKeys(&public_key, &base_key));
  184. blink::WebCryptoKey derived_key;
  185. const blink::WebCryptoAlgorithm import_algorithm =
  186. CreateHmacImportAlgorithmNoLength(blink::kWebCryptoAlgorithmIdSha256);
  187. ASSERT_EQ(Status::Success(),
  188. DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
  189. import_algorithm, import_algorithm, true,
  190. blink::kWebCryptoKeyUsageSign, &derived_key));
  191. ASSERT_EQ(blink::kWebCryptoAlgorithmIdHmac, derived_key.Algorithm().Id());
  192. ASSERT_EQ(blink::kWebCryptoAlgorithmIdSha256,
  193. derived_key.Algorithm().HmacParams()->GetHash().Id());
  194. ASSERT_EQ(512u, derived_key.Algorithm().HmacParams()->LengthBits());
  195. // Export the key and verify its contents.
  196. std::vector<uint8_t> raw_key;
  197. EXPECT_EQ(Status::Success(),
  198. ExportKey(blink::kWebCryptoKeyFormatRaw, derived_key, &raw_key));
  199. EXPECT_EQ(64u, raw_key.size());
  200. }
  201. // Derive an HMAC key with no specified length (just the hash of SHA-512).
  202. //
  203. // This fails, because ECDH using P-521 can only generate 528 bits, however HMAC
  204. // SHA-512 requires 1024 bits.
  205. //
  206. // In practice, authors won't be directly generating keys from key agreement
  207. // schemes, as that is frequently insecure, and instead be using KDFs to expand
  208. // and generate keys. For simplicity of testing, however, test using an HMAC
  209. // key.
  210. TEST_F(WebCryptoEcdhTest, DeriveKeyHmacSha512NoLength) {
  211. blink::WebCryptoKey public_key;
  212. blink::WebCryptoKey base_key;
  213. ASSERT_TRUE(LoadTestKeys(&public_key, &base_key));
  214. blink::WebCryptoKey derived_key;
  215. const blink::WebCryptoAlgorithm import_algorithm =
  216. CreateHmacImportAlgorithmNoLength(blink::kWebCryptoAlgorithmIdSha512);
  217. ASSERT_EQ(Status::ErrorEcdhLengthTooBig(528),
  218. DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
  219. import_algorithm, import_algorithm, true,
  220. blink::kWebCryptoKeyUsageSign, &derived_key));
  221. }
  222. // Try deriving an AES key of length 128 bits.
  223. TEST_F(WebCryptoEcdhTest, DeriveKeyAes128) {
  224. blink::WebCryptoKey public_key;
  225. blink::WebCryptoKey base_key;
  226. ASSERT_TRUE(LoadTestKeys(&public_key, &base_key));
  227. blink::WebCryptoKey derived_key;
  228. ASSERT_EQ(Status::Success(),
  229. DeriveKey(CreateEcdhDeriveParams(public_key), base_key,
  230. CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesGcm),
  231. CreateAesGcmDerivedKeyParams(128), true,
  232. blink::kWebCryptoKeyUsageEncrypt, &derived_key));
  233. ASSERT_EQ(blink::kWebCryptoAlgorithmIdAesGcm, derived_key.Algorithm().Id());
  234. ASSERT_EQ(128, derived_key.Algorithm().AesParams()->LengthBits());
  235. // Export the key and verify its contents.
  236. std::vector<uint8_t> raw_key;
  237. EXPECT_EQ(Status::Success(),
  238. ExportKey(blink::kWebCryptoKeyFormatRaw, derived_key, &raw_key));
  239. EXPECT_EQ(16u, raw_key.size());
  240. }
  241. TEST_F(WebCryptoEcdhTest, ImportKeyEmptyUsage) {
  242. blink::WebCryptoKey key;
  243. base::Value::List tests = ReadJsonTestFileAsList("ecdh.json");
  244. const base::Value& test_value = tests[0];
  245. ASSERT_TRUE(test_value.is_dict());
  246. const base::DictionaryValue* test =
  247. &base::Value::AsDictionaryValue(test_value);
  248. // Import the public key.
  249. const base::DictionaryValue* public_key_json = nullptr;
  250. EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json));
  251. blink::WebCryptoNamedCurve curve =
  252. GetCurveNameFromDictionary(public_key_json);
  253. ASSERT_EQ(
  254. Status::Success(),
  255. ImportKey(blink::kWebCryptoKeyFormatJwk, MakeJsonVector(*public_key_json),
  256. CreateEcdhImportAlgorithm(curve), true, 0, &key));
  257. EXPECT_EQ(0, key.Usages());
  258. // Import the private key.
  259. const base::DictionaryValue* private_key_json = nullptr;
  260. EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json));
  261. curve = GetCurveNameFromDictionary(private_key_json);
  262. ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
  263. ImportKey(blink::kWebCryptoKeyFormatJwk,
  264. MakeJsonVector(*private_key_json),
  265. CreateEcdhImportAlgorithm(curve), true, 0, &key));
  266. }
  267. } // namespace
  268. } // namespace webcrypto