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 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(
49 NID_X9_62_prime256v1));
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
GetBIGNUM(FileTest * t,const char * key)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
TEST(ECDHTest,TestVectors)71 TEST(ECDHTest, TestVectors) {
72 FileTestGTest("crypto/ecdh_extra/ecdh_tests.txt", [](FileTest *t) {
73 bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
74 ASSERT_TRUE(group);
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());
89 ASSERT_TRUE(key);
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()));
96 ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(group.get(), pub_key.get(),
97 x.get(), y.get(), nullptr));
98 ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
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()));
101 ASSERT_TRUE(EC_KEY_check_key(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
RunWycheproofTest(FileTest * t)130 static void RunWycheproofTest(FileTest *t) {
131 t->IgnoreInstruction("encoding");
132
133 bssl::UniquePtr<EC_GROUP> group = GetWycheproofCurve(t, "curve", true);
134 ASSERT_TRUE(group);
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"));
139 WycheproofResult result;
140 ASSERT_TRUE(GetWycheproofResult(t, &result));
141 std::vector<uint8_t> shared;
142 ASSERT_TRUE(t->GetBytes(&shared, "shared"));
143
144 // Wycheproof stores the peer key in an SPKI to mimic a Java API mistake.
145 // This is non-standard and error-prone.
146 CBS cbs;
147 CBS_init(&cbs, peer_spki.data(), peer_spki.size());
148 bssl::UniquePtr<EVP_PKEY> peer_evp(EVP_parse_public_key(&cbs));
149 if (!peer_evp) {
150 // Note some of Wycheproof's "acceptable" entries are unsupported by
151 // BoringSSL because they test explicit curves (forbidden by RFC 5480),
152 // while others are supported because they used compressed coordinates. If
153 // the peer key fails to parse, we consider it to match "acceptable", but if
154 // the resulting shared secret matches below, it too matches "acceptable".
155 //
156 // TODO(davidben): Use the flags field to disambiguate these. Possibly
157 // first get the Wycheproof folks to use flags more consistently.
158 EXPECT_NE(WycheproofResult::kValid, result);
159 return;
160 }
161 EC_KEY *peer_ec = EVP_PKEY_get0_EC_KEY(peer_evp.get());
162 ASSERT_TRUE(peer_ec);
163
164 bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
165 ASSERT_TRUE(key);
166 ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
167 ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
168
169 std::vector<uint8_t> actual((EC_GROUP_get_degree(group.get()) + 7) / 8);
170 int ret =
171 ECDH_compute_key(actual.data(), actual.size(),
172 EC_KEY_get0_public_key(peer_ec), key.get(), nullptr);
173 if (result == WycheproofResult::kInvalid) {
174 EXPECT_EQ(-1, ret);
175 } else {
176 EXPECT_EQ(static_cast<int>(actual.size()), ret);
177 EXPECT_EQ(Bytes(shared), Bytes(actual.data(), static_cast<size_t>(ret)));
178 }
179 }
180
TEST(ECDHTest,WycheproofP224)181 TEST(ECDHTest, WycheproofP224) {
182 FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp224r1_test.txt",
183 RunWycheproofTest);
184 }
185
TEST(ECDHTest,WycheproofP256)186 TEST(ECDHTest, WycheproofP256) {
187 FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp256r1_test.txt",
188 RunWycheproofTest);
189 }
190
TEST(ECDHTest,WycheproofP384)191 TEST(ECDHTest, WycheproofP384) {
192 FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp384r1_test.txt",
193 RunWycheproofTest);
194 }
195
TEST(ECDHTest,WycheproofP512)196 TEST(ECDHTest, WycheproofP512) {
197 FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp521r1_test.txt",
198 RunWycheproofTest);
199 }
200
201 // MakeCustomGroup returns an |EC_GROUP| containing a non-standard group. (P-256
202 // with the wrong generator.)
MakeCustomGroup()203 static bssl::UniquePtr<EC_GROUP> MakeCustomGroup() {
204 static const uint8_t kP[] = {
205 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
206 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
207 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
208 };
209 static const uint8_t kA[] = {
210 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
211 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
212 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
213 };
214 static const uint8_t kB[] = {
215 0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd,
216 0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53,
217 0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b,
218 };
219 static const uint8_t kX[] = {
220 0xe6, 0x2b, 0x69, 0xe2, 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e,
221 0x0d, 0x94, 0x8a, 0x4c, 0xd5, 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d,
222 0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, 0x9d, 0xdc, 0xba, 0x5a,
223 };
224 static const uint8_t kY[] = {
225 0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, 0xc3, 0xc4, 0xa3,
226 0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, 0x1c, 0xf5,
227 0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1,
228 };
229 static const uint8_t kOrder[] = {
230 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
231 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17,
232 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
233 };
234 bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
235 bssl::UniquePtr<BIGNUM> p(BN_bin2bn(kP, sizeof(kP), nullptr));
236 bssl::UniquePtr<BIGNUM> a(BN_bin2bn(kA, sizeof(kA), nullptr));
237 bssl::UniquePtr<BIGNUM> b(BN_bin2bn(kB, sizeof(kB), nullptr));
238 bssl::UniquePtr<BIGNUM> x(BN_bin2bn(kX, sizeof(kX), nullptr));
239 bssl::UniquePtr<BIGNUM> y(BN_bin2bn(kY, sizeof(kY), nullptr));
240 bssl::UniquePtr<BIGNUM> order(BN_bin2bn(kOrder, sizeof(kOrder), nullptr));
241 if (!ctx || !p || !a || !b || !x || !y || !order) {
242 return nullptr;
243 }
244 bssl::UniquePtr<EC_GROUP> group(
245 EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
246 if (!group) {
247 return nullptr;
248 }
249 bssl::UniquePtr<EC_POINT> generator(EC_POINT_new(group.get()));
250 if (!generator ||
251 !EC_POINT_set_affine_coordinates_GFp(group.get(), generator.get(),
252 x.get(), y.get(), ctx.get()) ||
253 !EC_GROUP_set_generator(group.get(), generator.get(), order.get(),
254 BN_value_one())) {
255 return nullptr;
256 }
257 return group;
258 }
259
TEST(ECDHTest,GroupMismatch)260 TEST(ECDHTest, GroupMismatch) {
261 const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
262 std::vector<EC_builtin_curve> curves(num_curves);
263 EC_get_builtin_curves(curves.data(), num_curves);
264
265 // Instantiate all the built-in curves.
266 std::vector<bssl::UniquePtr<EC_GROUP>> groups;
267 for (const auto &curve : curves) {
268 groups.emplace_back(EC_GROUP_new_by_curve_name(curve.nid));
269 ASSERT_TRUE(groups.back());
270 }
271
272 // Also create some arbitrary group. (This is P-256 with the wrong generator.)
273 groups.push_back(MakeCustomGroup());
274 ASSERT_TRUE(groups.back());
275
276 for (const auto &a : groups) {
277 for (const auto &b : groups) {
278 if (a.get() == b.get()) {
279 continue;
280 }
281
282 bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
283 ASSERT_TRUE(EC_KEY_set_group(key.get(), a.get()));
284 ASSERT_TRUE(EC_KEY_generate_key(key.get()));
285
286 // ECDH across the groups should not work.
287 char out[64];
288 const EC_POINT *peer = EC_GROUP_get0_generator(b.get());
289 EXPECT_EQ(-1,
290 ECDH_compute_key(out, sizeof(out), peer, key.get(), nullptr));
291 ERR_clear_error();
292 }
293 }
294 }
295