ecdh_test.cc
Go to the documentation of this file.
1 /* Copyright (c) 2016, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include <stdio.h>
16 
17 #include <utility>
18 #include <vector>
19 
20 #include <gtest/gtest.h>
21 
22 #include <openssl/bn.h>
23 #include <openssl/bytestring.h>
24 #include <openssl/crypto.h>
25 #include <openssl/ec.h>
26 #include <openssl/ec_key.h>
27 #include <openssl/ecdh.h>
28 #include <openssl/err.h>
29 #include <openssl/evp.h>
30 #include <openssl/nid.h>
31 #include <openssl/sha.h>
32 
33 #include "../test/file_test.h"
34 #include "../test/test_util.h"
35 #include "../test/wycheproof_util.h"
36 
37 
38 static bssl::UniquePtr<EC_GROUP> GetCurve(FileTest *t, const char *key) {
39  std::string curve_name;
40  if (!t->GetAttribute(&curve_name, key)) {
41  return nullptr;
42  }
43 
44  if (curve_name == "P-224") {
45  return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp224r1));
46  }
47  if (curve_name == "P-256") {
48  return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(
50  }
51  if (curve_name == "P-384") {
52  return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp384r1));
53  }
54  if (curve_name == "P-521") {
55  return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp521r1));
56  }
57 
58  t->PrintLine("Unknown curve '%s'", curve_name.c_str());
59  return nullptr;
60 }
61 
62 static bssl::UniquePtr<BIGNUM> GetBIGNUM(FileTest *t, const char *key) {
63  std::vector<uint8_t> bytes;
64  if (!t->GetBytes(&bytes, key)) {
65  return nullptr;
66  }
67 
68  return bssl::UniquePtr<BIGNUM>(BN_bin2bn(bytes.data(), bytes.size(), nullptr));
69 }
70 
71 TEST(ECDHTest, TestVectors) {
72  FileTestGTest("crypto/ecdh_extra/ecdh_tests.txt", [](FileTest *t) {
73  bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
75  bssl::UniquePtr<BIGNUM> priv_key = GetBIGNUM(t, "Private");
76  ASSERT_TRUE(priv_key);
77  bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
78  ASSERT_TRUE(x);
79  bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
80  ASSERT_TRUE(y);
81  bssl::UniquePtr<BIGNUM> peer_x = GetBIGNUM(t, "PeerX");
82  ASSERT_TRUE(peer_x);
83  bssl::UniquePtr<BIGNUM> peer_y = GetBIGNUM(t, "PeerY");
84  ASSERT_TRUE(peer_y);
85  std::vector<uint8_t> z;
86  ASSERT_TRUE(t->GetBytes(&z, "Z"));
87 
88  bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
90  bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get()));
91  ASSERT_TRUE(pub_key);
92  bssl::UniquePtr<EC_POINT> peer_pub_key(EC_POINT_new(group.get()));
93  ASSERT_TRUE(peer_pub_key);
94  ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
95  ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
97  x.get(), y.get(), nullptr));
99  group.get(), peer_pub_key.get(), peer_x.get(), peer_y.get(), nullptr));
100  ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
102 
103  std::vector<uint8_t> actual_z;
104  // Make |actual_z| larger than expected to ensure |ECDH_compute_key| returns
105  // the right amount of data.
106  actual_z.resize(z.size() + 1);
107  int ret = ECDH_compute_key(actual_z.data(), actual_z.size(),
108  peer_pub_key.get(), key.get(), nullptr);
109  ASSERT_GE(ret, 0);
110  EXPECT_EQ(Bytes(z), Bytes(actual_z.data(), static_cast<size_t>(ret)));
111 
112  // Test |ECDH_compute_key| truncates.
113  actual_z.resize(z.size() - 1);
114  ret = ECDH_compute_key(actual_z.data(), actual_z.size(), peer_pub_key.get(),
115  key.get(), nullptr);
116  ASSERT_GE(ret, 0);
117  EXPECT_EQ(Bytes(z.data(), z.size() - 1),
118  Bytes(actual_z.data(), static_cast<size_t>(ret)));
119 
120  // Test that |ECDH_compute_key_fips| hashes as expected.
121  uint8_t digest[SHA256_DIGEST_LENGTH], expected_digest[SHA256_DIGEST_LENGTH];
122  ASSERT_TRUE(ECDH_compute_key_fips(digest, sizeof(digest),
123  peer_pub_key.get(), key.get()));
124  SHA256(z.data(), z.size(), expected_digest);
125  EXPECT_EQ(Bytes(digest), Bytes(expected_digest));
126  });
127 }
128 
129 
130 static void RunWycheproofTest(FileTest *t) {
131  t->IgnoreInstruction("encoding");
132 
133  bssl::UniquePtr<EC_GROUP> group = GetWycheproofCurve(t, "curve", true);
135  bssl::UniquePtr<BIGNUM> priv_key = GetWycheproofBIGNUM(t, "private", false);
136  ASSERT_TRUE(priv_key);
137  std::vector<uint8_t> peer_spki;
138  ASSERT_TRUE(t->GetBytes(&peer_spki, "public"));
141  std::vector<uint8_t> shared;
142  ASSERT_TRUE(t->GetBytes(&shared, "shared"));
143  // BoringSSL supports compressed coordinates.
144  bool is_valid = result.IsValid({"CompressedPoint"});
145 
146  // Wycheproof stores the peer key in an SPKI to mimic a Java API mistake.
147  // This is non-standard and error-prone.
148  CBS cbs;
149  CBS_init(&cbs, peer_spki.data(), peer_spki.size());
150  bssl::UniquePtr<EVP_PKEY> peer_evp(EVP_parse_public_key(&cbs));
151  if (!peer_evp || CBS_len(&cbs) != 0) {
152  EXPECT_FALSE(is_valid);
153  return;
154  }
155  EC_KEY *peer_ec = EVP_PKEY_get0_EC_KEY(peer_evp.get());
156  ASSERT_TRUE(peer_ec);
157 
158  bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
159  ASSERT_TRUE(key);
160  ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
161  ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
162 
163  std::vector<uint8_t> actual((EC_GROUP_get_degree(group.get()) + 7) / 8);
164  int ret =
165  ECDH_compute_key(actual.data(), actual.size(),
166  EC_KEY_get0_public_key(peer_ec), key.get(), nullptr);
167  if (is_valid) {
168  EXPECT_EQ(static_cast<int>(actual.size()), ret);
169  EXPECT_EQ(Bytes(shared), Bytes(actual.data(), static_cast<size_t>(ret)));
170  } else {
171  EXPECT_EQ(-1, ret);
172  }
173 }
174 
175 TEST(ECDHTest, WycheproofP224) {
176  FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp224r1_test.txt",
178 }
179 
180 TEST(ECDHTest, WycheproofP256) {
181  FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp256r1_test.txt",
183 }
184 
185 TEST(ECDHTest, WycheproofP384) {
186  FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp384r1_test.txt",
188 }
189 
190 TEST(ECDHTest, WycheproofP512) {
191  FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp521r1_test.txt",
193 }
194 
195 // MakeCustomGroup returns an |EC_GROUP| containing a non-standard group. (P-256
196 // with the wrong generator.)
197 static bssl::UniquePtr<EC_GROUP> MakeCustomGroup() {
198  static const uint8_t kP[] = {
199  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
200  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
201  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
202  };
203  static const uint8_t kA[] = {
204  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
205  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
206  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
207  };
208  static const uint8_t kB[] = {
209  0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd,
210  0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53,
211  0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b,
212  };
213  static const uint8_t kX[] = {
214  0xe6, 0x2b, 0x69, 0xe2, 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e,
215  0x0d, 0x94, 0x8a, 0x4c, 0xd5, 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d,
216  0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, 0x9d, 0xdc, 0xba, 0x5a,
217  };
218  static const uint8_t kY[] = {
219  0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, 0xc3, 0xc4, 0xa3,
220  0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, 0x1c, 0xf5,
221  0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1,
222  };
223  static const uint8_t kOrder[] = {
224  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
225  0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17,
226  0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
227  };
228  bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
229  bssl::UniquePtr<BIGNUM> p(BN_bin2bn(kP, sizeof(kP), nullptr));
230  bssl::UniquePtr<BIGNUM> a(BN_bin2bn(kA, sizeof(kA), nullptr));
231  bssl::UniquePtr<BIGNUM> b(BN_bin2bn(kB, sizeof(kB), nullptr));
232  bssl::UniquePtr<BIGNUM> x(BN_bin2bn(kX, sizeof(kX), nullptr));
233  bssl::UniquePtr<BIGNUM> y(BN_bin2bn(kY, sizeof(kY), nullptr));
234  bssl::UniquePtr<BIGNUM> order(BN_bin2bn(kOrder, sizeof(kOrder), nullptr));
235  if (!ctx || !p || !a || !b || !x || !y || !order) {
236  return nullptr;
237  }
238  bssl::UniquePtr<EC_GROUP> group(
239  EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
240  if (!group) {
241  return nullptr;
242  }
243  bssl::UniquePtr<EC_POINT> generator(EC_POINT_new(group.get()));
244  if (!generator ||
245  !EC_POINT_set_affine_coordinates_GFp(group.get(), generator.get(),
246  x.get(), y.get(), ctx.get()) ||
247  !EC_GROUP_set_generator(group.get(), generator.get(), order.get(),
248  BN_value_one())) {
249  return nullptr;
250  }
251  return group;
252 }
253 
254 TEST(ECDHTest, GroupMismatch) {
255  const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
256  std::vector<EC_builtin_curve> curves(num_curves);
257  EC_get_builtin_curves(curves.data(), num_curves);
258 
259  // Instantiate all the built-in curves.
260  std::vector<bssl::UniquePtr<EC_GROUP>> groups;
261  for (const auto &curve : curves) {
262  groups.emplace_back(EC_GROUP_new_by_curve_name(curve.nid));
263  ASSERT_TRUE(groups.back());
264  }
265 
266  // Also create some arbitrary group. (This is P-256 with the wrong generator.)
267  groups.push_back(MakeCustomGroup());
268  ASSERT_TRUE(groups.back());
269 
270  for (const auto &a : groups) {
271  for (const auto &b : groups) {
272  if (a.get() == b.get()) {
273  continue;
274  }
275 
276  bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
277  ASSERT_TRUE(EC_KEY_set_group(key.get(), a.get()));
279 
280  // ECDH across the groups should not work.
281  char out[64];
282  const EC_POINT *peer = EC_GROUP_get0_generator(b.get());
283  EXPECT_EQ(-1,
284  ECDH_compute_key(out, sizeof(out), peer, key.get(), nullptr));
285  ERR_clear_error();
286  }
287  }
288 }
EXPECT_FALSE
#define EXPECT_FALSE(condition)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:1970
_gevent_test_main.result
result
Definition: _gevent_test_main.py:96
bn.h
EC_POINT_new
#define EC_POINT_new
Definition: boringssl_prefix_symbols.h:1384
gen_build_yaml.out
dictionary out
Definition: src/benchmark/gen_build_yaml.py:24
Bytes
Definition: boringssl-with-bazel/src/crypto/test/test_util.h:38
cbs_st
Definition: bytestring.h:39
EC_KEY_new
#define EC_KEY_new
Definition: boringssl_prefix_symbols.h:1355
ctx
Definition: benchmark-async.c:30
NID_X9_62_prime256v1
#define NID_X9_62_prime256v1
Definition: nid.h:1914
GetCurve
static bssl::UniquePtr< EC_GROUP > GetCurve(FileTest *t, const char *key)
Definition: ecdh_test.cc:38
EC_KEY_generate_key
#define EC_KEY_generate_key
Definition: boringssl_prefix_symbols.h:1342
evp.h
WycheproofResult
Definition: wycheproof_util.h:34
y
const double y
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3611
BN_bin2bn
#define BN_bin2bn
Definition: boringssl_prefix_symbols.h:900
ECDH_compute_key_fips
#define ECDH_compute_key_fips
Definition: boringssl_prefix_symbols.h:1297
EC_KEY_set_group
#define EC_KEY_set_group
Definition: boringssl_prefix_symbols.h:1365
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
EC_KEY_set_public_key
#define EC_KEY_set_public_key
Definition: boringssl_prefix_symbols.h:1367
ecdh.h
ASSERT_GE
#define ASSERT_GE(val1, val2)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:2072
CBS_len
#define CBS_len
Definition: boringssl_prefix_symbols.h:1089
RunWycheproofTest
static void RunWycheproofTest(FileTest *t)
Definition: ecdh_test.cc:130
ctx
static struct test_ctx ctx
Definition: test-ipc-send-recv.c:65
EC_GROUP_new_by_curve_name
#define EC_GROUP_new_by_curve_name
Definition: boringssl_prefix_symbols.h:1331
a
int a
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:88
cbs
const CBS * cbs
Definition: third_party/boringssl-with-bazel/src/crypto/trust_token/internal.h:107
xds_manager.p
p
Definition: xds_manager.py:60
CBS_init
#define CBS_init
Definition: boringssl_prefix_symbols.h:1085
z
Uncopyable z
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3612
uint8_t
unsigned char uint8_t
Definition: stdint-msvc2008.h:78
BN_value_one
const OPENSSL_EXPORT BIGNUM * BN_value_one(void)
EXPECT_EQ
#define EXPECT_EQ(a, b)
Definition: iomgr/time_averaged_stats_test.cc:27
EC_GROUP_set_generator
#define EC_GROUP_set_generator
Definition: boringssl_prefix_symbols.h:1335
FileTestGTest
void FileTestGTest(const char *path, std::function< void(FileTest *)> run_test)
Definition: file_test_gtest.cc:68
kOrder
static const scalar kOrder
Definition: spake25519.c:324
bytestring.h
GetWycheproofCurve
bssl::UniquePtr< EC_GROUP > GetWycheproofCurve(FileTest *t, const char *key, bool instruction)
Definition: wycheproof_util.cc:108
EC_POINT_set_affine_coordinates_GFp
#define EC_POINT_set_affine_coordinates_GFp
Definition: boringssl_prefix_symbols.h:1389
NID_secp521r1
#define NID_secp521r1
Definition: nid.h:3172
sha.h
FileTest
Definition: file_test.h:90
GetWycheproofResult
bool GetWycheproofResult(FileTest *t, WycheproofResult *out)
Definition: wycheproof_util.cc:49
err.h
crypto.h
EC_KEY_get0_public_key
#define EC_KEY_get0_public_key
Definition: boringssl_prefix_symbols.h:1346
ec_key.h
x
int x
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3610
BN_CTX_new
#define BN_CTX_new
Definition: boringssl_prefix_symbols.h:885
EC_GROUP_get_degree
#define EC_GROUP_get_degree
Definition: boringssl_prefix_symbols.h:1328
b
uint64_t b
Definition: abseil-cpp/absl/container/internal/layout_test.cc:53
TEST
TEST(ECDHTest, TestVectors)
Definition: ecdh_test.cc:71
GetBIGNUM
static bssl::UniquePtr< BIGNUM > GetBIGNUM(FileTest *t, const char *key)
Definition: ecdh_test.cc:62
ec_key_st
Definition: third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/internal.h:723
SHA256
#define SHA256
Definition: boringssl_prefix_symbols.h:2154
EC_get_builtin_curves
#define EC_get_builtin_curves
Definition: boringssl_prefix_symbols.h:1394
nid.h
make_dist_html.groups
list groups
Definition: make_dist_html.py:120
key
const char * key
Definition: hpack_parser_table.cc:164
upload.group
group
Definition: bloaty/third_party/googletest/googlemock/scripts/upload.py:397
bytes
uint8 bytes[10]
Definition: bloaty/third_party/protobuf/src/google/protobuf/io/coded_stream_unittest.cc:153
EVP_PKEY_get0_EC_KEY
#define EVP_PKEY_get0_EC_KEY
Definition: boringssl_prefix_symbols.h:1629
ec_point_st
Definition: third_party/boringssl-with-bazel/src/crypto/fipsmodule/ec/internal.h:615
ret
UniquePtr< SSL_SESSION > ret
Definition: ssl_x509.cc:1029
GetWycheproofBIGNUM
bssl::UniquePtr< BIGNUM > GetWycheproofBIGNUM(FileTest *t, const char *key, bool instruction)
Definition: wycheproof_util.cc:132
SHA256_DIGEST_LENGTH
#define SHA256_DIGEST_LENGTH
Definition: sha.h:155
EVP_parse_public_key
#define EVP_parse_public_key
Definition: boringssl_prefix_symbols.h:1743
EC_KEY_check_key
#define EC_KEY_check_key
Definition: boringssl_prefix_symbols.h:1338
ASSERT_TRUE
#define ASSERT_TRUE(condition)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:1973
EC_KEY_set_private_key
#define EC_KEY_set_private_key
Definition: boringssl_prefix_symbols.h:1366
ERR_clear_error
#define ERR_clear_error
Definition: boringssl_prefix_symbols.h:1413
NID_secp224r1
#define NID_secp224r1
Definition: nid.h:3160
MakeCustomGroup
static bssl::UniquePtr< EC_GROUP > MakeCustomGroup()
Definition: ecdh_test.cc:197
ECDH_compute_key
#define ECDH_compute_key
Definition: boringssl_prefix_symbols.h:1296
NID_secp384r1
#define NID_secp384r1
Definition: nid.h:3168
ec.h
EC_GROUP_get0_generator
#define EC_GROUP_get0_generator
Definition: boringssl_prefix_symbols.h:1322
EC_GROUP_new_curve_GFp
#define EC_GROUP_new_curve_GFp
Definition: boringssl_prefix_symbols.h:1332
cbs_st::data
const uint8_t * data
Definition: bytestring.h:40


grpc
Author(s):
autogenerated on Fri May 16 2025 02:58:18