LCOV - code coverage report
Current view: top level - ballet/ed25519 - fd_curve25519_scalar.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 26 69 37.7 %
Date: 2026-02-08 06:05:17 Functions: 7 225 3.1 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_ballet_ed25519_fd_curve25519_scalar_h
       2             : #define HEADER_fd_src_ballet_ed25519_fd_curve25519_scalar_h
       3             : 
       4             : /* fd_curve25519_scalar.h provides the public Curve25519 scalar API.
       5             : 
       6             :    All operations in this API should be assumed to take a variable
       7             :    amount of time depending on inputs.  (And thus should not be exposed
       8             :    to secret data) */
       9             : 
      10             : #include "../fd_ballet_base.h"
      11             : 
      12             : static const uchar fd_curve25519_scalar_zero[ 32 ] = {
      13             :   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      14             :   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      15             : };
      16             : 
      17             : static const uchar fd_curve25519_scalar_one[ 32 ] = {
      18             :   1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      19             :   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      20             : };
      21             : 
      22             : /* l   = 2^252 + 27742317777372353535851937790883648493
      23             :        = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed
      24             :    l-1 = 0x10...ec */
      25             : static const uchar fd_curve25519_scalar_minus_one[ 32 ] = {
      26             :   0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
      27             :   0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
      28             :   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      29             :   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
      30             : };
      31             : 
      32             : FD_PROTOTYPES_BEGIN
      33             : 
      34             : /* fd_curve25519_scalar_reduce computes s mod l where s is a 512-bit value.  s
      35             :    is stored in 64-byte little endian form and in points to the first
      36             :    byte of s.  l is:
      37             : 
      38             :      2^252 + 27742317777372353535851937790883648493.
      39             : 
      40             :    The result can be represented as a 256-bit value and stored in a
      41             :    32-byte little endian form.  out points to where to store the result.
      42             : 
      43             :    Does no input argument checking.  The caller takes a write interest
      44             :    in out and a read interest in in for the duration of the call.
      45             :    Returns out and, on return, out will be populated with the 256-bit
      46             :    result.  In-place operation fine. */
      47             : 
      48             : uchar *
      49             : fd_curve25519_scalar_reduce( uchar       out[ 32 ],
      50             :                              uchar const in [ 64 ] );
      51             : 
      52             : /* fd_curve25519_scalar_validate checks whether the given Ed25519 scalar n
      53             :    matches the canonical byte representation.
      54             :    Not constant time and thus should not be exposed to secrets.
      55             :    Returns s if canonical, NULL otherwise. */
      56             : 
      57             : static inline uchar const *
      58     7122342 : fd_curve25519_scalar_validate( uchar const s[ 32 ] ) {
      59             :   /* If none of the top 4 bits are set, then the scalar fits into S \in [0, 2^252),
      60             :      which is a tighter range than [0, L), which is about [0, 2^252.2). In this case
      61             :      we can "succeed-fast" and skip the full canonical check. */
      62     7122342 :   if ( FD_UNLIKELY( s[31] & 0xF0 ) ) {
      63             :     /* Assuming canonical and IID scalars, the chance of the 252nd bit being
      64             :        set is roughly 1/2^128 or log2(1 - (2^252 - 1) / L). */
      65             : 
      66             :     /* If any of the top 3 bits are set, the scalar representation must be invalid. */
      67     3217517 :     if ( FD_LIKELY( s[31] & 0xE0 ) ) return NULL;
      68             : 
      69             :     /* Nothing left for us to do except determine if `s` is between [2^252, L) or not. */
      70     3013626 :     ulong s0 = fd_ulong_load_8_fast( s      );
      71     3013626 :     ulong s1 = fd_ulong_load_8_fast( s +  8 );
      72     3013626 :     ulong s2 = fd_ulong_load_8_fast( s + 16 );
      73     3013626 :     ulong s3 = fd_ulong_load_8_fast( s + 24 );
      74     3013626 :     ulong l0 = *(ulong *)(&fd_curve25519_scalar_minus_one[  0 ]);
      75     3013626 :     ulong l1 = *(ulong *)(&fd_curve25519_scalar_minus_one[  8 ]);
      76     3013626 :     ulong l2 = *(ulong *)(&fd_curve25519_scalar_minus_one[ 16 ]);
      77     3013626 :     ulong l3 = *(ulong *)(&fd_curve25519_scalar_minus_one[ 24 ]);
      78     3013626 :     return (
      79     3013626 :       (s3 < l3)
      80     3013626 :       || ((s3 == l3) && (s2 < l2))
      81     3013626 :       || ((s3 == l3) && (s2 == l2) && (s1 < l1))
      82     3013626 :       || ((s3 == l3) && (s2 == l2) && (s1 == l1) && (s0 <= l0))
      83     3013626 :     ) ? s : NULL;
      84     3217517 :   }
      85     3904825 :   return s;
      86     7122342 : }
      87             : 
      88             : /* fd_curve25519_scalar_muladd computes s = (a*b+c) mod l where a, b and c
      89             :    are 256-bit values.  a is stored in 32-byte little endian form and a
      90             :    points to the first byte of a.  Similarly for b and c.  l is:
      91             : 
      92             :      2^252 + 27742317777372353535851937790883648493.
      93             : 
      94             :    The result can be represented as a 256-bit value and stored in a
      95             :    32-byte little endian form.  s points to where to store the result.
      96             : 
      97             :    Does no input argument checking.  The caller takes a write interest
      98             :    in s and a read interest in a, b and c for the duration of the call.
      99             :    Returns s and, on return, s will be populated with the 256-bit
     100             :    result.  In-place operation fine. */
     101             : 
     102             : uchar *
     103             : fd_curve25519_scalar_muladd( uchar       s[ 32 ],
     104             :                              uchar const * a,
     105             :                              uchar const b[ 32 ],
     106             :                              uchar const c[ 32 ] );
     107             : 
     108             : static inline uchar *
     109             : fd_curve25519_scalar_mul   ( uchar *       s,
     110             :                              uchar const * a,
     111           0 :                              uchar const * b ) {
     112           0 :   return fd_curve25519_scalar_muladd( s, a, b, fd_curve25519_scalar_zero );
     113           0 : }
     114             : 
     115             : static inline uchar *
     116             : fd_curve25519_scalar_add   ( uchar *       s,
     117             :                              uchar const * a,
     118           0 :                              uchar const * b ) {
     119           0 :   return fd_curve25519_scalar_muladd( s, a, fd_curve25519_scalar_one, b );
     120           0 : }
     121             : 
     122             : static inline uchar *
     123             : fd_curve25519_scalar_sub   ( uchar *       s,
     124             :                              uchar const * a,
     125           0 :                              uchar const * b ) {
     126             :   //TODO implement dedicated neg/sub
     127           0 :   return fd_curve25519_scalar_muladd( s, fd_curve25519_scalar_minus_one, b, a );
     128           0 : }
     129             : 
     130             : static inline uchar *
     131             : fd_curve25519_scalar_neg   ( uchar *       s,
     132           6 :                              uchar const * a ) {
     133             :   //TODO implement dedicated neg/sub
     134           6 :   return fd_curve25519_scalar_muladd( s, fd_curve25519_scalar_minus_one, a, fd_curve25519_scalar_zero );
     135           6 : }
     136             : 
     137             : static inline uchar *
     138             : fd_curve25519_scalar_set   ( uchar *       s,
     139           6 :                              uchar const * a ) {
     140           6 :   return fd_memcpy( s, a, 32 );
     141           6 : }
     142             : 
     143             : static inline uchar *
     144             : fd_curve25519_scalar_from_u64( uchar *     s,
     145           0 :                                ulong const x ) {
     146           0 :   fd_memset( s, 0, 32 );
     147           0 :   *((ulong *)s) = x;
     148           0 :   return s;
     149           0 : }
     150             : 
     151             : static inline uchar *
     152             : fd_curve25519_scalar_inv( uchar *       s,
     153           0 :                           uchar const * a ) {
     154           0 :   uchar t[ 32 ];
     155             :   // TODO: use mul chain to save ~12% https://briansmith.org/ecc-inversion-addition-chains-01#curve25519_scalar_inversion
     156             :   /* the bits of -2 are the same as -1, except the first few (that we skip):
     157             :      -1 = 0xEC ... = b 1110 1100 ...
     158             :      -2 = 0xEB ... = b 1110 1011 ...
     159             :                        ^ bit 7 ^ bit 0
     160             :    */
     161             :   /* bit 0 == 1 */
     162           0 :   fd_memcpy( t, a, 32 );
     163           0 :   fd_memcpy( s, a, 32 );
     164             :   /* bit 1 == 1 */
     165           0 :   fd_curve25519_scalar_mul( t, t, t );
     166           0 :   fd_curve25519_scalar_mul( s, s, t );
     167             :   /* bit 2 == 0 */
     168           0 :   fd_curve25519_scalar_mul( t, t, t );
     169             :   /* from bit 3 on, use -1 bits */
     170           0 :   for( ulong i=3; i<=252; i++ ) {
     171           0 :     fd_curve25519_scalar_mul( t, t, t );
     172           0 :     if( (fd_curve25519_scalar_minus_one[ i/8 ] & (1 << (i % 8))) ) {
     173           0 :       fd_curve25519_scalar_mul( s, s, t );
     174           0 :     }
     175           0 :   }
     176           0 :   return s;
     177           0 : }
     178             : 
     179             : static inline void
     180             : fd_curve25519_scalar_batch_inv( uchar       s     [ 32 ], /* sz scalars */
     181             :                                 uchar       allinv[ 32 ], /* 1 scalar */
     182             :                                 uchar const a     [ 32 ], /* sz scalars */
     183           0 :                                 ulong       sz ) {
     184           0 :   uchar acc[ 32 ];
     185           0 :   fd_memcpy( acc, fd_curve25519_scalar_one, 32 );
     186           0 :   for( ulong i=0; i<sz; i++ ) {
     187           0 :     fd_memcpy( &s[ i*32 ], acc, 32 );
     188           0 :     fd_curve25519_scalar_mul( acc, acc, &a[ i*32 ] );
     189           0 :   }
     190             : 
     191           0 :   fd_curve25519_scalar_inv( acc, acc );
     192           0 :   fd_memcpy( allinv, acc, 32 );
     193             : 
     194           0 :   for( int i=(int)sz-1; i>=0; i-- ) {
     195           0 :     fd_curve25519_scalar_mul( &s[ i*32 ], &s[ i*32 ], acc );
     196           0 :     fd_curve25519_scalar_mul( acc, acc, &a[ i*32 ] );
     197           0 :   }
     198           0 : }
     199             : 
     200             : void
     201             : fd_curve25519_scalar_wnaf( short       slides[ 256 ], /* 256-entry */
     202             :                            uchar const n[ 32 ],       /* 32-byte, assumes valid scalar */
     203             :                            int         bits );               /* range: [1:12], 1 = NAF */
     204             : 
     205             : FD_PROTOTYPES_END
     206             : 
     207             : #endif /* HEADER_fd_src_ballet_ed25519_fd_curve25519_scalar_h */

Generated by: LCOV version 1.14