• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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