LCOV - code coverage report
Current view: top level - ballet/ed25519 - fd_f25519.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 164 176 93.2 %
Date: 2026-10-16 04:29:28 Functions: 7 8 87.5 %

          Line data    Source code
       1             : #include "fd_f25519.h"
       2             : #include "../hex/fd_hex.h"
       3             : 
       4             : #if FD_HAS_AVX512
       5             : #include "avx512/fd_f25519.c"
       6             : #else
       7             : #include "ref/fd_f25519.c"
       8             : #endif
       9             : 
      10             : #ifdef FD_HAS_S2NBIGNUM
      11             : #include <stdint.h>
      12             : #include "../../third_party/s2n-bignum/include/s2n-bignum.h"
      13             : #include "../../util/sanitize/fd_msan.h"
      14             : #endif
      15             : 
      16             : void
      17             : fd_f25519_debug( char const * name,
      18           0 :                  fd_f25519_t const * a ) {
      19           0 :   char *
      20           0 :   fd_hex_encode( char *       FD_RESTRICT dst,
      21           0 :                  void const * FD_RESTRICT src,
      22           0 :                  ulong                    sz );
      23           0 :   uchar out[ 32 ];
      24           0 :   char buf[ 65 ] = { 0 };
      25           0 :   fd_f25519_tobytes( out, a );
      26           0 :   fd_hex_encode( buf, out, 32UL );
      27           0 :   FD_LOG_WARNING(( "%s: %s", name, buf ));
      28           0 :   FD_LOG_HEXDUMP_WARNING(( name, a, sizeof(fd_f25519_t) ));
      29           0 : }
      30             : 
      31             : /* fd_f25519_pow22523 computes r = a^(2^252-3), and returns r. */
      32             : fd_f25519_t *
      33             : fd_f25519_pow22523( fd_f25519_t *       r,
      34     2629289 :                     fd_f25519_t const * a ) {
      35     2629289 :   fd_f25519_t t0[1];
      36     2629289 :   fd_f25519_t t1[1];
      37     2629289 :   fd_f25519_t t2[1];
      38             : 
      39     2629289 :   fd_f25519_sqr( t0, a      );
      40     2629289 :   fd_f25519_sqr( t1, t0     );
      41     5258578 :   for( int i=1; i<  2; i++ ) fd_f25519_sqr( t1, t1 );
      42             : 
      43     2629289 :   fd_f25519_mul( t1, a,  t1 );
      44     2629289 :   fd_f25519_mul( t0, t0, t1 );
      45     2629289 :   fd_f25519_sqr( t0, t0     );
      46     2629289 :   fd_f25519_mul( t0, t1, t0 );
      47     2629289 :   fd_f25519_sqr( t1, t0     );
      48    13146445 :   for( int i=1; i<  5; i++ ) fd_f25519_sqr( t1, t1 );
      49             : 
      50     2629289 :   fd_f25519_mul( t0, t1, t0 );
      51     2629289 :   fd_f25519_sqr( t1, t0     );
      52    26292890 :   for( int i=1; i< 10; i++ ) fd_f25519_sqr( t1, t1 );
      53             : 
      54     2629289 :   fd_f25519_mul( t1, t1, t0 );
      55     2629289 :   fd_f25519_sqr( t2, t1     );
      56    52585780 :   for( int i=1; i< 20; i++ ) fd_f25519_sqr( t2, t2 );
      57             : 
      58     2629289 :   fd_f25519_mul( t1, t2, t1 );
      59     2629289 :   fd_f25519_sqr( t1, t1     );
      60    26292890 :   for( int i=1; i< 10; i++ ) fd_f25519_sqr( t1, t1 );
      61             : 
      62     2629289 :   fd_f25519_mul( t0, t1, t0 );
      63     2629289 :   fd_f25519_sqr( t1, t0     );
      64   131464450 :   for( int i=1; i< 50; i++ ) fd_f25519_sqr( t1, t1 );
      65             : 
      66     2629289 :   fd_f25519_mul( t1, t1, t0 );
      67     2629289 :   fd_f25519_sqr( t2, t1     );
      68   262928900 :   for( int i=1; i<100; i++ ) fd_f25519_sqr( t2, t2 );
      69             : 
      70     2629289 :   fd_f25519_mul( t1, t2, t1 );
      71     2629289 :   fd_f25519_sqr( t1, t1     );
      72   131464450 :   for( int i=1; i< 50; i++ ) fd_f25519_sqr( t1, t1 );
      73             : 
      74     2629289 :   fd_f25519_mul( t0, t1, t0 );
      75     2629289 :   fd_f25519_sqr( t0, t0     );
      76     5258578 :   for( int i=1; i<  2; i++ ) fd_f25519_sqr( t0, t0 );
      77             : 
      78     2629289 :   fd_f25519_mul(r, t0, a  );
      79     2629289 :   return r;
      80     2629289 : }
      81             : 
      82             : #ifdef FD_HAS_S2NBIGNUM
      83             : 
      84             : /* fd_f25519_inv computes r = 1/a, and returns r. */
      85             : fd_f25519_t *
      86             : fd_f25519_inv( fd_f25519_t *       r,
      87      260309 :                fd_f25519_t const * a ) {
      88      260309 :   ulong x[ 4 ];
      89      260309 :   ulong z[ 4 ];
      90      260309 :   fd_f25519_tobytes( (uchar *)x, a );
      91      260309 :   bignum_inv_p25519( z, x );
      92      260309 :   fd_msan_unpoison( z, 32UL );
      93      260309 :   return fd_f25519_frombytes( r, (uchar const *)z );
      94      260309 : }
      95             : 
      96             : #else
      97             : 
      98             : /* fd_f25519_inv computes r = 1/a, and returns r. */
      99             : fd_f25519_t *
     100             : fd_f25519_inv( fd_f25519_t *       r,
     101             :                fd_f25519_t const * a ) {
     102             :   fd_f25519_t t0[1];
     103             :   fd_f25519_t t1[1];
     104             :   fd_f25519_t t2[1];
     105             :   fd_f25519_t t3[1];
     106             : 
     107             :   /* Compute z**-1 = z**(2**255 - 19 - 2) with the exponent as
     108             :      2**255 - 21 = (2**5) * (2**250 - 1) + 11. */
     109             : 
     110             :   fd_f25519_sqr( t0,  a     );                        /* t0 = z**2 */
     111             :   fd_f25519_sqr( t1, t0     );
     112             :   fd_f25519_sqr( t1, t1     );                        /* t1 = t0**(2**2) = z**8 */
     113             :   fd_f25519_mul( t1,  a, t1 );                        /* t1 = z * t1 = z**9 */
     114             :   fd_f25519_mul( t0, t0, t1 );                        /* t0 = t0 * t1 = z**11 -- stash t0 away for the end. */
     115             :   fd_f25519_sqr( t2, t0     );                        /* t2 = t0**2 = z**22 */
     116             :   fd_f25519_mul( t1, t1, t2 );                        /* t1 = t1 * t2 = z**(2**5 - 1) */
     117             :   fd_f25519_sqr( t2, t1     );
     118             :   for( int i=1; i<  5; i++ ) fd_f25519_sqr( t2, t2 ); /* t2 = t1**(2**5) = z**((2**5) * (2**5 - 1)) */
     119             :   fd_f25519_mul( t1, t2, t1 );                        /* t1 = t1 * t2 = z**((2**5 + 1) * (2**5 - 1)) = z**(2**10 - 1) */
     120             :   fd_f25519_sqr( t2, t1     );
     121             :   for( int i=1; i< 10; i++ ) fd_f25519_sqr( t2, t2 );
     122             :   fd_f25519_mul( t2, t2, t1 );                        /* t2 = z**(2**20 - 1) */
     123             :   fd_f25519_sqr( t3, t2     );
     124             :   for( int i=1; i< 20; i++ ) fd_f25519_sqr( t3, t3 );
     125             :   fd_f25519_mul( t2, t3, t2 );                        /* t2 = z**(2**40 - 1) */
     126             :   for( int i=0; i< 10; i++ ) fd_f25519_sqr( t2, t2 ); /* t2 = z**(2**10) * (2**40 - 1) */
     127             :   fd_f25519_mul( t1, t2, t1 );                        /* t1 = z**(2**50 - 1) */
     128             :   fd_f25519_sqr( t2, t1     );
     129             :   for( int i=1; i< 50; i++ ) fd_f25519_sqr( t2, t2 );
     130             :   fd_f25519_mul( t2, t2, t1 );                        /* t2 = z**(2**100 - 1) */
     131             :   fd_f25519_sqr( t3, t2     );
     132             :   for( int i=1; i<100; i++ ) fd_f25519_sqr( t3, t3 );
     133             :   fd_f25519_mul( t2, t3, t2 );                        /* t2 = z**(2**200 - 1) */
     134             :   fd_f25519_sqr( t2, t2     );
     135             :   for( int i=1; i< 50; i++ ) fd_f25519_sqr( t2, t2 ); /* t2 = z**((2**50) * (2**200 - 1) */
     136             :   fd_f25519_mul( t1, t2, t1 );                        /* t1 = z**(2**250 - 1) */
     137             :   fd_f25519_sqr( t1, t1     );
     138             :   for( int i=1; i<  5; i++ ) fd_f25519_sqr( t1, t1 ); /* t1 = z**((2**5) * (2**250 - 1)) */
     139             :   return fd_f25519_mul( r, t1, t0 );                  /* Recall t0 = z**11; out = z**(2**255 - 21) */
     140             : }
     141             : 
     142             : #endif /* FD_HAS_S2NBIGNUM */
     143             : 
     144             : /* Variable time Jacobi symbol mod p, a port of
     145             :    secp256k1_jacobi64_maybe_var from libsecp256k1 v0.7.1
     146             :    (src/modinv64_impl.h, MIT license, see NOTICE).  It runs Bernstein-
     147             :    Yang "posdivsteps" in batches of 62 on numbers stored as 5 signed
     148             :    62-bit limbs, tracking the sign of the Jacobi symbol (g|f) from f mod
     149             :    8 and g mod 4.  See doc/safegcd_implementation.md in libsecp256k1. */
     150             : 
     151    34870512 : #define FD_F25519_M62 (ULONG_MAX>>2)
     152             : 
     153             : /* fd_f25519_posdivsteps_62_var runs 62 posdivsteps on f,g given their
     154             :    bottom 64 bits f0,g0 and returns the new eta.  t is set to the
     155             :    transition matrix [u,v,q,r] scaled by 2^62.  Bit 0 of *jac is flipped
     156             :    iff the Jacobi symbol (g|f) changes sign. */
     157             : 
     158             : static long
     159             : fd_f25519_posdivsteps_62_var( long  eta,
     160             :                               ulong f0,
     161             :                               ulong g0,
     162             :                               long  t[ 4 ],
     163     9267253 :                               int * jac ) {
     164     9267253 :   ulong u = 1UL, v = 0UL, q = 0UL, r = 1UL;
     165     9267253 :   ulong f = f0, g = g0;
     166     9267253 :   int   i = 62;
     167     9267253 :   int   j = *jac;
     168             : 
     169   164279256 :   for(;;) {
     170             :     /* Divide g by 2 as often as possible (at most i times).  When
     171             :        dividing by an odd power of 2, the symbol flips if f mod 8 is 3
     172             :        or 5. */
     173   164279256 :     int zeros = fd_ulong_find_lsb( g | (ULONG_MAX << i) );
     174   164279256 :     g   >>= zeros;
     175   164279256 :     u   <<= zeros;
     176   164279256 :     v   <<= zeros;
     177   164279256 :     eta  -= zeros;
     178   164279256 :     i    -= zeros;
     179   164279256 :     j    ^= (int)( (ulong)zeros & ((f>>1) ^ (f>>2)) );
     180   164279256 :     if( !i ) break;
     181             : 
     182   155012003 :     ulong m;
     183   155012003 :     ulong w;
     184   155012003 :     int   limit;
     185   155012003 :     if( eta<0L ) {
     186             :       /* Swap f,g.  The symbol flips if both are 3 mod 4.  Then cancel
     187             :          up to 6 bottom bits of g with a multiple of f. */
     188   149837612 :       ulong tmp;
     189   149837612 :       eta = -eta;
     190   149837612 :       tmp = f; f = g; g = tmp;
     191   149837612 :       tmp = u; u = q; q = tmp;
     192   149837612 :       tmp = v; v = r; r = tmp;
     193   149837612 :       j    ^= (int)( (f & g)>>1 );
     194   149837612 :       limit = fd_int_min( (int)eta+1, i );
     195   149837612 :       m     = (ULONG_MAX >> (64-limit)) & 63UL;
     196   149837612 :       w     = (f*g*(f*f-2UL)) & m;
     197   149837612 :     } else {
     198             :       /* Cancel up to 4 bottom bits of g with a multiple of f. */
     199     5174391 :       limit = fd_int_min( (int)eta+1, i );
     200     5174391 :       m     = (ULONG_MAX >> (64-limit)) & 15UL;
     201     5174391 :       w     = f + (((f+1UL) & 4UL)<<1);
     202     5174391 :       w     = (-w*g) & m;
     203     5174391 :     }
     204   155012003 :     g += f*w;
     205   155012003 :     q += u*w;
     206   155012003 :     r += v*w;
     207   155012003 :   }
     208             : 
     209     9267253 :   t[0] = (long)u; t[1] = (long)v; t[2] = (long)q; t[3] = (long)r;
     210     9267253 :   *jac = j;
     211     9267253 :   return eta;
     212     9267253 : }
     213             : 
     214             : /* fd_f25519_update_fg_62_var computes [f,g] = t [f,g] / 2^62 on the
     215             :    bottom len limbs of f and g. */
     216             : 
     217             : static void
     218             : fd_f25519_update_fg_62_var( int        len,
     219             :                             long *     f,
     220             :                             long *     g,
     221     9267253 :                             long const t[ 4 ] ) {
     222     9267253 :   long const u = t[0], v = t[1], q = t[2], r = t[3];
     223     9267253 :   int128 cf = (int128)u*(int128)f[0] + (int128)v*(int128)g[0];
     224     9267253 :   int128 cg = (int128)q*(int128)f[0] + (int128)r*(int128)g[0];
     225     9267253 :   cf >>= 62;
     226     9267253 :   cg >>= 62;
     227    25244929 :   for( int i=1; i<len; i++ ) {
     228    15977676 :     cf += (int128)u*(int128)f[i] + (int128)v*(int128)g[i];
     229    15977676 :     cg += (int128)q*(int128)f[i] + (int128)r*(int128)g[i];
     230    15977676 :     f[i-1] = (long)( (ulong)cf & FD_F25519_M62 ); cf >>= 62;
     231    15977676 :     g[i-1] = (long)( (ulong)cg & FD_F25519_M62 ); cg >>= 62;
     232    15977676 :   }
     233     9267253 :   f[len-1] = (long)cf;
     234     9267253 :   g[len-1] = (long)cg;
     235     9267253 : }
     236             : 
     237             : /* fd_f25519_jacobi_var returns the Jacobi symbol (x|p) in {-1,1} for x
     238             :    in [1,p) given as 5 non-negative 62-bit limbs, or 0 if it did not
     239             :    converge within 25*62 posdivsteps (never observed for random input,
     240             :    typically ~12 batches are needed). */
     241             : 
     242             : static int
     243      728790 : fd_f25519_jacobi_var( long const x[ 5 ] ) {
     244      728790 :   long f[5] = { (1L<<62)-19L, (1L<<62)-1L, (1L<<62)-1L, (1L<<62)-1L, 127L }; /* p */
     245      728790 :   long g[5] = { x[0], x[1], x[2], x[3], x[4] };
     246      728790 :   int  len  = 5;
     247      728790 :   long eta  = -1L;
     248      728790 :   int  jac  = 0;
     249             : 
     250     9267382 :   for( int cnt=0; cnt<25; cnt++ ) {
     251     9267253 :     long t[4];
     252     9267253 :     eta = fd_f25519_posdivsteps_62_var( eta, (ulong)f[0] | ((ulong)f[1]<<62), (ulong)g[0] | ((ulong)g[1]<<62), t, &jac );
     253     9267253 :     fd_f25519_update_fg_62_var( len, f, g, t );
     254             : 
     255             :     /* Done when f==1 */
     256     9267253 :     if( f[0]==1L ) {
     257      749592 :       long hi = 0L;
     258      821586 :       for( int j=1; j<len; j++ ) hi |= f[j];
     259      749592 :       if( !hi ) return 1 - 2*(jac & 1);
     260      749592 :     }
     261             : 
     262             :     /* Drop the top limb when it is zero in both f and g */
     263     8538592 :     if( len>1 && !(f[len-1] | g[len-1]) ) len--;
     264     8538592 :   }
     265         129 :   return 0;
     266      728790 : }
     267             : 
     268             : int
     269      728832 : fd_f25519_is_square_var( fd_f25519_t const * a ) {
     270      728832 :   uchar buf[32];
     271      728832 :   fd_f25519_tobytes( buf, a ); /* canonical, in [0,p) */
     272      728832 :   ulong a0 = fd_ulong_load_8_fast( buf    );
     273      728832 :   ulong a1 = fd_ulong_load_8_fast( buf+ 8 );
     274      728832 :   ulong a2 = fd_ulong_load_8_fast( buf+16 );
     275      728832 :   ulong a3 = fd_ulong_load_8_fast( buf+24 );
     276      728832 :   if( FD_UNLIKELY( !(a0|a1|a2|a3) ) ) return 1;
     277             : 
     278      728790 :   long x[5] = { (long)(   a0                   & FD_F25519_M62 ),
     279      728790 :                 (long)( ((a0>>62) | (a1<< 2)) & FD_F25519_M62 ),
     280      728790 :                 (long)( ((a1>>60) | (a2<< 4)) & FD_F25519_M62 ),
     281      728790 :                 (long)( ((a2>>58) | (a3<< 6)) & FD_F25519_M62 ),
     282      728790 :                 (long)(   a3>>56                               ) };
     283      728790 :   int jac = fd_f25519_jacobi_var( x );
     284      728790 :   if( FD_UNLIKELY( !jac ) ) {
     285             :     /* Did not converge: a is a nonzero square iff sqrt(a/1) exists */
     286         129 :     fd_f25519_t r[1];
     287         129 :     return fd_f25519_sqrt_ratio( r, a, fd_f25519_one );
     288         129 :   }
     289      728661 :   return jac>0;
     290      728790 : }
     291             : 
     292             : #undef FD_F25519_M62
     293             : 
     294             : /* fd_f25519_sqrt_ratio computes r = (u * v^3) * (u * v^7)^((p-5)/8),
     295             :    returns 0 on success, 1 on failure. */
     296             : int
     297             : fd_f25519_sqrt_ratio( fd_f25519_t *       r,
     298             :                       fd_f25519_t const * u,
     299     2515550 :                       fd_f25519_t const * v ) {
     300             :   /* r = (u * v^3) * (u * v^7)^((p-5)/8) */
     301     2515550 :   fd_f25519_t  v2[1]; fd_f25519_sqr(  v2, v      );
     302     2515550 :   fd_f25519_t  v3[1]; fd_f25519_mul(  v3, v2, v  );
     303     2515550 :   fd_f25519_t uv3[1]; fd_f25519_mul( uv3, u,  v3 );
     304     2515550 :   fd_f25519_t  v6[1]; fd_f25519_sqr(  v6, v3     );
     305     2515550 :   fd_f25519_t  v7[1]; fd_f25519_mul(  v7, v6, v  );
     306     2515550 :   fd_f25519_t uv7[1]; fd_f25519_mul( uv7, u,  v7 );
     307     2515550 :   fd_f25519_pow22523( r, uv7    );
     308     2515550 :   fd_f25519_mul     ( r, r, uv3 );
     309             : 
     310             :   /* check = v * r^2 */
     311     2515550 :   fd_f25519_t check[1];
     312     2515550 :   fd_f25519_sqr( check, r        );
     313     2515550 :   fd_f25519_mul( check, check, v );
     314             : 
     315             :   /* (correct_sign_sqrt)    check == u
     316             :      (flipped_sign_sqrt)    check == !u
     317             :      (flipped_sign_sqrt_i)  check == (!u * SQRT_M1) */
     318     2515550 :   fd_f25519_t u_neg[1];        fd_f25519_neg( u_neg,        u );
     319     2515550 :   fd_f25519_t u_neg_sqrtm1[1]; fd_f25519_mul( u_neg_sqrtm1, u_neg, fd_f25519_sqrtm1 );
     320     2515550 :   int correct_sign_sqrt   = fd_f25519_eq( check, u );
     321     2515550 :   int flipped_sign_sqrt   = fd_f25519_eq( check, u_neg );
     322     2515550 :   int flipped_sign_sqrt_i = fd_f25519_eq( check, u_neg_sqrtm1 );
     323             : 
     324             :   /* r_prime = SQRT_M1 * r */
     325     2515550 :   fd_f25519_t r_prime[1];
     326     2515550 :   fd_f25519_mul( r_prime, r, fd_f25519_sqrtm1 );
     327             : 
     328             :   /* r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r) */
     329     2515550 :   fd_f25519_if( r, flipped_sign_sqrt|flipped_sign_sqrt_i, r_prime, r );
     330     2515550 :   fd_f25519_abs( r, r );
     331     2515550 :   return correct_sign_sqrt|flipped_sign_sqrt;
     332     2515550 : }

Generated by: LCOV version 1.14