LCOV - code coverage report
Current view: top level - ballet/secp256r1 - fd_secp256r1_s2n.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 150 232 64.7 %
Date: 2026-11-11 12:40:43 Functions: 14 56 25.0 %

          Line data    Source code
       1             : #include <stdint.h>
       2             : #include "../../third_party/s2n-bignum/include/s2n-bignum.h"
       3             : 
       4             : /* On CPUs without ADX (mulx/adcx/adox), redirect the ADX-optimized
       5             :    s2n-bignum symbols to their _alt equivalents, which use only base
       6             :    x86-64 instructions and are functionally identical. */
       7             : #ifndef __ADX__
       8             : #define bignum_demont_p256     bignum_demont_p256_alt
       9             : #define bignum_mod_n256        bignum_mod_n256_alt
      10             : #define bignum_montmul_p256    bignum_montmul_p256_alt
      11             : #define bignum_montsqr_p256    bignum_montsqr_p256_alt
      12             : #define bignum_mul_4_8         bignum_mul_4_8_alt
      13             : #define bignum_tomont_p256     bignum_tomont_p256_alt
      14             : #define p256_montjdouble       p256_montjdouble_alt
      15             : #define p256_montjmixadd       p256_montjmixadd_alt
      16             : #define p256_montjscalarmul    p256_montjscalarmul_alt
      17             : #define p256_scalarmulbase     p256_scalarmulbase_alt
      18             : #endif
      19             : 
      20             : #include "fd_secp256r1_table.c"
      21             : 
      22             : /* p256_base_table_size is used for p256_scalarmulbase with a blocksize=6.
      23             :    B=2^(6-1)=32, there are 43 blocks i with 6*i<=256 (6*42=252<=256), and
      24             :    43 * 32 * 8 = 11008 entries.
      25             :    https://github.com/awslabs/s2n-bignum/blob/9061e8b76522beafa5ca020f3c8d99b23eba4fbc/x86/p256/p256_scalarmulbase.S#L14-L23 */
      26             : FD_STATIC_ASSERT( sizeof(fd_secp256r1_base_point_table)==(43UL*32UL*8UL)*sizeof(ulong), p256_base_table_size );
      27             : 
      28             : /* Scalars */
      29             : 
      30             : static inline int
      31       14886 : fd_secp256r1_scalar_is_zero( fd_secp256r1_scalar_t const * a ) {
      32       14886 :   return fd_uint256_eq( a, fd_secp256r1_const_zero );
      33       14886 : }
      34             : 
      35             : static inline fd_secp256r1_scalar_t *
      36             : fd_secp256r1_scalar_frombytes( fd_secp256r1_scalar_t * r,
      37       13093 :                                uchar const             in[ 32 ] ) {
      38       13093 :   memcpy( r->buf, in, 32 );
      39       13093 :   fd_uint256_bswap( r, r );
      40       13093 :   if( FD_LIKELY( fd_uint256_cmp( r, fd_secp256r1_const_n )<0 ) ) {
      41       12946 :     return r;
      42       12946 :   };
      43         147 :   return NULL;
      44       13093 : }
      45             : 
      46             : static inline fd_secp256r1_scalar_t *
      47             : fd_secp256r1_scalar_frombytes_positive( fd_secp256r1_scalar_t * r,
      48       12946 :                                         uchar const             in[ 32 ] ) {
      49       12946 :   memcpy( r->buf, in, 32 );
      50       12946 :   fd_uint256_bswap( r, r );
      51       12946 :   if( FD_LIKELY( fd_uint256_cmp( r, fd_secp256r1_const_n_m1_half )<=0 ) ) {
      52        7448 :     return r;
      53        7448 :   };
      54        5498 :   return NULL;
      55       12946 : }
      56             : 
      57             : static inline void
      58             : fd_secp256r1_scalar_from_digest( fd_secp256r1_scalar_t * r,
      59        7298 :                                  uchar const             in[ 32 ] ) {
      60        7298 :   memcpy( r->buf, in, 32 );
      61        7298 :   fd_uint256_bswap( r, r );
      62        7298 :   bignum_mod_n256_4( r->limbs, r->limbs );
      63        7298 : }
      64             : 
      65             : static inline fd_secp256r1_scalar_t *
      66             : fd_secp256r1_scalar_mul( fd_secp256r1_scalar_t *       r,
      67             :                          fd_secp256r1_scalar_t const * a,
      68       14591 :                          fd_secp256r1_scalar_t const * b ) {
      69       14591 :   ulong t[ 8 ];
      70       14591 :   bignum_mul_4_8( t, (ulong *)a->limbs, (ulong *)b->limbs );
      71       14591 :   bignum_mod_n256( r->limbs, 8, t );
      72       14591 :   return r;
      73       14591 : }
      74             : 
      75             : static inline fd_secp256r1_scalar_t *
      76             : fd_secp256r1_scalar_inv( fd_secp256r1_scalar_t       * r,
      77        7300 :                          fd_secp256r1_scalar_t const * a ) {
      78        7300 :   ulong t[ 12 ];
      79        7300 :   bignum_modinv( 4, r->limbs, (ulong *)a->limbs, (ulong *)fd_secp256r1_const_n[0].limbs, t );
      80        7300 :   return r;
      81        7300 : }
      82             : 
      83             : /* Field */
      84             : 
      85             : static inline fd_secp256r1_fp_t *
      86             : fd_secp256r1_fp_set( fd_secp256r1_fp_t * r,
      87       14597 :                      fd_secp256r1_fp_t const * a ) {
      88       14597 :   r->limbs[0] = a->limbs[0];
      89       14597 :   r->limbs[1] = a->limbs[1];
      90       14597 :   r->limbs[2] = a->limbs[2];
      91       14597 :   r->limbs[3] = a->limbs[3];
      92       14597 :   return r;
      93       14597 : }
      94             : 
      95             : /* r = -a, Montgomery domain */
      96             : static inline fd_secp256r1_fp_t *
      97             : fd_secp256r1_fp_neg( fd_secp256r1_fp_t *       r,
      98           0 :                      fd_secp256r1_fp_t const * a ) {
      99           0 :   bignum_optneg_p256( r->limbs, 1UL, (ulong *)a->limbs );
     100           0 :   return r;
     101           0 : }
     102             : 
     103             : static inline fd_secp256r1_fp_t *
     104             : fd_secp256r1_fp_mul( fd_secp256r1_fp_t *       r,
     105             :                      fd_secp256r1_fp_t const * a,
     106           0 :                      fd_secp256r1_fp_t const * b ) {
     107           0 :   bignum_montmul_p256( r->limbs, a->limbs, b->limbs );
     108           0 :   return r;
     109           0 : }
     110             : 
     111             : static inline fd_secp256r1_fp_t *
     112             : fd_secp256r1_fp_sqr( fd_secp256r1_fp_t *       r,
     113           0 :                      fd_secp256r1_fp_t const * a ) {
     114           0 :   bignum_montsqr_p256( r->limbs, a->limbs );
     115           0 :   return r;
     116           0 : }
     117             : 
     118             : /* r = 1/a, Montgomery domain.  a MUST not be 0. */
     119             : static inline fd_secp256r1_fp_t *
     120             : fd_secp256r1_fp_inv( fd_secp256r1_fp_t *       r,
     121           0 :                      fd_secp256r1_fp_t const * a ) {
     122           0 :   bignum_montinv_p256( r->limbs, a->limbs );
     123           0 :   return r;
     124           0 : }
     125             : 
     126             : static inline fd_secp256r1_fp_t *
     127             : fd_secp256r1_fp_tomont( fd_secp256r1_fp_t *       r,
     128           0 :                         fd_secp256r1_fp_t const * a ) {
     129           0 :   bignum_tomont_p256( r->limbs, a->limbs );
     130           0 :   return r;
     131           0 : }
     132             : 
     133             : static inline fd_secp256r1_fp_t *
     134             : fd_secp256r1_fp_demont( fd_secp256r1_fp_t *       r,
     135           0 :                         fd_secp256r1_fp_t const * a ) {
     136           0 :   bignum_demont_p256( r->limbs, a->limbs );
     137           0 :   return r;
     138           0 : }
     139             : 
     140             : static inline fd_secp256r1_fp_t *
     141             : fd_secp256r1_fp_frombytes( fd_secp256r1_fp_t * r,
     142        7350 :                            uchar const             in[ 32 ] ) {
     143        7350 :   memcpy( r->buf, in, 32 );
     144        7350 :   fd_uint256_bswap( r, r );
     145        7357 :   if( FD_LIKELY( fd_uint256_cmp( r, fd_secp256r1_const_p )<0 ) ) {
     146        7357 :     return r;
     147 >1844*10^16 :   };
     148 >1844*10^16 :   return NULL;
     149        7350 : }
     150             : 
     151             : static inline fd_secp256r1_fp_t *
     152             : fd_secp256r1_fp_sqrt( fd_secp256r1_fp_t *       r,
     153        7365 :                       fd_secp256r1_fp_t const * a ) {
     154             :   /* https://github.com/golang/go/blob/master/src/crypto/internal/fips140/nistec/p256.go#L656 */
     155        7365 :   fd_secp256r1_fp_t _t0[1], _t1[1];
     156        7365 :   ulong * t0 = _t0->limbs;
     157        7365 :   ulong * t1 = _t1->limbs;
     158        7365 :   ulong * x = (ulong *)a->limbs;
     159             : 
     160        7365 :         bignum_montsqr_p256( t0, x );
     161        7365 :         bignum_montmul_p256( t0, t0, x );
     162       14736 :         bignum_montsqr_p256( t1, t0 ); for( int i=1; i<2; i++ ) bignum_montsqr_p256( t1, t1 );
     163        7365 :         bignum_montmul_p256( t0, t0, t1);
     164       29489 :         bignum_montsqr_p256( t1, t0 ); for( int i=1; i<4; i++ ) bignum_montsqr_p256( t1, t1 );
     165        7365 :         bignum_montmul_p256( t0, t0, t1);
     166       58869 :         bignum_montsqr_p256( t1, t0 ); for( int i=1; i<8; i++ ) bignum_montsqr_p256( t1, t1 );
     167        7365 :         bignum_montmul_p256( t0, t0, t1);
     168      117590 :         bignum_montsqr_p256( t1, t0 ); for( int i=1; i<16; i++ ) bignum_montsqr_p256( t1, t1 );
     169        7365 :         bignum_montmul_p256( t0, t0, t1);
     170      241833 :         for( int i=0; i<32; i++ ) bignum_montsqr_p256( t0, t0 );
     171        7365 :         bignum_montmul_p256( t0, t0, x );
     172      707711 :         for( int i=0; i<96; i++ ) bignum_montsqr_p256( t0, t0 );
     173        7365 :         bignum_montmul_p256( t0, t0, x );
     174      693039 :         for( int i=0; i<94; i++ ) bignum_montsqr_p256( t0, t0 );
     175             : 
     176        7365 :   bignum_montsqr_p256( t1, t0 );
     177        7365 :   if( FD_UNLIKELY( !fd_uint256_eq( _t1, a ) ) ) {
     178          73 :     return NULL;
     179          73 :   }
     180             : 
     181        7292 :   return fd_secp256r1_fp_set( r, _t0 );
     182        7365 : }
     183             : 
     184             : /* Points */
     185             : 
     186             : static inline int
     187           0 : fd_secp256r1_point_is_inf( fd_secp256r1_point_t const * a ) {
     188           0 :   return fd_uint256_eq( a->z, fd_secp256r1_const_zero );
     189           0 : }
     190             : 
     191             : static inline void
     192           0 : fd_secp256r1_point_set_inf( fd_secp256r1_point_t * r ) {
     193           0 :   fd_secp256r1_fp_set( r->x, fd_secp256r1_const_zero );
     194           0 :   fd_secp256r1_fp_set( r->y, fd_secp256r1_const_one_mont );
     195           0 :   fd_secp256r1_fp_set( r->z, fd_secp256r1_const_zero );
     196           0 : }
     197             : 
     198             : /* Returns 1 if (x, y), Montgomery domain, satisfies y^2 = x^3 + ax + b. */
     199             : static inline int
     200             : fd_secp256r1_point_on_curve( fd_secp256r1_fp_t const * x,
     201           0 :                              fd_secp256r1_fp_t const * y ) {
     202           0 :   fd_secp256r1_fp_t lhs[1], rhs[1];
     203           0 :   bignum_montsqr_p256( lhs->limbs, y->limbs );
     204           0 :   bignum_montsqr_p256( rhs->limbs, x->limbs );
     205           0 :   bignum_add_p256    ( rhs->limbs, rhs->limbs, fd_secp256r1_const_a_mont[0].limbs );
     206           0 :   bignum_montmul_p256( rhs->limbs, rhs->limbs, x->limbs );
     207           0 :   bignum_add_p256    ( rhs->limbs, rhs->limbs, fd_secp256r1_const_b_mont[0].limbs );
     208           0 :   return fd_uint256_eq( lhs, rhs );
     209           0 : }
     210             : 
     211             : static inline int
     212           0 : fd_secp256r1_point_validate_uncompressed( uchar const in[ 65 ] ) {
     213           0 :   if( FD_UNLIKELY( in[ 0 ]!=0x04U ) ) return FD_SECP256R1_FAILURE;
     214             : 
     215           0 :   fd_secp256r1_fp_t x[1], y[1];
     216           0 :   if( FD_UNLIKELY( !fd_secp256r1_fp_frombytes( x, in+1  ) ) ) return FD_SECP256R1_FAILURE;
     217           0 :   if( FD_UNLIKELY( !fd_secp256r1_fp_frombytes( y, in+33 ) ) ) return FD_SECP256R1_FAILURE;
     218             : 
     219           0 :   bignum_tomont_p256( x->limbs, x->limbs );
     220           0 :   bignum_tomont_p256( y->limbs, y->limbs );
     221           0 :   return fd_secp256r1_point_on_curve( x, y );
     222           0 : }
     223             : 
     224             : static inline fd_secp256r1_point_t *
     225             : fd_secp256r1_point_frombytes( fd_secp256r1_point_t * r,
     226        7357 :                               uchar const            in[ 33 ] ) {
     227        7357 :   fd_secp256r1_fp_t y2[1], demont_y[1];
     228             : 
     229        7357 :   uchar sgn = in[0];
     230        7357 :   if( FD_UNLIKELY( sgn!=2U && sgn!=3U ) ) {
     231           5 :     return FD_SECP256R1_FAILURE;
     232           5 :   }
     233             : 
     234        7352 :   if( FD_UNLIKELY( !fd_secp256r1_fp_frombytes( r->x, in+1 ) ) ) {
     235           0 :     return FD_SECP256R1_FAILURE;
     236           0 :   }
     237             : 
     238        7352 :   bignum_tomont_p256( r->x->limbs, r->x->limbs );
     239             : 
     240             :   /* y^2 = x^3 + ax + b */
     241        7352 :   bignum_montsqr_p256( y2->limbs, r->x->limbs );
     242        7352 :   bignum_add_p256    ( y2->limbs, y2->limbs, (ulong *)fd_secp256r1_const_a_mont[0].limbs );
     243        7352 :   bignum_montmul_p256( y2->limbs, y2->limbs, r->x->limbs );
     244        7352 :   bignum_add_p256    ( y2->limbs, y2->limbs, (ulong *)fd_secp256r1_const_b_mont[0].limbs );
     245             : 
     246             :   /* y = sqrt(y^2) */
     247        7352 :   if( FD_UNLIKELY( !fd_secp256r1_fp_sqrt( r->y, y2 ) ) ) {
     248          73 :     return FD_SECP256R1_FAILURE;
     249          73 :   }
     250             : 
     251             :   /* choose y or -y */
     252        7279 :   bignum_demont_p256( demont_y->limbs, r->y->limbs );
     253        7279 :   ulong cond = (demont_y->limbs[0] % 2) != (sgn == 3U);
     254        7279 :   bignum_optneg_p256( r->y->limbs, cond, r->y->limbs );
     255             : 
     256        7279 :   fd_secp256r1_fp_set( r->z, fd_secp256r1_const_one_mont );
     257             : 
     258        7279 :   return r;
     259        7352 : }
     260             : 
     261             : static inline int
     262             : fd_secp256r1_point_eq_x( fd_secp256r1_point_t const *  p,
     263        7302 :                          fd_secp256r1_scalar_t const * r ) {
     264        7302 :   fd_secp256r1_fp_t affine_x[1];
     265        7302 :   fd_secp256r1_scalar_t * affine_x_mod_n = affine_x;
     266             : 
     267        7302 :   if( FD_UNLIKELY( fd_uint256_eq( p->z, fd_secp256r1_const_zero ) ) ) {
     268          22 :     return FD_SECP256R1_FAILURE;
     269          22 :   }
     270             : 
     271             :   /* x = demont(X / Z^2) mod n */
     272        7280 :   bignum_montinv_p256( affine_x->limbs, (ulong *)p->z->limbs );
     273        7280 :   bignum_montsqr_p256( affine_x->limbs, affine_x->limbs );
     274        7280 :   bignum_montmul_p256( affine_x->limbs, affine_x->limbs, (ulong *)p->x->limbs );
     275        7280 :   bignum_demont_p256( affine_x_mod_n->limbs, affine_x->limbs );
     276        7280 :   bignum_mod_n256_4 ( affine_x_mod_n->limbs, affine_x_mod_n->limbs );
     277             : 
     278        7280 :   if( FD_LIKELY( fd_uint256_eq( r, affine_x_mod_n ) ) ) {
     279        4645 :     return FD_SECP256R1_SUCCESS;
     280        4645 :   }
     281        2635 :   return FD_SECP256R1_FAILURE;
     282        7280 : }
     283             : 
     284             : /* Given the projective point `r` and the affine point `p`,
     285             :    returns 1 if they are equal and 0 otherwise.
     286             :    Assumes that `p` X and Y coordinates are in Montgomery domain. */
     287             : static inline int
     288             : fd_secp256r1_point_eq_mixed( fd_secp256r1_point_t const * a,
     289        7291 :                              ulong                const   b[ 8 ] ) {
     290        7291 :   fd_secp256r1_fp_t x[1], y[1];
     291        7291 :   fd_memcpy( x->limbs, b+0, sizeof(fd_secp256r1_fp_t) );
     292        7291 :   fd_memcpy( y->limbs, b+4, sizeof(fd_secp256r1_fp_t) );
     293             :   /* Indicates if the affine point is zero. */
     294        7291 :   int is_zero = fd_uint256_eq( x, fd_secp256r1_const_zero ) & fd_uint256_eq( y, fd_secp256r1_const_zero );
     295             : 
     296             :   /* Easy cases */
     297        7291 :   if( FD_UNLIKELY( fd_uint256_eq( a->z, fd_secp256r1_const_zero ) ) ) {
     298           0 :     return is_zero;
     299           0 :   }
     300        7291 :   if( FD_UNLIKELY( is_zero ) ) {
     301           0 :     return 0;
     302           0 :   }
     303             : 
     304        7291 :   fd_secp256r1_fp_t z1z1[1];
     305        7291 :   bignum_montsqr_p256( z1z1->limbs, (ulong *)a->z->limbs );
     306             : 
     307        7291 :   fd_secp256r1_fp_t temp[1];
     308        7291 :   bignum_montmul_p256( temp->limbs, (ulong *)x->limbs, z1z1->limbs );
     309             : 
     310        7291 :   if( FD_UNLIKELY( fd_uint256_eq( a->x, temp ) ) ) {
     311          35 :     bignum_montmul_p256( temp->limbs, z1z1->limbs,  (ulong *)a->z->limbs );
     312          35 :     bignum_montmul_p256( temp->limbs, temp->limbs, (ulong *)y->limbs );
     313          35 :     return fd_uint256_eq( a->y, temp );
     314        7256 :   } else {
     315        7256 :     return 0;
     316        7256 :   }
     317        7291 : }
     318             : 
     319             : /* Adds projective point `a` and affine-Montgomery point `b`, both
     320             :    in Montgomery domain. Handles identity elements and the equal-point
     321             :    (doubling) case. */
     322             : static inline void
     323             : fd_secp256r1_point_add_mixed( fd_secp256r1_point_t *       r,
     324             :                               fd_secp256r1_point_t const * a,
     325        7299 :                               ulong const                  b[ 8 ] ) {
     326        7299 :   int b_is_zero = fd_uint256_eq( (fd_uint256_t const *)(b+0), fd_secp256r1_const_zero ) &
     327        7299 :                   fd_uint256_eq( (fd_uint256_t const *)(b+4), fd_secp256r1_const_zero );
     328             : 
     329        7299 :   if( FD_UNLIKELY( b_is_zero ) ) {
     330             :     /* a + 0 = a */
     331           0 :     if( r != a ) fd_memcpy( r, a, sizeof(fd_secp256r1_point_t) );
     332           0 :     return;
     333           0 :   }
     334             : 
     335        7299 :   if( FD_UNLIKELY( fd_secp256r1_point_eq_mixed( a, b ) ) ) {
     336          13 :     p256_montjdouble( (ulong *)r, (ulong *)a );
     337        7286 :   } else {
     338             :     /* Also handles a == 0 and a == -b */
     339        7286 :     p256_montjmixadd( (ulong *)r, (ulong *)a, b );
     340        7286 :   }
     341        7299 : }
     342             : 
     343             : static inline void
     344             : fd_secp256r1_double_scalar_mul_base( fd_secp256r1_point_t *        r,
     345             :                                      fd_secp256r1_scalar_t const * u1,
     346             :                                      fd_secp256r1_point_t const *  a,
     347        7296 :                                      fd_secp256r1_scalar_t const * u2 ) {
     348             :   /* u1*G */
     349        7296 :   ulong rtmp[ 8 ];
     350        7296 :   p256_scalarmulbase( rtmp, (ulong *)u1->limbs, 6, (ulong *)fd_secp256r1_base_point_table );
     351        7296 :   bignum_tomont_p256( rtmp, rtmp );
     352        7296 :   bignum_tomont_p256( rtmp+4, rtmp+4 );
     353             : 
     354             :   /* u2*A */
     355        7296 :   p256_montjscalarmul( (ulong *)r, (ulong *)u2->limbs, (ulong *)a );
     356             : 
     357             :   /* u1*G + u2*A */
     358        7296 :   fd_secp256r1_point_add_mixed( r, r, rtmp );
     359        7296 : }
     360             : 
     361             : /* r = 2a.  Maps infinity to infinity.  r may alias a. */
     362             : static inline void
     363             : fd_secp256r1_point_double( fd_secp256r1_point_t *       r,
     364           0 :                            fd_secp256r1_point_t const * a ) {
     365           0 :   p256_montjdouble( (ulong *)r, (ulong const *)a );
     366           0 : }
     367             : 
     368             : /* r = s*a. s is a plain LE integer of 4 limbs, reduced mod n internally. */
     369             : static inline void
     370             : fd_secp256r1_point_scalarmul( fd_secp256r1_point_t *       r,
     371             :                               ulong const                  s[ 4 ],
     372           0 :                               fd_secp256r1_point_t const * a ) {
     373           0 :   p256_montjscalarmul( (ulong *)r, s, (ulong const *)a );
     374           0 : }
     375             : 
     376             : /* Converts the Jacobian point a to affine-Montgomery (x, y) laid out
     377             :    as 8 limbs, (0, 0) for infinity. */
     378             : static inline void
     379             : fd_secp256r1_point_toaffine_mont( ulong                        r[ 8 ],
     380           0 :                                   fd_secp256r1_point_t const * a ) {
     381           0 :   if( FD_UNLIKELY( fd_secp256r1_point_is_inf( a ) ) ) {
     382           0 :     memset( r, 0, 8*sizeof(ulong) );
     383           0 :     return;
     384           0 :   }
     385           0 :   ulong zinv[ 4 ], zinv2[ 4 ];
     386           0 :   bignum_montinv_p256( zinv, a->z->limbs );
     387           0 :   bignum_montsqr_p256( zinv2, zinv );
     388           0 :   bignum_montmul_p256( r,   a->x->limbs, zinv2 );
     389           0 :   bignum_montmul_p256( r+4, zinv2, zinv );
     390           0 :   bignum_montmul_p256( r+4, r+4, a->y->limbs );
     391           0 : }
     392             : 
     393             : /* r = a + b, Jacobian points. r may alias a. */
     394             : static inline void
     395             : fd_secp256r1_point_add( fd_secp256r1_point_t *       r,
     396             :                         fd_secp256r1_point_t const * a,
     397           0 :                         fd_secp256r1_point_t const * b ) {
     398           0 :   ulong baff[ 8 ];
     399           0 :   fd_secp256r1_point_toaffine_mont( baff, b );
     400           0 :   fd_secp256r1_point_add_mixed( r, a, baff );
     401           0 : }

Generated by: LCOV version 1.14