Require that EC points are on the curve. This removes a sharp corner in the API where |ECDH_compute_key| assumed that callers were either using ephemeral keys, or else had already checked that the public key was on the curve. A public key that's not on the curve can be in a small subgroup and thus the result can leak information about the private key. This change causes |EC_POINT_set_affine_coordinates_GFp| to require that points are on the curve. |EC_POINT_oct2point| already does this. Change-Id: I77d10ce117b6efd87ebb4a631be3a9630f5e6636 Reviewed-on: https://boringssl-review.googlesource.com/5861 Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/ec/ec.c b/crypto/ec/ec.c index c8f8dbc..4891daa 100644 --- a/crypto/ec/ec.c +++ b/crypto/ec/ec.c
@@ -814,7 +814,16 @@ OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS); return 0; } - return ec_GFp_simple_point_set_affine_coordinates(group, point, x, y, ctx); + if (!ec_GFp_simple_point_set_affine_coordinates(group, point, x, y, ctx)) { + return 0; + } + + if (!EC_POINT_is_on_curve(group, point, ctx)) { + OPENSSL_PUT_ERROR(EC, EC_R_POINT_IS_NOT_ON_CURVE); + return 0; + } + + return 1; } int EC_POINT_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a,
diff --git a/crypto/ec/ec_test.cc b/crypto/ec/ec_test.cc index 5af42d5..3f45bd0 100644 --- a/crypto/ec/ec_test.cc +++ b/crypto/ec/ec_test.cc
@@ -173,12 +173,84 @@ return true; } +bool TestSetAffine(const int nid) { + ScopedEC_KEY key(EC_KEY_new_by_curve_name(nid)); + if (!key) { + return false; + } + + const EC_GROUP *const group = EC_KEY_get0_group(key.get()); + + if (!EC_KEY_generate_key(key.get())) { + fprintf(stderr, "EC_KEY_generate_key failed with nid %d\n", nid); + ERR_print_errors_fp(stderr); + return false; + } + + if (!EC_POINT_is_on_curve(group, EC_KEY_get0_public_key(key.get()), + nullptr)) { + fprintf(stderr, "generated point is not on curve with nid %d", nid); + ERR_print_errors_fp(stderr); + return false; + } + + ScopedBIGNUM x(BN_new()); + ScopedBIGNUM y(BN_new()); + if (!EC_POINT_get_affine_coordinates_GFp(group, + EC_KEY_get0_public_key(key.get()), + x.get(), y.get(), nullptr)) { + fprintf(stderr, "EC_POINT_get_affine_coordinates_GFp failed with nid %d\n", + nid); + ERR_print_errors_fp(stderr); + return false; + } + + ScopedEC_POINT point(EC_POINT_new(group)); + if (!point) { + return false; + } + + if (!EC_POINT_set_affine_coordinates_GFp(group, point.get(), x.get(), y.get(), + nullptr)) { + fprintf(stderr, "EC_POINT_set_affine_coordinates_GFp failed with nid %d\n", + nid); + ERR_print_errors_fp(stderr); + return false; + } + + // Subtract one from |y| to make the point no longer on the curve. + if (!BN_sub(y.get(), y.get(), BN_value_one())) { + return false; + } + + ScopedEC_POINT invalid_point(EC_POINT_new(group)); + if (!invalid_point) { + return false; + } + + if (EC_POINT_set_affine_coordinates_GFp(group, invalid_point.get(), x.get(), + y.get(), nullptr)) { + fprintf(stderr, + "EC_POINT_set_affine_coordinates_GFp succeeded with invalid " + "coordinates with nid %d\n", + nid); + ERR_print_errors_fp(stderr); + return false; + } + + return true; +} + int main(void) { CRYPTO_library_init(); ERR_load_crypto_strings(); if (!Testd2i_ECPrivateKey() || - !TestZeroPadding()) { + !TestZeroPadding() || + !TestSetAffine(NID_secp224r1) || + !TestSetAffine(NID_X9_62_prime256v1) || + !TestSetAffine(NID_secp384r1) || + !TestSetAffine(NID_secp521r1)) { fprintf(stderr, "failed\n"); return 1; }
diff --git a/include/openssl/ec.h b/include/openssl/ec.h index fe1c89e..ac36a32 100644 --- a/include/openssl/ec.h +++ b/include/openssl/ec.h
@@ -220,8 +220,10 @@ BIGNUM *x, BIGNUM *y, BN_CTX *ctx); -/* EC_POINT_set_affine_coordinates_GFp sets the value of |p| to be (|x|, |y|). The - * |ctx| argument may be used if not NULL. */ +/* EC_POINT_set_affine_coordinates_GFp sets the value of |p| to be (|x|, |y|). + * The |ctx| argument may be used if not NULL. It returns one on success or + * zero on error. Note that, unlike with OpenSSL, it's considered an error if + * the point is not on the curve. */ OPENSSL_EXPORT int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *group, EC_POINT *point, const BIGNUM *x,