Check EC_POINT/EC_GROUP compatibility more accurately.

Currently we only check that the underlying EC_METHODs match, which
avoids the points being in different forms, but not that the points are
on the same curves. (We fixed the APIs early on so off-curve EC_POINTs
cannot be created.)

In particular, this comes up with folks implementating Java's crypto
APIs with ECDH_compute_key. These APIs are both unfortunate and should
not be mimicked, as they allow folks to mismatch the groups on the two
multiple EC_POINTs. Instead, ECDH APIs should take the public value as a
byte string.

Thanks also to Java's poor crypto APIs, we must support custom curves,
which makes this particularly gnarly. This CL makes EC_GROUP_cmp work
with custom curves and adds an additional subtle requirement to
EC_GROUP_set_generator.

Annoyingly, this change is additionally subtle because we now have a
reference cycle to hack around.

Change-Id: I2efbc4bd5cb65fee5f66527bd6ccad6b9d5120b9
Reviewed-on: https://boringssl-review.googlesource.com/22245
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/ecdh/ecdh_test.cc b/crypto/ecdh/ecdh_test.cc
index b311445..6d48408 100644
--- a/crypto/ecdh/ecdh_test.cc
+++ b/crypto/ecdh/ecdh_test.cc
@@ -14,6 +14,7 @@
 
 #include <stdio.h>
 
+#include <utility>
 #include <vector>
 
 #include <gtest/gtest.h>
@@ -23,6 +24,7 @@
 #include <openssl/ec.h>
 #include <openssl/ec_key.h>
 #include <openssl/ecdh.h>
+#include <openssl/err.h>
 #include <openssl/nid.h>
 
 #include "../test/file_test.h"
@@ -112,3 +114,98 @@
               Bytes(actual_z.data(), static_cast<size_t>(ret)));
   });
 }
+
+// MakeCustomGroup returns an |EC_GROUP| containing a non-standard group. (P-256
+// with the wrong generator.)
+static bssl::UniquePtr<EC_GROUP> MakeCustomGroup() {
+  static const uint8_t kP[] = {
+      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  };
+  static const uint8_t kA[] = {
+      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
+  };
+  static const uint8_t kB[] = {
+      0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd,
+      0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53,
+      0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b,
+  };
+  static const uint8_t kX[] = {
+      0xe6, 0x2b, 0x69, 0xe2, 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e,
+      0x0d, 0x94, 0x8a, 0x4c, 0xd5, 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d,
+      0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, 0x9d, 0xdc, 0xba, 0x5a,
+  };
+  static const uint8_t kY[] = {
+      0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, 0xc3, 0xc4, 0xa3,
+      0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, 0x1c, 0xf5,
+      0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1,
+  };
+  static const uint8_t kOrder[] = {
+      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17,
+      0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
+  };
+  bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
+  bssl::UniquePtr<BIGNUM> p(BN_bin2bn(kP, sizeof(kP), nullptr));
+  bssl::UniquePtr<BIGNUM> a(BN_bin2bn(kA, sizeof(kA), nullptr));
+  bssl::UniquePtr<BIGNUM> b(BN_bin2bn(kB, sizeof(kB), nullptr));
+  bssl::UniquePtr<BIGNUM> x(BN_bin2bn(kX, sizeof(kX), nullptr));
+  bssl::UniquePtr<BIGNUM> y(BN_bin2bn(kY, sizeof(kY), nullptr));
+  bssl::UniquePtr<BIGNUM> order(BN_bin2bn(kOrder, sizeof(kOrder), nullptr));
+  if (!ctx || !p || !a || !b || !x || !y || !order) {
+    return nullptr;
+  }
+  bssl::UniquePtr<EC_GROUP> group(
+      EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
+  if (!group) {
+    return nullptr;
+  }
+  bssl::UniquePtr<EC_POINT> generator(EC_POINT_new(group.get()));
+  if (!generator ||
+      !EC_POINT_set_affine_coordinates_GFp(group.get(), generator.get(),
+                                           x.get(), y.get(), ctx.get()) ||
+      !EC_GROUP_set_generator(group.get(), generator.get(), order.get(),
+                              BN_value_one())) {
+    return nullptr;
+  }
+  return group;
+}
+
+TEST(ECDHTest, GroupMismatch) {
+  const size_t num_curves = EC_get_builtin_curves(nullptr, 0);
+  std::vector<EC_builtin_curve> curves(num_curves);
+  EC_get_builtin_curves(curves.data(), num_curves);
+
+  // Instantiate all the built-in curves.
+  std::vector<bssl::UniquePtr<EC_GROUP>> groups;
+  for (const auto &curve : curves) {
+    groups.emplace_back(EC_GROUP_new_by_curve_name(curve.nid));
+    ASSERT_TRUE(groups.back());
+  }
+
+  // Also create some arbitrary group. (This is P-256 with the wrong generator.)
+  groups.push_back(MakeCustomGroup());
+  ASSERT_TRUE(groups.back());
+
+  for (const auto &a : groups) {
+    for (const auto &b : groups) {
+      if (a.get() == b.get()) {
+        continue;
+      }
+
+      bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
+      ASSERT_TRUE(EC_KEY_set_group(key.get(), a.get()));
+      ASSERT_TRUE(EC_KEY_generate_key(key.get()));
+
+      // ECDH across the groups should not work.
+      char out[64];
+      const EC_POINT *peer = EC_GROUP_get0_generator(b.get());
+      EXPECT_EQ(-1,
+                ECDH_compute_key(out, sizeof(out), peer, key.get(), nullptr));
+      ERR_clear_error();
+    }
+  }
+}
diff --git a/crypto/fipsmodule/ec/ec.c b/crypto/fipsmodule/ec/ec.c
index 7caa5d8..600aedc 100644
--- a/crypto/fipsmodule/ec/ec.c
+++ b/crypto/fipsmodule/ec/ec.c
@@ -80,6 +80,8 @@
 #include "../delocate.h"
 
 
+static void ec_point_free(EC_POINT *point, int free_group);
+
 static const uint8_t kP224Params[6 * 28] = {
     // p
     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -366,6 +368,19 @@
   return ret;
 }
 
+static void ec_group_set0_generator(EC_GROUP *group, EC_POINT *generator) {
+  assert(group->generator == NULL);
+  assert(group == generator->group);
+
+  // Avoid a reference cycle. |group->generator| does not maintain an owning
+  // pointer to |group|.
+  group->generator = generator;
+  int is_zero = CRYPTO_refcount_dec_and_test_zero(&group->references);
+
+  assert(!is_zero);
+  (void)is_zero;
+}
+
 EC_GROUP *EC_GROUP_new_curve_GFp(const BIGNUM *p, const BIGNUM *a,
                                  const BIGNUM *b, BN_CTX *ctx) {
   EC_GROUP *ret = ec_group_new(EC_GFp_mont_method());
@@ -373,9 +388,10 @@
     return NULL;
   }
 
-  if (ret->meth->group_set_curve == 0) {
+  if (ret->meth->group_set_curve == NULL) {
     OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return 0;
+    EC_GROUP_free(ret);
+    return NULL;
   }
   if (!ret->meth->group_set_curve(ret, p, a, b, ctx)) {
     EC_GROUP_free(ret);
@@ -386,9 +402,13 @@
 
 int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator,
                            const BIGNUM *order, const BIGNUM *cofactor) {
-  if (group->curve_name != NID_undef || group->generator != NULL) {
+  if (group->curve_name != NID_undef || group->generator != NULL ||
+      generator->group != group) {
     // |EC_GROUP_set_generator| may only be used with |EC_GROUP|s returned by
     // |EC_GROUP_new_curve_GFp| and may only used once on each group.
+    // Additionally, |generator| must been created from
+    // |EC_GROUP_new_curve_GFp|, not a copy, so that
+    // |generator->group->generator| is set correctly.
     return 0;
   }
 
@@ -398,10 +418,16 @@
     return 0;
   }
 
-  group->generator = EC_POINT_new(group);
-  return group->generator != NULL &&
-         EC_POINT_copy(group->generator, generator) &&
-         BN_copy(&group->order, order);
+  EC_POINT *copy = EC_POINT_new(group);
+  if (copy == NULL ||
+      !EC_POINT_copy(copy, generator) ||
+      !BN_copy(&group->order, order)) {
+    EC_POINT_free(copy);
+    return 0;
+  }
+
+  ec_group_set0_generator(group, copy);
+  return 1;
 }
 
 static EC_GROUP *ec_group_new_from_data(unsigned built_in_index) {
@@ -460,7 +486,7 @@
     group->order_mont = monts[built_in_index];
   }
 
-  group->generator = P;
+  ec_group_set0_generator(group, P);
   P = NULL;
   ok = 1;
 
@@ -501,7 +527,7 @@
 }
 
 void EC_GROUP_free(EC_GROUP *group) {
-  if (!group ||
+  if (group == NULL ||
       !CRYPTO_refcount_dec_and_test_zero(&group->references)) {
     return;
   }
@@ -510,7 +536,7 @@
     group->meth->group_finish(group);
   }
 
-  EC_POINT_free(group->generator);
+  ec_point_free(group->generator, 0 /* don't free group */);
   BN_free(&group->order);
 
   OPENSSL_free(group);
@@ -533,9 +559,29 @@
 }
 
 int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b, BN_CTX *ignored) {
-  return a->curve_name == NID_undef ||
-         b->curve_name == NID_undef ||
-         a->curve_name != b->curve_name;
+  // Note this function returns 0 if equal and non-zero otherwise.
+  if (a == b) {
+    return 0;
+  }
+  if (a->curve_name != b->curve_name) {
+    return 1;
+  }
+  if (a->curve_name != NID_undef) {
+    // Built-in curves may be compared by curve name alone.
+    return 0;
+  }
+
+  // |a| and |b| are both custom curves. We compare the entire curve
+  // structure. If |a| or |b| is incomplete (due to legacy OpenSSL mistakes,
+  // custom curve construction is sadly done in two parts) but otherwise not the
+  // same object, we consider them always unequal.
+  return a->generator == NULL ||
+         b->generator == NULL ||
+         BN_cmp(&a->order, &b->order) != 0 ||
+         BN_cmp(&a->field, &b->field) != 0 ||
+         BN_cmp(&a->a, &b->a) != 0 ||
+         BN_cmp(&a->b, &b->b) != 0 ||
+         ec_GFp_simple_cmp(a, a->generator, b->generator, NULL) != 0;
 }
 
 const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *group) {
@@ -585,9 +631,9 @@
     return NULL;
   }
 
-  ret->meth = group->meth;
-
-  if (!ec_GFp_simple_point_init(ret)) {
+  ret->group = EC_GROUP_dup(group);
+  if (ret->group == NULL ||
+      !ec_GFp_simple_point_init(ret)) {
     OPENSSL_free(ret);
     return NULL;
   }
@@ -595,20 +641,25 @@
   return ret;
 }
 
-void EC_POINT_free(EC_POINT *point) {
+static void ec_point_free(EC_POINT *point, int free_group) {
   if (!point) {
     return;
   }
-
   ec_GFp_simple_point_finish(point);
-
+  if (free_group) {
+    EC_GROUP_free(point->group);
+  }
   OPENSSL_free(point);
 }
 
+void EC_POINT_free(EC_POINT *point) {
+  ec_point_free(point, 1 /* free group */);
+}
+
 void EC_POINT_clear_free(EC_POINT *point) { EC_POINT_free(point); }
 
 int EC_POINT_copy(EC_POINT *dest, const EC_POINT *src) {
-  if (dest->meth != src->meth) {
+  if (EC_GROUP_cmp(dest->group, src->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -634,7 +685,7 @@
 }
 
 int EC_POINT_set_to_infinity(const EC_GROUP *group, EC_POINT *point) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -642,7 +693,7 @@
 }
 
 int EC_POINT_is_at_infinity(const EC_GROUP *group, const EC_POINT *point) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -651,7 +702,7 @@
 
 int EC_POINT_is_on_curve(const EC_GROUP *group, const EC_POINT *point,
                          BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -660,7 +711,8 @@
 
 int EC_POINT_cmp(const EC_GROUP *group, const EC_POINT *a, const EC_POINT *b,
                  BN_CTX *ctx) {
-  if ((group->meth != a->meth) || (a->meth != b->meth)) {
+  if (EC_GROUP_cmp(group, a->group, NULL) != 0 ||
+      EC_GROUP_cmp(group, b->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return -1;
   }
@@ -668,7 +720,7 @@
 }
 
 int EC_POINT_make_affine(const EC_GROUP *group, EC_POINT *point, BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -678,7 +730,7 @@
 int EC_POINTs_make_affine(const EC_GROUP *group, size_t num, EC_POINT *points[],
                           BN_CTX *ctx) {
   for (size_t i = 0; i < num; i++) {
-    if (group->meth != points[i]->meth) {
+    if (EC_GROUP_cmp(group, points[i]->group, NULL) != 0) {
       OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
       return 0;
     }
@@ -693,7 +745,7 @@
     OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -703,7 +755,7 @@
 int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *group, EC_POINT *point,
                                         const BIGNUM *x, const BIGNUM *y,
                                         BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -721,8 +773,9 @@
 
 int EC_POINT_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
                  const EC_POINT *b, BN_CTX *ctx) {
-  if ((group->meth != r->meth) || (r->meth != a->meth) ||
-      (a->meth != b->meth)) {
+  if (EC_GROUP_cmp(group, r->group, NULL) != 0 ||
+      EC_GROUP_cmp(group, a->group, NULL) != 0 ||
+      EC_GROUP_cmp(group, b->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -732,7 +785,8 @@
 
 int EC_POINT_dbl(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
                  BN_CTX *ctx) {
-  if ((group->meth != r->meth) || (r->meth != a->meth)) {
+  if (EC_GROUP_cmp(group, r->group, NULL) != 0 ||
+      EC_GROUP_cmp(group, a->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -741,7 +795,7 @@
 
 
 int EC_POINT_invert(const EC_GROUP *group, EC_POINT *a, BN_CTX *ctx) {
-  if (group->meth != a->meth) {
+  if (EC_GROUP_cmp(group, a->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -759,8 +813,8 @@
     return 0;
   }
 
-  if (group->meth != r->meth ||
-      (p != NULL && group->meth != p->meth)) {
+  if (EC_GROUP_cmp(group, r->group, NULL) != 0 ||
+      (p != NULL && EC_GROUP_cmp(group, p->group, NULL) != 0)) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -772,7 +826,7 @@
                                              EC_POINT *point, const BIGNUM *x,
                                              const BIGNUM *y, const BIGNUM *z,
                                              BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
diff --git a/crypto/fipsmodule/ec/ec_test.cc b/crypto/fipsmodule/ec/ec_test.cc
index 48b60ee..0ee7378 100644
--- a/crypto/fipsmodule/ec/ec_test.cc
+++ b/crypto/fipsmodule/ec/ec_test.cc
@@ -276,6 +276,33 @@
 
   // The key must be valid according to the new group too.
   EXPECT_TRUE(EC_KEY_check_key(key2.get()));
+
+  // Make a second instance of |group|.
+  bssl::UniquePtr<EC_GROUP> group2(
+      EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
+  ASSERT_TRUE(group2);
+  bssl::UniquePtr<EC_POINT> generator2(EC_POINT_new(group2.get()));
+  ASSERT_TRUE(generator2);
+  ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
+      group2.get(), generator2.get(), gx.get(), gy.get(), ctx.get()));
+  ASSERT_TRUE(EC_GROUP_set_generator(group2.get(), generator2.get(),
+                                     order.get(), BN_value_one()));
+
+  EXPECT_EQ(0, EC_GROUP_cmp(group.get(), group.get(), NULL));
+  EXPECT_EQ(0, EC_GROUP_cmp(group2.get(), group.get(), NULL));
+
+  // group3 uses the wrong generator.
+  bssl::UniquePtr<EC_GROUP> group3(
+      EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
+  ASSERT_TRUE(group3);
+  bssl::UniquePtr<EC_POINT> generator3(EC_POINT_new(group3.get()));
+  ASSERT_TRUE(generator3);
+  ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
+      group3.get(), generator3.get(), x.get(), y.get(), ctx.get()));
+  ASSERT_TRUE(EC_GROUP_set_generator(group3.get(), generator3.get(),
+                                     order.get(), BN_value_one()));
+
+  EXPECT_NE(0, EC_GROUP_cmp(group.get(), group3.get(), NULL));
 }
 
 class ECCurveTest : public testing::TestWithParam<EC_builtin_curve> {};
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h
index 1042310..f065744 100644
--- a/crypto/fipsmodule/ec/internal.h
+++ b/crypto/fipsmodule/ec/internal.h
@@ -113,6 +113,8 @@
 struct ec_group_st {
   const EC_METHOD *meth;
 
+  // Unlike all other |EC_POINT|s, |generator| does not own |generator->group|
+  // to avoid a reference cycle.
   EC_POINT *generator;
   BIGNUM order;
 
@@ -137,7 +139,9 @@
 } /* EC_GROUP */;
 
 struct ec_point_st {
-  const EC_METHOD *meth;
+  // group is an owning reference to |group|, unless this is
+  // |group->generator|.
+  EC_GROUP *group;
 
   BIGNUM X;
   BIGNUM Y;
diff --git a/crypto/fipsmodule/ec/oct.c b/crypto/fipsmodule/ec/oct.c
index cf51e4b..7d62395 100644
--- a/crypto/fipsmodule/ec/oct.c
+++ b/crypto/fipsmodule/ec/oct.c
@@ -251,7 +251,7 @@
 
 int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *point,
                        const uint8_t *buf, size_t len, BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -261,7 +261,7 @@
 size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point,
                           point_conversion_form_t form, uint8_t *buf,
                           size_t len, BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
@@ -396,7 +396,7 @@
 int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group,
                                             EC_POINT *point, const BIGNUM *x,
                                             int y_bit, BN_CTX *ctx) {
-  if (group->meth != point->meth) {
+  if (EC_GROUP_cmp(group, point->group, NULL) != 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
     return 0;
   }
diff --git a/crypto/fipsmodule/ec/simple.c b/crypto/fipsmodule/ec/simple.c
index 36b90c8..f7faa45 100644
--- a/crypto/fipsmodule/ec/simple.c
+++ b/crypto/fipsmodule/ec/simple.c
@@ -796,11 +796,11 @@
   const BIGNUM *tmp1_, *tmp2_;
   int ret = -1;
 
-  if (EC_POINT_is_at_infinity(group, a)) {
-    return EC_POINT_is_at_infinity(group, b) ? 0 : 1;
+  if (ec_GFp_simple_is_at_infinity(group, a)) {
+    return ec_GFp_simple_is_at_infinity(group, b) ? 0 : 1;
   }
 
-  if (EC_POINT_is_at_infinity(group, b)) {
+  if (ec_GFp_simple_is_at_infinity(group, b)) {
     return 1;
   }
 
diff --git a/include/openssl/ec.h b/include/openssl/ec.h
index f866ae9..dee41b7 100644
--- a/include/openssl/ec.h
+++ b/include/openssl/ec.h
@@ -302,7 +302,7 @@
 // EC_GROUP_set_generator sets the generator for |group| to |generator|, which
 // must have the given order and cofactor. It may only be used with |EC_GROUP|
 // objects returned by |EC_GROUP_new_curve_GFp| and may only be used once on
-// each group.
+// each group. |generator| must have been created using |group|.
 OPENSSL_EXPORT int EC_GROUP_set_generator(EC_GROUP *group,
                                           const EC_POINT *generator,
                                           const BIGNUM *order,