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   // 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 
TEST(ECDHTest,WycheproofP224)175 TEST(ECDHTest, WycheproofP224) {
176   FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp224r1_test.txt",
177                 RunWycheproofTest);
178 }
179 
TEST(ECDHTest,WycheproofP256)180 TEST(ECDHTest, WycheproofP256) {
181   FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp256r1_test.txt",
182                 RunWycheproofTest);
183 }
184 
TEST(ECDHTest,WycheproofP384)185 TEST(ECDHTest, WycheproofP384) {
186   FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp384r1_test.txt",
187                 RunWycheproofTest);
188 }
189 
TEST(ECDHTest,WycheproofP512)190 TEST(ECDHTest, WycheproofP512) {
191   FileTestGTest("third_party/wycheproof_testvectors/ecdh_secp521r1_test.txt",
192                 RunWycheproofTest);
193 }
194 
195 // MakeCustomGroup returns an |EC_GROUP| containing a non-standard group. (P-256
196 // with the wrong generator.)
MakeCustomGroup()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 
TEST(ECDHTest,GroupMismatch)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()));
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