1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00

Prepare for EdDSA lengths not 7 mod 8. (NFC)

An EdDSA public key is transmitted as an integer just big enough to
store the curve point's y value plus one extra bit. The topmost bit of
the integer is set to indicate the parity of the x value.

In Ed25519, this works out very nicely: the curve modulus is just
under 2^255, so the y value occupies all but one bit of the topmost
byte of the integer, leaving the y parity to fit neatly in the spare
bit. But in Ed448, the curve modulus occupies a whole number of bytes
(56 of them) and the format standardises that you therefore have to
use a 57-byte string for the public key, with the highest-order
(final) byte having zero in the low 7 of its bits.

To support this, I've arranged that the Edwards-curve modes in
sshecc.c now set curve->fieldBytes to the right number of bytes to
hold curve->fieldBits+1 bits, instead of the obvious curve->fieldBits.
Doing it that way means fewer special cases everywhere else in the
code.

Also, this means changing the order of the validity check: we now wait
until after we've checked and cleared the topmost bit to see if the
rest is within range. That deals with checking the intervening 7 zero
bits.
This commit is contained in:
Simon Tatham 2020-03-02 07:02:08 +00:00
parent 6c226e4c57
commit 4e6c69d5df

View File

@ -44,19 +44,20 @@
*/
static void initialise_common(
struct ec_curve *curve, EllipticCurveType type, mp_int *p)
struct ec_curve *curve, EllipticCurveType type, mp_int *p,
unsigned extrabits)
{
curve->type = type;
curve->p = mp_copy(p);
curve->fieldBits = mp_get_nbits(p);
curve->fieldBytes = (curve->fieldBits + 7) / 8;
curve->fieldBytes = (curve->fieldBits + extrabits + 7) / 8;
}
static void initialise_wcurve(
struct ec_curve *curve, mp_int *p, mp_int *a, mp_int *b,
mp_int *nonsquare, mp_int *G_x, mp_int *G_y, mp_int *G_order)
{
initialise_common(curve, EC_WEIERSTRASS, p);
initialise_common(curve, EC_WEIERSTRASS, p, 0);
curve->w.wc = ecc_weierstrass_curve(p, a, b, nonsquare);
@ -68,7 +69,7 @@ static void initialise_mcurve(
struct ec_curve *curve, mp_int *p, mp_int *a, mp_int *b,
mp_int *G_x, unsigned log2_cofactor)
{
initialise_common(curve, EC_MONTGOMERY, p);
initialise_common(curve, EC_MONTGOMERY, p, 0);
curve->m.mc = ecc_montgomery_curve(p, a, b);
curve->m.log2_cofactor = log2_cofactor;
@ -81,7 +82,9 @@ static void initialise_ecurve(
mp_int *nonsquare, mp_int *G_x, mp_int *G_y, mp_int *G_order,
unsigned log2_cofactor)
{
initialise_common(curve, EC_EDWARDS, p);
/* Ensure curve->fieldBytes is long enough to store an extra bit
* for a compressed point */
initialise_common(curve, EC_EDWARDS, p, 1);
curve->e.ec = ecc_edwards_curve(p, d, a, nonsquare);
curve->e.log2_cofactor = log2_cofactor;
@ -366,16 +369,15 @@ static mp_int *BinarySource_get_mp_le(BinarySource *src)
}
#define get_mp_le(src) BinarySource_get_mp_le(BinarySource_UPCAST(src))
static void BinarySink_put_mp_le_unsigned(BinarySink *bs, mp_int *x)
static void BinarySink_put_mp_le_fixedlen(BinarySink *bs, mp_int *x,
size_t bytes)
{
size_t bytes = (mp_get_nbits(x) + 7) / 8;
put_uint32(bs, bytes);
for (size_t i = 0; i < bytes; ++i)
put_byte(bs, mp_get_byte(x, i));
}
#define put_mp_le_unsigned(bs, x) \
BinarySink_put_mp_le_unsigned(BinarySink_UPCAST(bs), x)
#define put_mp_le_fixedlen(bs, x, bytes) \
BinarySink_put_mp_le_fixedlen(BinarySink_UPCAST(bs), x, bytes)
static WeierstrassPoint *ecdsa_decode(
ptrlen encoded, const struct ec_curve *curve)
@ -494,20 +496,20 @@ static void BinarySink_put_wpoint(
static EdwardsPoint *eddsa_decode(ptrlen encoded, const struct ec_curve *curve)
{
assert(curve->type == EC_EDWARDS);
assert(curve->fieldBits % 8 == 7);
mp_int *y = mp_from_bytes_le(encoded);
if (mp_get_nbits(y) > curve->fieldBits+1) {
/* The topmost bit of the encoding isn't part of y, so it stores
* the bottom bit of x. Extract it, and zero that bit in y. */
unsigned desired_x_parity = mp_get_bit(y, curve->fieldBytes * 8 - 1);
mp_set_bit(y, curve->fieldBytes * 8 - 1, 0);
/* What's left should now be within the range of the curve's modulus */
if (mp_cmp_hs(y, curve->p)) {
mp_free(y);
return NULL;
}
/* The topmost bit of the encoding isn't part of y, so it stores
* the bottom bit of x. Extract it, and zero that bit in y. */
unsigned desired_x_parity = mp_get_bit(y, curve->fieldBits);
mp_set_bit(y, curve->fieldBits, 0);
EdwardsPoint *P = ecc_edwards_point_new_from_y(
curve->e.ec, y, desired_x_parity);
mp_free(y);
@ -758,7 +760,7 @@ static void eddsa_private_blob(ssh_key *key, BinarySink *bs)
/* EdDSA stores the private key integer little-endian and unsigned */
assert(ek->privateKey);
put_mp_le_unsigned(bs, ek->privateKey);
put_mp_le_fixedlen(bs, ek->privateKey, ek->curve->fieldBytes);
}
static ssh_key *ecdsa_new_priv(const ssh_keyalg *alg, ptrlen pub, ptrlen priv)
@ -846,7 +848,7 @@ static void eddsa_openssh_blob(ssh_key *key, BinarySink *bs)
ptrlen pub = make_ptrlen(pub_sb->s + 4, pub_sb->len - 4);
strbuf *priv_sb = strbuf_new_nm();
put_mp_le_unsigned(priv_sb, ek->privateKey);
put_mp_le_fixedlen(priv_sb, ek->privateKey, ek->curve->fieldBytes);
ptrlen priv = make_ptrlen(priv_sb->s + 4, priv_sb->len - 4);
put_stringpl(bs, pub);