Embed BN_MONT_CTX into EC_GROUP.

The delocate machinery makes it annoying to have pointers in structures.
Also this is a hair more compact.

Bug: 20
Change-Id: I2bc2dd97018277b5be55fd560f4171b7b85928ff
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60929
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/ec_extra/ec_derive.c b/crypto/ec_extra/ec_derive.c
index 7e666d5..efc95f2 100644
--- a/crypto/ec_extra/ec_derive.c
+++ b/crypto/ec_extra/ec_derive.c
@@ -75,10 +75,10 @@
       // enough. 2^(num_bytes(order)) < 2^8 * order, so:
       //
       //    priv < 2^8 * order * 2^128 < order * order < order * R
-      !BN_from_montgomery(priv, priv, group->order, ctx) ||
+      !BN_from_montgomery(priv, priv, &group->order, ctx) ||
       // Multiply by R^2 and do another Montgomery reduction to compute
       // priv * R^-1 * R^2 * R^-1 = priv mod order.
-      !BN_to_montgomery(priv, priv, group->order, ctx) ||
+      !BN_to_montgomery(priv, priv, &group->order, ctx) ||
       !EC_POINT_mul(group, pub, priv, NULL, NULL, ctx) ||
       !EC_KEY_set_group(key, group) || !EC_KEY_set_public_key(key, pub) ||
       !EC_KEY_set_private_key(key, priv)) {
diff --git a/crypto/ec_extra/hash_to_curve.c b/crypto/ec_extra/hash_to_curve.c
index 380bc2e..d9d9d5b 100644
--- a/crypto/ec_extra/hash_to_curve.c
+++ b/crypto/ec_extra/hash_to_curve.c
@@ -179,12 +179,12 @@
                           size_t msg_len) {
   size_t L;
   uint8_t buf[4 * EC_MAX_BYTES];
-  if (!num_bytes_to_derive(&L, &group->field->N, k) ||
+  if (!num_bytes_to_derive(&L, &group->field.N, k) ||
       !expand_message_xmd(md, buf, 2 * L, msg, msg_len, dst, dst_len)) {
     return 0;
   }
   BN_ULONG words[2 * EC_MAX_WORDS];
-  size_t num_words = 2 * group->field->N.width;
+  size_t num_words = 2 * group->field.N.width;
   big_endian_to_words(words, num_words, buf, L);
   group->meth->felem_reduce(group, out1, words, num_words);
   big_endian_to_words(words, num_words, buf + L, L);
@@ -231,7 +231,7 @@
 }
 
 OPENSSL_UNUSED static int is_3mod4(const EC_GROUP *group) {
-  return group->field->N.width > 0 && (group->field->N.d[0] & 3) == 3;
+  return group->field.N.width > 0 && (group->field.N.d[0] & 3) == 3;
 }
 
 // sqrt_ratio_3mod4 implements the operation described in appendix F.2.1.2
@@ -354,8 +354,8 @@
 
   // Compute |c1| = (p - 3) / 4.
   BN_ULONG c1[EC_MAX_WORDS];
-  size_t num_c1 = group->field->N.width;
-  if (!bn_copy_words(c1, num_c1, &group->field->N)) {
+  size_t num_c1 = group->field.N.width;
+  if (!bn_copy_words(c1, num_c1, &group->field.N)) {
     return 0;
   }
   bn_rshift_words(c1, c1, /*shift=*/2, /*num=*/num_c1);
@@ -371,7 +371,7 @@
 
 static int felem_from_u8(const EC_GROUP *group, EC_FELEM *out, uint8_t a) {
   uint8_t bytes[EC_MAX_BYTES] = {0};
-  size_t len = BN_num_bytes(&group->field->N);
+  size_t len = BN_num_bytes(&group->field.N);
   bytes[len - 1] = a;
   return ec_felem_from_bytes(group, out, bytes, len);
 }
diff --git a/crypto/fipsmodule/bn/internal.h b/crypto/fipsmodule/bn/internal.h
index 7b939de..06ebe16 100644
--- a/crypto/fipsmodule/bn/internal.h
+++ b/crypto/fipsmodule/bn/internal.h
@@ -599,6 +599,13 @@
 OPENSSL_EXPORT int bn_lcm_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
                                     BN_CTX *ctx);
 
+// bn_mont_ctx_init zero-initialies |mont|.
+void bn_mont_ctx_init(BN_MONT_CTX *mont);
+
+// bn_mont_ctx_cleanup releases memory associated with |mont|, without freeing
+// |mont| itself.
+void bn_mont_ctx_cleanup(BN_MONT_CTX *mont);
+
 
 // Constant-time modular arithmetic.
 //
diff --git a/crypto/fipsmodule/bn/montgomery.c b/crypto/fipsmodule/bn/montgomery.c
index a5df5ff..f219d42 100644
--- a/crypto/fipsmodule/bn/montgomery.c
+++ b/crypto/fipsmodule/bn/montgomery.c
@@ -121,17 +121,24 @@
 #include "../../internal.h"
 
 
+void bn_mont_ctx_init(BN_MONT_CTX *mont) {
+  OPENSSL_memset(mont, 0, sizeof(BN_MONT_CTX));
+  BN_init(&mont->RR);
+  BN_init(&mont->N);
+}
+
+void bn_mont_ctx_cleanup(BN_MONT_CTX *mont) {
+  BN_free(&mont->RR);
+  BN_free(&mont->N);
+}
+
 BN_MONT_CTX *BN_MONT_CTX_new(void) {
   BN_MONT_CTX *ret = OPENSSL_malloc(sizeof(BN_MONT_CTX));
-
   if (ret == NULL) {
     return NULL;
   }
 
-  OPENSSL_memset(ret, 0, sizeof(BN_MONT_CTX));
-  BN_init(&ret->RR);
-  BN_init(&ret->N);
-
+  bn_mont_ctx_init(ret);
   return ret;
 }
 
@@ -140,8 +147,7 @@
     return;
   }
 
-  BN_free(&mont->RR);
-  BN_free(&mont->N);
+  bn_mont_ctx_cleanup(mont);
   OPENSSL_free(mont);
 }
 
diff --git a/crypto/fipsmodule/ec/ec.c b/crypto/fipsmodule/ec/ec.c
index 1537491..a1d6e24 100644
--- a/crypto/fipsmodule/ec/ec.c
+++ b/crypto/fipsmodule/ec/ec.c
@@ -280,6 +280,8 @@
 
   ret->references = 1;
   ret->meth = meth;
+  bn_mont_ctx_init(&ret->field);
+  bn_mont_ctx_init(&ret->order);
 
   if (!ec_GFp_simple_group_set_curve(ret, p, a, b, ctx)) {
     EC_GROUP_free(ret);
@@ -291,16 +293,13 @@
 
 static int ec_group_set_generator(EC_GROUP *group, const EC_AFFINE *generator,
                                   const BIGNUM *order) {
-  assert(group->generator == NULL);
+  assert(!group->has_order);
 
-  BN_MONT_CTX_free(group->order);
-  group->order = BN_MONT_CTX_new_for_modulus(order, NULL);
-  if (group->order == NULL) {
+  if (!BN_MONT_CTX_set(&group->order, order, NULL)) {
     return 0;
   }
 
-  group->field_greater_than_order = BN_cmp(&group->field->N, order) > 0;
-
+  group->field_greater_than_order = BN_cmp(&group->field.N, order) > 0;
   group->generator = EC_POINT_new(group);
   if (group->generator == NULL) {
     return 0;
@@ -314,6 +313,7 @@
 
   assert(!is_zero);
   (void)is_zero;
+  group->has_order = 1;
   return 1;
 }
 
@@ -389,7 +389,7 @@
       !BN_lshift1(tmp, order)) {
     goto err;
   }
-  if (BN_cmp(tmp, &group->field->N) <= 0) {
+  if (BN_cmp(tmp, &group->field.N) <= 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_GROUP_ORDER);
     goto err;
   }
@@ -521,9 +521,9 @@
     return;
   }
 
-  ec_point_free(group->generator, 0 /* don't free group */);
-  BN_MONT_CTX_free(group->order);
-  BN_MONT_CTX_free(group->field);
+  ec_point_free(group->generator, /*free_group=*/0);
+  bn_mont_ctx_cleanup(&group->order);
+  bn_mont_ctx_cleanup(&group->field);
   OPENSSL_free(group);
 }
 
@@ -561,8 +561,8 @@
   return a->meth != b->meth ||
          a->generator == NULL ||
          b->generator == NULL ||
-         BN_cmp(&a->order->N, &b->order->N) != 0 ||
-         BN_cmp(&a->field->N, &b->field->N) != 0 ||
+         BN_cmp(&a->order.N, &b->order.N) != 0 ||
+         BN_cmp(&a->field.N, &b->field.N) != 0 ||
          !ec_felem_equal(a, &a->a, &b->a) ||
          !ec_felem_equal(a, &a->b, &b->b) ||
          !ec_GFp_simple_points_equal(a, &a->generator->raw, &b->generator->raw);
@@ -573,8 +573,8 @@
 }
 
 const BIGNUM *EC_GROUP_get0_order(const EC_GROUP *group) {
-  assert(group->order != NULL);
-  return &group->order->N;
+  assert(group->has_order);
+  return &group->order.N;
 }
 
 int EC_GROUP_get_order(const EC_GROUP *group, BIGNUM *order, BN_CTX *ctx) {
@@ -585,7 +585,7 @@
 }
 
 int EC_GROUP_order_bits(const EC_GROUP *group) {
-  return BN_num_bits(&group->order->N);
+  return BN_num_bits(&group->order.N);
 }
 
 int EC_GROUP_get_cofactor(const EC_GROUP *group, BIGNUM *cofactor,
@@ -602,7 +602,7 @@
 int EC_GROUP_get_curve_name(const EC_GROUP *group) { return group->curve_name; }
 
 unsigned EC_GROUP_get_degree(const EC_GROUP *group) {
-  return BN_num_bits(&group->field->N);
+  return BN_num_bits(&group->field.N);
 }
 
 const char *EC_curve_nid2nist(int nid) {
@@ -1162,7 +1162,7 @@
 int ec_get_x_coordinate_as_bytes(const EC_GROUP *group, uint8_t *out,
                                  size_t *out_len, size_t max_out,
                                  const EC_JACOBIAN *p) {
-  size_t len = BN_num_bytes(&group->field->N);
+  size_t len = BN_num_bytes(&group->field.N);
   assert(len <= EC_MAX_BYTES);
   if (max_out < len) {
     OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL);
diff --git a/crypto/fipsmodule/ec/ec_key.c b/crypto/fipsmodule/ec/ec_key.c
index d21d509..90a4404 100644
--- a/crypto/fipsmodule/ec/ec_key.c
+++ b/crypto/fipsmodule/ec/ec_key.c
@@ -93,8 +93,8 @@
 
   OPENSSL_memset(wrapped, 0, sizeof(EC_WRAPPED_SCALAR));
   wrapped->bignum.d = wrapped->scalar.words;
-  wrapped->bignum.width = group->order->N.width;
-  wrapped->bignum.dmax = group->order->N.width;
+  wrapped->bignum.width = group->order.N.width;
+  wrapped->bignum.dmax = group->order.N.width;
   wrapped->bignum.flags = BN_FLG_STATIC_DATA;
   return wrapped;
 }
diff --git a/crypto/fipsmodule/ec/ec_montgomery.c b/crypto/fipsmodule/ec/ec_montgomery.c
index 74c71be..92289a5 100644
--- a/crypto/fipsmodule/ec/ec_montgomery.c
+++ b/crypto/fipsmodule/ec/ec_montgomery.c
@@ -78,33 +78,33 @@
 
 static void ec_GFp_mont_felem_to_montgomery(const EC_GROUP *group,
                                             EC_FELEM *out, const EC_FELEM *in) {
-  bn_to_montgomery_small(out->words, in->words, group->field->N.width,
-                         group->field);
+  bn_to_montgomery_small(out->words, in->words, group->field.N.width,
+                         &group->field);
 }
 
 static void ec_GFp_mont_felem_from_montgomery(const EC_GROUP *group,
                                               EC_FELEM *out,
                                               const EC_FELEM *in) {
-  bn_from_montgomery_small(out->words, group->field->N.width, in->words,
-                           group->field->N.width, group->field);
+  bn_from_montgomery_small(out->words, group->field.N.width, in->words,
+                           group->field.N.width, &group->field);
 }
 
 static void ec_GFp_mont_felem_inv0(const EC_GROUP *group, EC_FELEM *out,
                                    const EC_FELEM *a) {
-  bn_mod_inverse0_prime_mont_small(out->words, a->words, group->field->N.width,
-                                   group->field);
+  bn_mod_inverse0_prime_mont_small(out->words, a->words, group->field.N.width,
+                                   &group->field);
 }
 
 void ec_GFp_mont_felem_mul(const EC_GROUP *group, EC_FELEM *r,
                            const EC_FELEM *a, const EC_FELEM *b) {
   bn_mod_mul_montgomery_small(r->words, a->words, b->words,
-                              group->field->N.width, group->field);
+                              group->field.N.width, &group->field);
 }
 
 void ec_GFp_mont_felem_sqr(const EC_GROUP *group, EC_FELEM *r,
                            const EC_FELEM *a) {
   bn_mod_mul_montgomery_small(r->words, a->words, a->words,
-                              group->field->N.width, group->field);
+                              group->field.N.width, &group->field);
 }
 
 void ec_GFp_mont_felem_to_bytes(const EC_GROUP *group, uint8_t *out,
@@ -127,8 +127,8 @@
 void ec_GFp_mont_felem_reduce(const EC_GROUP *group, EC_FELEM *out,
                               const BN_ULONG *words, size_t num) {
   // Convert "from" Montgomery form so the value is reduced mod p.
-  bn_from_montgomery_small(out->words, group->field->N.width, words, num,
-                           group->field);
+  bn_from_montgomery_small(out->words, group->field.N.width, words, num,
+                           &group->field);
   // Convert "to" Montgomery form to remove the R^-1 factor added.
   ec_GFp_mont_felem_to_montgomery(group, out, out);
   // Convert to Montgomery form to match this implementation's representation.
@@ -138,8 +138,8 @@
 void ec_GFp_mont_felem_exp(const EC_GROUP *group, EC_FELEM *out,
                            const EC_FELEM *a, const BN_ULONG *exp,
                            size_t num_exp) {
-  bn_mod_exp_mont_small(out->words, a->words, group->field->N.width, exp,
-                        num_exp, group->field);
+  bn_mod_exp_mont_small(out->words, a->words, group->field.N.width, exp,
+                        num_exp, &group->field);
 }
 
 static int ec_GFp_mont_point_get_affine_coordinates(const EC_GROUP *group,
@@ -425,7 +425,7 @@
                                         const EC_JACOBIAN *p,
                                         const EC_SCALAR *r) {
   if (!group->field_greater_than_order ||
-      group->field->N.width != group->order->N.width) {
+      group->field.N.width != group->order.N.width) {
     // Do not bother optimizing this case. p > order in all commonly-used
     // curves.
     return ec_GFp_simple_cmp_x_coordinate(group, p, r);
@@ -441,8 +441,7 @@
   EC_FELEM r_Z2, Z2_mont, X;
   ec_GFp_mont_felem_mul(group, &Z2_mont, &p->Z, &p->Z);
   // r < order < p, so this is valid.
-  OPENSSL_memcpy(r_Z2.words, r->words,
-                 group->field->N.width * sizeof(BN_ULONG));
+  OPENSSL_memcpy(r_Z2.words, r->words, group->field.N.width * sizeof(BN_ULONG));
   ec_GFp_mont_felem_mul(group, &r_Z2, &r_Z2, &Z2_mont);
   ec_GFp_mont_felem_from_montgomery(group, &X, &p->X);
 
@@ -454,10 +453,10 @@
   // Therefore there is a small possibility, less than 1/2^128, that group_order
   // < p.x < P. in that case we need not only to compare against |r| but also to
   // compare against r+group_order.
-  BN_ULONG carry = bn_add_words(r_Z2.words, r->words, group->order->N.d,
-                                group->field->N.width);
-  if (carry == 0 && bn_less_than_words(r_Z2.words, group->field->N.d,
-                                       group->field->N.width)) {
+  BN_ULONG carry = bn_add_words(r_Z2.words, r->words, group->order.N.d,
+                                group->field.N.width);
+  if (carry == 0 &&
+      bn_less_than_words(r_Z2.words, group->field.N.d, group->field.N.width)) {
     // r + group_order < p, so compare (r + group_order) * Z^2 against X.
     ec_GFp_mont_felem_mul(group, &r_Z2, &r_Z2, &Z2_mont);
     if (ec_felem_equal(group, &r_Z2, &X)) {
diff --git a/crypto/fipsmodule/ec/felem.c b/crypto/fipsmodule/ec/felem.c
index e211f69..bc7bbb7 100644
--- a/crypto/fipsmodule/ec/felem.c
+++ b/crypto/fipsmodule/ec/felem.c
@@ -25,10 +25,9 @@
 
 int ec_bignum_to_felem(const EC_GROUP *group, EC_FELEM *out, const BIGNUM *in) {
   uint8_t bytes[EC_MAX_BYTES];
-  size_t len = BN_num_bytes(&group->field->N);
+  size_t len = BN_num_bytes(&group->field.N);
   assert(sizeof(bytes) >= len);
-  if (BN_is_negative(in) ||
-      BN_cmp(in, &group->field->N) >= 0 ||
+  if (BN_is_negative(in) || BN_cmp(in, &group->field.N) >= 0 ||
       !BN_bn2bin_padded(bytes, len, in)) {
     OPENSSL_PUT_ERROR(EC, EC_R_COORDINATES_OUT_OF_RANGE);
     return 0;
@@ -57,11 +56,11 @@
 void ec_felem_neg(const EC_GROUP *group, EC_FELEM *out, const EC_FELEM *a) {
   // -a is zero if a is zero and p-a otherwise.
   BN_ULONG mask = ec_felem_non_zero_mask(group, a);
-  BN_ULONG borrow = bn_sub_words(out->words, group->field->N.d, a->words,
-                                 group->field->N.width);
+  BN_ULONG borrow = bn_sub_words(out->words, group->field.N.d, a->words,
+                                 group->field.N.width);
   assert(borrow == 0);
   (void)borrow;
-  for (int i = 0; i < group->field->N.width; i++) {
+  for (int i = 0; i < group->field.N.width; i++) {
     out->words[i] &= mask;
   }
 }
@@ -69,20 +68,20 @@
 void ec_felem_add(const EC_GROUP *group, EC_FELEM *out, const EC_FELEM *a,
                   const EC_FELEM *b) {
   EC_FELEM tmp;
-  bn_mod_add_words(out->words, a->words, b->words, group->field->N.d, tmp.words,
-                   group->field->N.width);
+  bn_mod_add_words(out->words, a->words, b->words, group->field.N.d, tmp.words,
+                   group->field.N.width);
 }
 
 void ec_felem_sub(const EC_GROUP *group, EC_FELEM *out, const EC_FELEM *a,
                   const EC_FELEM *b) {
   EC_FELEM tmp;
-  bn_mod_sub_words(out->words, a->words, b->words, group->field->N.d, tmp.words,
-                   group->field->N.width);
+  bn_mod_sub_words(out->words, a->words, b->words, group->field.N.d, tmp.words,
+                   group->field.N.width);
 }
 
 BN_ULONG ec_felem_non_zero_mask(const EC_GROUP *group, const EC_FELEM *a) {
   BN_ULONG mask = 0;
-  for (int i = 0; i < group->field->N.width; i++) {
+  for (int i = 0; i < group->field.N.width; i++) {
     mask |= a->words[i];
   }
   return ~constant_time_is_zero_w(mask);
@@ -90,11 +89,11 @@
 
 void ec_felem_select(const EC_GROUP *group, EC_FELEM *out, BN_ULONG mask,
                      const EC_FELEM *a, const EC_FELEM *b) {
-  bn_select_words(out->words, mask, a->words, b->words, group->field->N.width);
+  bn_select_words(out->words, mask, a->words, b->words, group->field.N.width);
 }
 
 int ec_felem_equal(const EC_GROUP *group, const EC_FELEM *a,
                    const EC_FELEM *b) {
   return CRYPTO_memcmp(a->words, b->words,
-                       group->field->N.width * sizeof(BN_ULONG)) == 0;
+                       group->field.N.width * sizeof(BN_ULONG)) == 0;
 }
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h
index dc86684..64f41bb 100644
--- a/crypto/fipsmodule/ec/internal.h
+++ b/crypto/fipsmodule/ec/internal.h
@@ -591,8 +591,8 @@
   // and Y are suitable for use as an |EC_AFFINE|.
   EC_POINT *generator;
 
-  BN_MONT_CTX *order;
-  BN_MONT_CTX *field;
+  BN_MONT_CTX order;
+  BN_MONT_CTX field;
 
   EC_FELEM a, b;  // Curve coefficients.
   EC_FELEM one;  // The value one.
@@ -603,6 +603,9 @@
   // arithmetic is optimized for -3.
   int a_is_minus3;
 
+  // has_order is one if |generator| and |order| have been initialized.
+  int has_order;
+
   // field_greater_than_order is one if |field| is greate than |order| and zero
   // otherwise.
   int field_greater_than_order;
diff --git a/crypto/fipsmodule/ec/oct.c b/crypto/fipsmodule/ec/oct.c
index 70e8956..6cb84f5 100644
--- a/crypto/fipsmodule/ec/oct.c
+++ b/crypto/fipsmodule/ec/oct.c
@@ -80,7 +80,7 @@
     return 0;
   }
 
-  const size_t field_len = BN_num_bytes(&group->field->N);
+  const size_t field_len = BN_num_bytes(&group->field.N);
   size_t output_len = 1 /* type byte */ + field_len;
   if (form == POINT_CONVERSION_UNCOMPRESSED) {
     // Uncompressed points have a second coordinate.
@@ -100,11 +100,11 @@
 
   size_t field_len;
   ec_felem_to_bytes(group, buf + 1, &field_len, &point->X);
-  assert(field_len == BN_num_bytes(&group->field->N));
+  assert(field_len == BN_num_bytes(&group->field.N));
 
   if (form == POINT_CONVERSION_UNCOMPRESSED) {
     ec_felem_to_bytes(group, buf + 1 + field_len, &field_len, &point->Y);
-    assert(field_len == BN_num_bytes(&group->field->N));
+    assert(field_len == BN_num_bytes(&group->field.N));
     buf[0] = form;
   } else {
     uint8_t y_buf[EC_MAX_BYTES];
@@ -117,7 +117,7 @@
 
 int ec_point_from_uncompressed(const EC_GROUP *group, EC_AFFINE *out,
                                const uint8_t *in, size_t len) {
-  const size_t field_len = BN_num_bytes(&group->field->N);
+  const size_t field_len = BN_num_bytes(&group->field.N);
   if (len != 1 + 2 * field_len || in[0] != POINT_CONVERSION_UNCOMPRESSED) {
     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
     return 0;
@@ -155,7 +155,7 @@
   }
 
   const int y_bit = form & 1;
-  const size_t field_len = BN_num_bytes(&group->field->N);
+  const size_t field_len = BN_num_bytes(&group->field.N);
   form = form & ~1u;
   if (form != POINT_CONVERSION_COMPRESSED ||
       len != 1 /* type byte */ + field_len) {
@@ -182,7 +182,7 @@
   if (x == NULL || !BN_bin2bn(buf + 1, field_len, x)) {
     goto err;
   }
-  if (BN_ucmp(x, &group->field->N) >= 0) {
+  if (BN_ucmp(x, &group->field.N) >= 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
     goto err;
   }
@@ -260,7 +260,7 @@
     return 0;
   }
 
-  const BIGNUM *field = &group->field->N;
+  const BIGNUM *field = &group->field.N;
   if (BN_is_negative(x) || BN_cmp(x, field) >= 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_COMPRESSED_POINT);
     return 0;
diff --git a/crypto/fipsmodule/ec/p256-nistz.c b/crypto/fipsmodule/ec/p256-nistz.c
index 9993941..cf09963 100644
--- a/crypto/fipsmodule/ec/p256-nistz.c
+++ b/crypto/fipsmodule/ec/p256-nistz.c
@@ -191,7 +191,7 @@
                                       const EC_SCALAR *p_scalar) {
   assert(p != NULL);
   assert(p_scalar != NULL);
-  assert(group->field->N.width == P256_LIMBS);
+  assert(group->field.N.width == P256_LIMBS);
 
   static const size_t kWindowSize = 5;
   static const crypto_word_t kMask = (1 << (5 /* kWindowSize */ + 1)) - 1;
@@ -208,7 +208,7 @@
   // not stored. All other values are actually stored with an offset of -1 in
   // table.
   P256_POINT *row = table;
-  assert(group->field->N.width == P256_LIMBS);
+  assert(group->field.N.width == P256_LIMBS);
   OPENSSL_memcpy(row[1 - 1].X, p->X.words, P256_LIMBS * sizeof(BN_ULONG));
   OPENSSL_memcpy(row[1 - 1].Y, p->Y.words, P256_LIMBS * sizeof(BN_ULONG));
   OPENSSL_memcpy(row[1 - 1].Z, p->Z.words, P256_LIMBS * sizeof(BN_ULONG));
@@ -305,7 +305,7 @@
   alignas(32) P256_POINT out;
   ecp_nistz256_windowed_mul(group, &out, p, scalar);
 
-  assert(group->field->N.width == P256_LIMBS);
+  assert(group->field.N.width == P256_LIMBS);
   OPENSSL_memcpy(r->X.words, out.X, P256_LIMBS * sizeof(BN_ULONG));
   OPENSSL_memcpy(r->Y.words, out.Y, P256_LIMBS * sizeof(BN_ULONG));
   OPENSSL_memcpy(r->Z.words, out.Z, P256_LIMBS * sizeof(BN_ULONG));
@@ -349,7 +349,7 @@
     ecp_nistz256_point_add_affine(&p, &p, &t);
   }
 
-  assert(group->field->N.width == P256_LIMBS);
+  assert(group->field.N.width == P256_LIMBS);
   OPENSSL_memcpy(r->X.words, p.X, P256_LIMBS * sizeof(BN_ULONG));
   OPENSSL_memcpy(r->Y.words, p.Y, P256_LIMBS * sizeof(BN_ULONG));
   OPENSSL_memcpy(r->Z.words, p.Z, P256_LIMBS * sizeof(BN_ULONG));
@@ -413,7 +413,7 @@
   ecp_nistz256_windowed_mul(group, &tmp, p_, p_scalar);
   ecp_nistz256_point_add(&p, &p, &tmp);
 
-  assert(group->field->N.width == P256_LIMBS);
+  assert(group->field.N.width == P256_LIMBS);
   OPENSSL_memcpy(r->X.words, p.X, P256_LIMBS * sizeof(BN_ULONG));
   OPENSSL_memcpy(r->Y.words, p.Y, P256_LIMBS * sizeof(BN_ULONG));
   OPENSSL_memcpy(r->Z.words, p.Z, P256_LIMBS * sizeof(BN_ULONG));
@@ -429,7 +429,7 @@
   }
 
   BN_ULONG z_inv2[P256_LIMBS];
-  assert(group->field->N.width == P256_LIMBS);
+  assert(group->field.N.width == P256_LIMBS);
   ecp_nistz256_mod_inverse_sqr_mont(z_inv2, point->Z.words);
 
   if (x != NULL) {
@@ -563,8 +563,8 @@
   }
 #endif
 
-  assert(group->order->N.width == P256_LIMBS);
-  if (!beeu_mod_inverse_vartime(out->words, in->words, group->order->N.d)) {
+  assert(group->order.N.width == P256_LIMBS);
+  if (!beeu_mod_inverse_vartime(out->words, in->words, group->order.N.d)) {
     return 0;
   }
 
@@ -580,8 +580,8 @@
     return 0;
   }
 
-  assert(group->order->N.width == P256_LIMBS);
-  assert(group->field->N.width == P256_LIMBS);
+  assert(group->order.N.width == P256_LIMBS);
+  assert(group->field.N.width == P256_LIMBS);
 
   // We wish to compare X/Z^2 with r. This is equivalent to comparing X with
   // r*Z^2. Note that X and Z are represented in Montgomery form, while r is
@@ -599,8 +599,8 @@
   // Therefore there is a small possibility, less than 1/2^128, that group_order
   // < p.x < P. in that case we need not only to compare against |r| but also to
   // compare against r+group_order.
-  BN_ULONG carry = bn_add_words(r_Z2, r->words, group->order->N.d, P256_LIMBS);
-  if (carry == 0 && bn_less_than_words(r_Z2, group->field->N.d, P256_LIMBS)) {
+  BN_ULONG carry = bn_add_words(r_Z2, r->words, group->order.N.d, P256_LIMBS);
+  if (carry == 0 && bn_less_than_words(r_Z2, group->field.N.d, P256_LIMBS)) {
     // r + group_order < p, so compare (r + group_order) * Z^2 against X.
     ecp_nistz256_mul_mont(r_Z2, r_Z2, Z2_mont);
     if (OPENSSL_memcmp(r_Z2, X, sizeof(r_Z2)) == 0) {
diff --git a/crypto/fipsmodule/ec/p256.c b/crypto/fipsmodule/ec/p256.c
index 35b9d45..e532e2f 100644
--- a/crypto/fipsmodule/ec/p256.c
+++ b/crypto/fipsmodule/ec/p256.c
@@ -710,12 +710,12 @@
   // Therefore there is a small possibility, less than 1/2^128, that group_order
   // < p.x < P. in that case we need not only to compare against |r| but also to
   // compare against r+group_order.
-  assert(group->field->N.width == group->order->N.width);
+  assert(group->field.N.width == group->order.N.width);
   EC_FELEM tmp;
-  BN_ULONG carry = bn_add_words(tmp.words, r->words, group->order->N.d,
-                                group->field->N.width);
+  BN_ULONG carry =
+      bn_add_words(tmp.words, r->words, group->order.N.d, group->field.N.width);
   if (carry == 0 &&
-      bn_less_than_words(tmp.words, group->field->N.d, group->field->N.width)) {
+      bn_less_than_words(tmp.words, group->field.N.d, group->field.N.width)) {
     fiat_p256_from_generic(r_Z2, &tmp);
     fiat_p256_mul(r_Z2, r_Z2, Z2_mont);
     if (OPENSSL_memcmp(&r_Z2, &X, sizeof(r_Z2)) == 0) {
diff --git a/crypto/fipsmodule/ec/scalar.c b/crypto/fipsmodule/ec/scalar.c
index 5c6f664..a86ee0f 100644
--- a/crypto/fipsmodule/ec/scalar.c
+++ b/crypto/fipsmodule/ec/scalar.c
@@ -23,9 +23,8 @@
 
 int ec_bignum_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
                         const BIGNUM *in) {
-  if (!bn_copy_words(out->words, group->order->N.width, in) ||
-      !bn_less_than_words(out->words, group->order->N.d,
-                          group->order->N.width)) {
+  if (!bn_copy_words(out->words, group->order.N.width, in) ||
+      !bn_less_than_words(out->words, group->order.N.d, group->order.N.width)) {
     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
     return 0;
   }
@@ -35,12 +34,12 @@
 int ec_scalar_equal_vartime(const EC_GROUP *group, const EC_SCALAR *a,
                             const EC_SCALAR *b) {
   return OPENSSL_memcmp(a->words, b->words,
-                        group->order->N.width * sizeof(BN_ULONG)) == 0;
+                        group->order.N.width * sizeof(BN_ULONG)) == 0;
 }
 
 int ec_scalar_is_zero(const EC_GROUP *group, const EC_SCALAR *a) {
   BN_ULONG mask = 0;
-  for (int i = 0; i < group->order->N.width; i++) {
+  for (int i = 0; i < group->order.N.width; i++) {
     mask |= a->words[i];
   }
   return mask == 0;
@@ -48,28 +47,27 @@
 
 int ec_random_nonzero_scalar(const EC_GROUP *group, EC_SCALAR *out,
                              const uint8_t additional_data[32]) {
-  return bn_rand_range_words(out->words, 1, group->order->N.d,
-                             group->order->N.width, additional_data);
+  return bn_rand_range_words(out->words, 1, group->order.N.d,
+                             group->order.N.width, additional_data);
 }
 
 void ec_scalar_to_bytes(const EC_GROUP *group, uint8_t *out, size_t *out_len,
                         const EC_SCALAR *in) {
-  size_t len = BN_num_bytes(&group->order->N);
-  bn_words_to_big_endian(out, len, in->words, group->order->N.width);
+  size_t len = BN_num_bytes(&group->order.N);
+  bn_words_to_big_endian(out, len, in->words, group->order.N.width);
   *out_len = len;
 }
 
 int ec_scalar_from_bytes(const EC_GROUP *group, EC_SCALAR *out,
                          const uint8_t *in, size_t len) {
-  if (len != BN_num_bytes(&group->order->N)) {
+  if (len != BN_num_bytes(&group->order.N)) {
     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
     return 0;
   }
 
-  bn_big_endian_to_words(out->words, group->order->N.width, in, len);
+  bn_big_endian_to_words(out->words, group->order.N.width, in, len);
 
-  if (!bn_less_than_words(out->words, group->order->N.d,
-                          group->order->N.width)) {
+  if (!bn_less_than_words(out->words, group->order.N.d, group->order.N.width)) {
     OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
     return 0;
   }
@@ -80,15 +78,15 @@
 void ec_scalar_reduce(const EC_GROUP *group, EC_SCALAR *out,
                       const BN_ULONG *words, size_t num) {
   // Convert "from" Montgomery form so the value is reduced modulo the order.
-  bn_from_montgomery_small(out->words, group->order->N.width, words, num,
-                           group->order);
+  bn_from_montgomery_small(out->words, group->order.N.width, words, num,
+                           &group->order);
   // Convert "to" Montgomery form to remove the R^-1 factor added.
   ec_scalar_to_montgomery(group, out, out);
 }
 
 void ec_scalar_add(const EC_GROUP *group, EC_SCALAR *r, const EC_SCALAR *a,
                    const EC_SCALAR *b) {
-  const BIGNUM *order = &group->order->N;
+  const BIGNUM *order = &group->order.N;
   BN_ULONG tmp[EC_MAX_WORDS];
   bn_mod_add_words(r->words, a->words, b->words, order->d, tmp, order->width);
   OPENSSL_cleanse(tmp, sizeof(tmp));
@@ -96,7 +94,7 @@
 
 void ec_scalar_sub(const EC_GROUP *group, EC_SCALAR *r, const EC_SCALAR *a,
                    const EC_SCALAR *b) {
-  const BIGNUM *order = &group->order->N;
+  const BIGNUM *order = &group->order.N;
   BN_ULONG tmp[EC_MAX_WORDS];
   bn_mod_sub_words(r->words, a->words, b->words, order->d, tmp, order->width);
   OPENSSL_cleanse(tmp, sizeof(tmp));
@@ -110,35 +108,35 @@
 
 void ec_scalar_select(const EC_GROUP *group, EC_SCALAR *out, BN_ULONG mask,
                       const EC_SCALAR *a, const EC_SCALAR *b) {
-  const BIGNUM *order = &group->order->N;
+  const BIGNUM *order = &group->order.N;
   bn_select_words(out->words, mask, a->words, b->words, order->width);
 }
 
 void ec_scalar_to_montgomery(const EC_GROUP *group, EC_SCALAR *r,
                              const EC_SCALAR *a) {
-  const BIGNUM *order = &group->order->N;
-  bn_to_montgomery_small(r->words, a->words, order->width, group->order);
+  const BIGNUM *order = &group->order.N;
+  bn_to_montgomery_small(r->words, a->words, order->width, &group->order);
 }
 
 void ec_scalar_from_montgomery(const EC_GROUP *group, EC_SCALAR *r,
                                const EC_SCALAR *a) {
-  const BIGNUM *order = &group->order->N;
+  const BIGNUM *order = &group->order.N;
   bn_from_montgomery_small(r->words, order->width, a->words, order->width,
-                           group->order);
+                           &group->order);
 }
 
 void ec_scalar_mul_montgomery(const EC_GROUP *group, EC_SCALAR *r,
                               const EC_SCALAR *a, const EC_SCALAR *b) {
-  const BIGNUM *order = &group->order->N;
+  const BIGNUM *order = &group->order.N;
   bn_mod_mul_montgomery_small(r->words, a->words, b->words, order->width,
-                              group->order);
+                              &group->order);
 }
 
 void ec_simple_scalar_inv0_montgomery(const EC_GROUP *group, EC_SCALAR *r,
                                       const EC_SCALAR *a) {
-  const BIGNUM *order = &group->order->N;
+  const BIGNUM *order = &group->order.N;
   bn_mod_inverse0_prime_mont_small(r->words, a->words, order->width,
-                                   group->order);
+                                   &group->order);
 }
 
 int ec_simple_scalar_to_montgomery_inv_vartime(const EC_GROUP *group,
diff --git a/crypto/fipsmodule/ec/simple.c b/crypto/fipsmodule/ec/simple.c
index 0b27040..624d3ce 100644
--- a/crypto/fipsmodule/ec/simple.c
+++ b/crypto/fipsmodule/ec/simple.c
@@ -104,12 +104,8 @@
     goto err;
   }
 
-  group->field = BN_MONT_CTX_new_for_modulus(p, ctx);
-  if (group->field == NULL) {
-    goto err;
-  }
-
-  if (!ec_bignum_to_felem(group, &group->a, a) ||
+  if (!BN_MONT_CTX_set(&group->field, p, ctx) ||
+      !ec_bignum_to_felem(group, &group->a, a) ||
       !ec_bignum_to_felem(group, &group->b, b) ||
       !ec_bignum_to_felem(group, &group->one, BN_value_one())) {
     goto err;
@@ -120,7 +116,7 @@
       !BN_add_word(tmp, 3)) {
     goto err;
   }
-  group->a_is_minus3 = (0 == BN_cmp(tmp, &group->field->N));
+  group->a_is_minus3 = (0 == BN_cmp(tmp, &group->field.N));
 
   ret = 1;
 
@@ -131,7 +127,7 @@
 
 int ec_GFp_simple_group_get_curve(const EC_GROUP *group, BIGNUM *p, BIGNUM *a,
                                   BIGNUM *b) {
-  if ((p != NULL && !BN_copy(p, &group->field->N)) ||
+  if ((p != NULL && !BN_copy(p, &group->field.N)) ||
       (a != NULL && !ec_felem_to_bignum(group, a, &group->a)) ||
       (b != NULL && !ec_felem_to_bignum(group, b, &group->b))) {
     return 0;
@@ -316,22 +312,21 @@
 
 void ec_GFp_simple_felem_to_bytes(const EC_GROUP *group, uint8_t *out,
                                   size_t *out_len, const EC_FELEM *in) {
-  size_t len = BN_num_bytes(&group->field->N);
-  bn_words_to_big_endian(out, len, in->words, group->field->N.width);
+  size_t len = BN_num_bytes(&group->field.N);
+  bn_words_to_big_endian(out, len, in->words, group->field.N.width);
   *out_len = len;
 }
 
 int ec_GFp_simple_felem_from_bytes(const EC_GROUP *group, EC_FELEM *out,
                                    const uint8_t *in, size_t len) {
-  if (len != BN_num_bytes(&group->field->N)) {
+  if (len != BN_num_bytes(&group->field.N)) {
     OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
     return 0;
   }
 
-  bn_big_endian_to_words(out->words, group->field->N.width, in, len);
+  bn_big_endian_to_words(out->words, group->field.N.width, in, len);
 
-  if (!bn_less_than_words(out->words, group->field->N.d,
-                          group->field->N.width)) {
+  if (!bn_less_than_words(out->words, group->field.N.d, group->field.N.width)) {
     OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
     return 0;
   }
diff --git a/crypto/fipsmodule/ec/simple_mul.c b/crypto/fipsmodule/ec/simple_mul.c
index ce5b317..796c408 100644
--- a/crypto/fipsmodule/ec/simple_mul.c
+++ b/crypto/fipsmodule/ec/simple_mul.c
@@ -48,7 +48,7 @@
     }
     if (i % 5 == 0) {
       // Compute the next window value.
-      const size_t width = group->order->N.width;
+      const size_t width = group->order.N.width;
       uint8_t window = bn_is_bit_set_words(scalar->words, width, i + 4) << 4;
       window |= bn_is_bit_set_words(scalar->words, width, i + 3) << 3;
       window |= bn_is_bit_set_words(scalar->words, width, i + 2) << 2;
@@ -99,7 +99,7 @@
                                          EC_JACOBIAN *out,
                                          const EC_JACOBIAN precomp[17],
                                          const EC_SCALAR *scalar, unsigned i) {
-  const size_t width = group->order->N.width;
+  const size_t width = group->order.N.width;
   uint8_t window = bn_is_bit_set_words(scalar->words, width, i + 4) << 5;
   window |= bn_is_bit_set_words(scalar->words, width, i + 3) << 4;
   window |= bn_is_bit_set_words(scalar->words, width, i + 2) << 3;
@@ -212,7 +212,7 @@
                                         EC_JACOBIAN *out,
                                         const EC_PRECOMP *precomp,
                                         const EC_SCALAR *scalar, unsigned i) {
-  const size_t width = group->order->N.width;
+  const size_t width = group->order.N.width;
   unsigned stride = ec_GFp_mont_comb_stride(group);
   // Select the bits corresponding to the comb shifted up by |i|.
   unsigned window = 0;
diff --git a/crypto/fipsmodule/ec/wnaf.c b/crypto/fipsmodule/ec/wnaf.c
index c9935fc..2cc0fbc 100644
--- a/crypto/fipsmodule/ec/wnaf.c
+++ b/crypto/fipsmodule/ec/wnaf.c
@@ -138,8 +138,8 @@
     // we shift and add at most one copy of |bit|, this will continue to hold
     // afterwards.
     window_val >>= 1;
-    window_val += bit * bn_is_bit_set_words(scalar->words,
-                                            group->order->N.width, j + w + 1);
+    window_val += bit * bn_is_bit_set_words(scalar->words, group->order.N.width,
+                                            j + w + 1);
     assert(window_val <= next_bit);
   }