1*f0865ec9SKyle Evans /* 2*f0865ec9SKyle Evans * Copyright (C) 2017 - This file is part of libecc project 3*f0865ec9SKyle Evans * 4*f0865ec9SKyle Evans * Authors: 5*f0865ec9SKyle Evans * Ryad BENADJILA <ryadbenadjila@gmail.com> 6*f0865ec9SKyle Evans * Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr> 7*f0865ec9SKyle Evans * Jean-Pierre FLORI <jean-pierre.flori@ssi.gouv.fr> 8*f0865ec9SKyle Evans * 9*f0865ec9SKyle Evans * Contributors: 10*f0865ec9SKyle Evans * Nicolas VIVET <nicolas.vivet@ssi.gouv.fr> 11*f0865ec9SKyle Evans * Karim KHALFALLAH <karim.khalfallah@ssi.gouv.fr> 12*f0865ec9SKyle Evans * 13*f0865ec9SKyle Evans * This software is licensed under a dual BSD and GPL v2 license. 14*f0865ec9SKyle Evans * See LICENSE file at the root folder of the project. 15*f0865ec9SKyle Evans */ 16*f0865ec9SKyle Evans #include <libecc/fp/fp_sqrt.h> 17*f0865ec9SKyle Evans #include <libecc/nn/nn_add.h> 18*f0865ec9SKyle Evans #include <libecc/nn/nn_logical.h> 19*f0865ec9SKyle Evans 20*f0865ec9SKyle Evans /* 21*f0865ec9SKyle Evans * Compute the legendre symbol of an element of Fp: 22*f0865ec9SKyle Evans * 23*f0865ec9SKyle Evans * Legendre(a) = a^((p-1)/2) (p) = { -1, 0, 1 } 24*f0865ec9SKyle Evans * 25*f0865ec9SKyle Evans */ 26*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET static int legendre(fp_src_t a) 27*f0865ec9SKyle Evans { 28*f0865ec9SKyle Evans int ret, iszero, cmp; 29*f0865ec9SKyle Evans fp pow; /* The result if the exponentiation is in Fp */ 30*f0865ec9SKyle Evans fp one; /* The element 1 in the field */ 31*f0865ec9SKyle Evans nn exp; /* The power exponent is in NN */ 32*f0865ec9SKyle Evans pow.magic = one.magic = WORD(0); 33*f0865ec9SKyle Evans exp.magic = WORD(0); 34*f0865ec9SKyle Evans 35*f0865ec9SKyle Evans /* Initialize elements */ 36*f0865ec9SKyle Evans ret = fp_check_initialized(a); EG(ret, err); 37*f0865ec9SKyle Evans ret = fp_init(&pow, a->ctx); EG(ret, err); 38*f0865ec9SKyle Evans ret = fp_init(&one, a->ctx); EG(ret, err); 39*f0865ec9SKyle Evans ret = nn_init(&exp, 0); EG(ret, err); 40*f0865ec9SKyle Evans 41*f0865ec9SKyle Evans /* Initialize our variables from the Fp context of the 42*f0865ec9SKyle Evans * input a. 43*f0865ec9SKyle Evans */ 44*f0865ec9SKyle Evans ret = fp_init(&pow, a->ctx); EG(ret, err); 45*f0865ec9SKyle Evans ret = fp_init(&one, a->ctx); EG(ret, err); 46*f0865ec9SKyle Evans ret = nn_init(&exp, 0); EG(ret, err); 47*f0865ec9SKyle Evans 48*f0865ec9SKyle Evans /* one = 1 in Fp */ 49*f0865ec9SKyle Evans ret = fp_one(&one); EG(ret, err); 50*f0865ec9SKyle Evans 51*f0865ec9SKyle Evans /* Compute the exponent (p-1)/2 52*f0865ec9SKyle Evans * The computation is done in NN, and the division by 2 53*f0865ec9SKyle Evans * is performed using a right shift by one 54*f0865ec9SKyle Evans */ 55*f0865ec9SKyle Evans ret = nn_dec(&exp, &(a->ctx->p)); EG(ret, err); 56*f0865ec9SKyle Evans ret = nn_rshift(&exp, &exp, 1); EG(ret, err); 57*f0865ec9SKyle Evans 58*f0865ec9SKyle Evans /* Compute a^((p-1)/2) in Fp using our fp_pow 59*f0865ec9SKyle Evans * API. 60*f0865ec9SKyle Evans */ 61*f0865ec9SKyle Evans ret = fp_pow(&pow, a, &exp); EG(ret, err); 62*f0865ec9SKyle Evans 63*f0865ec9SKyle Evans ret = fp_iszero(&pow, &iszero); EG(ret, err); 64*f0865ec9SKyle Evans ret = fp_cmp(&pow, &one, &cmp); EG(ret, err); 65*f0865ec9SKyle Evans if (iszero) { 66*f0865ec9SKyle Evans ret = 0; 67*f0865ec9SKyle Evans } else if (cmp == 0) { 68*f0865ec9SKyle Evans ret = 1; 69*f0865ec9SKyle Evans } else { 70*f0865ec9SKyle Evans ret = -1; 71*f0865ec9SKyle Evans } 72*f0865ec9SKyle Evans 73*f0865ec9SKyle Evans err: 74*f0865ec9SKyle Evans /* Cleaning */ 75*f0865ec9SKyle Evans fp_uninit(&pow); 76*f0865ec9SKyle Evans fp_uninit(&one); 77*f0865ec9SKyle Evans nn_uninit(&exp); 78*f0865ec9SKyle Evans 79*f0865ec9SKyle Evans return ret; 80*f0865ec9SKyle Evans } 81*f0865ec9SKyle Evans 82*f0865ec9SKyle Evans /* 83*f0865ec9SKyle Evans * We implement the Tonelli-Shanks algorithm for finding 84*f0865ec9SKyle Evans * square roots (quadratic residues) modulo a prime number, 85*f0865ec9SKyle Evans * i.e. solving the equation: 86*f0865ec9SKyle Evans * x^2 = n (p) 87*f0865ec9SKyle Evans * where p is a prime number. This can be seen as an equation 88*f0865ec9SKyle Evans * over the finite field Fp where a and x are elements of 89*f0865ec9SKyle Evans * this finite field. 90*f0865ec9SKyle Evans * Source: https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm 91*f0865ec9SKyle Evans * All ≡ are taken to mean (mod p) unless stated otherwise. 92*f0865ec9SKyle Evans * Input : p an odd prime, and an integer n . 93*f0865ec9SKyle Evans * Step 0. Check that n is indeed a square : (n | p) must be ≡ 1 94*f0865ec9SKyle Evans * Step 1. [Factors out powers of 2 from p-1] Define q -odd- and s such as p-1 = q * 2^s 95*f0865ec9SKyle Evans * - if s = 1 , i.e p ≡ 3 (mod 4) , output the two solutions r ≡ +/- n^((p+1)/4) . 96*f0865ec9SKyle Evans * Step 2. Select a non-square z such as (z | p) = -1 , and set c ≡ z^q . 97*f0865ec9SKyle Evans * Step 3. Set r ≡ n ^((q+1)/2) , t ≡ n^q, m = s . 98*f0865ec9SKyle Evans * Step 4. Loop. 99*f0865ec9SKyle Evans * - if t ≡ 1 output r, p-r . 100*f0865ec9SKyle Evans * - Otherwise find, by repeated squaring, the lowest i , 0 < i < m , such as t^(2^i) ≡ 1 101*f0865ec9SKyle Evans * - Let b ≡ c^(2^(m-i-1)), and set r ≡ r*b, t ≡ t*b^2 , c ≡ b^2 and m = i. 102*f0865ec9SKyle Evans * 103*f0865ec9SKyle Evans * NOTE: the algorithm is NOT constant time. 104*f0865ec9SKyle Evans * 105*f0865ec9SKyle Evans * The outputs, sqrt1 and sqrt2 ARE initialized by the function. 106*f0865ec9SKyle Evans * The function returns 0 on success, -1 on error (in which case values of sqrt1 and sqrt2 107*f0865ec9SKyle Evans * must not be considered). 108*f0865ec9SKyle Evans * 109*f0865ec9SKyle Evans * Aliasing is supported. 110*f0865ec9SKyle Evans * 111*f0865ec9SKyle Evans */ 112*f0865ec9SKyle Evans int fp_sqrt(fp_t sqrt1, fp_t sqrt2, fp_src_t n) 113*f0865ec9SKyle Evans { 114*f0865ec9SKyle Evans int ret, iszero, cmp, isodd; 115*f0865ec9SKyle Evans nn q, s, one_nn, two_nn, m, i, tmp_nn; 116*f0865ec9SKyle Evans fp z, t, b, r, c, one_fp, tmp_fp, __n; 117*f0865ec9SKyle Evans fp_t _n = &__n; 118*f0865ec9SKyle Evans q.magic = s.magic = one_nn.magic = two_nn.magic = m.magic = WORD(0); 119*f0865ec9SKyle Evans i.magic = tmp_nn.magic = z.magic = t.magic = b.magic = WORD(0); 120*f0865ec9SKyle Evans r.magic = c.magic = one_fp.magic = tmp_fp.magic = __n.magic = WORD(0); 121*f0865ec9SKyle Evans 122*f0865ec9SKyle Evans ret = nn_init(&q, 0); EG(ret, err); 123*f0865ec9SKyle Evans ret = nn_init(&s, 0); EG(ret, err); 124*f0865ec9SKyle Evans ret = nn_init(&tmp_nn, 0); EG(ret, err); 125*f0865ec9SKyle Evans ret = nn_init(&one_nn, 0); EG(ret, err); 126*f0865ec9SKyle Evans ret = nn_init(&two_nn, 0); EG(ret, err); 127*f0865ec9SKyle Evans ret = nn_init(&m, 0); EG(ret, err); 128*f0865ec9SKyle Evans ret = nn_init(&i, 0); EG(ret, err); 129*f0865ec9SKyle Evans ret = fp_init(&z, n->ctx); EG(ret, err); 130*f0865ec9SKyle Evans ret = fp_init(&t, n->ctx); EG(ret, err); 131*f0865ec9SKyle Evans ret = fp_init(&b, n->ctx); EG(ret, err); 132*f0865ec9SKyle Evans ret = fp_init(&r, n->ctx); EG(ret, err); 133*f0865ec9SKyle Evans ret = fp_init(&c, n->ctx); EG(ret, err); 134*f0865ec9SKyle Evans ret = fp_init(&one_fp, n->ctx); EG(ret, err); 135*f0865ec9SKyle Evans ret = fp_init(&tmp_fp, n->ctx); EG(ret, err); 136*f0865ec9SKyle Evans 137*f0865ec9SKyle Evans /* Handle input aliasing */ 138*f0865ec9SKyle Evans ret = fp_copy(_n, n); EG(ret, err); 139*f0865ec9SKyle Evans 140*f0865ec9SKyle Evans /* Initialize outputs */ 141*f0865ec9SKyle Evans ret = fp_init(sqrt1, _n->ctx); EG(ret, err); 142*f0865ec9SKyle Evans ret = fp_init(sqrt2, _n->ctx); EG(ret, err); 143*f0865ec9SKyle Evans 144*f0865ec9SKyle Evans /* one_nn = 1 in NN */ 145*f0865ec9SKyle Evans ret = nn_one(&one_nn); EG(ret, err); 146*f0865ec9SKyle Evans /* two_nn = 2 in NN */ 147*f0865ec9SKyle Evans ret = nn_set_word_value(&two_nn, WORD(2)); EG(ret, err); 148*f0865ec9SKyle Evans 149*f0865ec9SKyle Evans /* If our p prime of Fp is 2, then return the input as square roots */ 150*f0865ec9SKyle Evans ret = nn_cmp(&(_n->ctx->p), &two_nn, &cmp); EG(ret, err); 151*f0865ec9SKyle Evans if (cmp == 0) { 152*f0865ec9SKyle Evans ret = fp_copy(sqrt1, _n); EG(ret, err); 153*f0865ec9SKyle Evans ret = fp_copy(sqrt2, _n); EG(ret, err); 154*f0865ec9SKyle Evans ret = 0; 155*f0865ec9SKyle Evans goto err; 156*f0865ec9SKyle Evans } 157*f0865ec9SKyle Evans 158*f0865ec9SKyle Evans /* Square root of 0 is 0 */ 159*f0865ec9SKyle Evans ret = fp_iszero(_n, &iszero); EG(ret, err); 160*f0865ec9SKyle Evans if (iszero) { 161*f0865ec9SKyle Evans ret = fp_zero(sqrt1); EG(ret, err); 162*f0865ec9SKyle Evans ret = fp_zero(sqrt2); EG(ret, err); 163*f0865ec9SKyle Evans ret = 0; 164*f0865ec9SKyle Evans goto err; 165*f0865ec9SKyle Evans } 166*f0865ec9SKyle Evans /* Step 0. Check that n is indeed a square : (n | p) must be ≡ 1 */ 167*f0865ec9SKyle Evans if (legendre(_n) != 1) { 168*f0865ec9SKyle Evans /* a is not a square */ 169*f0865ec9SKyle Evans ret = -1; 170*f0865ec9SKyle Evans goto err; 171*f0865ec9SKyle Evans } 172*f0865ec9SKyle Evans /* Step 1. [Factors out powers of 2 from p-1] Define q -odd- and s such as p-1 = q * 2^s */ 173*f0865ec9SKyle Evans /* s = 0 */ 174*f0865ec9SKyle Evans ret = nn_zero(&s); EG(ret, err); 175*f0865ec9SKyle Evans /* q = p - 1 */ 176*f0865ec9SKyle Evans ret = nn_copy(&q, &(_n->ctx->p)); EG(ret, err); 177*f0865ec9SKyle Evans ret = nn_dec(&q, &q); EG(ret, err); 178*f0865ec9SKyle Evans while (1) { 179*f0865ec9SKyle Evans /* i is used as a temporary unused variable here */ 180*f0865ec9SKyle Evans ret = nn_divrem(&tmp_nn, &i, &q, &two_nn); EG(ret, err); 181*f0865ec9SKyle Evans ret = nn_inc(&s, &s); EG(ret, err); 182*f0865ec9SKyle Evans ret = nn_copy(&q, &tmp_nn); EG(ret, err); 183*f0865ec9SKyle Evans /* If r is odd, we have finished our division */ 184*f0865ec9SKyle Evans ret = nn_isodd(&q, &isodd); EG(ret, err); 185*f0865ec9SKyle Evans if (isodd) { 186*f0865ec9SKyle Evans break; 187*f0865ec9SKyle Evans } 188*f0865ec9SKyle Evans } 189*f0865ec9SKyle Evans /* - if s = 1 , i.e p ≡ 3 (mod 4) , output the two solutions r ≡ +/- n^((p+1)/4) . */ 190*f0865ec9SKyle Evans ret = nn_cmp(&s, &one_nn, &cmp); EG(ret, err); 191*f0865ec9SKyle Evans if (cmp == 0) { 192*f0865ec9SKyle Evans ret = nn_inc(&tmp_nn, &(_n->ctx->p)); EG(ret, err); 193*f0865ec9SKyle Evans ret = nn_rshift(&tmp_nn, &tmp_nn, 2); EG(ret, err); 194*f0865ec9SKyle Evans ret = fp_pow(sqrt1, _n, &tmp_nn); EG(ret, err); 195*f0865ec9SKyle Evans ret = fp_neg(sqrt2, sqrt1); EG(ret, err); 196*f0865ec9SKyle Evans ret = 0; 197*f0865ec9SKyle Evans goto err; 198*f0865ec9SKyle Evans } 199*f0865ec9SKyle Evans /* Step 2. Select a non-square z such as (z | p) = -1 , and set c ≡ z^q . */ 200*f0865ec9SKyle Evans ret = fp_zero(&z); EG(ret, err); 201*f0865ec9SKyle Evans while (legendre(&z) != -1) { 202*f0865ec9SKyle Evans ret = fp_inc(&z, &z); EG(ret, err); 203*f0865ec9SKyle Evans } 204*f0865ec9SKyle Evans ret = fp_pow(&c, &z, &q); EG(ret, err); 205*f0865ec9SKyle Evans /* Step 3. Set r ≡ n ^((q+1)/2) , t ≡ n^q, m = s . */ 206*f0865ec9SKyle Evans ret = nn_inc(&tmp_nn, &q); EG(ret, err); 207*f0865ec9SKyle Evans ret = nn_rshift(&tmp_nn, &tmp_nn, 1); EG(ret, err); 208*f0865ec9SKyle Evans ret = fp_pow(&r, _n, &tmp_nn); EG(ret, err); 209*f0865ec9SKyle Evans ret = fp_pow(&t, _n, &q); EG(ret, err); 210*f0865ec9SKyle Evans ret = nn_copy(&m, &s); EG(ret, err); 211*f0865ec9SKyle Evans ret = fp_one(&one_fp); EG(ret, err); 212*f0865ec9SKyle Evans 213*f0865ec9SKyle Evans /* Step 4. Loop. */ 214*f0865ec9SKyle Evans while (1) { 215*f0865ec9SKyle Evans /* - if t ≡ 1 output r, p-r . */ 216*f0865ec9SKyle Evans ret = fp_cmp(&t, &one_fp, &cmp); EG(ret, err); 217*f0865ec9SKyle Evans if (cmp == 0) { 218*f0865ec9SKyle Evans ret = fp_copy(sqrt1, &r); EG(ret, err); 219*f0865ec9SKyle Evans ret = fp_neg(sqrt2, sqrt1); EG(ret, err); 220*f0865ec9SKyle Evans ret = 0; 221*f0865ec9SKyle Evans goto err; 222*f0865ec9SKyle Evans } 223*f0865ec9SKyle Evans /* - Otherwise find, by repeated squaring, the lowest i , 0 < i < m , such as t^(2^i) ≡ 1 */ 224*f0865ec9SKyle Evans ret = nn_one(&i); EG(ret, err); 225*f0865ec9SKyle Evans ret = fp_copy(&tmp_fp, &t); EG(ret, err); 226*f0865ec9SKyle Evans while (1) { 227*f0865ec9SKyle Evans ret = fp_sqr(&tmp_fp, &tmp_fp); EG(ret, err); 228*f0865ec9SKyle Evans ret = fp_cmp(&tmp_fp, &one_fp, &cmp); EG(ret, err); 229*f0865ec9SKyle Evans if (cmp == 0) { 230*f0865ec9SKyle Evans break; 231*f0865ec9SKyle Evans } 232*f0865ec9SKyle Evans ret = nn_inc(&i, &i); EG(ret, err); 233*f0865ec9SKyle Evans ret = nn_cmp(&i, &m, &cmp); EG(ret, err); 234*f0865ec9SKyle Evans if (cmp == 0) { 235*f0865ec9SKyle Evans /* i has reached m, that should not happen ... */ 236*f0865ec9SKyle Evans ret = -2; 237*f0865ec9SKyle Evans goto err; 238*f0865ec9SKyle Evans } 239*f0865ec9SKyle Evans } 240*f0865ec9SKyle Evans /* - Let b ≡ c^(2^(m-i-1)), and set r ≡ r*b, t ≡ t*b^2 , c ≡ b^2 and m = i. */ 241*f0865ec9SKyle Evans ret = nn_sub(&tmp_nn, &m, &i); EG(ret, err); 242*f0865ec9SKyle Evans ret = nn_dec(&tmp_nn, &tmp_nn); EG(ret, err); 243*f0865ec9SKyle Evans ret = fp_copy(&b, &c); EG(ret, err); 244*f0865ec9SKyle Evans ret = nn_iszero(&tmp_nn, &iszero); EG(ret, err); 245*f0865ec9SKyle Evans while (!iszero) { 246*f0865ec9SKyle Evans ret = fp_sqr(&b, &b); EG(ret, err); 247*f0865ec9SKyle Evans ret = nn_dec(&tmp_nn, &tmp_nn); EG(ret, err); 248*f0865ec9SKyle Evans ret = nn_iszero(&tmp_nn, &iszero); EG(ret, err); 249*f0865ec9SKyle Evans } 250*f0865ec9SKyle Evans /* r ≡ r*b */ 251*f0865ec9SKyle Evans ret = fp_mul(&r, &r, &b); EG(ret, err); 252*f0865ec9SKyle Evans /* c ≡ b^2 */ 253*f0865ec9SKyle Evans ret = fp_sqr(&c, &b); EG(ret, err); 254*f0865ec9SKyle Evans /* t ≡ t*b^2 */ 255*f0865ec9SKyle Evans ret = fp_mul(&t, &t, &c); EG(ret, err); 256*f0865ec9SKyle Evans /* m = i */ 257*f0865ec9SKyle Evans ret = nn_copy(&m, &i); EG(ret, err); 258*f0865ec9SKyle Evans } 259*f0865ec9SKyle Evans 260*f0865ec9SKyle Evans err: 261*f0865ec9SKyle Evans /* Uninitialize local variables */ 262*f0865ec9SKyle Evans nn_uninit(&q); 263*f0865ec9SKyle Evans nn_uninit(&s); 264*f0865ec9SKyle Evans nn_uninit(&tmp_nn); 265*f0865ec9SKyle Evans nn_uninit(&one_nn); 266*f0865ec9SKyle Evans nn_uninit(&two_nn); 267*f0865ec9SKyle Evans nn_uninit(&m); 268*f0865ec9SKyle Evans nn_uninit(&i); 269*f0865ec9SKyle Evans fp_uninit(&z); 270*f0865ec9SKyle Evans fp_uninit(&t); 271*f0865ec9SKyle Evans fp_uninit(&b); 272*f0865ec9SKyle Evans fp_uninit(&r); 273*f0865ec9SKyle Evans fp_uninit(&c); 274*f0865ec9SKyle Evans fp_uninit(&one_fp); 275*f0865ec9SKyle Evans fp_uninit(&tmp_fp); 276*f0865ec9SKyle Evans fp_uninit(&__n); 277*f0865ec9SKyle Evans 278*f0865ec9SKyle Evans PTR_NULLIFY(_n); 279*f0865ec9SKyle Evans 280*f0865ec9SKyle Evans return ret; 281*f0865ec9SKyle Evans } 282