Merge pull request #9828

6c39594 crypto: add Ed25519->X25519 conversion functions (jeffro256)

ACKs: UkoeHB, j-berman
This commit is contained in:
tobtoht
2026-07-22 11:34:31 +00:00
2 changed files with 58 additions and 1 deletions
+34 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -3904,6 +3904,39 @@ int sc_isnonzero(const unsigned char *s) {
s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1;
}
static int edwardsYZ_to_x25519(unsigned char *xbytes, const fe Y, const fe Z) {
//! @see Section 4.1 of RFC 7748: https://www.rfc-editor.org/rfc/rfc7748.html#section-4.1
//
// y = Y/Z
// x_mont = (1 + y) / (1 - y)
// = (1 + Y/Z) / (1 - Y/Z)
// = (Z + Y) / (Z - Y)
fe tmp0;
fe tmp1;
int r;
fe_add(tmp0, Z, Y); // Z + Y
fe_sub(tmp1, Z, Y); // Z - Y
r = -!fe_isnonzero(tmp1); // succeed iff 0 != (Z - Y). AKA fail if identity point or some invalid reprs
fe_invert(tmp1, tmp1); // 1/(Z - Y)
fe_mul(tmp0, tmp0, tmp1); // (Z + Y) / (Z - Y)
fe_tobytes(xbytes, tmp0); // tobytes((Z + Y) / (Z - Y))
return r; // 0 on success, otherwise -1
}
int ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h) {
return edwardsYZ_to_x25519(xbytes, h->Y, h->Z);
}
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s) {
ge_p3 h;
const int r = ge_frombytes_vartime(&h, s);
if (0 != r)
return r;
return edwardsYZ_to_x25519(xbytes, h.Y, h.Z);
}
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
// https://eprint.iacr.org/2008/522
// X == T == 0 and Y/Z == 1
+24
View File
@@ -165,6 +165,30 @@ void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b,
int sc_check(const unsigned char *);
int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
/**
* @brief Convert Ed25519 y-coord to X25519 x-coord, AKA "ConvertPointE()" in the Carrot spec
* @param[out] xbytes X25519 x-coord as a 255-bit little endian integer
* @param h Ed25519 point in projective representation
* @return 0 on success, otherwise non-0 on failure
*
* Returns failure on Ed25519's identity point. Assumes `h` has valid field element representations,
* that Z != 0, and that Y/Z is a valid y-coordinate for Ed25519. Returns success iff Y != Z (Y == Z
* corresponds with the identity point in valid point representations). The runtime is constant.
*/
int ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h);
/**
* @brief Convert Ed25519 y-coord to X25519 x-coord, AKA "ConvertPointE()" in the Carrot spec
* @param[out] xbytes X25519 x-coord as a 255-bit little endian integer
* @param s Ed25519 point in compressed Y representation
* @return 0 on success, otherwise non-0 on failure
*
* Returns failure on Ed25519's identity point (repr {1, 0, 0, ...}). Otherwise, returns success iff
* `s` is a valid compressed Y representation of an Ed25519 point. The runtime is variable *only* in
* whether `s` is a valid representation. In other words, for all valid Ed25519 points, the runtime
* is constant.
*/
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s);
// internal
uint64_t load_3(const unsigned char *in);
uint64_t load_4(const unsigned char *in);