xref: /aosp_15_r20/external/boringssl/src/crypto/ecdh_extra/ecdh_test.cc (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
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 
GetCurve(FileTest * t,const char * key)38 static const 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 EC_group_p224();
46   }
47   if (curve_name == "P-256") {
48     return EC_group_p256();
49   }
50   if (curve_name == "P-384") {
51     return EC_group_p384();
52   }
53   if (curve_name == "P-521") {
54     return EC_group_p521();
55   }
56 
57   t->PrintLine("Unknown curve '%s'", curve_name.c_str());
58   return nullptr;
59 }
60 
GetBIGNUM(FileTest * t,const char * key)61 static bssl::UniquePtr<BIGNUM> GetBIGNUM(FileTest *t, const char *key) {
62   std::vector<uint8_t> bytes;
63   if (!t->GetBytes(&bytes, key)) {
64     return nullptr;
65   }
66 
67   return bssl::UniquePtr<BIGNUM>(BN_bin2bn(bytes.data(), bytes.size(), nullptr));
68 }
69 
TEST(ECDHTest,TestVectors)70 TEST(ECDHTest, TestVectors) {
71   FileTestGTest("crypto/ecdh_extra/ecdh_tests.txt", [](FileTest *t) {
72     const EC_GROUP *group = GetCurve(t, "Curve");
73     ASSERT_TRUE(group);
74     bssl::UniquePtr<BIGNUM> priv_key = GetBIGNUM(t, "Private");
75     ASSERT_TRUE(priv_key);
76     bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
77     ASSERT_TRUE(x);
78     bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
79     ASSERT_TRUE(y);
80     bssl::UniquePtr<BIGNUM> peer_x = GetBIGNUM(t, "PeerX");
81     ASSERT_TRUE(peer_x);
82     bssl::UniquePtr<BIGNUM> peer_y = GetBIGNUM(t, "PeerY");
83     ASSERT_TRUE(peer_y);
84     std::vector<uint8_t> z;
85     ASSERT_TRUE(t->GetBytes(&z, "Z"));
86 
87     bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
88     ASSERT_TRUE(key);
89     bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group));
90     ASSERT_TRUE(pub_key);
91     bssl::UniquePtr<EC_POINT> peer_pub_key(EC_POINT_new(group));
92     ASSERT_TRUE(peer_pub_key);
93     ASSERT_TRUE(EC_KEY_set_group(key.get(), group));
94     ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
95     ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(group, pub_key.get(),
96                                                     x.get(), y.get(), nullptr));
97     ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
98         group, peer_pub_key.get(), peer_x.get(), peer_y.get(), nullptr));
99     ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
100     ASSERT_TRUE(EC_KEY_check_key(key.get()));
101 
102     std::vector<uint8_t> actual_z;
103     // Make |actual_z| larger than expected to ensure |ECDH_compute_key| returns
104     // the right amount of data.
105     actual_z.resize(z.size() + 1);
106     int ret = ECDH_compute_key(actual_z.data(), actual_z.size(),
107                                peer_pub_key.get(), key.get(), nullptr);
108     ASSERT_GE(ret, 0);
109     EXPECT_EQ(Bytes(z), Bytes(actual_z.data(), static_cast<size_t>(ret)));
110 
111     // Test |ECDH_compute_key| truncates.
112     actual_z.resize(z.size() - 1);
113     ret = ECDH_compute_key(actual_z.data(), actual_z.size(), peer_pub_key.get(),
114                            key.get(), nullptr);
115     ASSERT_GE(ret, 0);
116     EXPECT_EQ(Bytes(z.data(), z.size() - 1),
117               Bytes(actual_z.data(), static_cast<size_t>(ret)));
118 
119     // Test that |ECDH_compute_key_fips| hashes as expected.
120     uint8_t digest[SHA256_DIGEST_LENGTH], expected_digest[SHA256_DIGEST_LENGTH];
121     ASSERT_TRUE(ECDH_compute_key_fips(digest, sizeof(digest),
122                                       peer_pub_key.get(), key.get()));
123     SHA256(z.data(), z.size(), expected_digest);
124     EXPECT_EQ(Bytes(digest), Bytes(expected_digest));
125   });
126 }
127 
128 
RunWycheproofTest(FileTest * t)129 static void RunWycheproofTest(FileTest *t) {
130   t->IgnoreInstruction("encoding");
131 
132   const EC_GROUP *group = GetWycheproofCurve(t, "curve", true);
133   ASSERT_TRUE(group);
134   bssl::UniquePtr<BIGNUM> priv_key = GetWycheproofBIGNUM(t, "private", false);
135   ASSERT_TRUE(priv_key);
136   std::vector<uint8_t> peer_spki;
137   ASSERT_TRUE(t->GetBytes(&peer_spki, "public"));
138   WycheproofResult result;
139   ASSERT_TRUE(GetWycheproofResult(t, &result));
140   std::vector<uint8_t> shared;
141   ASSERT_TRUE(t->GetBytes(&shared, "shared"));
142   // BoringSSL supports compressed coordinates.
143   bool is_valid = result.IsValid({"CompressedPoint"});
144 
145   // Wycheproof stores the peer key in an SPKI to mimic a Java API mistake.
146   // This is non-standard and error-prone.
147   CBS cbs;
148   CBS_init(&cbs, peer_spki.data(), peer_spki.size());
149   bssl::UniquePtr<EVP_PKEY> peer_evp(EVP_parse_public_key(&cbs));
150   if (!peer_evp || CBS_len(&cbs) != 0) {
151     EXPECT_FALSE(is_valid);
152     return;
153   }
154   EC_KEY *peer_ec = EVP_PKEY_get0_EC_KEY(peer_evp.get());
155   ASSERT_TRUE(peer_ec);
156 
157   bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
158   ASSERT_TRUE(key);
159   ASSERT_TRUE(EC_KEY_set_group(key.get(), group));
160   ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
161 
162   std::vector<uint8_t> actual((EC_GROUP_get_degree(group) + 7) / 8);
163   int ret =
164       ECDH_compute_key(actual.data(), actual.size(),
165                        EC_KEY_get0_public_key(peer_ec), key.get(), nullptr);
166   if (is_valid) {
167     EXPECT_EQ(static_cast<int>(actual.size()), ret);
168     EXPECT_EQ(Bytes(shared), Bytes(actual.data(), static_cast<size_t>(ret)));
169   } else {
170     EXPECT_EQ(-1, ret);
171   }
172 }
173 
TEST(ECDHTest,WycheproofP224)174 TEST(ECDHTest, WycheproofP224) {
175   FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp224r1_test.txt",
176                 RunWycheproofTest);
177 }
178 
TEST(ECDHTest,WycheproofP256)179 TEST(ECDHTest, WycheproofP256) {
180   FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp256r1_test.txt",
181                 RunWycheproofTest);
182 }
183 
TEST(ECDHTest,WycheproofP384)184 TEST(ECDHTest, WycheproofP384) {
185   FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp384r1_test.txt",
186                 RunWycheproofTest);
187 }
188 
TEST(ECDHTest,WycheproofP512)189 TEST(ECDHTest, WycheproofP512) {
190   FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp521r1_test.txt",
191                 RunWycheproofTest);
192 }
193 
194 // MakeCustomGroup returns an |EC_GROUP| containing a non-standard group. (P-256
195 // with the wrong generator.)
MakeCustomGroup()196 static bssl::UniquePtr<EC_GROUP> MakeCustomGroup() {
197   static const uint8_t kP[] = {
198       0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
199       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
200       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
201   };
202   static const uint8_t kA[] = {
203       0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
204       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
205       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
206   };
207   static const uint8_t kB[] = {
208       0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd,
209       0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53,
210       0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b,
211   };
212   static const uint8_t kX[] = {
213       0xe6, 0x2b, 0x69, 0xe2, 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e,
214       0x0d, 0x94, 0x8a, 0x4c, 0xd5, 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d,
215       0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, 0x9d, 0xdc, 0xba, 0x5a,
216   };
217   static const uint8_t kY[] = {
218       0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, 0xc3, 0xc4, 0xa3,
219       0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, 0x1c, 0xf5,
220       0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1,
221   };
222   static const uint8_t kOrder[] = {
223       0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
224       0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17,
225       0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
226   };
227   bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
228   bssl::UniquePtr<BIGNUM> p(BN_bin2bn(kP, sizeof(kP), nullptr));
229   bssl::UniquePtr<BIGNUM> a(BN_bin2bn(kA, sizeof(kA), nullptr));
230   bssl::UniquePtr<BIGNUM> b(BN_bin2bn(kB, sizeof(kB), nullptr));
231   bssl::UniquePtr<BIGNUM> x(BN_bin2bn(kX, sizeof(kX), nullptr));
232   bssl::UniquePtr<BIGNUM> y(BN_bin2bn(kY, sizeof(kY), nullptr));
233   bssl::UniquePtr<BIGNUM> order(BN_bin2bn(kOrder, sizeof(kOrder), nullptr));
234   if (!ctx || !p || !a || !b || !x || !y || !order) {
235     return nullptr;
236   }
237   bssl::UniquePtr<EC_GROUP> group(
238       EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
239   if (!group) {
240     return nullptr;
241   }
242   bssl::UniquePtr<EC_POINT> generator(EC_POINT_new(group.get()));
243   if (!generator ||
244       !EC_POINT_set_affine_coordinates_GFp(group.get(), generator.get(),
245                                            x.get(), y.get(), ctx.get()) ||
246       !EC_GROUP_set_generator(group.get(), generator.get(), order.get(),
247                               BN_value_one())) {
248     return nullptr;
249   }
250   return group;
251 }
252 
TEST(ECDHTest,GroupMismatch)253 TEST(ECDHTest, GroupMismatch) {
254   const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
255   std::vector<EC_builtin_curve> curves(num_curves);
256   EC_get_builtin_curves(curves.data(), num_curves);
257 
258   // Instantiate all the built-in curves.
259   std::vector<bssl::UniquePtr<EC_GROUP>> groups;
260   for (const auto &curve : curves) {
261     groups.emplace_back(EC_GROUP_new_by_curve_name(curve.nid));
262     ASSERT_TRUE(groups.back());
263   }
264 
265   // Also create some arbitrary group. (This is P-256 with the wrong generator.)
266   groups.push_back(MakeCustomGroup());
267   ASSERT_TRUE(groups.back());
268 
269   for (const auto &a : groups) {
270     for (const auto &b : groups) {
271       if (a.get() == b.get()) {
272         continue;
273       }
274 
275       bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
276       ASSERT_TRUE(key);
277       ASSERT_TRUE(EC_KEY_set_group(key.get(), a.get()));
278       ASSERT_TRUE(EC_KEY_generate_key(key.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 }
289