diff --git a/crypto/ec/build.info b/crypto/ec/build.info index d4ffd1412..3d7ea9084 100644 --- a/crypto/ec/build.info +++ b/crypto/ec/build.info @@ -2,7 +2,7 @@ LIBS=../../libcrypto SOURCE[../../libcrypto]=\ ec_lib.c ecp_smpl.c ecp_mont.c ecp_nist.c ec_cvt.c ec_mult.c \ ec_err.c ec_curve.c ec_check.c ec_print.c ec_asn1.c ec_key.c \ - ec2_smpl.c ec_ameth.c ec_pmeth.c eck_prn.c \ + ec2_smpl.c ec_ameth.c ec_pmeth.c eck_prn.c ecp_ed25519.c \ ecp_nistp224.c ecp_nistp256.c ecp_nistp521.c ecp_nistputil.c \ ecp_oct.c ec2_oct.c ec_oct.c ec_kmeth.c ecdh_ossl.c ecdh_kdf.c \ ecdsa_ossl.c ecdsa_sign.c ecdsa_vrf.c curve25519.c ecx_meth.c \ diff --git a/crypto/ec/ec_curve.c b/crypto/ec/ec_curve.c index 8de486cbd..e8153df6f 100644 --- a/crypto/ec/ec_curve.c +++ b/crypto/ec/ec_curve.c @@ -2790,6 +2790,43 @@ static const struct { }; #endif /* OPENSSL_NO_SM2 */ +static const struct { + EC_CURVE_DATA h; + unsigned char data[0 + 32 * 6]; +} _EC_ED25519 = { + { + NID_ED25519, 0, 32, 8 + }, + { + /* no seed */ + + /* p */ + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed, + /* a = 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* b */ + 0x52, 0x03, 0x6c, 0xee, 0x2b, 0x6f, 0xfe, 0x73, 0x8c, 0xc7, 0x40, 0x79, + 0x77, 0x79, 0xe8, 0x98, 0x00, 0x70, 0x0a, 0x4d, 0x41, 0x41, 0xd8, 0xab, + 0x75, 0xeb, 0x4d, 0xca, 0x13, 0x59, 0x78, 0xa3, + /* x */ + 0x21, 0x69, 0x36, 0xd3, 0xcd, 0x6e, 0x53, 0xfe, 0xc0, 0xa4, 0xe2, 0x31, + 0xfd, 0xd6, 0xdc, 0x5c, 0x69, 0x2c, 0xc7, 0x60, 0x95, 0x25, 0xa7, 0xb2, + 0xc9, 0x56, 0x2d, 0x60, 0x8f, 0x25, 0xd5, 0x1a, + /* y */ + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x58, + /* order */ + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0xde, 0xf9, 0xde, 0xa2, 0xf7, 0x9c, 0xd6, + 0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed, + } +}; + typedef struct _ec_list_element_st { int nid; const EC_CURVE_DATA *data; @@ -3003,6 +3040,8 @@ static const ec_list_element curve_list[] = { {NID_sm2, &_EC_sm2p256v1.h, 0, "SM2 curve over a 256 bit prime field"}, #endif + {NID_ED25519, &_EC_ED25519.h, EC_GFp_ed25519_method, + "ED25519 curve over a 256 bit prime field"}, }; #define curve_list_length OSSL_NELEM(curve_list) diff --git a/crypto/ec/ec_err.c b/crypto/ec/ec_err.c index b7de21e73..b6dbe35aa 100644 --- a/crypto/ec/ec_err.c +++ b/crypto/ec/ec_err.c @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -88,6 +88,10 @@ static const ERR_STRING_DATA EC_str_functs[] = { "ec_GF2m_simple_point_set_affine_coordinates"}, {ERR_PACK(ERR_LIB_EC, EC_F_EC_GF2M_SIMPLE_SET_COMPRESSED_COORDINATES, 0), "ec_GF2m_simple_set_compressed_coordinates"}, + {ERR_PACK(ERR_LIB_EC, EC_F_EC_GFP_ED25519_POINT2OCT, 0), + "ec_GFp_ed25519_point2oct"}, + {ERR_PACK(ERR_LIB_EC, EC_F_EC_GFP_ED25519_POINT_SET_AFFINE_COORDINATES, 0), + "ec_GFp_ed25519_point_set_affine_coordinates"}, {ERR_PACK(ERR_LIB_EC, EC_F_EC_GFP_MONT_FIELD_DECODE, 0), "ec_GFp_mont_field_decode"}, {ERR_PACK(ERR_LIB_EC, EC_F_EC_GFP_MONT_FIELD_ENCODE, 0), diff --git a/crypto/ec/ec_local.h b/crypto/ec/ec_local.h index 6ea26193e..62ed646f2 100644 --- a/crypto/ec/ec_local.h +++ b/crypto/ec/ec_local.h @@ -306,6 +306,7 @@ struct ec_point_st { * Z) represents (X/Z^2, Y/Z^3) if Z != 0 */ int Z_is_one; /* enable optimized point arithmetics for * special case */ + BIGNUM *T; }; static ossl_inline int ec_point_is_compat(const EC_POINT *point, diff --git a/crypto/ec/ecp_ed25519.c b/crypto/ec/ecp_ed25519.c new file mode 100644 index 000000000..d5eb9ab0c --- /dev/null +++ b/crypto/ec/ecp_ed25519.c @@ -0,0 +1,1096 @@ +/* + * Copyright 2022 The BabaSSL Project Authors. All Rights Reserved. + * + * Licensed under the BabaSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://github.com/BabaSSL/BabaSSL/blob/master/LICENSE + */ + +#include <openssl/err.h> +#include "ec_local.h" + +static int ec_GFp_ed25519_point_init(EC_POINT *point) +{ + point->X = BN_new(); + point->Y = BN_new(); + point->Z = BN_new(); + point->T = BN_new(); + point->Z_is_one = 0; + + if (point->X == NULL || point->Y == NULL || point->Z == NULL || point->T == NULL) { + BN_free(point->X); + BN_free(point->Y); + BN_free(point->Z); + BN_free(point->T); + return 0; + } + return 1; +} + +static void ec_GFp_ed25519_point_finish(EC_POINT *point) +{ + BN_free(point->X); + BN_free(point->Y); + BN_free(point->Z); + BN_free(point->T); +} + +static void ec_GFp_ed25519_point_clear_finish(EC_POINT *point) +{ + BN_clear_free(point->X); + BN_clear_free(point->Y); + BN_clear_free(point->Z); + BN_clear_free(point->T); + point->Z_is_one = 0; +} + +static int ec_GFp_ed25519_point_copy(EC_POINT *dest, const EC_POINT *src) +{ + if (!BN_copy(dest->X, src->X)) + return 0; + if (!BN_copy(dest->Y, src->Y)) + return 0; + if (!BN_copy(dest->Z, src->Z)) + return 0; + if (!BN_copy(dest->T, src->T)) + return 0; + dest->Z_is_one = src->Z_is_one; + dest->curve_name = src->curve_name; + + return 1; +} + +static int ec_GFp_ed25519_point_set_to_infinity(const EC_GROUP *group, + EC_POINT *point) +{ + point->Z_is_one = 1; + BN_zero(point->X); + BN_one(point->Y); + BN_one(point->Z); + BN_zero(point->T); + return 1; +} + +static int ec_GFp_ed25519_point_is_at_infinity(const EC_GROUP *group, + const EC_POINT *point) +{ + return BN_is_zero(point->X) && BN_is_one(point->Y) && + BN_is_one(point->Z) && BN_is_zero(point->T); +} + +static int ec_GFp_ed25519_group_init(EC_GROUP *group) +{ + group->field = BN_new(); + //group->a = BN_new(); + group->b = BN_new(); + group->order = BN_new(); + group->cofactor = BN_new(); + if (group->field == NULL || group->b == NULL || group->order == NULL + || group->cofactor == NULL) { + BN_free(group->field); + //BN_free(group->a); + BN_free(group->b); + BN_free(group->order); + BN_free(group->cofactor); + return 0; + } + //group->a_is_minus3 = 0; + return 1; +} + +static void ec_GFp_ed25519_group_finish(EC_GROUP *group) +{ + BN_free(group->field); + //BN_free(group->a); + BN_free(group->b); + BN_free(group->order); + BN_free(group->cofactor); + group->order = NULL; + group->cofactor = NULL; +} + +static void ec_GFp_ed25519_group_clear_finish(EC_GROUP *group) +{ + BN_clear_free(group->field); + //BN_clear_free(group->a); + BN_clear_free(group->b); + BN_clear_free(group->order); + BN_clear_free(group->cofactor); + group->order = NULL; + group->cofactor = NULL; +} + +static int ec_GFp_ed25519_group_copy(EC_GROUP *dest, const EC_GROUP *src) +{ + if (!BN_copy(dest->field, src->field)) + return 0; + /* + if (!BN_copy(dest->a, src->a)) + return 0; + */ + if (!BN_copy(dest->b, src->b)) + return 0; + + if (!BN_copy(dest->order, src->order)) + return 0; + + if (!BN_copy(dest->cofactor, src->cofactor)) + return 0; + + //dest->a_is_minus3 = src->a_is_minus3; + + return 1; +} + +static int ec_GFp_ed25519_group_set_curve(EC_GROUP *group, const BIGNUM *p, + const BIGNUM *a, const BIGNUM *b, + BN_CTX *ctx) +{ + int ret = 0; + BN_CTX *new_ctx = NULL; + BIGNUM *tmp_b; + + /* p must be a prime >= 5 */ + if (BN_num_bits(p) < 5 || !BN_is_odd(p)) { + return 0; + } + + if (ctx == NULL) { + ctx = new_ctx = BN_CTX_new(); + if (ctx == NULL) + return 0; + } + + BN_CTX_start(ctx); + tmp_b = BN_CTX_get(ctx); + if (tmp_b == NULL) + goto err; + + /* group->field */ + if (!BN_copy(group->field, p)) + goto err; + BN_set_negative(group->field, 0); + + /* ignore group->a */ + + /* group->b is used to save parameter d of the edwards curve */ + if (!BN_nnmod(tmp_b, b, p, ctx)) + goto err; + if (group->meth->field_encode) { + if (!group->meth->field_encode(group, group->b, tmp_b, ctx)) + goto err; + } else if (!BN_copy(group->b, tmp_b)) + goto err; + + ret = 1; + + err: + BN_CTX_end(ctx); + BN_CTX_free(new_ctx); + return ret; +} + +static int ec_GFp_ed25519_group_get_curve(const EC_GROUP *group, BIGNUM *p, + BIGNUM *a, BIGNUM *b, BN_CTX *ctx) +{ + if (p != NULL) { + if (!BN_copy(p, group->field)) + return 0; + } + + return 1; +} + +static int ec_GFp_ed25519_group_check_discriminant(const EC_GROUP *group, + BN_CTX *ctx) +{ + /* TODO */ + return 1; +} + +static int ec_GFp_ed25519_point_set_affine_coordinates(const EC_GROUP *group, + EC_POINT *point, + const BIGNUM *x, + const BIGNUM *y, + BN_CTX *ctx) +{ + BN_CTX *new_ctx = NULL; + const BIGNUM *z = BN_value_one(); + int ret = 0, Z_is_one; + + if (x == NULL || y == NULL) { + /* + * unlike for projective coordinates, we do not tolerate this + */ + ECerr(EC_F_EC_GFP_ED25519_POINT_SET_AFFINE_COORDINATES, + ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (ctx == NULL) { + ctx = new_ctx = BN_CTX_new(); + if (ctx == NULL) + return 0; + } + + if (x != NULL) { + if (!BN_nnmod(point->X, x, group->field, ctx)) + goto err; + if (group->meth->field_encode) { + if (!group->meth->field_encode(group, point->X, point->X, ctx)) + goto err; + } + } + + if (y != NULL) { + if (!BN_nnmod(point->Y, y, group->field, ctx)) + goto err; + if (group->meth->field_encode) { + if (!group->meth->field_encode(group, point->Y, point->Y, ctx)) + goto err; + } + } + + if (!BN_nnmod(point->Z, z, group->field, ctx)) + goto err; + Z_is_one = BN_is_one(point->Z); + if (group->meth->field_encode) { + if (Z_is_one && (group->meth->field_set_to_one != 0)) { + if (!group->meth->field_set_to_one(group, point->Z, ctx)) + goto err; + } else { + if (!group-> + meth->field_encode(group, point->Z, point->Z, ctx)) + goto err; + } + } + point->Z_is_one = Z_is_one; + + if (Z_is_one) { + if (!group->meth->field_mul(group, point->T, point->X, point->Y, ctx)) + goto err; + } else { + //TODO + } + + ret = 1; + + err: + BN_CTX_free(new_ctx); + return ret; +} + +static int ec_GFp_ed25519_point_get_affine_coordinates(const EC_GROUP *group, + const EC_POINT *point, + BIGNUM *x, BIGNUM *y, + BN_CTX *ctx) +{ + BN_CTX *new_ctx = NULL; + BIGNUM *Z, *Z_1; + const BIGNUM *Z_; + int ret = 0; + + if (EC_POINT_is_at_infinity(group, point)) { + return 0; + } + + if (ctx == NULL) { + ctx = new_ctx = BN_CTX_new(); + if (ctx == NULL) + return 0; + } + + BN_CTX_start(ctx); + Z = BN_CTX_get(ctx); + Z_1 = BN_CTX_get(ctx); + if (Z_1 == NULL) + goto err; + + /* transform (X, Y, Z) into (x, y) := (X/Z, Y/Z) */ + + if (group->meth->field_decode) { + if (!group->meth->field_decode(group, Z, point->Z, ctx)) + goto err; + Z_ = Z; + } else { + Z_ = point->Z; + } + + if (BN_is_one(Z_)) { + if (group->meth->field_decode) { + if (x != NULL) { + if (!group->meth->field_decode(group, x, point->X, ctx)) + goto err; + } + if (y != NULL) { + if (!group->meth->field_decode(group, y, point->Y, ctx)) + goto err; + } + } else { + if (x != NULL) { + if (!BN_copy(x, point->X)) + goto err; + } + if (y != NULL) { + if (!BN_copy(y, point->Y)) + goto err; + } + } + } else { + if (!group->meth->field_inv(group, Z_1, Z_, ctx)) { + goto err; + } + + if (x != NULL) { + /* + * in the Montgomery case, field_mul will cancel out Montgomery + * factor in X: + */ + if (!group->meth->field_mul(group, x, point->X, Z_1, ctx)) + goto err; + } + + if (y != NULL) { + /* + * in the Montgomery case, field_mul will cancel out Montgomery + * factor in Y: + */ + if (!group->meth->field_mul(group, y, point->Y, Z_1, ctx)) + goto err; + } + } + + ret = 1; + + err: + BN_CTX_end(ctx); + BN_CTX_free(new_ctx); + return ret; +} + +static int ec_GFp_ed25519_point_set_compressed_coordinates(const EC_GROUP *group, + EC_POINT *point, + const BIGNUM *x, + int y_bit, + BN_CTX *ctx) +{ + /* TODO */ + return 1; +} + +static size_t ec_GFp_ed25519_point2oct(const EC_GROUP *group, + const EC_POINT *point, + point_conversion_form_t form, + unsigned char *buf, size_t len, + BN_CTX *ctx) +{ + size_t ret; + BN_CTX *new_ctx = NULL; + int used_ctx = 0; + BIGNUM *x, *y; + size_t field_len, i, skip; + + form = POINT_CONVERSION_UNCOMPRESSED; + + if ((form != POINT_CONVERSION_COMPRESSED) + && (form != POINT_CONVERSION_UNCOMPRESSED) + && (form != POINT_CONVERSION_HYBRID)) { + ECerr(EC_F_EC_GFP_ED25519_POINT2OCT, EC_R_INVALID_FORM); + goto err; + } + + if (EC_POINT_is_at_infinity(group, point)) { + /* encodes to a single 0 octet */ + if (buf != NULL) { + if (len < 1) { + ECerr(EC_F_EC_GFP_ED25519_POINT2OCT, EC_R_BUFFER_TOO_SMALL); + return 0; + } + buf[0] = 0; + } + return 1; + } + + /* ret := required output buffer length */ + field_len = BN_num_bytes(group->field); + ret = + (form == + POINT_CONVERSION_COMPRESSED) ? 1 + field_len : 1 + 2 * field_len; + + /* if 'buf' is NULL, just return required length */ + if (buf != NULL) { + if (len < ret) { + ECerr(EC_F_EC_GFP_ED25519_POINT2OCT, EC_R_BUFFER_TOO_SMALL); + goto err; + } + + if (ctx == NULL) { + ctx = new_ctx = BN_CTX_new(); + if (ctx == NULL) + return 0; + } + + BN_CTX_start(ctx); + used_ctx = 1; + x = BN_CTX_get(ctx); + y = BN_CTX_get(ctx); + if (y == NULL) + goto err; + + if (!EC_POINT_get_affine_coordinates(group, point, x, y, ctx)) + goto err; + + if ((form == POINT_CONVERSION_COMPRESSED + || form == POINT_CONVERSION_HYBRID) && BN_is_odd(y)) + buf[0] = form + 1; + else + buf[0] = form; + + i = 1; + + skip = field_len - BN_num_bytes(x); + if (skip > field_len) { + ECerr(EC_F_EC_GFP_ED25519_POINT2OCT, ERR_R_INTERNAL_ERROR); + goto err; + } + while (skip > 0) { + buf[i++] = 0; + skip--; + } + skip = BN_bn2bin(x, buf + i); + i += skip; + if (i != 1 + field_len) { + ECerr(EC_F_EC_GFP_ED25519_POINT2OCT, ERR_R_INTERNAL_ERROR); + goto err; + } + + if (form == POINT_CONVERSION_UNCOMPRESSED + || form == POINT_CONVERSION_HYBRID) { + skip = field_len - BN_num_bytes(y); + if (skip > field_len) { + ECerr(EC_F_EC_GFP_ED25519_POINT2OCT, ERR_R_INTERNAL_ERROR); + goto err; + } + while (skip > 0) { + buf[i++] = 0; + skip--; + } + skip = BN_bn2bin(y, buf + i); + i += skip; + } + + if (i != ret) { + ECerr(EC_F_EC_GFP_ED25519_POINT2OCT, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + if (used_ctx) + BN_CTX_end(ctx); + BN_CTX_free(new_ctx); + return ret; + + err: + if (used_ctx) + BN_CTX_end(ctx); + BN_CTX_free(new_ctx); + return 0; +} + +static int ec_GFp_ed25519_oct2point(const EC_GROUP *group, + EC_POINT *point, const unsigned char *buf, + size_t len, BN_CTX *ctx) +{ + return ec_GFp_simple_oct2point(group, point, buf, len, ctx); +} + +static int ec_GFp_ed25519_point_add(const EC_GROUP *group, EC_POINT *r, + const EC_POINT *a, const EC_POINT *b, + BN_CTX *ctx) +{ + int (*field_mul) (const EC_GROUP *, BIGNUM *, const BIGNUM *, + const BIGNUM *, BN_CTX *); + const BIGNUM *p; + BN_CTX *new_ctx = NULL; + BIGNUM *n1, *n2, *n3, *n4, *n5, *n6; + int ret = 0; + + if (a == b) + return EC_POINT_dbl(group, r, a, ctx); + if (EC_POINT_is_at_infinity(group, a)) + return EC_POINT_copy(r, b); + if (EC_POINT_is_at_infinity(group, b)) + return EC_POINT_copy(r, a); + + field_mul = group->meth->field_mul; + p = group->field; + + if (ctx == NULL) { + ctx = new_ctx = BN_CTX_new(); + if (ctx == NULL) + return 0; + } + + BN_CTX_start(ctx); + n1 = BN_CTX_get(ctx); + n2 = BN_CTX_get(ctx); + n3 = BN_CTX_get(ctx); + n4 = BN_CTX_get(ctx); + n5 = BN_CTX_get(ctx); + n6 = BN_CTX_get(ctx); + if (n6 == NULL) + goto end; + + /* n1 = Ya - Xa */ + if (!BN_mod_sub_quick(n1, a->Y, a->X, p)) + goto end; + + /* n2 = Yb - Xb */ + if (!BN_mod_sub_quick(n2, b->Y, b->X, p)) + goto end; + + /* n1 = n1 * n2 */ + if (!field_mul(group, n1, n1, n2, ctx)) + goto end; + + /* n3 = Ya + Xa */ + if (!BN_mod_add_quick(n3, a->Y, a->X, p)) + goto end; + + /* n4 = Yb + Xb */ + if (!BN_mod_add_quick(n4, b->Y, b->X, p)) + goto end; + + /* n2 = n3 * n4 */ + if (!field_mul(group, n2, n3, n4, ctx)) + goto end; + + /* n3 = Ta * 2 * d * Tb */ + if (!BN_lshift1(n3, a->T)) + goto end; + if (!field_mul(group, n3, n3, group->b, ctx)) + goto end; + if (!field_mul(group, n3, n3, b->T, ctx)) + goto end; + + /* n4 = Za * 2 * Zb */ + if (!BN_lshift1(n4, a->Z)) + goto end; + if (!field_mul(group, n4, n4, b->Z, ctx)) + goto end; + + /* n5 = n2 - n1 */ + if (!BN_mod_sub_quick(n5, n2, n1, p)) + goto end; + + /* n6 = n2 + n1 */ + if (!BN_mod_add_quick(n6, n2, n1, p)) + goto end; + + /* n1 = n4 - n3 */ + if (!BN_mod_sub_quick(n1, n4, n3, p)) + goto end; + + /* n2 = n4 + n3 */ + if (!BN_mod_add_quick(n2, n4, n3, p)) + goto end; + + /* Xr = n5 * n1 */ + if (!field_mul(group, r->X, n5, n1, ctx)) + goto end; + + /* Yr = n6 * n2 */ + if (!field_mul(group, r->Y, n6, n2, ctx)) + goto end; + + /* Zr = n1 * n2 */ + if (!field_mul(group, r->Z, n1, n2, ctx)) + goto end; + r->Z_is_one = BN_is_one(r->Z); + + /* Tr = n5 * n6 */ + if (!field_mul(group, r->T, n5, n6, ctx)) + goto end; + + ret = 1; + + end: + BN_CTX_end(ctx); + BN_CTX_free(new_ctx); + return ret; +} + +static int ec_GFp_ed25519_point_dbl(const EC_GROUP *group, EC_POINT *r, + const EC_POINT *a, BN_CTX *ctx) +{ + int (*field_mul) (const EC_GROUP *, BIGNUM *, const BIGNUM *, + const BIGNUM *, BN_CTX *); + int (*field_sqr) (const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *); + const BIGNUM *p; + BN_CTX *new_ctx = NULL; + BIGNUM *n1, *n2, *n3, *n4, *n5, *n6; + int ret = 0; + + if (EC_POINT_is_at_infinity(group, a)) + return ec_GFp_ed25519_point_set_to_infinity(group, r); + + field_mul = group->meth->field_mul; + field_sqr = group->meth->field_sqr; + p = group->field; + + if (ctx == NULL) { + ctx = new_ctx = BN_CTX_new(); + if (ctx == NULL) + return 0; + } + + BN_CTX_start(ctx); + n1 = BN_CTX_get(ctx); + n2 = BN_CTX_get(ctx); + n3 = BN_CTX_get(ctx); + n4 = BN_CTX_get(ctx); + n5 = BN_CTX_get(ctx); + n6 = BN_CTX_get(ctx); + if (n6 == NULL) + goto err; + + /* n1 = Xa ^ 2 */ + if (!field_sqr(group, n1, a->X, ctx)) + goto err; + + /* n2 = Ya ^ 2 */ + if (!field_sqr(group, n2, a->Y, ctx)) + goto err; + + /* n3 = 2 * Za ^ 2 */ + if (!field_sqr(group, n3, a->Z, ctx)) + goto err; + if (!BN_lshift1(n3, n3)) + goto err; + + /* n4 = (Xa + Ya) ^ 2 */ + if (!BN_mod_add_quick(n4, a->X, a->Y, p)) + goto err; + if (!field_sqr(group, n4, n4, ctx)) + goto err; + + /* n4 = n4 - n1 - n2 */ + if (!BN_mod_sub_quick(n4, n4, n1, p)) + goto err; + if (!BN_mod_sub_quick(n4, n4, n2, p)) + goto err; + + /* n5 = n2 - n1 */ + if (!BN_mod_sub_quick(n5, n2, n1, p)) + goto err; + + /* n6 = n5 - n3 */ + if (!BN_mod_sub_quick(n6, n5, n3, p)) + goto err; + + /* n3 = -n1 - n2 */ + BN_set_negative(n1, 1); + if (!BN_mod_sub_quick(n3, n1, n2, p)) + goto err; + + /* Xr = n4 * n6 */ + if (!field_mul(group, r->X, n4, n6, ctx)) + goto err; + + /* Yr = n5 * n3 */ + if (!field_mul(group, r->Y, n5, n3, ctx)) + goto err; + + /* Zr = n6 * n5 */ + if (!field_mul(group, r->Z, n6, n5, ctx)) + goto err; + r->Z_is_one = BN_is_one(r->Z); + + /* Tr = n4 * n3 */ + if (!field_mul(group, r->T, n4, n3, ctx)) + goto err; + + ret = 1; + + err: + BN_CTX_end(ctx); + BN_CTX_free(new_ctx); + return ret; +} + +static int ec_GFp_ed25519_point_mul(const EC_GROUP *group, EC_POINT *r, + const BIGNUM *scalar, const EC_POINT *point, + BN_CTX *ctx) +{ + int ret = 0, i, bits; + EC_POINT *p = NULL; + + if (group->meth->add == NULL || group->meth->dbl == NULL) + return 0; + + if (!EC_POINT_set_to_infinity(group, r)) + goto err; + + if ((p = EC_POINT_new(group)) == NULL) + return 0; + + if (!EC_POINT_copy(p, point)) + goto err; + + bits = BN_num_bits(scalar); + + for (i = 0; i < bits; i++) { + if (BN_is_bit_set(scalar, i)) { + if (!group->meth->add(group, r, r, p, ctx)) + goto err; + } + + if (!group->meth->dbl(group, p, p, ctx)) + goto err; + } + + ret = 1; + + err: + EC_POINT_free(p); + return ret; +} + +static int ec_GFp_ed25519_point_invert(const EC_GROUP *group, EC_POINT *point, + BN_CTX *ctx) +{ + if (EC_POINT_is_at_infinity(group, point) || BN_is_zero(point->Y)) + /* point is its own inverse */ + return 1; + + if (!BN_usub(point->X, group->field, point->X)) + return 0; + + return BN_usub(point->T, group->field, point->T); +} + +static int ec_GFp_ed25519_points_mul(const EC_GROUP *group, EC_POINT *r, + const BIGNUM *scalar, size_t num, + const EC_POINT *points[], + const BIGNUM *scalars[], BN_CTX *ctx) +{ + int ret = 0; + EC_POINT *p = NULL; + + //if (num != 1 || group->meth->add == NULL || group->meth->dbl == NULL) + if (group->meth->add == NULL || group->meth->dbl == NULL) + return 0; + + if (scalar != NULL) { + if (!ec_GFp_ed25519_point_mul(group, r, scalar, group->generator, ctx)) + goto end; + } + + if (num == 0) { + ret = 1; + goto end; + } else if (num == 1){ + if ((p = EC_POINT_new(group)) == NULL) + goto end; + + if (!ec_GFp_ed25519_point_mul(group, p, scalars[0], points[0], ctx)) + goto end; + + if (scalar != NULL) { + if (!group->meth->add(group, r, r, p, ctx)) + goto end; + } else { + if (!EC_POINT_copy(r, p)) + goto end; + } + + ret = 1; + } + + end: + EC_POINT_free(p); + return ret; +} + +static int ec_GFp_ed25519_point_is_on_curve(const EC_GROUP *group, + const EC_POINT *point, BN_CTX *ctx) +{ + /* TODO */ + return 1; +} + +static int ec_GFp_ed25519_point_cmp(const EC_GROUP *group, const EC_POINT *a, + const EC_POINT *b, BN_CTX *ctx) +{ + /*- + * return values: + * -1 error + * 0 equal (in affine coordinates) + * 1 not equal + */ + + int (*field_mul) (const EC_GROUP *, BIGNUM *, const BIGNUM *, + const BIGNUM *, BN_CTX *); + BN_CTX *new_ctx = NULL; + BIGNUM *tmp1, *tmp2; + 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_POINT_is_at_infinity(group, b)) + return 1; + + if (a->Z_is_one && b->Z_is_one) { + return ((BN_cmp(a->X, b->X) == 0) && BN_cmp(a->Y, b->Y) == 0) ? 0 : 1; + } + + field_mul = group->meth->field_mul; + + if (ctx == NULL) { + ctx = new_ctx = BN_CTX_new(); + if (ctx == NULL) + return -1; + } + + BN_CTX_start(ctx); + tmp1 = BN_CTX_get(ctx); + tmp2 = BN_CTX_get(ctx); + if (tmp2 == NULL) + goto end; + + /*- + * We have to decide whether + * (X_a/Z_a, Y_a/Z_a) = (X_b/Z_b, Y_b/Z_b), + * or equivalently, whether + * (X_a*Z_b, Y_a*Z_b) = (X_b*Z_a, Y_b*Z_a). + */ + + if (!b->Z_is_one) { + if (!field_mul(group, tmp1, a->X, b->Z, ctx)) + goto end; + tmp1_ = tmp1; + } else + tmp1_ = a->X; + if (!a->Z_is_one) { + if (!field_mul(group, tmp2, b->X, a->Z, ctx)) + goto end; + tmp2_ = tmp2; + } else + tmp2_ = b->X; + + /* compare X_a*Z_b with X_b*Z_a */ + if (BN_cmp(tmp1_, tmp2_) != 0) { + ret = 1; /* points differ */ + goto end; + } + + if (!b->Z_is_one) { + if (!field_mul(group, tmp1, a->Y, b->Z, ctx)) + goto end; + tmp1_ = tmp1; + } else + tmp1_ = a->Y; + if (!a->Z_is_one) { + if (!field_mul(group, tmp2, b->Y, a->Z, ctx)) + goto end; + tmp2_ = tmp2; + } else + tmp2_ = b->Y; + + /* compare Y_a*Z_b with Y_b*Z_a */ + if (BN_cmp(tmp1_, tmp2_) != 0) { + ret = 1; /* points differ */ + goto end; + } + + /* points are equal */ + ret = 0; + + end: + BN_CTX_end(ctx); + BN_CTX_free(new_ctx); + return ret; +} + +/* r = pow(a, 2^q, p) */ +static int ec_GFp_ed25519_field_pow2(const EC_GROUP *group, BIGNUM *r, + const BIGNUM *a, int q, BN_CTX *ctx) +{ + if (!BN_copy(r, a)) + return 0; + + while (q > 0) { + if (!group->meth->field_sqr(group, r, r, ctx)) + return 0; + q--; + } + + return 1; +} + +static int ec_GFp_ed25519_field_inv(const EC_GROUP *group, BIGNUM *r, + const BIGNUM *a, BN_CTX *ctx) +{ + int (*field_mul) (const EC_GROUP *, BIGNUM *, const BIGNUM *, + const BIGNUM *, BN_CTX *); + int (*field_sqr) (const EC_GROUP *, BIGNUM *, const BIGNUM *, BN_CTX *); + BIGNUM *n1, *n2, *n3, *n4; + BN_CTX *new_ctx = NULL; + int ret = 0; + + if (ctx == NULL && (ctx = new_ctx = BN_CTX_secure_new()) == NULL) + return 0; + + field_mul = group->meth->field_mul; + field_sqr = group->meth->field_sqr; + + BN_CTX_start(ctx); + n1 = BN_CTX_get(ctx); + n2 = BN_CTX_get(ctx); + n3 = BN_CTX_get(ctx); + n4 = BN_CTX_get(ctx); + if (n4 == NULL) + goto err; + + /* z2: n1 = a ^ 2 */ + if (!field_sqr(group, n1, a, ctx)) + goto err; + + /* z9: n2 = pow2(n1, 2) * a */ + if (!ec_GFp_ed25519_field_pow2(group, n2, n1, 2, ctx)) + goto err; + if (!field_mul(group, n2, n2, a, ctx)) + goto err; + + /* z11: n3 = n2 * n1 */ + if (!field_mul(group, n3, n2, n1, ctx)) + goto err; + + /* z2_5_0: n4 = n3 ^ 2 * n2 */ + if (!field_sqr(group, n4, n3, ctx)) + goto err; + if (!field_mul(group, n4, n4, n2, ctx)) + goto err; + + /* z2_10_0: n1 = pow2(n4, 5) * n4 */ + if (!ec_GFp_ed25519_field_pow2(group, n1, n4, 5, ctx)) + goto err; + if (!field_mul(group, n1, n1, n4, ctx)) + goto err; + + /* z2_20_0: n2 = pow2(n1, 10) * n1 */ + if (!ec_GFp_ed25519_field_pow2(group, n2, n1, 10, ctx)) + goto err; + if (!field_mul(group, n2, n2, n1, ctx)) + goto err; + + /* z2_40_0: n4 = pow2(n2, 20) * n2 */ + if (!ec_GFp_ed25519_field_pow2(group, n4, n2, 20, ctx)) + goto err; + if (!field_mul(group, n4, n4, n2, ctx)) + goto err; + + /* z2_50_0: n2 = pow2(n4, 10) * n1 */ + if (!ec_GFp_ed25519_field_pow2(group, n2, n4, 10, ctx)) + goto err; + if (!field_mul(group, n2, n2, n1, ctx)) + goto err; + + /* z2_100_0: n4 = pow2(n2, 10) * n2 */ + if (!ec_GFp_ed25519_field_pow2(group, n4, n2, 50, ctx)) + goto err; + if (!field_mul(group, n4, n4, n2, ctx)) + goto err; + + /* z2_200_0: n1 = pow2(n4, 100) * n4 */ + if (!ec_GFp_ed25519_field_pow2(group, n1, n4, 100, ctx)) + goto err; + if (!field_mul(group, n1, n1, n4, ctx)) + goto err; + + /* z2_250_0: n4 = pow2(n1, 100) * n2 */ + if (!ec_GFp_ed25519_field_pow2(group, n4, n1, 50, ctx)) + goto err; + if (!field_mul(group, n4, n4, n2, ctx)) + goto err; + + /* r = pow2(n4, 5) * n3 */ + if (!ec_GFp_ed25519_field_pow2(group, r, n4, 5, ctx)) + goto err; + if (!field_mul(group, r, r, n3, ctx)) + goto err; + + ret = 1; + + err: + BN_CTX_end(ctx); + BN_CTX_free(new_ctx); + return ret; +} + +const EC_METHOD *EC_GFp_ed25519_method(void) +{ + static const EC_METHOD ret = { + EC_FLAGS_CUSTOM_CURVE, + NID_ED25519, + ec_GFp_ed25519_group_init, + ec_GFp_ed25519_group_finish, + ec_GFp_ed25519_group_clear_finish, + ec_GFp_ed25519_group_copy, + ec_GFp_ed25519_group_set_curve, + ec_GFp_ed25519_group_get_curve, + ec_GFp_simple_group_get_degree, + ec_group_simple_order_bits, + ec_GFp_ed25519_group_check_discriminant, + ec_GFp_ed25519_point_init, + ec_GFp_ed25519_point_finish, + ec_GFp_ed25519_point_clear_finish, + ec_GFp_ed25519_point_copy, + ec_GFp_ed25519_point_set_to_infinity, + 0, /* point_set_Jprojective_coordinates_GFp */ + 0, /* point_get_Jprojective_coordinates_GFp */ + ec_GFp_ed25519_point_set_affine_coordinates, + ec_GFp_ed25519_point_get_affine_coordinates, + ec_GFp_ed25519_point_set_compressed_coordinates, + ec_GFp_ed25519_point2oct, + ec_GFp_ed25519_oct2point, + ec_GFp_ed25519_point_add, + ec_GFp_ed25519_point_dbl, + ec_GFp_ed25519_point_invert, + ec_GFp_ed25519_point_is_at_infinity, + ec_GFp_ed25519_point_is_on_curve, + ec_GFp_ed25519_point_cmp, + ec_GFp_simple_make_affine, + 0, /* points_make_affine */ + ec_GFp_ed25519_points_mul, + 0, /* precompute_mult */ + 0, /* have_precompute_mult */ + ec_GFp_simple_field_mul, + ec_GFp_simple_field_sqr, + 0, /* field_div */ + ec_GFp_ed25519_field_inv, + 0, /* field_encode */ + 0, /* field_decode */ + 0, /* field_set_to_one */ + ec_key_simple_priv2oct, + ec_key_simple_oct2priv, + 0, /* set private */ + ec_key_simple_generate_key, + ec_key_simple_check_key, + ec_key_simple_generate_public_key, + 0, /* keycopy */ + 0, /* keyfinish */ + ecdh_simple_compute_key, + 0, /* field_inverse_mod_ord */ + 0, /* blind_coordinates */ + 0, /* ladder_pre */ + 0, /* ladder_step */ + 0 /* ladder_post */ + }; + + return &ret; +} diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 598937560..99c06a210 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1,4 +1,4 @@ -# Copyright 1999-2021 The OpenSSL Project Authors. All Rights Reserved. +# Copyright 1999-2022 The OpenSSL Project Authors. All Rights Reserved. # # Licensed under the OpenSSL license (the "License"). You may not use # this file except in compliance with the License. You can obtain a copy @@ -539,6 +539,9 @@ EC_F_EC_GF2M_SIMPLE_POINT_SET_AFFINE_COORDINATES:163:\ ec_GF2m_simple_point_set_affine_coordinates EC_F_EC_GF2M_SIMPLE_SET_COMPRESSED_COORDINATES:164:\ ec_GF2m_simple_set_compressed_coordinates +EC_F_EC_GFP_ED25519_POINT2OCT:299:ec_GFp_ed25519_point2oct +EC_F_EC_GFP_ED25519_POINT_SET_AFFINE_COORDINATES:300:\ + ec_GFp_ed25519_point_set_affine_coordinates EC_F_EC_GFP_MONT_FIELD_DECODE:133:ec_GFp_mont_field_decode EC_F_EC_GFP_MONT_FIELD_ENCODE:134:ec_GFp_mont_field_encode EC_F_EC_GFP_MONT_FIELD_INV:297:ec_GFp_mont_field_inv diff --git a/include/openssl/ec.h b/include/openssl/ec.h index 2255f62db..9a196a19b 100644 --- a/include/openssl/ec.h +++ b/include/openssl/ec.h @@ -95,6 +95,7 @@ const EC_METHOD *EC_GFp_nistp521_method(void); const EC_METHOD *EC_GF2m_simple_method(void); # endif +const EC_METHOD *EC_GFp_ed25519_method(void); /********************************************************************/ /* EC_GROUP functions */ diff --git a/include/openssl/ecerr.h b/include/openssl/ecerr.h index 1cf9053cc..d48c3705b 100644 --- a/include/openssl/ecerr.h +++ b/include/openssl/ecerr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -75,6 +75,8 @@ int ERR_load_EC_strings(void); # define EC_F_EC_GF2M_SIMPLE_POINT_GET_AFFINE_COORDINATES 162 # define EC_F_EC_GF2M_SIMPLE_POINT_SET_AFFINE_COORDINATES 163 # define EC_F_EC_GF2M_SIMPLE_SET_COMPRESSED_COORDINATES 164 +# define EC_F_EC_GFP_ED25519_POINT2OCT 299 +# define EC_F_EC_GFP_ED25519_POINT_SET_AFFINE_COORDINATES 300 # define EC_F_EC_GFP_MONT_FIELD_DECODE 133 # define EC_F_EC_GFP_MONT_FIELD_ENCODE 134 # define EC_F_EC_GFP_MONT_FIELD_INV 297 diff --git a/test/ec_elgamal_internal_test.c b/test/ec_elgamal_internal_test.c index ae197c11b..2ab5da37c 100644 --- a/test/ec_elgamal_internal_test.c +++ b/test/ec_elgamal_internal_test.c @@ -310,6 +310,9 @@ static int ec_elgamal_tests(void) if (!TEST_true(ec_elgamal_test(NID_sm2))) return 0; + if (!TEST_true(ec_elgamal_test(NID_ED25519))) + return 0; + return 1; } diff --git a/util/libcrypto.num b/util/libcrypto.num index 255d9cfc3..2213a775d 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -4662,3 +4662,4 @@ EC_ELGAMAL_decrypt 6608 1_1_1h EXIST::FUNCTION:EC,EC_ELGAMA EC_ELGAMAL_CTX_set_decrypt_table 6609 1_1_1h EXIST::FUNCTION:EC,EC_ELGAMAL EC_ELGAMAL_DECRYPT_TABLE_free 6610 1_1_1h EXIST::FUNCTION:EC,EC_ELGAMAL EC_ELGAMAL_DECRYPT_TABLE_new 6611 1_1_1h EXIST::FUNCTION:EC,EC_ELGAMAL +EC_GFp_ed25519_method 6612 1_1_1h EXIST::FUNCTION:EC