Remove BIGNUM from uncompressed coordinate parsing.
Compressed coordinates still use BIGNUM. I've moved the curve check to
an EC_FELEM-based ec_point_set_affine_coordinates and implemented a
tighter one than ec_GFp_simple_is_on_curve, which currently needs to
branch on Jacobian vs. affine and potentially leaks information. (A
later CL will make it conservatively always perform a Jacobian check.)
The Trust Tokens implementation will eventually need to deserialize
points, so this avoids needing to allocate EC_POINTs everywhere.
Likewise if we ever get around to adding a better ECDH, this will let us
avoid pulling in BIGNUMs.
Bug: chromium:1014199, 242
Change-Id: I93162ba3680d38cb3c0eacff1eb8f42a445246ea
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/40587
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/ec/ec.c b/crypto/fipsmodule/ec/ec.c
index d32ac7e..2fa1676 100644
--- a/crypto/fipsmodule/ec/ec.c
+++ b/crypto/fipsmodule/ec/ec.c
@@ -811,6 +811,42 @@
return 1;
}
+int ec_point_set_affine_coordinates(const EC_GROUP *group, EC_RAW_POINT *out,
+ const EC_FELEM *x, const EC_FELEM *y) {
+ void (*const felem_mul)(const EC_GROUP *, EC_FELEM *r, const EC_FELEM *a,
+ const EC_FELEM *b) = group->meth->felem_mul;
+ void (*const felem_sqr)(const EC_GROUP *, EC_FELEM *r, const EC_FELEM *a) =
+ group->meth->felem_sqr;
+
+ // Check if the point is on the curve.
+ EC_FELEM lhs, rhs;
+ felem_sqr(group, &lhs, y); // lhs = y^2
+ felem_sqr(group, &rhs, x); // rhs = x^2
+ ec_felem_add(group, &rhs, &rhs, &group->a); // rhs = x^2 + a
+ felem_mul(group, &rhs, &rhs, x); // rhs = x^3 + ax
+ ec_felem_add(group, &rhs, &rhs, &group->b); // rhs = x^3 + ax + b
+ if (!ec_felem_equal(group, &lhs, &rhs)) {
+ // In the event of an error, defend against the caller not checking the
+ // return value by setting a known safe value: the base point.
+ const EC_POINT *generator = EC_GROUP_get0_generator(group);
+ if (generator) {
+ ec_GFp_simple_point_copy(out, &generator->raw);
+ } else {
+ // The generator can be missing if the caller is in the process of
+ // constructing an arbitrary group. In this case, we give up and use the
+ // point at infinity.
+ ec_GFp_simple_point_set_to_infinity(group, out);
+ }
+ OPENSSL_PUT_ERROR(EC, EC_R_POINT_IS_NOT_ON_CURVE);
+ return 0;
+ }
+
+ out->X = *x;
+ out->Y = *y;
+ out->Z = group->one;
+ return 1;
+}
+
int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *group, EC_POINT *point,
const BIGNUM *x, const BIGNUM *y,
BN_CTX *ctx) {
@@ -818,21 +854,17 @@
OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
return 0;
}
- if (!ec_GFp_simple_point_set_affine_coordinates(group, &point->raw, x, y)) {
+
+ if (x == NULL || y == NULL) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
- if (!EC_POINT_is_on_curve(group, point, ctx)) {
- // In the event of an error, defend against the caller not checking the
- // return value by setting a known safe value: the base point.
- const EC_POINT *generator = EC_GROUP_get0_generator(group);
- // The generator can be missing if the caller is in the process of
- // constructing an arbitrary group. In this, we give up and hope they're
- // checking the return value.
- if (generator) {
- ec_GFp_simple_point_copy(&point->raw, &generator->raw);
- }
- OPENSSL_PUT_ERROR(EC, EC_R_POINT_IS_NOT_ON_CURVE);
+ EC_FELEM x_felem, y_felem;
+ if (!ec_bignum_to_felem(group, &x_felem, x) ||
+ !ec_bignum_to_felem(group, &y_felem, y) ||
+ !ec_point_set_affine_coordinates(group, &point->raw, &x_felem,
+ &y_felem)) {
return 0;
}
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h
index 2ac8b83..e3f2156 100644
--- a/crypto/fipsmodule/ec/internal.h
+++ b/crypto/fipsmodule/ec/internal.h
@@ -234,6 +234,13 @@
// (X/Z^2, Y/Z^3) if Z != 0 and the point at infinity otherwise.
} EC_RAW_POINT;
+// ec_point_set_affine_coordinates sets |out|'s to a point with affine
+// coordinates |x| and |y|. It returns one if the point is on the curve and
+// zero otherwise. If the point is not on the curve, the value of |out| is
+// undefined.
+int ec_point_set_affine_coordinates(const EC_GROUP *group, EC_RAW_POINT *out,
+ const EC_FELEM *x, const EC_FELEM *y);
+
// ec_point_mul_scalar sets |r| to |p| * |scalar|. Both inputs are considered
// secret.
int ec_point_mul_scalar(const EC_GROUP *group, EC_RAW_POINT *r,
@@ -282,6 +289,12 @@
point_conversion_form_t form, uint8_t *buf,
size_t len);
+// ec_point_from_uncompressed parses |in| as a point in uncompressed form and
+// sets the result to |out|. It returns one on success and zero if the input was
+// invalid.
+int ec_point_from_uncompressed(const EC_GROUP *group, EC_RAW_POINT *out,
+ const uint8_t *in, size_t len);
+
// Implementation details.
@@ -441,9 +454,6 @@
void ec_GFp_simple_point_init(EC_RAW_POINT *);
void ec_GFp_simple_point_copy(EC_RAW_POINT *, const EC_RAW_POINT *);
void ec_GFp_simple_point_set_to_infinity(const EC_GROUP *, EC_RAW_POINT *);
-int ec_GFp_simple_point_set_affine_coordinates(const EC_GROUP *, EC_RAW_POINT *,
- const BIGNUM *x,
- const BIGNUM *y);
void ec_GFp_mont_add(const EC_GROUP *, EC_RAW_POINT *r, const EC_RAW_POINT *a,
const EC_RAW_POINT *b);
void ec_GFp_mont_dbl(const EC_GROUP *, EC_RAW_POINT *r, const EC_RAW_POINT *a);
diff --git a/crypto/fipsmodule/ec/oct.c b/crypto/fipsmodule/ec/oct.c
index e8a98a7..a2a0fc8 100644
--- a/crypto/fipsmodule/ec/oct.c
+++ b/crypto/fipsmodule/ec/oct.c
@@ -125,55 +125,58 @@
return output_len;
}
+int ec_point_from_uncompressed(const EC_GROUP *group, EC_RAW_POINT *out,
+ const uint8_t *in, size_t len) {
+ const size_t field_len = BN_num_bytes(&group->field);
+ if (len != 1 + 2 * field_len || in[0] != POINT_CONVERSION_UNCOMPRESSED) {
+ OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
+ return 0;
+ }
+
+ EC_FELEM x, y;
+ if (!ec_felem_from_bytes(group, &x, in + 1, field_len) ||
+ !ec_felem_from_bytes(group, &y, in + 1 + field_len, field_len) ||
+ !ec_point_set_affine_coordinates(group, out, &x, &y)) {
+ return 0;
+ }
+
+ return 1;
+}
+
static int ec_GFp_simple_oct2point(const EC_GROUP *group, EC_POINT *point,
const uint8_t *buf, size_t len,
BN_CTX *ctx) {
- BN_CTX *new_ctx = NULL;
- int ret = 0, used_ctx = 0;
-
if (len == 0) {
OPENSSL_PUT_ERROR(EC, EC_R_BUFFER_TOO_SMALL);
- goto err;
+ return 0;
}
point_conversion_form_t form = buf[0];
- const int y_bit = form & 1;
- form = form & ~1U;
- if ((form != POINT_CONVERSION_COMPRESSED &&
- form != POINT_CONVERSION_UNCOMPRESSED) ||
- (form == POINT_CONVERSION_UNCOMPRESSED && y_bit)) {
- OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
- goto err;
- }
-
- const size_t field_len = BN_num_bytes(&group->field);
- size_t enc_len = 1 /* type byte */ + field_len;
if (form == POINT_CONVERSION_UNCOMPRESSED) {
- // Uncompressed points have a second coordinate.
- enc_len += field_len;
+ return ec_point_from_uncompressed(group, &point->raw, buf, len);
}
- if (len != enc_len) {
+ const int y_bit = form & 1;
+ const size_t field_len = BN_num_bytes(&group->field);
+ form = form & ~1u;
+ if (form != POINT_CONVERSION_COMPRESSED ||
+ len != 1 /* type byte */ + field_len) {
OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
- goto err;
+ return 0;
}
+ BN_CTX *new_ctx = NULL;
if (ctx == NULL) {
ctx = new_ctx = BN_CTX_new();
if (ctx == NULL) {
- goto err;
+ return 0;
}
}
+ int ret = 0;
BN_CTX_start(ctx);
- used_ctx = 1;
BIGNUM *x = BN_CTX_get(ctx);
- BIGNUM *y = BN_CTX_get(ctx);
- if (x == NULL || y == NULL) {
- goto err;
- }
-
- if (!BN_bin2bn(buf + 1, field_len, x)) {
+ if (x == NULL || !BN_bin2bn(buf + 1, field_len, x)) {
goto err;
}
if (BN_ucmp(x, &group->field) >= 0) {
@@ -181,30 +184,14 @@
goto err;
}
- if (form == POINT_CONVERSION_COMPRESSED) {
- if (!EC_POINT_set_compressed_coordinates_GFp(group, point, x, y_bit, ctx)) {
- goto err;
- }
- } else {
- if (!BN_bin2bn(buf + 1 + field_len, field_len, y)) {
- goto err;
- }
- if (BN_ucmp(y, &group->field) >= 0) {
- OPENSSL_PUT_ERROR(EC, EC_R_INVALID_ENCODING);
- goto err;
- }
-
- if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
- goto err;
- }
+ if (!EC_POINT_set_compressed_coordinates_GFp(group, point, x, y_bit, ctx)) {
+ goto err;
}
ret = 1;
err:
- if (used_ctx) {
- BN_CTX_end(ctx);
- }
+ BN_CTX_end(ctx);
BN_CTX_free(new_ctx);
return ret;
}
diff --git a/crypto/fipsmodule/ec/simple.c b/crypto/fipsmodule/ec/simple.c
index fe8386f..19e9ad5 100644
--- a/crypto/fipsmodule/ec/simple.c
+++ b/crypto/fipsmodule/ec/simple.c
@@ -171,24 +171,6 @@
ec_GFp_simple_point_init(point);
}
-int ec_GFp_simple_point_set_affine_coordinates(const EC_GROUP *group,
- EC_RAW_POINT *point,
- const BIGNUM *x,
- const BIGNUM *y) {
- if (x == NULL || y == NULL) {
- OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
- return 0;
- }
-
- if (!ec_bignum_to_felem(group, &point->X, x) ||
- !ec_bignum_to_felem(group, &point->Y, y)) {
- return 0;
- }
- OPENSSL_memcpy(&point->Z, &group->one, sizeof(EC_FELEM));
-
- return 1;
-}
-
void ec_GFp_simple_invert(const EC_GROUP *group, EC_RAW_POINT *point) {
ec_felem_neg(group, &point->Y, &point->Y);
}