Commit 91908fe3 authored by Wim Lewis's avatar Wim Lewis

Implementations of sqrt in P-192 and P-224.

Documentation, formatting, additional test cases, general cleanup.
parent 05624cde
......@@ -180,7 +180,7 @@ hogweed_SOURCES = sexp.c sexp-format.c \
ecc-mul-g-eh.c ecc-mul-a-eh.c \
ecc-mul-g.c ecc-mul-a.c ecc-hash.c ecc-random.c \
ecc-point.c ecc-point-from-octets.c ecc-point-compact.c \
ecc-point-recover.c \
ecc-point-recover.c tonelli-shanks.c \
ecc-scalar.c ecc-point-mul.c ecc-point-mul-g.c \
ecc-ecdsa-sign.c ecdsa-sign.c \
ecc-ecdsa-verify.c ecdsa-verify.c ecdsa-keygen.c \
......
......@@ -110,6 +110,42 @@ ecc_192_modp (const struct ecc_modulo *m UNUSED, mp_limb_t *rp)
#define ecc_192_modp ecc_mod
#endif
static void
ecc_192_sqrt (const struct ecc_modulo * m,
mp_limb_t * rp,
const mp_limb_t * cp,
mp_limb_t * scratch)
{
mp_size_t size = m->size;
/* This computes the square root modulo p192 using the identity:
sqrt(c) = c^(2^190 - 2^62) (mod P-192)
which can be seen as a special case of Tonelli-Shanks with e=1.
*/
/* We use our scratch space for three temporaries (TA, TB, TC) all of
which are 2*size long to allow for multiplication/squaring */
#define TA scratch
#define TB (scratch + 2*size)
#define TC (scratch + 4*size)
ecc_mod_pow2n_mul(m, TA, cp, cp, TB, 1); /* [1] TA <-- c^3 */
ecc_mod_pow2n_mul(m, TB, TA, TA, TC, 2); /* [2] TB <-- c^(2^4 - 1) */
ecc_mod_pow2n_mul(m, TA, TB, TB, TC, 4); /* [3] TA <-- c^(2^8 - 1) */
ecc_mod_pow2n_mul(m, TB, TA, TA, TC, 8); /* [4] TB <-- c^(2^16 - 1) */
ecc_mod_pow2n_mul(m, TA, TB, TB, TC, 16); /* [5] TA <-- c^(2^32 - 1) */
ecc_mod_pow2n_mul(m, TB, TA, TA, TC, 32); /* [6] TB <-- c^(2^64 - 1) */
ecc_mod_pow2n_mul(m, TC, TB, TB, TA, 64); /* [7] TC <-- c^(2^128 - 1) */
ecc_mod_pow2n (m, rp, TC, TA, 62); /* [8] r <-- c^(2^190 - 2^62) */
#undef TA
#undef TB
#undef TC
}
const struct ecc_curve _nettle_secp_192r1 =
{
{
......@@ -118,7 +154,7 @@ const struct ecc_curve _nettle_secp_192r1 =
ECC_BMODP_SIZE,
ECC_REDC_SIZE,
ECC_MOD_INV_ITCH (ECC_LIMB_SIZE),
0,
6 * ECC_LIMB_SIZE,
ecc_p,
ecc_Bmodp,
......@@ -130,7 +166,7 @@ const struct ecc_curve _nettle_secp_192r1 =
ecc_192_modp,
ecc_mod_inv,
NULL,
NULL,
ecc_192_sqrt,
},
{
192,
......
......@@ -62,6 +62,75 @@ ecc_224_modp (const struct ecc_modulo *m, mp_limb_t *rp);
# error Configuration error
#endif
static void
ecc_224_sqrt (const struct ecc_modulo *m,
mp_limb_t * rp,
const mp_limb_t * cp,
mp_limb_t * scratch)
{
mp_size_t size = m->size;
unsigned int i;
#define S1 (scratch)
#define S1a (scratch + size)
#define S2 (scratch + 2*size)
#define S3 (scratch + 4*size)
/* Setup for the Tonelli-Shanks algorithm */
/* scratch is assumed to have space for XXX */
/* The algorithm starts by decomposing p (the modulus) into e and s s.t. (p-1) = s*2^e */
/* These are precomputed for us as e = ECC_SQRT_E = 96, and s = 2^128 - 1 */
/* We also have a precomputed ecc_sqrt_z = z^s where z is an arbitrary quadratic nonresidue in p */
/* compute S1 = c^(2^64 - 1), S1a = c^(2^32 - 1) */
const mp_limb_t *f = cp;
for (i = 0; i < 6; i++)
{
if (f == S1)
{
mpn_copyi (S3, S1, size);
f = S3;
}
ecc_mod_pow2n_mul (m, S1, f, f, S2, 1 << i);
f = S1;
}
/* compute S2 = c^(2^127 - 1) */
mpn_copyi (S1a, S3, size);
ecc_mod_pow2n_mul (m, S2, S1, S1a, S3, 32); /* S2 = c^(2^96 - 1) */
for (i = 0; i < 31; i++)
{
ecc_mod_sqr (m, S3, S2);
ecc_mod_mul (m, S2, S3, cp);
}
/* Finish up with:
R <-- c^( (s+1) / 2 ) = c^(2^127)
c <-- z^s (precomputed)
t <-- c^s = c^(2^128 - 1)
*/
ecc_mod_mul (m, S1, S2, cp);
ecc_mod_sqr (m, S3, S2);
ecc_mod_mul (m, S1a, S3, cp);
mpn_copyi (rp, S1, size);
mpn_copyi (S1, ecc_sqrt_z, size);
/* We are now set up for the Tonelli-Shanks inner loop:
R is in *rp
c is at S1
t is at S1 + size */
if (!ecc_tonelli_shanks (m, rp, ECC_SQRT_E, ecc_unit, S1))
return; /* Our caller will notice that we didn't find an actual sqrt */
#undef S1
#undef S2
#undef S3
}
const struct ecc_curve _nettle_secp_224r1 =
{
{
......@@ -70,7 +139,7 @@ const struct ecc_curve _nettle_secp_224r1 =
ECC_BMODP_SIZE,
-ECC_REDC_SIZE,
ECC_MOD_INV_ITCH (ECC_LIMB_SIZE),
0,
6 * ECC_LIMB_SIZE,
ecc_p,
ecc_Bmodp,
......@@ -82,7 +151,7 @@ const struct ecc_curve _nettle_secp_224r1 =
USE_REDC ? ecc_224_redc : ecc_224_modp,
ecc_mod_inv,
NULL,
NULL,
ecc_224_sqrt,
},
{
224,
......
......@@ -250,6 +250,8 @@ ecc_256_sqrt (const struct ecc_modulo *m,
/* This computes the square root modulo p256 using the identity:
sqrt(c) = c^(2^254 − 2^222 + 2^190 + 2^94) (mod P-256)
which can be seen as a special case of Tonelli-Shanks with e=1.
*/
/* We use our scratch space for three temporaries (TA, TB, TC) all of
......
......@@ -159,7 +159,11 @@ ecc_384_sqrt (const struct ecc_modulo *m,
sqrt(c) = c^(2^382 − 2^126 - 2^94 + 2^30) (mod P-384)
which is from Routine 3.2.12 of "Mathematical routines for the NIST prime elliptic curves", April 5, 2010, author unknown.
which can be seen as a special case of Tonelli-Shanks with e=1.
The specific sqr/mul schedule is from Routine 3.2.12 of
"Mathematical routines for the NIST prime elliptic curves", April
5, 2010, author unknown.
*/
/* We use our scratch space for several temporaries, all of
......
......@@ -84,6 +84,8 @@ ecc_521_sqrt (const struct ecc_modulo *m,
/* This computes the square root modulo p256 using the identity:
sqrt(c) = c^(2^519) (mod P-521)
which can be seen as a special case of Tonelli-Shanks with e=1.
*/
mpn_copyi(scratch, cp, m->size);
......
......@@ -52,6 +52,7 @@
#define ecc_mod_pow2n_mul _nettle_ecc_mod_pow2n_mul
#define ecc_mod_pow2n _nettle_ecc_mod_pow2n
#define ecc_mod_inplc_mul _nettle_ecc_mod_inplc_mul
#define ecc_tonelli_shanks _nettle_ecc_tonelli_shanks
#define ecc_mod_random _nettle_ecc_mod_random
#define ecc_mod _nettle_ecc_mod
#define ecc_mod_inv _nettle_ecc_mod_inv
......@@ -270,6 +271,13 @@ ecc_mod_inplc_mul(const struct ecc_modulo *m,
const mp_limb_t *src2,
mp_limb_t *scratch);
int
ecc_tonelli_shanks(const struct ecc_modulo *m,
mp_limb_t *rp,
unsigned int M,
const mp_limb_t *unit,
mp_limb_t *scratch);
#define ecc_modp_add(ecc, r, a, b) \
ecc_mod_add (&(ecc)->p, (r), (a), (b))
#define ecc_modp_sub(ecc, r, a, b) \
......
......@@ -158,7 +158,12 @@ ecc_mod_pow2n (const struct ecc_modulo *m,
{
mp_size_t size = m->size;
if (scount & 1)
if (scount == 0)
{
mpn_copyi(dst, src, size);
return;
}
else if (scount & 1)
{
mpn_sqr (dst, src, size);
m->reduce (m, dst);
......
......@@ -147,7 +147,8 @@ ecc_recover_y_weierstrass (const struct ecc_curve *c,
if ((rp[0] & 1) ? !y_sign : y_sign)
{
/* Zero is its own negation, which means we can't satisfy this request */
/* TODO: Check whether any of our curves intersect y=0? P-256 doesn't. */
/* (This shouldn't be reachable with any of our NIST curves, since none of them
have a solution for Y=0) */
if (mpn_zero_p (rp, size))
return 0;
......
......@@ -1137,7 +1137,7 @@ output_curve (const struct ecc_curve *ecc, unsigned bits_per_limb)
mpz_fdiv_q_2exp (s, s, e);
/* Find a non-square g, g^{(p-1)/2} = -1,
and z = g^{(p-1)/4 */
and z = g^s */
for (g = 2; ; g++)
{
mpz_set_ui (z, g);
......@@ -1155,6 +1155,7 @@ output_curve (const struct ecc_curve *ecc, unsigned bits_per_limb)
}
mpz_add_ui (t, t, 1);
assert (mpz_cmp (t, ecc->p) == 0);
printf ("/* g = %u */\n", g);
output_bignum ("ecc_sqrt_z", z, limb_size, bits_per_limb);
mpz_fdiv_q_2exp (t, s, 1);
......
......@@ -4811,6 +4811,32 @@ the function returns 1. Otherwise, it returns 0. Currently, the
infinity point (or zero point, with additive notation) is not allowed.
@end deftypefun
@cindex Compact point representation
@deftypefun int ecc_point_set_compact (struct ecc_point *@var{p}, const mpz_t @var{x}, int @var{y})
Recover a point's coordinates from a compact representation consisting
of its @math{x} coordinate and a single bit of information about its
@math{y} coordinate (see @cite{RFC 6090}). @var{y} should contain the
least significant bit (0 or 1) of the actual coordinate. If the
parameters correspond to a valid point on the curve other than the
point at infinity, it copies the point to @var{p} and returns
1. Otherwise, it returns 0.
Currently, this function is only supported for the @abbr{NIST} curves
(@code{secp256r1}, @i{etc.}). For other curves, such as
@code{Curve25519}, it always returns 0.
This function is not side-channel silent.
@end deftypefun
@cindex @code{ECPoint}
@deftypefun int ecc_point_set_from_octets (struct ecc_point *@var{p}, size_t @var{len}, const uint8_t *@var{buf})
Check that the given octet string, interpreted according to the format
used by standards such as @cite{RFC 5480} and @cite{X9.62}, represents
a point on the curve. If so, the coordinates are copied and converted
to internal representation, by calling @code{ecc_point_set} or
@code{ecc_point_set_compact} as appropriate. If successful, returns
1. Otherwise, it returns 0.
@end deftypefun
@deftypefun void ecc_point_get (const struct ecc_point *@var{p}, mpz_t @var{x}, mpz_t @var{y})
Extracts the coordinate of the point @var{p}. The output parameters
@var{x} or @var{y} may be NULL if the caller doesn't want that
......
......@@ -241,6 +241,12 @@ ecc-redc-test$(EXEEXT): ecc-redc-test.$(OBJEXT)
ecc-sqrt-test$(EXEEXT): ecc-sqrt-test.$(OBJEXT)
$(LINK) ecc-sqrt-test.$(OBJEXT) $(TEST_OBJS) -o ecc-sqrt-test$(EXEEXT)
ecc-point-compact-test$(EXEEXT): ecc-point-compact-test.$(OBJEXT)
$(LINK) ecc-point-compact-test.$(OBJEXT) $(TEST_OBJS) -o ecc-point-compact-test$(EXEEXT)
ecc-point-from-octets-test$(EXEEXT): ecc-point-from-octets-test.$(OBJEXT)
$(LINK) ecc-point-from-octets-test.$(OBJEXT) $(TEST_OBJS) -o ecc-point-from-octets-test$(EXEEXT)
ecc-dup-test$(EXEEXT): ecc-dup-test.$(OBJEXT)
$(LINK) ecc-dup-test.$(OBJEXT) $(TEST_OBJS) -o ecc-dup-test$(EXEEXT)
......
......@@ -143,6 +143,80 @@ test_point_reconstruction (const struct ecc_curve *ecc,
mpz_clear (y_in);
}
static void
test_secp192r1_compact (void)
{
const struct ecc_curve *ecc = &_nettle_secp_192r1;
/* The generator point */
test_point_reconstruction (ecc,
"188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
1,
"07192b95ffc8da78631011ed6b24cdd573f977a11e794811");
/* Misc points */
test_point_reconstruction (ecc,
"1faee4205a4f669d2d0a8f25e3bcec9a62a6952965bf6d31",
0,
"5ff2cdfa508a2581892367087c696f179e7a4d7e8260fb06");
test_point_reconstruction (ecc,
"3afc9fe857c9cac94b333b0e0c2f5eaca8352670472e6c9a",
1,
"fffffffffffffffeffffffffffffffffffffffffffffffff");
test_point_reconstruction (ecc,
"6640909960af1640197342c0c58b40406b56a622c1258431",
0,
"000000000000000000000001000000000000000000000000");
}
static void
test_secp224r1_compact (void)
{
const struct ecc_curve *ecc = &_nettle_secp_224r1;
/* The generator point */
test_point_reconstruction (ecc,
"b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
0,
"bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34");
/* Special cases, eg to increase branch coverage in our
implementation (thanks to SageMath / Pari for computations) */
test_point_reconstruction (ecc, /* Y = 1 */
"3b5889352ddf7468bf8c0729212aa1b2a3fcb1a844b8be91abb753d5",
1,
"00000000000000000000000000000000000000000000000000000001");
test_point_reconstruction (ecc, /* Y = -1 */
"3b5889352ddf7468bf8c0729212aa1b2a3fcb1a844b8be91abb753d5",
0,
"ffffffffffffffffffffffffffffffff000000000000000000000000");
test_point_reconstruction (ecc, /* Y^2 intermediate is 2^98-1 */
"760737a5a69ba5f84740bcafca96f6cc3049d98fa95a45c9d159126b",
1,
"86d5a7d677bc09173bd94be84efb59936faca004c9bada763b39a531");
/* From Google Wycheproof */
test_point_reconstruction (ecc, /* testcase 21 - edge cases for ephemeral key */
"00000000000000ffffffffffffff0000000000000100000000000000",
0,
"73ca5f8f104997a2399e0c7f25e72a75ec29fc4542533d3fea89a33a");
test_point_reconstruction (ecc, /* testcase 26 - edge cases for ephemeral key */
"0a15c112ff784b1445e889f955be7e3ffdf451a2c0e76ab5cb32cf41",
0,
"3d4df973c563c6decdd435e4f864557e4c273096d9941ca4260a266e");
test_point_reconstruction (ecc, /* testcase 28 - edge cases for ephemeral key */
"661ac958c0febbc718ccf39cefc6b66c4231fbb9a76f35228a3bf5c3",
1,
"103b8040e3cb41966fc64a68cacb0c14053f87d27e8ed7bf2d7fe51b");
test_point_reconstruction (ecc, /* testcase 65 - invalid public key */
"0ca753db5ddeca474241f8d2dafc0844343fd0e37eded2f0192d51b2",
0, NULL);
}
static void
test_secp256r1_compact (void)
{
......@@ -172,7 +246,7 @@ test_secp256r1_compact (void)
"efdde3b32872a9effcf3b94cbf73aa7b39f9683ece9121b9852167f4e3da609b",
1, NULL);
/* Special cses tailored to our implementation (thanks to Wolfram Alpha for computations) */
/* Special cases tailored to our implementation (thanks to Wolfram Alpha for computations) */
/* Incur an annoying underflow in computation of Y */
test_point_reconstruction (ecc,
......@@ -299,10 +373,58 @@ test_secp521r1_compact (void)
}
static void
test_out_of_range (void)
{
mpz_t x;
struct ecc_point pt;
int res;
mpz_init (x);
mpz_set_si (x, -1);
ecc_point_init (&pt, &_nettle_secp_521r1);
res = ecc_point_set_compact (&pt, x, 0);
if (res != 0)
{
fprintf (stderr, "Failed to reject X<0\n");
dump_result_point (&pt);
abort ();
}
ecc_point_clear (&pt);
mpz_set_si (x, 0);
ecc_point_init (&pt, &_nettle_secp_256r1);
res = ecc_point_set_compact (&pt, x, 0);
if (res == 0)
{
fprintf (stderr, "Failed to accept X==0 on P-256\n");
abort ();
}
ecc_point_clear (&pt);
mpz_ui_pow_ui (x, 2, 521);
mpz_sub_ui (x, x, 1);
ecc_point_init (&pt, &_nettle_secp_521r1);
res = ecc_point_set_compact (&pt, x, 0);
if (res != 0)
{
fprintf (stderr, "Failed to reject X==p\n");
dump_result_point (&pt);
abort ();
}
ecc_point_clear (&pt);
mpz_clear (x);
}
void
test_main (void)
{
test_secp192r1_compact ();
test_secp224r1_compact ();
test_secp256r1_compact ();
test_secp384r1_compact ();
test_secp521r1_compact ();
test_out_of_range ();
}
......@@ -122,10 +122,15 @@ test_main (void)
which isn't a valid public key. */
test_point_from_octets (&_nettle_secp_256r1, "00", NULL, NULL);
/* Strings with invalid lengths */
test_point_from_octets (&_nettle_secp_256r1, "02", NULL, NULL);
test_point_from_octets (&_nettle_secp_256r1, "04", NULL, NULL);
test_point_from_octets (&_nettle_secp_256r1, "06", NULL, NULL);
test_point_from_octets (&_nettle_secp_224r1,
"01AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
NULL, NULL);
/* A valid point */
test_point_from_octets (&_nettle_secp_256r1,
"04"
......@@ -145,6 +150,10 @@ test_main (void)
"ffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
"0000000000000000000000000000000000000000000000000000000000000000",
NULL, NULL);
test_point_from_octets (&_nettle_secp_224r1, /* X > p */
"02"
"ffffffffffffffffffffffffffffffffffffffffffffffffffff0000",
NULL, NULL);
/* Compressed points */
test_point_from_octets (&_nettle_secp_256r1,
......@@ -184,6 +193,10 @@ test_main (void)
"58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e62"
"080d36bd224d7405509295eed02a17150e03b314f96da37445b0d1d29377d12c",
NULL, NULL);
test_point_from_octets (&_nettle_secp_256r1,
"06"
"58fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e62",
NULL, NULL);
test_point_from_octets (&_nettle_secp_256r1,
"07"
"e9484e58f3331b66ffed6d90cb1c78065fa28cfba5c7dd4352013d3252ee4277"
......
/* tonelli-shanks.c
Copyright (C) 2019 Wim Lewis
This file is part of GNU Nettle.
GNU Nettle is free software: you can redistribute it and/or
modify it under the terms of either:
* the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your
option) any later version.
or
* the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.
or both in parallel, as here.
GNU Nettle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received copies of the GNU General Public License and
the GNU Lesser General Public License along with this program. If
not, see http://www.gnu.org/licenses/.
*/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include "ecc-internal.h"
/* Test whether a and b are congruent.
"a" may be in the range [0, 2p) (that is, almost modulo-reduced,
but may contain one multiple of p).
"b" is assumed to be fully reduced mod p.
*/
static int
congruent (const struct ecc_modulo * p, const mp_limb_t * a,
const mp_limb_t * b, mp_limb_t * scratch)
{
mp_size_t size = p->size;
int cy;
cy = mpn_sub_n (scratch, a, b, size);
if (cy == 0)
{
/* a was larger than or equal to b; if they were congruent,
scratch now contains either 0 or p. */
return (mpn_zero_p (scratch, size) ||
0 == mpn_cmp (scratch, p->m, size));
}
else
{
/* a was smaller than b; they can't be congruent. */
return 0;
}
}
/* Inner loop of the Tonelli-Shanks algorithm for square root.
The input to this part of the algorithm is three bignums (R, c, t) and
a smallnum (e). R should be in *rp (which is also where the root will
be stored); c and t should be at the beginning of scratch;
and space for another two variables (each 2*size limbs long) should exist in scratch.
*/
int
ecc_tonelli_shanks (const struct ecc_modulo * m,
mp_limb_t * rp,
unsigned int e,
const mp_limb_t * unit,
mp_limb_t * scratch)
{
mp_size_t size = m->size;
#define c (scratch)
#define t (scratch + size)
#define S1 (scratch + 2*size)
#define S2 (scratch + 4*size)
for (;;)
{
/* Search for i such that t^(2^i) = 1; 0 <= i < e */
unsigned i = 0;
if (congruent (m, t, unit, S2))
{
/* Return R, which is conveniently stored at *rp already */
return 1;
}
/* Repeatedly compute: dst_i <-- t^(2^i); i++ */
for (;;)
{
if ((i + 1) >= e)
{
return 0; /* Failure: our input wasn't a quadratic residue */
}
mp_limb_t *dst_i = (i % 2 == 0) ? S1 : S2;
mp_limb_t *other_i = (i % 2 == 0) ? S2 : S1;
ecc_mod_sqr (m, dst_i, (i == 0) ? t : other_i);
i++;
if (congruent (m, dst_i, unit, other_i))
{
break;
}
}
/* i is at least 1, and strictly less than e, which guarantees this loop will terminate */
/* Update our loop variables:
e' <-- i
c' <-- c^(2^(e-i))
t' <-- t*c'
R' <-- R*c^(2^(e-i-1))
*/
ecc_mod_pow2n (m, S1, c, S2, e - i - 1);
e = i;
ecc_mod_mul (m, S2, rp, S1);
mpn_copyi (rp, S2, size);
ecc_mod_sqr (m, S2, S1);
mpn_copyi (c, S2, size);
ecc_mod_mul (m, S2, t, c);
mpn_copyi (t, S2, size);
}
#undef c
#undef t
#undef S1
#undef S2
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment