#include #include #include "csidh.h" #include "rng.h" /* specific to p, should perhaps be somewhere else */ const unsigned primes[num_primes] = { 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 587, }; const u512 four_sqrt_p = {{ 0x85e2579c786882cf, 0x4e3433657e18da95, 0x850ae5507965a0b3, 0xa15bc4e676475964, }}; const public_key base = {0}; /* A = 0 */ void csidh_private(private_key *priv) { memset(&priv->e, 0, sizeof(priv->e)); for (size_t i = 0; i < num_primes; ) { int8_t buf[64]; randombytes(buf, sizeof(buf)); for (size_t j = 0; j < sizeof(buf); ++j) { if (buf[j] <= max_exponent && buf[j] >= -max_exponent) { priv->e[i / 2] |= (buf[j] & 0xf) << i % 2 * 4; if (++i >= num_primes) break; } } } } /* compute [(p+1)/l] P for all l in our list of primes. */ /* divide and conquer is much faster than doing it naively, * but uses more memory. */ static void cofactor_multiples(proj *P, const proj *A, size_t lower, size_t upper) { assert(lower < upper); if (upper - lower == 1) return; size_t mid = lower + (upper - lower + 1) / 2; u512 cl = u512_1, cu = u512_1; for (size_t i = lower; i < mid; ++i) u512_mul3_64(&cu, &cu, primes[i]); for (size_t i = mid; i < upper; ++i) u512_mul3_64(&cl, &cl, primes[i]); xMUL(&P[mid], A, &P[lower], &cu); xMUL(&P[lower], A, &P[lower], &cl); cofactor_multiples(P, A, lower, mid); cofactor_multiples(P, A, mid, upper); } /* never accepts invalid keys. */ bool validate(public_key const *in) { const proj A = {in->A, fp_1}; do { proj P[num_primes]; fp_random(&P->x); P->z = fp_1; /* maximal 2-power in p+1 */ xDBL(P, &A, P); xDBL(P, &A, P); cofactor_multiples(P, &A, 0, num_primes); u512 order = u512_1; for (size_t i = num_primes - 1; i < num_primes; --i) { /* we only gain information if [(p+1)/l] P is non-zero */ if (memcmp(&P[i].z, &fp_0, sizeof(fp))) { u512 tmp; u512_set(&tmp, primes[i]); xMUL(&P[i], &A, &P[i], &tmp); if (memcmp(&P[i].z, &fp_0, sizeof(fp))) /* P does not have order dividing p+1. */ return false; u512_mul3_64(&order, &order, primes[i]); if (u512_sub3(&tmp, &four_sqrt_p, &order)) /* returns borrow */ /* order > 4 sqrt(p), hence definitely supersingular */ return true; } } /* P didn't have big enough order to prove supersingularity. */ } while (1); } /* compute x^3 + Ax^2 + x */ static void montgomery_rhs(fp *rhs, fp const *A, fp const *x) { fp tmp; *rhs = *x; fp_sq1(rhs); fp_mul3(&tmp, A, x); fp_add2(rhs, &tmp); fp_add2(rhs, &fp_1); fp_mul2(rhs, x); } /* totally not constant-time. */ void action(public_key *out, public_key const *in, private_key const *priv) { u512 k[2]; u512_set(&k[0], 4); /* maximal 2-power in p+1 */ u512_set(&k[1], 4); /* maximal 2-power in p+1 */ uint8_t e[2][num_primes]; for (size_t i = 0; i < num_primes; ++i) { int8_t t = (int8_t) (priv->e[i / 2] << i % 2 * 4) >> 4; if (t > 0) { e[0][i] = t; e[1][i] = 0; u512_mul3_64(&k[1], &k[1], primes[i]); } else if (t < 0) { e[1][i] = -t; e[0][i] = 0; u512_mul3_64(&k[0], &k[0], primes[i]); } else { e[0][i] = 0; e[1][i] = 0; u512_mul3_64(&k[0], &k[0], primes[i]); u512_mul3_64(&k[1], &k[1], primes[i]); } } proj A = {in->A, fp_1}; bool done[2] = {false, false}; do { assert(!memcmp(&A.z, &fp_1, sizeof(fp))); proj P; fp_random(&P.x); P.z = fp_1; fp rhs; montgomery_rhs(&rhs, &A.x, &P.x); bool sign = !fp_issquare(&rhs); if (done[sign]) continue; xMUL(&P, &A, &P, &k[sign]); done[sign] = true; for (size_t i = 0; i < num_primes; ++i) { if (e[sign][i]) { u512 cof = u512_1; for (size_t j = i + 1; j < num_primes; ++j) if (e[sign][j]) u512_mul3_64(&cof, &cof, primes[j]); proj K; xMUL(&K, &A, &P, &cof); if (memcmp(&K.z, &fp_0, sizeof(fp))) { xISOG(&A, &P, &K, primes[i]); if (!--e[sign][i]) u512_mul3_64(&k[sign], &k[sign], primes[i]); } } done[sign] &= !e[sign][i]; } fp_inv(&A.z); fp_mul2(&A.x, &A.z); A.z = fp_1; } while (!(done[0] && done[1])); out->A = A.x; } /* includes public-key validation. */ bool csidh(public_key *out, public_key const *in, private_key const *priv) { if (!validate(in)) { fp_random(&out->A); return false; } action(out, in, priv); return true; }