From 025475c248a29c258171156d8ced8d8d646a340a Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Mon, 20 Apr 2020 21:34:39 +0200 Subject: [PATCH] Add points read/write support for Edwards curves Note that decoding a point for curves over prime field p with p = 1 (mod 8) is not supported. This is not the case of Ed25519 and Ed448. Supporting that would require a more complex algorithm to compute the square root, for instance the Tonelli-Shanks algorithm. Note that the square root formulas come from the RFC Erratas 5758 and 5759. FIXME: mbedtls_ecp_point_read_binary_ed() has to be defined after the mbedtls_mpi_xxx_mod functions as some computation are needed to read a point. Currently mbedtls_ecp_point_read_binary() is defined before those functions, so a forward declaration is needed. This should probably be fixed by reshuffling functions in ecp.c. Signed-off-by: Aurelien Jarno --- library/ecp.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/library/ecp.c b/library/ecp.c index fc43ef8d1e40..ab3670261f26 100644 --- a/library/ecp.c +++ b/library/ecp.c @@ -799,11 +799,44 @@ int mbedtls_ecp_point_write_binary( const mbedtls_ecp_group *grp, } } #endif +#if defined(ECP_EDWARDS) + if( mbedtls_ecp_get_type( grp ) == MBEDTLS_ECP_TYPE_EDWARDS ) + { + /* Only the compressed format is defined for Edwards curves. */ + ECP_VALIDATE_RET( format == MBEDTLS_ECP_PF_COMPRESSED ); + + /* We need to add an extra bit to store the least significant bit of X. */ + plen = ( mbedtls_mpi_bitlen( &grp->P ) + 1 + 7 ) >> 3; + + *olen = plen; + if( buflen < *olen ) + return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL ); + + if( mbedtls_mpi_cmp_int( &P->Z, 1 ) != 0 ) + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + + MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary_le( &P->Y, buf, plen ) ); + + /* Store the least significant bit of X into the most significant + * bit of the final octet. */ + if( mbedtls_mpi_get_bit( &P->X, 0 )) + buf[plen - 1] |= 0x80; + } +#endif cleanup: return( ret ); } +#if defined(ECP_EDWARDS) +/* FIXME: The function is defined later in this file as reading a binary + point on an Edwards curve needs computation and thus access to the + mbedtls_mpi_xxx_mod functions. */ +static int mbedtls_ecp_point_read_binary_ed( const mbedtls_ecp_group *grp, + mbedtls_ecp_point *pt, + const unsigned char *buf, size_t ilen ); +#endif + /* * Import a point from unsigned binary data (SEC1 2.3.4 and RFC7748) */ @@ -861,6 +894,12 @@ int mbedtls_ecp_point_read_binary( const mbedtls_ecp_group *grp, MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &pt->Z, 1 ) ); } #endif +#if defined(ECP_EDWARDS) + if( mbedtls_ecp_get_type( grp ) == MBEDTLS_ECP_TYPE_EDWARDS ) + { + MBEDTLS_MPI_CHK( mbedtls_ecp_point_read_binary_ed( grp, pt, buf, ilen ) ); + } +#endif cleanup: return( ret ); @@ -1156,6 +1195,131 @@ static inline int mbedtls_mpi_shift_l_mod( const mbedtls_ecp_group *grp, return( ret ); } +#if defined(ECP_EDWARDS) +/* + * Import and Edward point from binary data (RFC8032) + */ +static int mbedtls_ecp_point_read_binary_ed( const mbedtls_ecp_group *grp, + mbedtls_ecp_point *pt, + const unsigned char *buf, size_t ilen ) +{ + int ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + mbedtls_mpi u, v, t; + mbedtls_mpi_uint r; + size_t plen; + int x_0; + + /* We need to add an extra bit to store the least significant bit of X. */ + plen = ( mbedtls_mpi_bitlen( &grp->P ) + 1 + 7 ) >> 3; + + if( plen != ilen ) + return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA ); + + mbedtls_mpi_init( &u ); mbedtls_mpi_init( &v ); mbedtls_mpi_init( &t ); + + /* Interpret the string as an integer in little-endian representation. */ + MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary_le( &pt->Y, buf, plen ) ); + + /* High bit of last digit is the least significant bit of the + * x-coordinate. Save it and clear it. */ + x_0 = mbedtls_mpi_get_bit ( &pt->Y, (plen * 8) - 1 ); + MBEDTLS_MPI_CHK( mbedtls_mpi_set_bit( &pt->Y, (plen * 8) - 1, 0 ) ); + + /* If the resulting y-coordinate is >= p, decoding fails. */ + if( mbedtls_mpi_cmp_mpi( &pt->Y, &grp->P ) >= 0 ) + { + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + + /* To recover the x-coordinates, the curve equation implies + x^2 = (y^2 - 1) / (d y^2 - a) (mod p). The denominator is always + non-zero mod p. Let u = y^2 - 1 and v = d y^2 - a. */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &u, &pt->Y, &pt->Y ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &v, &grp->B, &u ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mod( grp, &v, &v, &grp->A ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &u, &u, 1 ) ); MOD_SUB( u ); + + /* We use different algorithm to compute the square root of (u/v) + * depending on p (mod 8). */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mod_int( &r, &grp->P, 8 ) ); + + if( r == 3 || r == 7 ) + { + /* This corresponds to p = 3 (mod 4) and for instance Ed448. * + * The candidate root is x = sqrt(u/v) = u (u v)^((p-3)/4) */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &pt->X, &u, &v ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &t, &grp->P, 3 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &t, 2 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &pt->X, &pt->X, &t, &grp->P, NULL ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &pt->X, &pt->X, &u ) ); + + /* If v * x^2 = u (mod p), the recovered x-coordinate is x. Otherwise, + * no square root exists, and the decoding fails. */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &t, &pt->X, &pt->X ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &t, &t, &v ) ); + if( mbedtls_mpi_cmp_mpi( &t, &u ) != 0 ) + { + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + } + else if( r == 5 ) + { + /* This corresponds for instance to Ed25519. The candidate root is + * x = sqrt(u/v) = u (u v)^((p-5)/8) */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &pt->X, &u, &v ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( &t, &grp->P, 5 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &t, 3 ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &pt->X, &pt->X, &t, &grp->P, NULL ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &pt->X, &pt->X, &u ) ); + + /* If v * x^2 = u (mod p), x is a square root. */ + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &t, &pt->X, &pt->X ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &t, &t, &v ) ); + if( mbedtls_mpi_cmp_mpi( &t, &u ) != 0 ) + { + /* Otherwise if v x^2 = -u (mod p), x * 2^((p-1)/4) is a square + * root. */ + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &t, &grp->P, &t ) ); + if( mbedtls_mpi_cmp_mpi( &t, &u ) != 0 ) + { + /* Otherwise decoding fails. */ + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + + /* x *= 2^((p-1)/4) */ + MBEDTLS_MPI_CHK( mbedtls_mpi_read_string( &t, 16, "2B8324804FC1DF0B2B4D00993DFBD7A72F431806AD2FE478C4EE1B274A0EA0B0" ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mod( grp, &pt->X, &pt->X, &t ) ); + } + } + else + { + /* Not implemented for p = 1 (mod 8). */ + ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + goto cleanup; + } + + /* Use the x_0 bit to select the right square root. */ + if( mbedtls_mpi_cmp_int( &pt->X, 0 ) == 0 && x_0 == 1 ) + { + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + if( mbedtls_mpi_get_bit( &pt->X, 0 ) != x_0 ) + MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &pt->X, &grp->P, &pt->X ) ); + + /* Set Z to 1 in projective coordinates. */ + MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &pt->Z, 1 ) ); + +cleanup: + mbedtls_mpi_free( &u ); mbedtls_mpi_free( &v ); mbedtls_mpi_free( &t ); + + return( ret ); +} +#endif + #if defined(ECP_SHORTWEIERSTRASS) /* * For curves in short Weierstrass form, we do all the internal operations in