ecdh_test.cc revision 29c1d2cf8620ad14e06d8e7ff91db8f4de04d481
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/crypto.h>
24#include <openssl/ec.h>
25#include <openssl/ec_key.h>
26#include <openssl/ecdh.h>
27#include <openssl/err.h>
28#include <openssl/nid.h>
29
30#include "../test/file_test.h"
31#include "../test/test_util.h"
32
33
34static bssl::UniquePtr<EC_GROUP> GetCurve(FileTest *t, const char *key) {
35  std::string curve_name;
36  if (!t->GetAttribute(&curve_name, key)) {
37    return nullptr;
38  }
39
40  if (curve_name == "P-224") {
41    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp224r1));
42  }
43  if (curve_name == "P-256") {
44    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(
45        NID_X9_62_prime256v1));
46  }
47  if (curve_name == "P-384") {
48    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp384r1));
49  }
50  if (curve_name == "P-521") {
51    return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp521r1));
52  }
53
54  t->PrintLine("Unknown curve '%s'", curve_name.c_str());
55  return nullptr;
56}
57
58static bssl::UniquePtr<BIGNUM> GetBIGNUM(FileTest *t, const char *key) {
59  std::vector<uint8_t> bytes;
60  if (!t->GetBytes(&bytes, key)) {
61    return nullptr;
62  }
63
64  return bssl::UniquePtr<BIGNUM>(BN_bin2bn(bytes.data(), bytes.size(), nullptr));
65}
66
67TEST(ECDHTest, TestVectors) {
68  FileTestGTest("crypto/ecdh/ecdh_tests.txt", [](FileTest *t) {
69    bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
70    ASSERT_TRUE(group);
71    bssl::UniquePtr<BIGNUM> priv_key = GetBIGNUM(t, "Private");
72    ASSERT_TRUE(priv_key);
73    bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
74    ASSERT_TRUE(x);
75    bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
76    ASSERT_TRUE(y);
77    bssl::UniquePtr<BIGNUM> peer_x = GetBIGNUM(t, "PeerX");
78    ASSERT_TRUE(peer_x);
79    bssl::UniquePtr<BIGNUM> peer_y = GetBIGNUM(t, "PeerY");
80    ASSERT_TRUE(peer_y);
81    std::vector<uint8_t> z;
82    ASSERT_TRUE(t->GetBytes(&z, "Z"));
83
84    bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
85    ASSERT_TRUE(key);
86    bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get()));
87    ASSERT_TRUE(pub_key);
88    bssl::UniquePtr<EC_POINT> peer_pub_key(EC_POINT_new(group.get()));
89    ASSERT_TRUE(peer_pub_key);
90    ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
91    ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
92    ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(group.get(), pub_key.get(),
93                                                    x.get(), y.get(), nullptr));
94    ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
95        group.get(), peer_pub_key.get(), peer_x.get(), peer_y.get(), nullptr));
96    ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
97    ASSERT_TRUE(EC_KEY_check_key(key.get()));
98
99    std::vector<uint8_t> actual_z;
100    // Make |actual_z| larger than expected to ensure |ECDH_compute_key| returns
101    // the right amount of data.
102    actual_z.resize(z.size() + 1);
103    int ret = ECDH_compute_key(actual_z.data(), actual_z.size(),
104                               peer_pub_key.get(), key.get(), nullptr);
105    ASSERT_GE(ret, 0);
106    EXPECT_EQ(Bytes(z), Bytes(actual_z.data(), static_cast<size_t>(ret)));
107
108    // Test |ECDH_compute_key| truncates.
109    actual_z.resize(z.size() - 1);
110    ret = ECDH_compute_key(actual_z.data(), actual_z.size(), peer_pub_key.get(),
111                           key.get(), nullptr);
112    ASSERT_GE(ret, 0);
113    EXPECT_EQ(Bytes(z.data(), z.size() - 1),
114              Bytes(actual_z.data(), static_cast<size_t>(ret)));
115  });
116}
117
118// MakeCustomGroup returns an |EC_GROUP| containing a non-standard group. (P-256
119// with the wrong generator.)
120static bssl::UniquePtr<EC_GROUP> MakeCustomGroup() {
121  static const uint8_t kP[] = {
122      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
123      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
124      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
125  };
126  static const uint8_t kA[] = {
127      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
128      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
129      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
130  };
131  static const uint8_t kB[] = {
132      0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd,
133      0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53,
134      0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b,
135  };
136  static const uint8_t kX[] = {
137      0xe6, 0x2b, 0x69, 0xe2, 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e,
138      0x0d, 0x94, 0x8a, 0x4c, 0xd5, 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d,
139      0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, 0x9d, 0xdc, 0xba, 0x5a,
140  };
141  static const uint8_t kY[] = {
142      0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, 0xc3, 0xc4, 0xa3,
143      0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, 0x1c, 0xf5,
144      0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1,
145  };
146  static const uint8_t kOrder[] = {
147      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
148      0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17,
149      0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
150  };
151  bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
152  bssl::UniquePtr<BIGNUM> p(BN_bin2bn(kP, sizeof(kP), nullptr));
153  bssl::UniquePtr<BIGNUM> a(BN_bin2bn(kA, sizeof(kA), nullptr));
154  bssl::UniquePtr<BIGNUM> b(BN_bin2bn(kB, sizeof(kB), nullptr));
155  bssl::UniquePtr<BIGNUM> x(BN_bin2bn(kX, sizeof(kX), nullptr));
156  bssl::UniquePtr<BIGNUM> y(BN_bin2bn(kY, sizeof(kY), nullptr));
157  bssl::UniquePtr<BIGNUM> order(BN_bin2bn(kOrder, sizeof(kOrder), nullptr));
158  if (!ctx || !p || !a || !b || !x || !y || !order) {
159    return nullptr;
160  }
161  bssl::UniquePtr<EC_GROUP> group(
162      EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
163  if (!group) {
164    return nullptr;
165  }
166  bssl::UniquePtr<EC_POINT> generator(EC_POINT_new(group.get()));
167  if (!generator ||
168      !EC_POINT_set_affine_coordinates_GFp(group.get(), generator.get(),
169                                           x.get(), y.get(), ctx.get()) ||
170      !EC_GROUP_set_generator(group.get(), generator.get(), order.get(),
171                              BN_value_one())) {
172    return nullptr;
173  }
174  return group;
175}
176
177TEST(ECDHTest, GroupMismatch) {
178  const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
179  std::vector<EC_builtin_curve> curves(num_curves);
180  EC_get_builtin_curves(curves.data(), num_curves);
181
182  // Instantiate all the built-in curves.
183  std::vector<bssl::UniquePtr<EC_GROUP>> groups;
184  for (const auto &curve : curves) {
185    groups.emplace_back(EC_GROUP_new_by_curve_name(curve.nid));
186    ASSERT_TRUE(groups.back());
187  }
188
189  // Also create some arbitrary group. (This is P-256 with the wrong generator.)
190  groups.push_back(MakeCustomGroup());
191  ASSERT_TRUE(groups.back());
192
193  for (const auto &a : groups) {
194    for (const auto &b : groups) {
195      if (a.get() == b.get()) {
196        continue;
197      }
198
199      bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
200      ASSERT_TRUE(EC_KEY_set_group(key.get(), a.get()));
201      ASSERT_TRUE(EC_KEY_generate_key(key.get()));
202
203      // ECDH across the groups should not work.
204      char out[64];
205      const EC_POINT *peer = EC_GROUP_get0_generator(b.get());
206      EXPECT_EQ(-1,
207                ECDH_compute_key(out, sizeof(out), peer, key.get(), nullptr));
208      ERR_clear_error();
209    }
210  }
211}
212