1 // Copyright 2021 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////////
16 #include "tink/internal/ec_util.h"
17
18 #include <stdint.h>
19
20 #include <memory>
21 #include <string>
22 #include <vector>
23
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26 #include "absl/status/status.h"
27 #include "absl/strings/escaping.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/str_split.h"
30 #include "absl/strings/string_view.h"
31 #include "absl/types/span.h"
32 #include "openssl/bn.h"
33 #include "openssl/ec.h"
34 #include "openssl/ecdsa.h"
35 #include "openssl/evp.h"
36 #include "include/rapidjson/document.h"
37 #include "tink/internal/bn_util.h"
38 #include "tink/internal/fips_utils.h"
39 #include "tink/internal/ssl_unique_ptr.h"
40 #include "tink/internal/ssl_util.h"
41 #include "tink/subtle/common_enums.h"
42 #include "tink/subtle/subtle_util.h"
43 #include "tink/subtle/wycheproof_util.h"
44 #include "tink/util/secret_data.h"
45 #include "tink/util/status.h"
46 #include "tink/util/statusor.h"
47 #include "tink/util/test_matchers.h"
48
49 namespace crypto {
50 namespace tink {
51 namespace internal {
52 namespace {
53
54 using ::crypto::tink::subtle::EcPointFormat;
55 using ::crypto::tink::subtle::EllipticCurveType;
56 using ::crypto::tink::subtle::WycheproofUtil;
57 using ::crypto::tink::test::IsOk;
58 using ::crypto::tink::test::IsOkAndHolds;
59 using ::crypto::tink::test::StatusIs;
60 using ::testing::AllOf;
61 using ::testing::ElementsAreArray;
62 using ::testing::Eq;
63 using ::testing::Field;
64 using ::testing::HasSubstr;
65 using ::testing::IsEmpty;
66 using ::testing::IsNull;
67 using ::testing::Matcher;
68 using ::testing::Not;
69 using ::testing::SizeIs;
70 using ::testing::TestParamInfo;
71 using ::testing::TestWithParam;
72 using ::testing::ValuesIn;
73
74 // Use wycheproof test vectors to verify Ed25519 key generation from a seed (the
75 // private key) results in the public/private key.
TEST(EcUtilTest,NewEd25519KeyWithWycheproofTestVectors)76 TEST(EcUtilTest, NewEd25519KeyWithWycheproofTestVectors) {
77 std::unique_ptr<rapidjson::Document> test_vectors =
78 WycheproofUtil::ReadTestVectors("eddsa_test.json");
79 ASSERT_THAT(test_vectors, Not(IsNull()));
80
81 // For this test we are only interested in Ed25519 keys.
82 for (const auto& test_group : (*test_vectors)["testGroups"].GetArray()) {
83 std::string private_key = WycheproofUtil::GetBytes(test_group["key"]["sk"]);
84 std::string public_key = WycheproofUtil::GetBytes(test_group["key"]["pk"]);
85
86 util::StatusOr<std::unique_ptr<Ed25519Key>> key =
87 NewEd25519Key(util::SecretDataFromStringView(private_key));
88 ASSERT_THAT(key, IsOk());
89 EXPECT_EQ((*key)->public_key, public_key);
90 EXPECT_EQ((*key)->private_key, private_key);
91 }
92 }
93
TEST(EcUtilTest,NewEd25519KeyInvalidSeed)94 TEST(EcUtilTest, NewEd25519KeyInvalidSeed) {
95 std::string valid_seed = absl::HexStringToBytes(
96 "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f");
97 // Seed that is too small.
98 for (int i = 0; i < 32; i++) {
99 EXPECT_THAT(
100 NewEd25519Key(util::SecretDataFromStringView(valid_seed.substr(0, i)))
101 .status(),
102 Not(IsOk()))
103 << " with seed of length " << i;
104 }
105 // Seed that is too large.
106 std::string large_seed = absl::StrCat(valid_seed, "a");
107 EXPECT_THAT(
108 NewEd25519Key(util::SecretDataFromStringView(large_seed)).status(),
109 Not(IsOk()))
110 << " with seed of length " << large_seed.size();
111 }
112
TEST(EcUtilTest,NewEcKeyReturnsWellFormedX25519Key)113 TEST(EcUtilTest, NewEcKeyReturnsWellFormedX25519Key) {
114 util::StatusOr<EcKey> ec_key =
115 NewEcKey(subtle::EllipticCurveType::CURVE25519);
116 ASSERT_THAT(ec_key, IsOk());
117 EXPECT_THAT(
118 *ec_key,
119 AllOf(Field(&EcKey::curve, Eq(subtle::EllipticCurveType::CURVE25519)),
120 Field(&EcKey::pub_x, SizeIs(X25519KeyPubKeySize())),
121 Field(&EcKey::pub_y, IsEmpty()),
122 Field(&EcKey::priv, SizeIs(X25519KeyPrivKeySize()))));
123 }
124
125 using EcUtilNewEcKeyWithSeed = TestWithParam<subtle::EllipticCurveType>;
126
127 // Matcher for the equality of two EcKeys.
EqualsEcKey(const EcKey & expected)128 Matcher<EcKey> EqualsEcKey(const EcKey& expected) {
129 return AllOf(Field(&EcKey::priv, Eq(expected.priv)),
130 Field(&EcKey::pub_x, Eq(expected.pub_x)),
131 Field(&EcKey::pub_y, Eq(expected.pub_y)),
132 Field(&EcKey::curve, Eq(expected.curve)));
133 }
134
TEST_P(EcUtilNewEcKeyWithSeed,KeysFromDifferentSeedAreDifferent)135 TEST_P(EcUtilNewEcKeyWithSeed, KeysFromDifferentSeedAreDifferent) {
136 if (IsFipsModeEnabled()) {
137 GTEST_SKIP() << "Not supported in FIPS-only mode";
138 }
139 if (!IsBoringSsl()) {
140 GTEST_SKIP() << "NewEcKey with seed is not supported with OpenSSL";
141 }
142
143 util::SecretData seed1 = util::SecretDataFromStringView(
144 absl::HexStringToBytes("000102030405060708090a0b0c0d0e0f"));
145 util::SecretData seed2 = util::SecretDataFromStringView(
146 absl::HexStringToBytes("0f0e0d0c0b0a09080706050403020100"));
147 subtle::EllipticCurveType curve = GetParam();
148
149 util::StatusOr<EcKey> keypair1 = NewEcKey(curve, seed1);
150 ASSERT_THAT(keypair1, IsOk());
151 util::StatusOr<EcKey> keypair2 = NewEcKey(curve, seed2);
152 ASSERT_THAT(keypair2, IsOk());
153 EXPECT_THAT(*keypair1, Not(EqualsEcKey(*keypair2)));
154 }
155
TEST_P(EcUtilNewEcKeyWithSeed,SameSeedGivesSameKey)156 TEST_P(EcUtilNewEcKeyWithSeed, SameSeedGivesSameKey) {
157 if (IsFipsModeEnabled()) {
158 GTEST_SKIP() << "Not supported in FIPS-only mode";
159 }
160 if (!IsBoringSsl()) {
161 GTEST_SKIP() << "NewEcKey with seed is not supported with OpenSSL";
162 }
163
164 util::SecretData seed1 = util::SecretDataFromStringView(
165 absl::HexStringToBytes("000102030405060708090a0b0c0d0e0f"));
166 subtle::EllipticCurveType curve = GetParam();
167
168 util::StatusOr<EcKey> keypair1 = NewEcKey(curve, seed1);
169 ASSERT_THAT(keypair1, IsOk());
170 util::StatusOr<EcKey> keypair2 = NewEcKey(curve, seed1);
171 ASSERT_THAT(keypair2, IsOk());
172 EXPECT_THAT(*keypair1, EqualsEcKey(*keypair2));
173 }
174
175 INSTANTIATE_TEST_SUITE_P(EcUtilNewEcKeyWithSeeds, EcUtilNewEcKeyWithSeed,
176 ValuesIn({subtle::NIST_P256, subtle::NIST_P384,
177 subtle::NIST_P521}));
178
TEST(EcUtilTest,GenerationWithSeedFailsWithWrongCurve)179 TEST(EcUtilTest, GenerationWithSeedFailsWithWrongCurve) {
180 if (IsFipsModeEnabled()) {
181 GTEST_SKIP() << "Not supported in FIPS-only mode";
182 }
183 if (!IsBoringSsl()) {
184 GTEST_SKIP() << "NewEcKey with seed is not supported with OpenSSL";
185 }
186 util::SecretData seed = util::SecretDataFromStringView(
187 absl::HexStringToBytes("000102030405060708090a0b0c0d0e0f"));
188 util::StatusOr<EcKey> keypair =
189 NewEcKey(subtle::EllipticCurveType::CURVE25519, seed);
190 EXPECT_THAT(keypair.status(), StatusIs(absl::StatusCode::kInternal));
191 }
192
TEST(EcUtilTest,NewEcKeyFromSeedUnimplementedIfOpenSsl)193 TEST(EcUtilTest, NewEcKeyFromSeedUnimplementedIfOpenSsl) {
194 if (IsFipsModeEnabled()) {
195 GTEST_SKIP() << "Not supported in FIPS-only mode";
196 }
197 if (IsBoringSsl()) {
198 GTEST_SKIP()
199 << "OpenSSL-only test; skipping because BoringSSL is being used";
200 }
201 util::SecretData seed = util::SecretDataFromStringView(
202 absl::HexStringToBytes("000102030405060708090a0b0c0d0e0f"));
203 util::StatusOr<EcKey> keypair =
204 NewEcKey(subtle::EllipticCurveType::CURVE25519, seed);
205 EXPECT_THAT(keypair.status(), StatusIs(absl::StatusCode::kUnimplemented));
206 }
207
TEST(EcUtilTest,NewX25519KeyGeneratesNewKeyEveryTime)208 TEST(EcUtilTest, NewX25519KeyGeneratesNewKeyEveryTime) {
209 util::StatusOr<std::unique_ptr<X25519Key>> keypair1 = NewX25519Key();
210 ASSERT_THAT(keypair1, IsOk());
211 util::StatusOr<std::unique_ptr<X25519Key>> keypair2 = NewX25519Key();
212 ASSERT_THAT(keypair2, IsOk());
213
214 auto priv_key1 =
215 absl::MakeSpan((*keypair1)->private_key, X25519KeyPrivKeySize());
216 auto priv_key2 =
217 absl::MakeSpan((*keypair2)->private_key, X25519KeyPrivKeySize());
218 auto pub_key1 =
219 absl::MakeSpan((*keypair1)->public_value, X25519KeyPubKeySize());
220 auto pub_key2 =
221 absl::MakeSpan((*keypair2)->public_value, X25519KeyPubKeySize());
222 EXPECT_THAT(priv_key1, Not(ElementsAreArray(priv_key2)));
223 EXPECT_THAT(pub_key1, Not(ElementsAreArray(pub_key2)));
224 }
225
TEST(EcUtilTest,X25519KeyToEcKeyAndBack)226 TEST(EcUtilTest, X25519KeyToEcKeyAndBack) {
227 util::StatusOr<std::unique_ptr<X25519Key>> x25519_key = NewX25519Key();
228 ASSERT_THAT(x25519_key, IsOk());
229 EcKey ec_key = EcKeyFromX25519Key(x25519_key->get());
230 ASSERT_EQ(ec_key.curve, EllipticCurveType::CURVE25519);
231
232 util::StatusOr<std::unique_ptr<X25519Key>> roundtrip_key =
233 X25519KeyFromEcKey(ec_key);
234 ASSERT_THAT(roundtrip_key, IsOk());
235 EXPECT_THAT(
236 absl::MakeSpan((*x25519_key)->private_key, X25519KeyPrivKeySize()),
237 ElementsAreArray(absl::MakeSpan((*roundtrip_key)->private_key,
238 X25519KeyPrivKeySize())));
239 EXPECT_THAT(
240 absl::MakeSpan((*x25519_key)->public_value, X25519KeyPubKeySize()),
241 ElementsAreArray(absl::MakeSpan((*roundtrip_key)->public_value,
242 X25519KeyPubKeySize())));
243 }
244
TEST(EcUtilTest,X25519KeyFromRandomPrivateKey)245 TEST(EcUtilTest, X25519KeyFromRandomPrivateKey) {
246 util::StatusOr<std::unique_ptr<X25519Key>> x25519_key = NewX25519Key();
247 ASSERT_THAT(x25519_key, IsOk());
248
249 absl::Span<uint8_t> pkey_span =
250 absl::MakeSpan((*x25519_key)->private_key, X25519KeyPrivKeySize());
251 util::StatusOr<std::unique_ptr<X25519Key>> roundtrip_key =
252 X25519KeyFromPrivateKey({pkey_span.begin(), pkey_span.end()});
253 ASSERT_THAT(roundtrip_key, IsOk());
254 EXPECT_THAT(
255 absl::MakeSpan((*x25519_key)->private_key, X25519KeyPrivKeySize()),
256 ElementsAreArray(absl::MakeSpan((*roundtrip_key)->private_key,
257 X25519KeyPrivKeySize())));
258 EXPECT_THAT(
259 absl::MakeSpan((*x25519_key)->public_value, X25519KeyPubKeySize()),
260 ElementsAreArray(absl::MakeSpan((*roundtrip_key)->public_value,
261 X25519KeyPubKeySize())));
262 }
263
264 struct X25519FunctionTestVector {
265 std::string private_key;
266 std::string expected_public_key;
267 };
268
269 // Returns some X25519 test vectors taken from
270 // https://datatracker.ietf.org/doc/html/rfc7748.
GetX25519FunctionTestVectors()271 std::vector<X25519FunctionTestVector> GetX25519FunctionTestVectors() {
272 return {
273 // https://datatracker.ietf.org/doc/html/rfc7748#section-5.2
274 {
275 /*private_key=*/
276 absl::HexStringToBytes("090000000000000000000000000000000000000000000"
277 "0000000000000000000"),
278 /*expected_public_key=*/
279 absl::HexStringToBytes("422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854"
280 "b783c60e80311ae3079"),
281 },
282 // https://datatracker.ietf.org/doc/html/rfc7748#section-6.1; Alice
283 {
284 /*private_key=*/
285 absl::HexStringToBytes("77076d0a7318a57d3c16c17251b26645df4c2f87ebc09"
286 "92ab177fba51db92c2a"),
287 /*expected_public_key=*/
288 absl::HexStringToBytes("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381"
289 "af4eba4a98eaa9b4e6a"),
290 },
291 // https://datatracker.ietf.org/doc/html/rfc7748#section-6.1; Bob
292 {
293 /*private_key=*/
294 absl::HexStringToBytes("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b"
295 "6fd1c2f8b27ff88e0eb"),
296 /*expected_public_key=*/
297 absl::HexStringToBytes("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b786"
298 "74dadfc7e146f882b4f"),
299 },
300 // Locally made up test vector
301 {
302 /*private_key=*/
303 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
304 /*expected_public_key=*/
305 absl::HexStringToBytes("4049502db92ca2342c3f92dac5d6de7c85db5df5407a5"
306 "b4996ce39f2efb7e827"),
307 },
308 };
309 }
310
311 using X25519FunctionTest = TestWithParam<X25519FunctionTestVector>;
312
TEST_P(X25519FunctionTest,ComputeX25519PublicKey)313 TEST_P(X25519FunctionTest, ComputeX25519PublicKey) {
314 X25519FunctionTestVector test_vector = GetParam();
315
316 util::StatusOr<std::unique_ptr<X25519Key>> key = X25519KeyFromPrivateKey(
317 util::SecretDataFromStringView(test_vector.private_key));
318 ASSERT_THAT(key, IsOk());
319 EXPECT_THAT(absl::MakeSpan((*key)->public_value, X25519KeyPubKeySize()),
320 ElementsAreArray(test_vector.expected_public_key));
321 }
322
323 INSTANTIATE_TEST_SUITE_P(X25519SharedSecretTests, X25519FunctionTest,
324 ValuesIn(GetX25519FunctionTestVectors()));
325
326 struct X25519SharedSecretTestVector {
327 std::string private_key;
328 std::string public_key;
329 std::string expected_shared_secret;
330 };
331
332 // Returns some X25519 test vectors taken from
333 // https://datatracker.ietf.org/doc/html/rfc7748#section-5.2.
GetX25519SharedSecretTestVectors()334 std::vector<X25519SharedSecretTestVector> GetX25519SharedSecretTestVectors() {
335 return {
336 {
337 /*private_key=*/
338 absl::HexStringToBytes("a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5"
339 "a18506a2244ba449ac4"),
340 /*public_key=*/
341 absl::HexStringToBytes("e6db6867583030db3594c1a424b15f7c726624ec26b33"
342 "53b10a903a6d0ab1c4c"),
343 /*expected_shared_secret=*/
344 absl::HexStringToBytes("c3da55379de9c6908e94ea4df28d084f32eccf03491c7"
345 "1f754b4075577a28552"),
346 },
347 {
348 /*private_key=*/
349 absl::HexStringToBytes("4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea0"
350 "1d42ca4169e7918ba0d"),
351 /*public_key=*/
352 absl::HexStringToBytes("e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03"
353 "c3efc4cd549c715a493"),
354 /*expected_shared_secret=*/
355 absl::HexStringToBytes("95cbde9476e8907d7aade45cb4b873f88b595a68799fa"
356 "152e6f8f7647aac7957"),
357 },
358 };
359 }
360
361 using X25519SharedSecretTest = TestWithParam<X25519SharedSecretTestVector>;
362
TEST_P(X25519SharedSecretTest,ComputeX25519SharedSecret)363 TEST_P(X25519SharedSecretTest, ComputeX25519SharedSecret) {
364 X25519SharedSecretTestVector test_vector = GetParam();
365
366 // Generate the EVP_PKEYs.
367 internal::SslUniquePtr<EVP_PKEY> ssl_priv_key(EVP_PKEY_new_raw_private_key(
368 /*type=*/EVP_PKEY_X25519, /*unused=*/nullptr,
369 /*in=*/reinterpret_cast<const uint8_t*>(test_vector.private_key.data()),
370 /*len=*/Ed25519KeyPrivKeySize()));
371 ASSERT_THAT(ssl_priv_key, Not(IsNull()));
372 internal::SslUniquePtr<EVP_PKEY> ssl_pub_key(EVP_PKEY_new_raw_public_key(
373 /*type=*/EVP_PKEY_X25519, /*unused=*/nullptr,
374 /*in=*/reinterpret_cast<const uint8_t*>(test_vector.public_key.data()),
375 /*len=*/Ed25519KeyPrivKeySize()));
376 ASSERT_THAT(ssl_pub_key, Not(IsNull()));
377
378 EXPECT_THAT(ComputeX25519SharedSecret(ssl_priv_key.get(), ssl_pub_key.get()),
379 IsOkAndHolds(util::SecretDataFromStringView(
380 test_vector.expected_shared_secret)));
381 }
382
383 INSTANTIATE_TEST_SUITE_P(X25519SharedSecretTests, X25519SharedSecretTest,
384 ValuesIn(GetX25519SharedSecretTestVectors()));
385
TEST(EcUtilTest,ComputeX25519SharedSecretInvalidKeyType)386 TEST(EcUtilTest, ComputeX25519SharedSecretInvalidKeyType) {
387 // Key pair of an invalid type EVP_PKEY_ED25519.
388 SslUniquePtr<EVP_PKEY_CTX> pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519,
389 /*e=*/nullptr));
390 ASSERT_THAT(pctx, Not(IsNull()));
391 ASSERT_EQ(EVP_PKEY_keygen_init(pctx.get()), 1);
392 EVP_PKEY* invalid_type_key_ptr = nullptr;
393 ASSERT_EQ(EVP_PKEY_keygen(pctx.get(), &invalid_type_key_ptr), 1);
394 SslUniquePtr<EVP_PKEY> invalid_type_key(invalid_type_key_ptr);
395
396 // Private and public key with valid type.
397 internal::SslUniquePtr<EVP_PKEY> ssl_priv_key(EVP_PKEY_new_raw_private_key(
398 /*type=*/EVP_PKEY_X25519, /*unused=*/nullptr,
399 /*in=*/
400 reinterpret_cast<const uint8_t*>(
401 absl::HexStringToBytes("a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5"
402 "a18506a2244ba449ac4")
403 .data()),
404 /*len=*/Ed25519KeyPrivKeySize()));
405 ASSERT_THAT(ssl_priv_key, Not(IsNull()));
406 internal::SslUniquePtr<EVP_PKEY> ssl_pub_key(EVP_PKEY_new_raw_public_key(
407 /*type=*/EVP_PKEY_X25519, /*unused=*/nullptr,
408 /*in=*/
409 reinterpret_cast<const uint8_t*>(
410 absl::HexStringToBytes("e6db6867583030db3594c1a424b15f7c726624ec26b33"
411 "53b10a903a6d0ab1c4c")
412 .data()),
413 /*len=*/Ed25519KeyPubKeySize()));
414 ASSERT_THAT(ssl_pub_key, Not(IsNull()));
415
416 EXPECT_THAT(
417 ComputeX25519SharedSecret(ssl_priv_key.get(), invalid_type_key.get())
418 .status(),
419 Not(IsOk()));
420 EXPECT_THAT(
421 ComputeX25519SharedSecret(invalid_type_key.get(), ssl_pub_key.get())
422 .status(),
423 Not(IsOk()));
424 }
425
426 struct EncodingTestVector {
427 EcPointFormat format;
428 std::string x_hex;
429 std::string y_hex;
430 std::string encoded_hex;
431 EllipticCurveType curve;
432 };
433
GetEncodingTestVectors()434 std::vector<EncodingTestVector> GetEncodingTestVectors() {
435 return {
436 {EcPointFormat::UNCOMPRESSED,
437 "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec6"
438 "619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a",
439 "00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327a"
440 "caac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
441 "0400093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918e"
442 "c6619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a00aa3fb2"
443 "448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327acaac1fa4"
444 "0424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
445 EllipticCurveType::NIST_P521},
446 {EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED,
447 "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec6"
448 "619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a",
449 "00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327a"
450 "caac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
451 "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec6"
452 "619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a00aa3fb244"
453 "8335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327acaac1fa404"
454 "24c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
455 EllipticCurveType::NIST_P521},
456 {EcPointFormat::COMPRESSED,
457 "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec6"
458 "619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a",
459 "00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327a"
460 "caac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf",
461 "0300093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918e"
462 "c6619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a",
463 EllipticCurveType::NIST_P521}};
464 }
465
466 using EcUtilEncodeDecodePointTest = TestWithParam<EncodingTestVector>;
467
TEST_P(EcUtilEncodeDecodePointTest,EcPointEncode)468 TEST_P(EcUtilEncodeDecodePointTest, EcPointEncode) {
469 const EncodingTestVector& test = GetParam();
470 util::StatusOr<SslUniquePtr<EC_POINT>> point =
471 GetEcPoint(test.curve, absl::HexStringToBytes(test.x_hex),
472 absl::HexStringToBytes(test.y_hex));
473 ASSERT_THAT(point, IsOk());
474
475 util::StatusOr<std::string> encoded_point =
476 EcPointEncode(test.curve, test.format, point->get());
477 ASSERT_THAT(encoded_point, IsOk());
478 EXPECT_EQ(test.encoded_hex, absl::BytesToHexString(*encoded_point));
479 }
480
TEST_P(EcUtilEncodeDecodePointTest,EcPointDecode)481 TEST_P(EcUtilEncodeDecodePointTest, EcPointDecode) {
482 const EncodingTestVector& test = GetParam();
483 // Get the test point and its encoded version.
484 util::StatusOr<SslUniquePtr<EC_POINT>> point =
485 GetEcPoint(test.curve, absl::HexStringToBytes(test.x_hex),
486 absl::HexStringToBytes(test.y_hex));
487 ASSERT_THAT(point, IsOk());
488 std::string encoded_str = absl::HexStringToBytes(test.encoded_hex);
489
490 util::StatusOr<SslUniquePtr<EC_GROUP>> ec_group =
491 EcGroupFromCurveType(test.curve);
492 util::StatusOr<SslUniquePtr<EC_POINT>> ec_point =
493 EcPointDecode(test.curve, test.format, encoded_str);
494 ASSERT_THAT(ec_point, IsOk());
495 EXPECT_EQ(EC_POINT_cmp(ec_group->get(), point->get(), ec_point->get(),
496 /*ctx=*/nullptr),
497 0);
498
499 // Modifying the 1st byte decoding fails.
500 encoded_str[0] = '0';
501 util::StatusOr<SslUniquePtr<EC_POINT>> ec_point2 =
502 EcPointDecode(test.curve, test.format, encoded_str);
503 EXPECT_THAT(ec_point2, Not(IsOk()));
504 if (test.format == EcPointFormat::UNCOMPRESSED ||
505 test.format == EcPointFormat::COMPRESSED) {
506 EXPECT_THAT(std::string(ec_point2.status().message()),
507 HasSubstr("point should start with"));
508 }
509 }
510
511 INSTANTIATE_TEST_SUITE_P(
512 EcUtilEncodeDecodePointTests, EcUtilEncodeDecodePointTest,
513 ValuesIn(GetEncodingTestVectors()),
__anon457384850202(const TestParamInfo<EcUtilEncodeDecodePointTest::ParamType>& info) 514 [](const TestParamInfo<EcUtilEncodeDecodePointTest::ParamType>& info) {
515 switch (info.param.format) {
516 case EcPointFormat::UNCOMPRESSED:
517 return "Uncompressed";
518 case EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED:
519 return "DoNotUseCrunchyUncompressed";
520 case EcPointFormat::COMPRESSED:
521 return "Compressed";
522 default:
523 return "Unknown";
524 }
525 });
526
TEST(EcUtilTest,EcFieldSizeInBytes)527 TEST(EcUtilTest, EcFieldSizeInBytes) {
528 EXPECT_THAT(EcFieldSizeInBytes(EllipticCurveType::NIST_P256),
529 IsOkAndHolds(256 / 8));
530 EXPECT_THAT(EcFieldSizeInBytes(EllipticCurveType::NIST_P384),
531 IsOkAndHolds(384 / 8));
532 EXPECT_THAT(EcFieldSizeInBytes(EllipticCurveType::NIST_P521),
533 IsOkAndHolds((521 + 7) / 8));
534 EXPECT_THAT(EcFieldSizeInBytes(EllipticCurveType::CURVE25519),
535 IsOkAndHolds(256 / 8));
536 EXPECT_THAT(EcFieldSizeInBytes(EllipticCurveType::UNKNOWN_CURVE).status(),
537 Not(IsOk()));
538 }
539
TEST(EcUtilTest,EcPointEncodingSizeInBytes)540 TEST(EcUtilTest, EcPointEncodingSizeInBytes) {
541 EXPECT_THAT(EcPointEncodingSizeInBytes(EllipticCurveType::NIST_P256,
542 EcPointFormat::UNCOMPRESSED),
543 IsOkAndHolds(2 * (256 / 8) + 1));
544 EXPECT_THAT(EcPointEncodingSizeInBytes(EllipticCurveType::NIST_P256,
545 EcPointFormat::COMPRESSED),
546 IsOkAndHolds(256 / 8 + 1));
547 EXPECT_THAT(EcPointEncodingSizeInBytes(EllipticCurveType::NIST_P384,
548 EcPointFormat::UNCOMPRESSED),
549 IsOkAndHolds(2 * (384 / 8) + 1));
550 EXPECT_THAT(EcPointEncodingSizeInBytes(EllipticCurveType::NIST_P384,
551 EcPointFormat::COMPRESSED),
552 IsOkAndHolds(384 / 8 + 1));
553 EXPECT_THAT(EcPointEncodingSizeInBytes(EllipticCurveType::NIST_P521,
554 EcPointFormat::UNCOMPRESSED),
555 IsOkAndHolds(2 * ((521 + 7) / 8) + 1));
556 EXPECT_THAT(EcPointEncodingSizeInBytes(EllipticCurveType::NIST_P521,
557 EcPointFormat::COMPRESSED),
558 IsOkAndHolds((521 + 7) / 8 + 1));
559 EXPECT_THAT(EcPointEncodingSizeInBytes(EllipticCurveType::CURVE25519,
560 EcPointFormat::COMPRESSED),
561 IsOkAndHolds(256 / 8));
562
563 EXPECT_THAT(EcPointEncodingSizeInBytes(EllipticCurveType::NIST_P256,
564 EcPointFormat::UNKNOWN_FORMAT)
565 .status(),
566 Not(IsOk()));
567 }
568
TEST(EcUtilTest,CurveTypeFromEcGroupSuccess)569 TEST(EcUtilTest, CurveTypeFromEcGroupSuccess) {
570 EC_GROUP* p256_group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
571 EC_GROUP* p384_group = EC_GROUP_new_by_curve_name(NID_secp384r1);
572 EC_GROUP* p521_group = EC_GROUP_new_by_curve_name(NID_secp521r1);
573
574 util::StatusOr<EllipticCurveType> p256_curve =
575 CurveTypeFromEcGroup(p256_group);
576 util::StatusOr<EllipticCurveType> p384_curve =
577 CurveTypeFromEcGroup(p384_group);
578 util::StatusOr<EllipticCurveType> p521_curve =
579 CurveTypeFromEcGroup(p521_group);
580
581 ASSERT_THAT(p256_curve, IsOkAndHolds(EllipticCurveType::NIST_P256));
582 ASSERT_THAT(p384_curve, IsOkAndHolds(EllipticCurveType::NIST_P384));
583 ASSERT_THAT(p521_curve, IsOkAndHolds(EllipticCurveType::NIST_P521));
584 }
585
TEST(EcUtilTest,CurveTypeFromEcGroupUnimplemented)586 TEST(EcUtilTest, CurveTypeFromEcGroupUnimplemented) {
587 EXPECT_THAT(
588 CurveTypeFromEcGroup(EC_GROUP_new_by_curve_name(NID_secp224r1)).status(),
589 StatusIs(absl::StatusCode::kUnimplemented));
590 }
591
TEST(EcUtilTest,EcGroupFromCurveTypeSuccess)592 TEST(EcUtilTest, EcGroupFromCurveTypeSuccess) {
593 util::StatusOr<SslUniquePtr<EC_GROUP>> p256_curve =
594 EcGroupFromCurveType(EllipticCurveType::NIST_P256);
595 util::StatusOr<SslUniquePtr<EC_GROUP>> p384_curve =
596 EcGroupFromCurveType(EllipticCurveType::NIST_P384);
597 util::StatusOr<SslUniquePtr<EC_GROUP>> p521_curve =
598 EcGroupFromCurveType(EllipticCurveType::NIST_P521);
599 ASSERT_THAT(p256_curve, IsOk());
600 ASSERT_THAT(p384_curve, IsOk());
601 ASSERT_THAT(p521_curve, IsOk());
602
603 SslUniquePtr<EC_GROUP> ssl_p256_group(
604 EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
605 SslUniquePtr<EC_GROUP> ssl_p384_group(
606 EC_GROUP_new_by_curve_name(NID_secp384r1));
607 SslUniquePtr<EC_GROUP> ssl_p521_group(
608 EC_GROUP_new_by_curve_name(NID_secp521r1));
609
610 EXPECT_EQ(
611 EC_GROUP_cmp(p256_curve->get(), ssl_p256_group.get(), /*ctx=*/nullptr),
612 0);
613 EXPECT_EQ(
614 EC_GROUP_cmp(p384_curve->get(), ssl_p384_group.get(), /*ctx=*/nullptr),
615 0);
616 EXPECT_EQ(
617 EC_GROUP_cmp(p521_curve->get(), ssl_p521_group.get(), /*ctx=*/nullptr),
618 0);
619 }
620
TEST(EcUtilTest,EcGroupFromCurveTypeUnimplemented)621 TEST(EcUtilTest, EcGroupFromCurveTypeUnimplemented) {
622 EXPECT_THAT(EcGroupFromCurveType(EllipticCurveType::UNKNOWN_CURVE).status(),
623 StatusIs(absl::StatusCode::kUnimplemented));
624 }
625
TEST(EcUtilTest,GetEcPointReturnsAValidPoint)626 TEST(EcUtilTest, GetEcPointReturnsAValidPoint) {
627 SslUniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(NID_secp521r1));
628 const unsigned int kCurveSizeInBytes =
629 (EC_GROUP_get_degree(group.get()) + 7) / 8;
630
631 constexpr absl::string_view kXCoordinateHex =
632 "00093057fb862f2ad2e82e581baeb3324e7b32946f2ba845a9beeed87d6995f54918ec6"
633 "619b9931955d5a89d4d74adf1046bb362192f2ef6bd3e3d2d04dd1f87054a";
634 constexpr absl::string_view kYCoordinateHex =
635 "00aa3fb2448335f694e3cda4ae0cc71b1b2f2a206fa802d7262f19983c44674fe15327a"
636 "caac1fa40424c395a6556cb8167312527fae5865ecffc14bbdc17da78cdcf";
637 util::StatusOr<SslUniquePtr<EC_POINT>> point = GetEcPoint(
638 EllipticCurveType::NIST_P521, absl::HexStringToBytes(kXCoordinateHex),
639 absl::HexStringToBytes(kYCoordinateHex));
640 ASSERT_THAT(point, IsOk());
641
642 // We check that we can decode this point and the result is the same as the
643 // original coordinates.
644 std::string xy;
645 subtle::ResizeStringUninitialized(&xy, 2 * kCurveSizeInBytes);
646 SslUniquePtr<BIGNUM> x(BN_new());
647 SslUniquePtr<BIGNUM> y(BN_new());
648 ASSERT_EQ(EC_POINT_get_affine_coordinates(group.get(), point->get(), x.get(),
649 y.get(), /*ctx=*/nullptr),
650 1);
651 ASSERT_THAT(
652 BignumToBinaryPadded(absl::MakeSpan(&xy[0], kCurveSizeInBytes), x.get()),
653 IsOk());
654 ASSERT_THAT(
655 BignumToBinaryPadded(
656 absl::MakeSpan(&xy[kCurveSizeInBytes], kCurveSizeInBytes), y.get()),
657 IsOk());
658 EXPECT_EQ(xy, absl::StrCat(absl::HexStringToBytes(kXCoordinateHex),
659 absl::HexStringToBytes(kYCoordinateHex)));
660 }
661
TEST(EcUtilTest,EcSignatureIeeeToDer)662 TEST(EcUtilTest, EcSignatureIeeeToDer) {
663 std::unique_ptr<rapidjson::Document> test_vectors =
664 WycheproofUtil::ReadTestVectors("ecdsa_webcrypto_test.json");
665 ASSERT_THAT(test_vectors, Not(IsNull()));
666 for (const auto& test_group : (*test_vectors)["testGroups"].GetArray()) {
667 EllipticCurveType curve =
668 WycheproofUtil::GetEllipticCurveType(test_group["key"]["curve"]);
669 if (curve == EllipticCurveType::UNKNOWN_CURVE) {
670 continue;
671 }
672 util::StatusOr<SslUniquePtr<EC_GROUP>> ec_group =
673 EcGroupFromCurveType(curve);
674 ASSERT_THAT(ec_group, IsOk());
675 // Read all the valid signatures.
676 for (const auto& test : test_group["tests"].GetArray()) {
677 std::string result = test["result"].GetString();
678 if (result != "valid") {
679 continue;
680 }
681 std::string sig = WycheproofUtil::GetBytes(test["sig"]);
682 util::StatusOr<std::string> der_encoded =
683 EcSignatureIeeeToDer(ec_group->get(), sig);
684 ASSERT_THAT(der_encoded, IsOk());
685
686 // Make sure we can reconstruct the IEEE format: [ s || r ].
687 const uint8_t* der_sig_data_ptr =
688 reinterpret_cast<const uint8_t*>(der_encoded->data());
689 SslUniquePtr<ECDSA_SIG> ecdsa_sig(d2i_ECDSA_SIG(
690 /*out=*/nullptr, &der_sig_data_ptr, der_encoded->size()));
691 ASSERT_THAT(ecdsa_sig, Not(IsNull()));
692 // Owned by OpenSSL/BoringSSL.
693 const BIGNUM* r;
694 const BIGNUM* s;
695 ECDSA_SIG_get0(ecdsa_sig.get(), &r, &s);
696 ASSERT_THAT(r, Not(IsNull()));
697 ASSERT_THAT(s, Not(IsNull()));
698
699 util::StatusOr<int32_t> field_size = EcFieldSizeInBytes(curve);
700 ASSERT_THAT(field_size, IsOk());
701 util::StatusOr<std::string> r_str = BignumToString(r, *field_size);
702 ASSERT_THAT(r_str, IsOk());
703 util::StatusOr<std::string> s_str = BignumToString(s, *field_size);
704 ASSERT_THAT(s_str, IsOk());
705 EXPECT_EQ(absl::StrCat(*r_str, *s_str), sig);
706 }
707 }
708 }
709
710 // ECDH test vector.
711 struct EcdhWycheproofTestVector {
712 std::string testcase_name;
713 EllipticCurveType curve;
714 std::string id;
715 std::string comment;
716 std::string pub_bytes;
717 std::string priv_bytes;
718 std::string expected_shared_bytes;
719 std::string result;
720 EcPointFormat format;
721 };
722
723 // Utility function to look for a `value` inside an array of flags `flags`.
HasFlag(const rapidjson::Value & flags,absl::string_view value)724 bool HasFlag(const rapidjson::Value& flags, absl::string_view value) {
725 if (!flags.IsArray()) {
726 return false;
727 }
728 for (const rapidjson::Value& flag : flags.GetArray()) {
729 if (std::string(flag.GetString()) == value) {
730 return true;
731 }
732 }
733 return false;
734 }
735
736 // Reads Wycheproof's ECDH test vectors from the given file `file_name`.
ReadEcdhWycheproofTestVectors(absl::string_view file_name)737 std::vector<EcdhWycheproofTestVector> ReadEcdhWycheproofTestVectors(
738 absl::string_view file_name) {
739 std::unique_ptr<rapidjson::Document> root =
740 WycheproofUtil::ReadTestVectors(std::string(file_name));
741 std::vector<EcdhWycheproofTestVector> test_vectors;
742 for (const rapidjson::Value& test_group : (*root)["testGroups"].GetArray()) {
743 // Tink only supports secp256r1, secp384r1 or secp521r1.
744 EllipticCurveType curve =
745 WycheproofUtil::GetEllipticCurveType(test_group["curve"]);
746 if (curve == EllipticCurveType::UNKNOWN_CURVE) {
747 continue;
748 }
749
750 for (const rapidjson::Value& test : test_group["tests"].GetArray()) {
751 // Wycheproof's ECDH public key uses ASN encoding while Tink uses X9.62
752 // format point encoding. For the purpose of testing, we note the
753 // followings:
754 // + The prefix of ASN encoding contains curve name, so we can skip test
755 // vector with "UnnamedCurve".
756 // + The suffix of ASN encoding is X9.62 format point encoding.
757 // TODO(quannguyen): Use X9.62 test vectors once it's available.
758 if (HasFlag(test["flags"], /*value=*/"UnnamedCurve")) {
759 continue;
760 }
761 // Get the format from "flags".
762 EcPointFormat format = EcPointFormat::UNCOMPRESSED;
763 if (HasFlag(test["flags"], /*value=*/"CompressedPoint")) {
764 format = EcPointFormat::COMPRESSED;
765 }
766 // Testcase name is of the form: <file_name_without_extension>_tcid<tcid>.
767 std::vector<std::string> file_name_tokens =
768 absl::StrSplit(file_name, '.');
769 test_vectors.push_back({
770 absl::StrCat(file_name_tokens[0], "_tcid", test["tcId"].GetInt()),
771 curve,
772 absl::StrCat(test["tcId"].GetInt()),
773 test["comment"].GetString(),
774 WycheproofUtil::GetBytes(test["public"]),
775 WycheproofUtil::GetBytes(test["private"]),
776 WycheproofUtil::GetBytes(test["shared"]),
777 test["result"].GetString(),
778 format,
779 });
780 }
781 }
782 return test_vectors;
783 }
784
785 using EcUtilComputeEcdhSharedSecretTest =
786 TestWithParam<EcdhWycheproofTestVector>;
787
TEST_P(EcUtilComputeEcdhSharedSecretTest,ComputeEcdhSharedSecretWycheproof)788 TEST_P(EcUtilComputeEcdhSharedSecretTest, ComputeEcdhSharedSecretWycheproof) {
789 EcdhWycheproofTestVector params = GetParam();
790
791 util::StatusOr<int32_t> point_size =
792 internal::EcPointEncodingSizeInBytes(params.curve, params.format);
793 ASSERT_THAT(point_size, IsOk());
794 if (*point_size > params.pub_bytes.size()) {
795 GTEST_SKIP();
796 }
797
798 std::string pub_bytes = params.pub_bytes.substr(
799 params.pub_bytes.size() - *point_size, *point_size);
800
801 util::StatusOr<SslUniquePtr<EC_POINT>> pub_key =
802 EcPointDecode(params.curve, params.format, pub_bytes);
803 if (!pub_key.ok()) {
804 // Make sure we didn't fail decoding a valid point, then we can terminate
805 // testing;
806 ASSERT_NE(params.result, "valid");
807 return;
808 }
809
810 util::StatusOr<SslUniquePtr<BIGNUM>> priv_key =
811 StringToBignum(params.priv_bytes);
812 ASSERT_THAT(priv_key, IsOk());
813
814 util::StatusOr<util::SecretData> shared_secret =
815 ComputeEcdhSharedSecret(params.curve, priv_key->get(), pub_key->get());
816
817 if (params.result == "invalid") {
818 EXPECT_THAT(shared_secret, Not(IsOk()));
819 } else {
820 EXPECT_THAT(shared_secret, IsOkAndHolds(util::SecretDataFromStringView(
821 params.expected_shared_bytes)));
822 }
823 }
824
GetEcUtilComputeEcdhSharedSecretParams()825 std::vector<EcdhWycheproofTestVector> GetEcUtilComputeEcdhSharedSecretParams() {
826 std::vector<EcdhWycheproofTestVector> test_vectors =
827 ReadEcdhWycheproofTestVectors(
828 /*file_name=*/"ecdh_secp256r1_test.json");
829 std::vector<EcdhWycheproofTestVector> others = ReadEcdhWycheproofTestVectors(
830 /*file_name=*/"ecdh_secp384r1_test.json");
831 test_vectors.insert(test_vectors.end(), others.begin(), others.end());
832 others = ReadEcdhWycheproofTestVectors(
833 /*file_name=*/"ecdh_secp521r1_test.json");
834 test_vectors.insert(test_vectors.end(), others.begin(), others.end());
835 others = ReadEcdhWycheproofTestVectors(
836 /*file_name=*/"ecdh_test.json");
837 test_vectors.insert(test_vectors.end(), others.begin(), others.end());
838 return test_vectors;
839 }
840
841 INSTANTIATE_TEST_SUITE_P(
842 EcUtilComputeEcdhSharedSecretTests, EcUtilComputeEcdhSharedSecretTest,
843 ValuesIn(GetEcUtilComputeEcdhSharedSecretParams()),
844 [](const TestParamInfo<EcUtilComputeEcdhSharedSecretTest::ParamType>&
__anon457384850302(const TestParamInfo<EcUtilComputeEcdhSharedSecretTest::ParamType>& info) 845 info) { return info.param.testcase_name; });
846
847 } // namespace
848 } // namespace internal
849 } // namespace tink
850 } // namespace crypto
851