LCOV - code coverage report
Current view: top level - ballet/ed25519 - fd_curve25519_scalar.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 32 74 43.2 %
Date: 2026-04-20 07:06:07 Functions: 13 225 5.8 %

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

Generated by: LCOV version 1.14