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 34 static 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 58 static 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 67 TEST(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.) 120 static 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 177 TEST(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