Hi,
Nikos pointed out to me that there's a slight difference between curve25519 as implemented by Nettle and the spec in RFC 7748.
As far as I see, the difference is that RFC 7748 says that bit 255 of an encoded x coordinates is ignored. Or more precisely, the high bit of the 31:st octet in the x input string is cleared before convertion into an integer. While nettle's curve25519_mul includes it in the computation, with the usual wrap-around, 2^255 = 19 (mod p). I don't see any difference in handling scalars. Do you agree?
So I'm considering this change,
diff --git a/curve25519-mul.c b/curve25519-mul.c index adb20cb..f5127d7 100644 --- a/curve25519-mul.c +++ b/curve25519-mul.c @@ -72,7 +72,11 @@ curve25519_mul (uint8_t *q, const uint8_t *n, const uint8_t *p) itch = ecc->p.size * 12; scratch = gmp_alloc_limbs (itch);
+ /* Note that 255 % GMP_NUMB_BITS == 0 isn't supported, so x1 always + holds at least 256 bits. */ mpn_set_base256_le (x1, ecc->p.size, p, CURVE25519_SIZE); + /* Clear bit 255, as required by RFC 7748. */ + x1[255/GMP_NUMB_BITS] &= ~((mp_limb_t) 1 << (255 % GMP_NUMB_BITS));
/* Initialize, x2 = x1, z2 = 1 */ mpn_copyi (x2, x1, ecc->p.size);
I wouldn't expect any problems from this, its a corner case with input values which are arguably invalid.
The motivation in the RFC, as I understand it, is to leave open for protocols to use the top bit for their own, without bothering to clear it before invoking curve25519. Which at first seems a bit silly, but there's some value in not leaving corner cases implementation defined, and it would maybe have been even more silly to require that implementations do wraparound of that improper high bit.
This change would also need some updates of testcases and documentation.
Regards, /Niels