Home | History | Annotate | Download | only in ecdh
      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