ecdsa_unittest.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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 "base/containers/span.h"
  7. #include "components/webcrypto/algorithm_dispatch.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. blink::WebCryptoAlgorithm CreateEcdsaKeyGenAlgorithm(
  16. blink::WebCryptoNamedCurve named_curve) {
  17. return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(
  18. blink::kWebCryptoAlgorithmIdEcdsa,
  19. new blink::WebCryptoEcKeyGenParams(named_curve));
  20. }
  21. blink::WebCryptoAlgorithm CreateEcdsaImportAlgorithm(
  22. blink::WebCryptoNamedCurve named_curve) {
  23. return CreateEcImportAlgorithm(blink::kWebCryptoAlgorithmIdEcdsa,
  24. named_curve);
  25. }
  26. blink::WebCryptoAlgorithm CreateEcdsaAlgorithm(
  27. blink::WebCryptoAlgorithmId hash_id) {
  28. return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(
  29. blink::kWebCryptoAlgorithmIdEcdsa,
  30. new blink::WebCryptoEcdsaParams(CreateAlgorithm(hash_id)));
  31. }
  32. class WebCryptoEcdsaTest : public WebCryptoTestBase {};
  33. // Generates some ECDSA key pairs. Validates basic properties on the keys, and
  34. // ensures the serialized key (as JWK) is unique. This test does nothing to
  35. // ensure that the keys are otherwise usable (by trying to sign/verify with
  36. // them).
  37. TEST_F(WebCryptoEcdsaTest, GenerateKeyIsRandom) {
  38. blink::WebCryptoNamedCurve named_curve = blink::kWebCryptoNamedCurveP256;
  39. std::vector<std::vector<uint8_t>> serialized_keys;
  40. // Generate a small sample of keys.
  41. for (int j = 0; j < 4; ++j) {
  42. blink::WebCryptoKey public_key;
  43. blink::WebCryptoKey private_key;
  44. ASSERT_EQ(Status::Success(),
  45. GenerateKeyPair(CreateEcdsaKeyGenAlgorithm(named_curve), true,
  46. blink::kWebCryptoKeyUsageSign, &public_key,
  47. &private_key));
  48. // Basic sanity checks on the generated key pair.
  49. EXPECT_EQ(blink::kWebCryptoKeyTypePublic, public_key.GetType());
  50. EXPECT_EQ(blink::kWebCryptoKeyTypePrivate, private_key.GetType());
  51. EXPECT_EQ(named_curve, public_key.Algorithm().EcParams()->NamedCurve());
  52. EXPECT_EQ(named_curve, private_key.Algorithm().EcParams()->NamedCurve());
  53. // Export the key pair to JWK.
  54. std::vector<uint8_t> key_bytes;
  55. ASSERT_EQ(Status::Success(),
  56. ExportKey(blink::kWebCryptoKeyFormatJwk, public_key, &key_bytes));
  57. serialized_keys.push_back(key_bytes);
  58. ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatJwk,
  59. private_key, &key_bytes));
  60. serialized_keys.push_back(key_bytes);
  61. }
  62. // Ensure all entries in the key sample set are unique. This is a simplistic
  63. // estimate of whether the generated keys appear random.
  64. EXPECT_FALSE(CopiesExist(serialized_keys));
  65. }
  66. TEST_F(WebCryptoEcdsaTest, GenerateKeyEmptyUsage) {
  67. blink::WebCryptoNamedCurve named_curve = blink::kWebCryptoNamedCurveP256;
  68. blink::WebCryptoKey public_key;
  69. blink::WebCryptoKey private_key;
  70. ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
  71. GenerateKeyPair(CreateEcdsaKeyGenAlgorithm(named_curve), true, 0,
  72. &public_key, &private_key));
  73. }
  74. // Verify that ECDSA signatures are probabilistic. Signing the same message two
  75. // times should yield different signatures. However both signatures should
  76. // verify correctly.
  77. TEST_F(WebCryptoEcdsaTest, SignatureIsRandom) {
  78. // Import a public and private keypair from "ec_private_keys.json". It doesn't
  79. // really matter which one is used since they are all valid. In this case
  80. // using the first one.
  81. base::Value::List private_keys =
  82. ReadJsonTestFileAsList("ec_private_keys.json");
  83. const base::Value& key_value = private_keys[0];
  84. ASSERT_TRUE(key_value.is_dict());
  85. const base::DictionaryValue* key_dict =
  86. &base::Value::AsDictionaryValue(key_value);
  87. blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(key_dict);
  88. const base::DictionaryValue* key_jwk;
  89. ASSERT_TRUE(key_dict->GetDictionary("jwk", &key_jwk));
  90. blink::WebCryptoKey private_key;
  91. ASSERT_EQ(
  92. Status::Success(),
  93. ImportKeyJwkFromDict(*key_jwk, CreateEcdsaImportAlgorithm(curve), true,
  94. blink::kWebCryptoKeyUsageSign, &private_key));
  95. // Erase the "d" member so the private key JWK can be used to import the
  96. // public key (WebCrypto doesn't provide a mechanism for importing a public
  97. // key given a private key).
  98. std::unique_ptr<base::DictionaryValue> key_jwk_copy =
  99. base::DictionaryValue::From(
  100. base::Value::ToUniquePtrValue(key_jwk->Clone()));
  101. key_jwk_copy->RemoveKey("d");
  102. blink::WebCryptoKey public_key;
  103. ASSERT_EQ(
  104. Status::Success(),
  105. ImportKeyJwkFromDict(*key_jwk_copy, CreateEcdsaImportAlgorithm(curve),
  106. true, blink::kWebCryptoKeyUsageVerify, &public_key));
  107. // Sign twice
  108. std::vector<uint8_t> message(10);
  109. blink::WebCryptoAlgorithm algorithm =
  110. CreateEcdsaAlgorithm(blink::kWebCryptoAlgorithmIdSha1);
  111. std::vector<uint8_t> signature1;
  112. std::vector<uint8_t> signature2;
  113. ASSERT_EQ(Status::Success(),
  114. Sign(algorithm, private_key, message, &signature1));
  115. ASSERT_EQ(Status::Success(),
  116. Sign(algorithm, private_key, message, &signature2));
  117. // The two signatures should be different.
  118. EXPECT_NE(signature1, signature2);
  119. // And both should be valid signatures which can be verified.
  120. bool signature_matches;
  121. ASSERT_EQ(Status::Success(), Verify(algorithm, public_key, signature1,
  122. message, &signature_matches));
  123. EXPECT_TRUE(signature_matches);
  124. ASSERT_EQ(Status::Success(), Verify(algorithm, public_key, signature2,
  125. message, &signature_matches));
  126. EXPECT_TRUE(signature_matches);
  127. }
  128. // Tests verify() for ECDSA using an assortment of keys, curves and hashes.
  129. // These tests also include expected failures for bad signatures and keys.
  130. TEST_F(WebCryptoEcdsaTest, VerifyKnownAnswer) {
  131. base::Value::List tests = ReadJsonTestFileAsList("ecdsa.json");
  132. for (const auto& test_value : tests) {
  133. SCOPED_TRACE(&test_value - &tests[0]);
  134. ASSERT_TRUE(test_value.is_dict());
  135. const base::DictionaryValue* test =
  136. &base::Value::AsDictionaryValue(test_value);
  137. blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test);
  138. blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test);
  139. std::vector<uint8_t> key_data =
  140. GetKeyDataFromJsonTestCase(test, key_format);
  141. // If the test didn't specify an error, that implies it expects success.
  142. std::string expected_error = "Success";
  143. test->GetString("error", &expected_error);
  144. // Import the public key.
  145. blink::WebCryptoKey key;
  146. Status status =
  147. ImportKey(key_format, key_data, CreateEcdsaImportAlgorithm(curve), true,
  148. blink::kWebCryptoKeyUsageVerify, &key);
  149. ASSERT_EQ(expected_error, StatusToString(status));
  150. if (status.IsError())
  151. continue;
  152. // Basic sanity checks on the imported public key.
  153. EXPECT_EQ(blink::kWebCryptoKeyTypePublic, key.GetType());
  154. EXPECT_EQ(blink::kWebCryptoKeyUsageVerify, key.Usages());
  155. EXPECT_EQ(curve, key.Algorithm().EcParams()->NamedCurve());
  156. // Now try to verify the given message and signature.
  157. std::vector<uint8_t> message = GetBytesFromHexString(test, "msg");
  158. std::vector<uint8_t> signature = GetBytesFromHexString(test, "sig");
  159. blink::WebCryptoAlgorithm hash = GetDigestAlgorithm(test, "hash");
  160. bool verify_result;
  161. status = Verify(CreateEcdsaAlgorithm(hash.Id()), key, signature, message,
  162. &verify_result);
  163. ASSERT_EQ(expected_error, StatusToString(status));
  164. if (status.IsError())
  165. continue;
  166. // If no error was expected, the verification's boolean must match
  167. // "verify_result" for the test.
  168. absl::optional<bool> expected_result = test->FindBoolKey("verify_result");
  169. ASSERT_TRUE(expected_result);
  170. EXPECT_EQ(expected_result.value(), verify_result);
  171. }
  172. }
  173. // The test file may include either public or private keys. In order to import
  174. // them successfully, the correct usages need to be specified. This function
  175. // determines what usages to use for the key.
  176. blink::WebCryptoKeyUsageMask GetExpectedUsagesForKeyImport(
  177. blink::WebCryptoKeyFormat key_format,
  178. const base::DictionaryValue* test) {
  179. blink::WebCryptoKeyUsageMask kPublicUsages = blink::kWebCryptoKeyUsageVerify;
  180. blink::WebCryptoKeyUsageMask kPrivateUsages = blink::kWebCryptoKeyUsageSign;
  181. switch (key_format) {
  182. case blink::kWebCryptoKeyFormatRaw:
  183. case blink::kWebCryptoKeyFormatSpki:
  184. return kPublicUsages;
  185. case blink::kWebCryptoKeyFormatPkcs8:
  186. return kPrivateUsages;
  187. case blink::kWebCryptoKeyFormatJwk: {
  188. const base::DictionaryValue* key = nullptr;
  189. if (!test->GetDictionary("key", &key))
  190. ADD_FAILURE() << "Missing key property";
  191. return key->FindKey("d") ? kPrivateUsages : kPublicUsages;
  192. }
  193. }
  194. // Appease compiler.
  195. return kPrivateUsages;
  196. }
  197. // Tests importing bad public/private keys in a variety of formats.
  198. TEST_F(WebCryptoEcdsaTest, ImportBadKeys) {
  199. base::Value::List tests = ReadJsonTestFileAsList("bad_ec_keys.json");
  200. for (const auto& test_value : tests) {
  201. SCOPED_TRACE(&test_value - &tests[0]);
  202. ASSERT_TRUE(test_value.is_dict());
  203. const base::DictionaryValue* test =
  204. &base::Value::AsDictionaryValue(test_value);
  205. blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test);
  206. blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test);
  207. std::vector<uint8_t> key_data =
  208. GetKeyDataFromJsonTestCase(test, key_format);
  209. std::string expected_error;
  210. ASSERT_TRUE(test->GetString("error", &expected_error));
  211. blink::WebCryptoKey key;
  212. Status status =
  213. ImportKey(key_format, key_data, CreateEcdsaImportAlgorithm(curve), true,
  214. GetExpectedUsagesForKeyImport(key_format, test), &key);
  215. ASSERT_EQ(expected_error, StatusToString(status));
  216. }
  217. }
  218. // Tests importing and exporting of EC private keys, using both JWK and PKCS8
  219. // formats.
  220. //
  221. // The test imports a key first using JWK, and then exporting it to JWK and
  222. // PKCS8. It does the same thing using PKCS8 as the original source of truth.
  223. TEST_F(WebCryptoEcdsaTest, ImportExportPrivateKey) {
  224. base::Value::List tests = ReadJsonTestFileAsList("ec_private_keys.json");
  225. for (const auto& test_value : tests) {
  226. SCOPED_TRACE(&test_value - &tests[0]);
  227. ASSERT_TRUE(test_value.is_dict());
  228. const base::DictionaryValue* test =
  229. &base::Value::AsDictionaryValue(test_value);
  230. blink::WebCryptoNamedCurve curve = GetCurveNameFromDictionary(test);
  231. const base::DictionaryValue* jwk_dict;
  232. EXPECT_TRUE(test->GetDictionary("jwk", &jwk_dict));
  233. std::vector<uint8_t> jwk_bytes = MakeJsonVector(*jwk_dict);
  234. std::vector<uint8_t> pkcs8_bytes = GetBytesFromHexString(
  235. test, test->FindKey("exported_pkcs8") ? "exported_pkcs8" : "pkcs8");
  236. // -------------------------------------------------
  237. // Test from JWK, and then export to {JWK, PKCS8}
  238. // -------------------------------------------------
  239. // Import the key using JWK
  240. blink::WebCryptoKey key;
  241. ASSERT_EQ(Status::Success(),
  242. ImportKey(blink::kWebCryptoKeyFormatJwk, jwk_bytes,
  243. CreateEcdsaImportAlgorithm(curve), true,
  244. blink::kWebCryptoKeyUsageSign, &key));
  245. // Export the key as JWK
  246. std::vector<uint8_t> exported_bytes;
  247. ASSERT_EQ(Status::Success(),
  248. ExportKey(blink::kWebCryptoKeyFormatJwk, key, &exported_bytes));
  249. // NOTE: The exported bytes can't be directly compared to jwk_bytes because
  250. // the exported JWK differs from the imported one. In particular it contains
  251. // extra properties for extractability and key_ops.
  252. //
  253. // Verification is instead done by using the first exported JWK bytes as the
  254. // expectation.
  255. jwk_bytes = exported_bytes;
  256. ASSERT_EQ(Status::Success(),
  257. ImportKey(blink::kWebCryptoKeyFormatJwk, jwk_bytes,
  258. CreateEcdsaImportAlgorithm(curve), true,
  259. blink::kWebCryptoKeyUsageSign, &key));
  260. // Export the key as JWK (again)
  261. ASSERT_EQ(Status::Success(),
  262. ExportKey(blink::kWebCryptoKeyFormatJwk, key, &exported_bytes));
  263. EXPECT_EQ(jwk_bytes, exported_bytes);
  264. // Export the key as PKCS8
  265. ASSERT_EQ(Status::Success(),
  266. ExportKey(blink::kWebCryptoKeyFormatPkcs8, key, &exported_bytes));
  267. EXPECT_EQ(pkcs8_bytes, exported_bytes);
  268. // -------------------------------------------------
  269. // Test from PKCS8, and then export to {JWK, PKCS8}
  270. // -------------------------------------------------
  271. // The imported PKCS8 bytes may differ from the exported bytes (in the case
  272. // where the publicKey was missing, it will be synthesized and written back
  273. // during export).
  274. std::vector<uint8_t> pkcs8_input_bytes = GetBytesFromHexString(
  275. test, test->FindKey("original_pkcs8") ? "original_pkcs8" : "pkcs8");
  276. base::span<const uint8_t> pkcs8_input_data(
  277. pkcs8_input_bytes.empty() ? pkcs8_bytes : pkcs8_input_bytes);
  278. // Import the key using PKCS8
  279. ASSERT_EQ(Status::Success(),
  280. ImportKey(blink::kWebCryptoKeyFormatPkcs8, pkcs8_input_data,
  281. CreateEcdsaImportAlgorithm(curve), true,
  282. blink::kWebCryptoKeyUsageSign, &key));
  283. // Export the key as PKCS8
  284. ASSERT_EQ(Status::Success(),
  285. ExportKey(blink::kWebCryptoKeyFormatPkcs8, key, &exported_bytes));
  286. EXPECT_EQ(pkcs8_bytes, exported_bytes);
  287. // Export the key as JWK
  288. ASSERT_EQ(Status::Success(),
  289. ExportKey(blink::kWebCryptoKeyFormatJwk, key, &exported_bytes));
  290. EXPECT_EQ(jwk_bytes, exported_bytes);
  291. }
  292. }
  293. } // namespace
  294. } // namespace webcrypto