Skip to main content

blvm_secp256k1/
scalar.rs

1//! Scalar arithmetic modulo the secp256k1 group order n.
2//!
3//! n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
4//!
5//! **Timing:** [`Scalar::inv`] is constant-time on `x86_64` and `aarch64` (modular inverse),
6//! and falls back to Fermat on other targets. [`Scalar::inv_var`] is the same as [`inv`](Scalar::inv).
7
8#[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
9mod scalar_asm {
10    use super::Scalar;
11
12    unsafe extern "C" {
13        /// libsecp256k1 scalar_mul_512: l8 = a * b (512-bit product).
14        /// SysV: rdi=l8, rsi=a, rdx=b.
15        fn blvm_secp256k1_scalar_mul_512(l8: *mut u64, a: *const Scalar, b: *const Scalar);
16
17        /// libsecp256k1 scalar_reduce_512: reduce 512-bit l mod n into r.
18        /// Returns overflow for final reduction. SysV: rdi=r, rsi=l.
19        fn blvm_secp256k1_scalar_reduce_512(r: *mut Scalar, l: *const u64) -> u64;
20    }
21
22    #[inline(always)]
23    pub(super) unsafe fn scalar_mul_512_asm(l: *mut u64, a: *const Scalar, b: *const Scalar) {
24        unsafe {
25            blvm_secp256k1_scalar_mul_512(l, a, b);
26        }
27    }
28
29    #[inline(always)]
30    pub(super) unsafe fn scalar_reduce_512_asm(r: *mut Scalar, l: *const u64) -> u64 {
31        unsafe { blvm_secp256k1_scalar_reduce_512(r, l) }
32    }
33}
34
35use num_bigint::BigUint;
36use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
37
38/// Scalar modulo group order n. 4x64 limb layout (x86_64, aarch64).
39#[repr(C)]
40#[derive(Clone, Copy, Debug)]
41pub struct Scalar {
42    pub d: [u64; 4],
43}
44
45// secp256k1 group order n
46const N_0: u64 = 0xBFD25E8CD0364141;
47const N_1: u64 = 0xBAAEDCE6AF48A03B;
48const N_2: u64 = 0xFFFFFFFFFFFFFFFE;
49const N_3: u64 = 0xFFFFFFFFFFFFFFFF;
50
51// 2^256 - n (for reduction)
52const N_C_0: u64 = 0x402DA1732FC9BEBF;
53const N_C_1: u64 = 0x4551231950B75FC4;
54const N_C_2: u64 = 1;
55
56// n/2 (for is_high)
57const N_H_0: u64 = 0xDFE92F46681B20A0;
58const N_H_1: u64 = 0x5D576E7357A4501D;
59const N_H_2: u64 = 0xFFFFFFFFFFFFFFFF;
60const N_H_3: u64 = 0x7FFFFFFFFFFFFFFF;
61
62// modulus n (for safegcd inv)
63#[allow(dead_code)]
64const N: Scalar = Scalar {
65    d: [N_0, N_1, N_2, N_3],
66};
67
68const LAMBDA: Scalar = Scalar {
69    d: [
70        0xDF02967C1B23BD72,
71        0x122E22EA20816678,
72        0xA5261C028812645A,
73        0x5363AD4CC05C30E0,
74    ],
75};
76
77impl Scalar {
78    pub fn zero() -> Self {
79        Self { d: [0, 0, 0, 0] }
80    }
81
82    pub fn one() -> Self {
83        Self { d: [1, 0, 0, 0] }
84    }
85
86    pub fn set_int(&mut self, v: u32) {
87        self.d[0] = v as u64;
88        self.d[1] = 0;
89        self.d[2] = 0;
90        self.d[3] = 0;
91    }
92
93    /// Set from 32-byte big-endian. Reduces mod n.
94    pub fn set_b32(&mut self, bin: &[u8; 32]) -> bool {
95        self.d[0] = read_be64(&bin[24..32]);
96        self.d[1] = read_be64(&bin[16..24]);
97        self.d[2] = read_be64(&bin[8..16]);
98        self.d[3] = read_be64(&bin[0..8]);
99        let overflow = self.check_overflow();
100        self.reduce(overflow as u64);
101        overflow
102    }
103
104    pub fn get_b32(&self, bin: &mut [u8; 32]) {
105        write_be64(&mut bin[0..8], self.d[3]);
106        write_be64(&mut bin[8..16], self.d[2]);
107        write_be64(&mut bin[16..24], self.d[1]);
108        write_be64(&mut bin[24..32], self.d[0]);
109    }
110
111    fn check_overflow(&self) -> bool {
112        let mut yes = 0u64;
113        let mut no = 0u64;
114        no |= (self.d[3] < N_3) as u64;
115        no |= (self.d[2] < N_2) as u64;
116        yes |= (self.d[2] > N_2) as u64 & !no;
117        no |= (self.d[1] < N_1) as u64;
118        yes |= (self.d[1] > N_1) as u64 & !no;
119        yes |= (self.d[0] >= N_0) as u64 & !no;
120        yes != 0
121    }
122
123    fn reduce(&mut self, overflow: u64) {
124        let mut t: u128 = self.d[0] as u128 + (overflow as u128 * N_C_0 as u128);
125        self.d[0] = t as u64;
126        t >>= 64;
127        t += self.d[1] as u128 + (overflow as u128 * N_C_1 as u128);
128        self.d[1] = t as u64;
129        t >>= 64;
130        t += self.d[2] as u128 + (overflow as u128 * N_C_2 as u128);
131        self.d[2] = t as u64;
132        t >>= 64;
133        t += self.d[3] as u128;
134        self.d[3] = t as u64;
135    }
136
137    pub fn is_zero(&self) -> bool {
138        (self.d[0] | self.d[1] | self.d[2] | self.d[3]) == 0
139    }
140
141    pub fn is_one(&self) -> bool {
142        (self.d[0] ^ 1) | self.d[1] | self.d[2] | self.d[3] == 0
143    }
144
145    /// True if scalar is odd (d[0] & 1).
146    pub fn is_odd(&self) -> bool {
147        self.d[0] & 1 != 0
148    }
149
150    /// True if scalar is even. Used by wnaf_fixed (Pippenger).
151    pub(crate) fn is_even(&self) -> bool {
152        self.d[0] & 1 == 0
153    }
154
155    /// self = a - b (mod n). Result in [0, n-1].
156    #[allow(dead_code)]
157    fn sub(&mut self, a: &Scalar, b: &Scalar) {
158        let mut neg_b = Scalar::zero();
159        neg_b.negate(b);
160        self.add(a, &neg_b);
161    }
162
163    /// self = self / 2. Only valid when self is even.
164    #[allow(dead_code)]
165    fn half(&mut self) {
166        self.d[0] = (self.d[0] >> 1) | (self.d[1] << 63);
167        self.d[1] = (self.d[1] >> 1) | (self.d[2] << 63);
168        self.d[2] = (self.d[2] >> 1) | (self.d[3] << 63);
169        self.d[3] >>= 1;
170    }
171
172    /// self = (self + n) / 2. Only valid when self is odd. Result in [0, n-1].
173    #[allow(dead_code)]
174    fn half_add_n(&mut self) {
175        let mut t: u128 = self.d[0] as u128 + N_0 as u128;
176        let c0 = t as u64;
177        let mut c1 = (t >> 64) as u64;
178        t = self.d[1] as u128 + N_1 as u128 + c1 as u128;
179        c1 = t as u64;
180        let mut c2 = (t >> 64) as u64;
181        t = self.d[2] as u128 + N_2 as u128 + c2 as u128;
182        c2 = t as u64;
183        let mut c3 = (t >> 64) as u64;
184        t = self.d[3] as u128 + N_3 as u128 + c3 as u128;
185        c3 = t as u64;
186        let c4 = (t >> 64) as u64;
187        self.d[0] = (c0 >> 1) | (c1 << 63);
188        self.d[1] = (c1 >> 1) | (c2 << 63);
189        self.d[2] = (c2 >> 1) | (c3 << 63);
190        self.d[3] = (c3 >> 1) | (c4 << 63);
191        self.reduce(self.check_overflow() as u64);
192    }
193
194    /// div2(M, x): x/2 mod n when x even, (x+n)/2 mod n when x odd.
195    ///
196    /// Constant-time: no branch on the scalar's parity bit. Builds a mask from
197    /// the low bit and adds `N * mask` before shifting, so both halves of the
198    /// original `if/else` execute in the same instruction stream.
199    pub fn div2(&mut self) {
200        // add_mask = u64::MAX when odd, 0 when even — no branch.
201        let add_mask = 0u64.wrapping_sub(self.d[0] & 1);
202        let mut t: u128 = self.d[0] as u128 + (N_0 & add_mask) as u128;
203        let c0 = t as u64;
204        t >>= 64;
205        t += self.d[1] as u128 + (N_1 & add_mask) as u128;
206        let c1 = t as u64;
207        t >>= 64;
208        t += self.d[2] as u128 + (N_2 & add_mask) as u128;
209        let c2 = t as u64;
210        t >>= 64;
211        t += self.d[3] as u128 + (N_3 & add_mask) as u128;
212        let c3 = t as u64;
213        let c4 = (t >> 64) as u64;
214        self.d[0] = (c0 >> 1) | (c1 << 63);
215        self.d[1] = (c1 >> 1) | (c2 << 63);
216        self.d[2] = (c2 >> 1) | (c3 << 63);
217        self.d[3] = (c3 >> 1) | (c4 << 63);
218        // For any valid scalar in [0, N-1] the result is already < N; reduce is a no-op
219        // (overflow = 0) but kept as a safety net — reduce(0) touches no data.
220        self.reduce(self.check_overflow() as u64);
221    }
222
223    /// r = a/2 (mod n). Same as libsecp256k1 `scalar_half` (used by `ecmult_const`).
224    pub fn half_modn(&mut self, a: &Scalar) {
225        *self = *a;
226        self.div2();
227    }
228
229    /// tmp = a + b (full 257-bit add, no reduction). Used when both are odd and we need (a+b)/2.
230    #[allow(dead_code)]
231    fn add_no_reduce(a: &Scalar, b: &Scalar) -> [u64; 5] {
232        let mut t: u128 = a.d[0] as u128 + b.d[0] as u128;
233        let c0 = t as u64;
234        let mut c1 = (t >> 64) as u64;
235        t = a.d[1] as u128 + b.d[1] as u128 + c1 as u128;
236        c1 = t as u64;
237        let mut c2 = (t >> 64) as u64;
238        t = a.d[2] as u128 + b.d[2] as u128 + c2 as u128;
239        c2 = t as u64;
240        let mut c3 = (t >> 64) as u64;
241        t = a.d[3] as u128 + b.d[3] as u128 + c3 as u128;
242        c3 = t as u64;
243        let c4 = (t >> 64) as u64;
244        [c0, c1, c2, c3, c4]
245    }
246
247    /// self = (c0..c4) >> 1, then reduce mod n.
248    #[allow(dead_code)]
249    fn set_from_5limb_half(&mut self, c: &[u64; 5]) {
250        self.d[0] = (c[0] >> 1) | (c[1] << 63);
251        self.d[1] = (c[1] >> 1) | (c[2] << 63);
252        self.d[2] = (c[2] >> 1) | (c[3] << 63);
253        self.d[3] = (c[3] >> 1) | (c[4] << 63);
254        self.reduce(self.check_overflow() as u64);
255    }
256
257    /// self = (a - b) >> 1 mod n. a and b odd. When a>=b, a-b is even; when a<b, a-b+n is odd, div2 adds n.
258    #[allow(dead_code)]
259    fn sub_half(&mut self, a: &Scalar, b: &Scalar) {
260        self.sub(a, b);
261        self.div2();
262    }
263
264    pub fn add(&mut self, a: &Scalar, b: &Scalar) -> bool {
265        let mut t: u128 = a.d[0] as u128 + b.d[0] as u128;
266        self.d[0] = t as u64;
267        t >>= 64;
268        t += a.d[1] as u128 + b.d[1] as u128;
269        self.d[1] = t as u64;
270        t >>= 64;
271        t += a.d[2] as u128 + b.d[2] as u128;
272        self.d[2] = t as u64;
273        t >>= 64;
274        t += a.d[3] as u128 + b.d[3] as u128;
275        self.d[3] = t as u64;
276        t >>= 64;
277        let overflow = t as u64 + self.check_overflow() as u64;
278        debug_assert!(overflow <= 1);
279        self.reduce(overflow);
280        overflow != 0
281    }
282
283    pub fn negate(&mut self, a: &Scalar) {
284        // Branchless mask: u64::MAX iff a != 0 (negate is a no-op on zero in mod-n sense).
285        let nz = a.d[0] | a.d[1] | a.d[2] | a.d[3];
286        let nonzero = 0u64.wrapping_sub((nz != 0) as u64);
287        let mut t: u128 = (!a.d[0]) as u128 + (N_0 + 1) as u128;
288        self.d[0] = (t as u64) & nonzero;
289        t >>= 64;
290        t += (!a.d[1]) as u128 + N_1 as u128;
291        self.d[1] = (t as u64) & nonzero;
292        t >>= 64;
293        t += (!a.d[2]) as u128 + N_2 as u128;
294        self.d[2] = (t as u64) & nonzero;
295        t >>= 64;
296        t += (!a.d[3]) as u128 + N_3 as u128;
297        self.d[3] = (t as u64) & nonzero;
298    }
299
300    pub fn mul(&mut self, a: &Scalar, b: &Scalar) {
301        let mut l = [0u64; 8];
302        scalar_mul_512(&mut l, a, b);
303        scalar_reduce_512(self, &l);
304    }
305
306    /// split_lambda: find r1, r2 such that r1 + r2*lambda == k (mod n)
307    pub fn split_lambda(r1: &mut Scalar, r2: &mut Scalar, k: &Scalar) {
308        const MINUS_B1: Scalar = Scalar {
309            d: [
310                (0x6F547FA9u64 << 32) | 0x0ABFE4C3,
311                (0xE4437ED6u64 << 32) | 0x010E8828,
312                0,
313                0,
314            ],
315        };
316        const MINUS_B2: Scalar = Scalar {
317            d: [
318                (0xD765CDA8u64 << 32) | 0x3DB1562C,
319                (0x8A280AC5u64 << 32) | 0x0774346D,
320                (0xFFFFFFFFu64 << 32) | 0xFFFFFFFE,
321                (0xFFFFFFFFu64 << 32) | 0xFFFFFFFF,
322            ],
323        };
324        const G1: Scalar = Scalar {
325            d: [
326                (0xE893209Au64 << 32) | 0x45DBB031,
327                (0x3DAA8A14u64 << 32) | 0x71E8CA7F,
328                (0xE86C90E4u64 << 32) | 0x9284EB15,
329                (0x3086D221u64 << 32) | 0xA7D46BCD,
330            ],
331        };
332        const G2: Scalar = Scalar {
333            d: [
334                (0x1571B4AEu64 << 32) | 0x8AC47F71,
335                (0x221208ACu64 << 32) | 0x9DF506C6,
336                (0x6F547FA9u64 << 32) | 0x0ABFE4C4,
337                (0xE4437ED6u64 << 32) | 0x010E8828,
338            ],
339        };
340
341        let mut c1 = Scalar::zero();
342        let mut c2 = Scalar::zero();
343        scalar_mul_shift_var(&mut c1, k, &G1, 384);
344        scalar_mul_shift_var(&mut c2, k, &G2, 384);
345        let mut t = Scalar::zero();
346        t.mul(&c1, &MINUS_B1);
347        c1 = t;
348        t.mul(&c2, &MINUS_B2);
349        c2 = t;
350        r2.add(&c1, &c2);
351        r1.mul(r2, &LAMBDA);
352        let mut neg = Scalar::zero();
353        neg.negate(r1);
354        r1.add(&neg, k);
355    }
356
357    /// Extract `count` bits at `offset` (0..256). Count in [1,32].
358    /// For single-limb (offset and offset+count-1 in same u64) uses fast path.
359    pub fn get_bits_limb32(&self, offset: u32, count: u32) -> u32 {
360        debug_assert!(count > 0 && count <= 32);
361        debug_assert!((offset + count - 1) >> 6 == offset >> 6);
362        let limb = offset >> 6;
363        let shift = offset & 0x3F;
364        let mask = if count == 32 {
365            u32::MAX
366        } else {
367            (1u32 << count) - 1
368        };
369        ((self.d[limb as usize] >> shift) as u32) & mask
370    }
371
372    /// Extract `count` bits at `offset`. Count in [1,32], offset+count <= 256.
373    pub fn get_bits_var(&self, offset: u32, count: u32) -> u32 {
374        debug_assert!(count > 0 && count <= 32);
375        debug_assert!(offset + count <= 256);
376        if (offset + count - 1) >> 6 == offset >> 6 {
377            self.get_bits_limb32(offset, count)
378        } else {
379            let limb = (offset >> 6) as usize;
380            let shift = offset & 0x3F;
381            let mask = if count == 32 {
382                u32::MAX
383            } else {
384                (1u32 << count) - 1
385            };
386            let lo = self.d[limb] >> shift;
387            let hi = self.d[limb + 1].wrapping_shl(64u32 - shift);
388            ((lo | hi) as u32) & mask
389        }
390    }
391
392    pub fn split_128(r1: &mut Scalar, r2: &mut Scalar, k: &Scalar) {
393        r1.d[0] = k.d[0];
394        r1.d[1] = k.d[1];
395        r1.d[2] = 0;
396        r1.d[3] = 0;
397        r2.d[0] = k.d[2];
398        r2.d[1] = k.d[3];
399        r2.d[2] = 0;
400        r2.d[3] = 0;
401    }
402
403    /// Modular inverse r = a^(-1) (mod n). If a is zero, r is zero.
404    /// On `x86_64` and `aarch64` this uses the same safegcd / `modinv64` path as libsecp256k1.
405    /// On other targets it falls back to Fermat; treat those targets as not secret-safe for timing.
406    pub fn inv(&mut self, a: &Scalar) {
407        if a.is_zero() {
408            *self = Scalar::zero();
409            return;
410        }
411        #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
412        {
413            use crate::modinv64::{SECP256K1_SCALAR_MODINV_MODINFO, modinv64};
414            let mut x = scalar_to_signed62(a);
415            modinv64(&mut x, &SECP256K1_SCALAR_MODINV_MODINFO);
416            scalar_from_signed62(self, &x);
417        }
418        #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
419        {
420            let a_big = scalar_to_biguint(a);
421            let n_big = scalar_to_biguint(&N);
422            let exp = &n_big - 2u32;
423            let inv_big = a_big.modpow(&exp, &n_big);
424            biguint_to_scalar(self, &inv_big);
425        }
426    }
427
428    /// Backwards compatibility: same as [`Self::inv`].
429    pub fn inv_var(&mut self, a: &Scalar) {
430        self.inv(a);
431    }
432
433    /// True if scalar is in the upper half [n/2, n).
434    pub fn is_high(&self) -> bool {
435        let mut yes = 0u64;
436        let mut no = 0u64;
437        no |= (self.d[3] < N_H_3) as u64;
438        yes |= (self.d[3] > N_H_3) as u64 & !no;
439        no |= (self.d[2] < N_H_2) as u64 & !yes;
440        no |= (self.d[1] < N_H_1) as u64 & !yes;
441        yes |= (self.d[1] > N_H_1) as u64 & !no;
442        yes |= (self.d[0] > N_H_0) as u64 & !no;
443        yes != 0
444    }
445
446    /// Conditionally negate: if flag != 0, negate in place. Returns 1 if negated, -1 if not.
447    ///
448    /// Constant-time: mask and nonzero are constructed without branches; the
449    /// return value is computed via branchless sign-extension of the mask's MSB.
450    pub fn cond_negate(&mut self, flag: i32) -> i32 {
451        // Build masks without branches.
452        let mask = 0u64.wrapping_sub((flag != 0) as u64); // 0 or u64::MAX
453        let nonzero = 0u64.wrapping_sub((!self.is_zero()) as u64); // 0 or u64::MAX
454        let mut t: u128 = (self.d[0] ^ mask) as u128;
455        t += ((N_0 + 1) & mask) as u128;
456        self.d[0] = (t as u64) & nonzero;
457        t >>= 64;
458        t += (self.d[1] ^ mask) as u128;
459        t += (N_1 & mask) as u128;
460        self.d[1] = (t as u64) & nonzero;
461        t >>= 64;
462        t += (self.d[2] ^ mask) as u128;
463        t += (N_2 & mask) as u128;
464        self.d[2] = (t as u64) & nonzero;
465        t >>= 64;
466        t += (self.d[3] ^ mask) as u128;
467        t += (N_3 & mask) as u128;
468        self.d[3] = (t as u64) & nonzero;
469        // Return 1 if negated, -1 if not — branchless via mask MSB.
470        // mask=0   → (0>>63)=0  → 0*2-1 = -1
471        // mask=MAX → (MAX>>63)=1 → 1*2-1 =  1
472        ((mask >> 63) as i32) * 2 - 1
473    }
474}
475
476impl ConstantTimeEq for Scalar {
477    fn ct_eq(&self, other: &Self) -> Choice {
478        self.d[0].ct_eq(&other.d[0])
479            & self.d[1].ct_eq(&other.d[1])
480            & self.d[2].ct_eq(&other.d[2])
481            & self.d[3].ct_eq(&other.d[3])
482    }
483}
484
485impl ConditionallySelectable for Scalar {
486    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
487        Self {
488            d: [
489                u64::conditional_select(&a.d[0], &b.d[0], choice),
490                u64::conditional_select(&a.d[1], &b.d[1], choice),
491                u64::conditional_select(&a.d[2], &b.d[2], choice),
492                u64::conditional_select(&a.d[3], &b.d[3], choice),
493            ],
494        }
495    }
496}
497
498/// Pack 4×64 scalar limbs into 5×62 signed limbs for modinv64.
499#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
500fn scalar_to_signed62(a: &Scalar) -> crate::modinv64::Signed62 {
501    const M62: u64 = u64::MAX >> 2;
502    let d = &a.d;
503    crate::modinv64::Signed62 {
504        v: [
505            (d[0] & M62) as i64,
506            ((d[0] >> 62 | d[1] << 2) & M62) as i64,
507            ((d[1] >> 60 | d[2] << 4) & M62) as i64,
508            ((d[2] >> 58 | d[3] << 6) & M62) as i64,
509            (d[3] >> 56) as i64,
510        ],
511    }
512}
513
514/// Unpack 5×62 signed limbs back to 4×64 scalar limbs.
515#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
516fn scalar_from_signed62(r: &mut Scalar, a: &crate::modinv64::Signed62) {
517    let v = &a.v;
518    r.d[0] = (v[0] as u64) | ((v[1] as u64) << 62);
519    r.d[1] = ((v[1] as u64) >> 2) | ((v[2] as u64) << 60);
520    r.d[2] = ((v[2] as u64) >> 4) | ((v[3] as u64) << 58);
521    r.d[3] = ((v[3] as u64) >> 6) | ((v[4] as u64) << 56);
522}
523
524#[allow(dead_code)]
525fn scalar_to_biguint(s: &Scalar) -> BigUint {
526    let mut bytes = [0u8; 32];
527    s.get_b32(&mut bytes);
528    BigUint::from_bytes_be(&bytes)
529}
530
531#[allow(dead_code)]
532fn biguint_to_scalar(r: &mut Scalar, b: &BigUint) {
533    let bytes = b.to_bytes_be();
534    let mut buf = [0u8; 32];
535    let len = bytes.len().min(32);
536    let start = 32 - len;
537    buf[start..].copy_from_slice(&bytes[..len]);
538    r.set_b32(&buf);
539}
540
541fn read_be64(b: &[u8]) -> u64 {
542    u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
543}
544
545fn write_be64(b: &mut [u8], v: u64) {
546    b[0..8].copy_from_slice(&v.to_be_bytes());
547}
548
549fn scalar_mul_512(l: &mut [u64; 8], a: &Scalar, b: &Scalar) {
550    #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
551    {
552        unsafe {
553            scalar_asm::scalar_mul_512_asm(l.as_mut_ptr(), a, b);
554        }
555    }
556    #[cfg(not(all(target_arch = "x86_64", not(target_os = "windows"))))]
557    {
558        scalar_mul_512_rust(l, a, b);
559    }
560}
561
562#[cfg(not(all(target_arch = "x86_64", not(target_os = "windows"))))]
563fn scalar_mul_512_rust(l: &mut [u64; 8], a: &Scalar, b: &Scalar) {
564    let mut c0: u64 = 0;
565    let mut c1: u64 = 0;
566    let mut c2: u32 = 0;
567
568    macro_rules! muladd_fast {
569        ($a:expr, $b:expr) => {{
570            let prod = ($a as u128) * ($b as u128);
571            let prod_lo = prod as u64;
572            let prod_hi = (prod >> 64) as u64;
573            let (lo, o) = c0.overflowing_add(prod_lo);
574            c0 = lo;
575            c1 += prod_hi + o as u64; // C: th = prod_hi + (c0 < tl)
576        }};
577    }
578    macro_rules! muladd {
579        ($a:expr, $b:expr) => {{
580            let prod = ($a as u128) * ($b as u128);
581            let hi = (prod >> 64) as u64;
582            let (lo, o1) = c0.overflowing_add(prod as u64);
583            c0 = lo;
584            let th = hi + o1 as u64;
585            let (mid, o2) = c1.overflowing_add(th);
586            c1 = mid;
587            c2 += o2 as u32;
588        }};
589    }
590    macro_rules! sumadd {
591        ($a:expr) => {{
592            let (lo, o) = c0.overflowing_add($a);
593            c0 = lo;
594            c1 += o as u64;
595            c2 += (c1 == 0 && o) as u32;
596        }};
597    }
598    macro_rules! extract {
599        () => {{
600            let n = c0;
601            c0 = c1;
602            c1 = c2 as u64;
603            c2 = 0;
604            n
605        }};
606    }
607    macro_rules! extract_fast {
608        () => {{
609            let n = c0;
610            c0 = c1;
611            c1 = 0;
612            n
613        }};
614    }
615
616    muladd_fast!(a.d[0], b.d[0]);
617    l[0] = extract_fast!();
618    muladd!(a.d[0], b.d[1]);
619    muladd!(a.d[1], b.d[0]);
620    l[1] = extract!();
621    muladd!(a.d[0], b.d[2]);
622    muladd!(a.d[1], b.d[1]);
623    muladd!(a.d[2], b.d[0]);
624    l[2] = extract!();
625    muladd!(a.d[0], b.d[3]);
626    muladd!(a.d[1], b.d[2]);
627    muladd!(a.d[2], b.d[1]);
628    muladd!(a.d[3], b.d[0]);
629    l[3] = extract!();
630    muladd!(a.d[1], b.d[3]);
631    muladd!(a.d[2], b.d[2]);
632    muladd!(a.d[3], b.d[1]);
633    l[4] = extract!();
634    muladd!(a.d[2], b.d[3]);
635    muladd!(a.d[3], b.d[2]);
636    l[5] = extract!();
637    muladd_fast!(a.d[3], b.d[3]);
638    l[6] = extract_fast!();
639    l[7] = c0;
640}
641
642#[allow(dead_code)]
643fn limbs_512_to_biguint(l: &[u64; 8]) -> BigUint {
644    let mut acc = BigUint::from(0u64);
645    for (i, &limb) in l.iter().enumerate() {
646        acc += BigUint::from(limb) << (64 * i);
647    }
648    acc
649}
650
651/// Limb-based 512→256 reduction mod n. Replaces BigUint for hot path.
652/// Port of libsecp256k1 scalar_reduce_512 C fallback (muladd/extract).
653#[cfg(not(all(target_arch = "x86_64", not(target_os = "windows"))))]
654fn scalar_reduce_512_limbs(r: &mut Scalar, l: &[u64; 8]) {
655    let n0 = l[4];
656    let n1 = l[5];
657    let n2 = l[6];
658    let n3 = l[7];
659
660    let mut c0: u64 = l[0];
661    let mut c1: u64 = 0;
662    let mut c2: u32 = 0;
663
664    macro_rules! muladd_fast {
665        ($a:expr, $b:expr) => {{
666            let prod = ($a as u128) * ($b as u128);
667            let (lo, o) = c0.overflowing_add(prod as u64);
668            c0 = lo;
669            c1 += (prod >> 64) as u64 + o as u64;
670        }};
671    }
672    macro_rules! muladd {
673        ($a:expr, $b:expr) => {{
674            let prod = ($a as u128) * ($b as u128);
675            let (lo, o1) = c0.overflowing_add(prod as u64);
676            c0 = lo;
677            let th = (prod >> 64) as u64 + o1 as u64;
678            let (mid, o2) = c1.overflowing_add(th);
679            c1 = mid;
680            c2 += o2 as u32;
681        }};
682    }
683    macro_rules! sumadd_fast {
684        ($a:expr) => {{
685            let (lo, o) = c0.overflowing_add($a);
686            c0 = lo;
687            c1 += o as u64;
688        }};
689    }
690    macro_rules! sumadd {
691        ($a:expr) => {{
692            let (lo, o) = c0.overflowing_add($a);
693            c0 = lo;
694            let (mid, o2) = c1.overflowing_add(o as u64);
695            c1 = mid;
696            c2 += o2 as u32;
697        }};
698    }
699    macro_rules! extract {
700        () => {{
701            let n = c0;
702            c0 = c1;
703            c1 = c2 as u64;
704            c2 = 0;
705            n
706        }};
707    }
708    macro_rules! extract_fast {
709        () => {{
710            let n = c0;
711            c0 = c1;
712            c1 = 0;
713            n
714        }};
715    }
716
717    // Reduce 512 bits into 385: m[0..6] = l[0..3] + n[0..3] * N_C
718    muladd_fast!(n0, N_C_0);
719    let m0 = extract_fast!();
720    sumadd_fast!(l[1]);
721    muladd!(n1, N_C_0);
722    muladd!(n0, N_C_1);
723    let m1 = extract!();
724    sumadd!(l[2]);
725    muladd!(n2, N_C_0);
726    muladd!(n1, N_C_1);
727    sumadd!(n0);
728    let m2 = extract!();
729    sumadd!(l[3]);
730    muladd!(n3, N_C_0);
731    muladd!(n2, N_C_1);
732    sumadd!(n1);
733    let m3 = extract!();
734    muladd!(n3, N_C_1);
735    sumadd!(n2);
736    let m4 = extract!();
737    sumadd_fast!(n3);
738    let m5 = extract_fast!();
739    let m6 = c0 as u32;
740
741    // Reduce 385 into 258: p[0..4] = m[0..3] + m[4..6] * N_C
742    c0 = m0;
743    c1 = 0;
744    c2 = 0;
745    muladd_fast!(m4, N_C_0);
746    let p0 = extract_fast!();
747    sumadd_fast!(m1);
748    muladd!(m5, N_C_0);
749    muladd!(m4, N_C_1);
750    let p1 = extract!();
751    sumadd!(m2);
752    muladd!(m6 as u64, N_C_0);
753    muladd!(m5, N_C_1);
754    sumadd!(m4);
755    let p2 = extract!();
756    sumadd_fast!(m3);
757    muladd_fast!(m6 as u64, N_C_1);
758    sumadd_fast!(m5);
759    let p3 = extract_fast!();
760    let p4 = (c0 + m6 as u64) as u32;
761
762    // Reduce 258 into 256: r = p[0..3] + p4 * N_C
763    let mut t: u128 = p0 as u128;
764    t += (N_C_0 as u128) * (p4 as u128);
765    r.d[0] = t as u64;
766    t >>= 64;
767    t += p1 as u128;
768    t += (N_C_1 as u128) * (p4 as u128);
769    r.d[1] = t as u64;
770    t >>= 64;
771    t += p2 as u128;
772    t += p4 as u128;
773    r.d[2] = t as u64;
774    t >>= 64;
775    t += p3 as u128;
776    r.d[3] = t as u64;
777    let c = (t >> 64) as u64;
778
779    // Final reduction
780    scalar_reduce(r, c + scalar_check_overflow(r));
781}
782
783/// Add overflow*N_C to r. overflow is 0 or 1.
784fn scalar_reduce(r: &mut Scalar, overflow: u64) {
785    let of = overflow as u128;
786    let mut t: u128 = r.d[0] as u128;
787    t += of * (N_C_0 as u128);
788    r.d[0] = t as u64;
789    t >>= 64;
790    t += r.d[1] as u128;
791    t += of * (N_C_1 as u128);
792    r.d[1] = t as u64;
793    t >>= 64;
794    t += r.d[2] as u128;
795    t += of * (N_C_2 as u128);
796    r.d[2] = t as u64;
797    t >>= 64;
798    r.d[3] = (t as u64).wrapping_add(r.d[3]);
799}
800
801/// Returns 1 if r >= n, else 0.
802fn scalar_check_overflow(r: &Scalar) -> u64 {
803    let mut yes = 0u64;
804    let mut no = 0u64;
805    no |= (r.d[3] < N_3) as u64;
806    no |= (r.d[2] < N_2) as u64;
807    yes |= (r.d[2] > N_2) as u64 & !no;
808    no |= (r.d[1] < N_1) as u64;
809    yes |= (r.d[1] > N_1) as u64 & !no;
810    yes |= (r.d[0] >= N_0) as u64 & !no;
811    yes
812}
813
814fn scalar_reduce_512(r: &mut Scalar, l: &[u64; 8]) {
815    #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
816    {
817        let c = unsafe { scalar_asm::scalar_reduce_512_asm(r, l.as_ptr()) };
818        scalar_reduce(r, c + scalar_check_overflow(r));
819    }
820    #[cfg(not(all(target_arch = "x86_64", not(target_os = "windows"))))]
821    {
822        scalar_reduce_512_limbs(r, l);
823    }
824}
825
826#[cfg(test)]
827#[test]
828fn test_scalar_reduce_n_plus_1() {
829    let l = [N_0 + 1, N_1, N_2, N_3, 0, 0, 0, 0];
830    let mut r = Scalar::zero();
831    scalar_reduce_512(&mut r, &l);
832    assert!(r.is_one(), "(n+1) mod n = 1, got r.d = {:?}", r.d);
833}
834
835#[cfg(test)]
836#[test]
837fn test_scalar_mul_inv2_times_2() {
838    let inv2_hex = "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1";
839    let inv2_bytes = hex::decode(inv2_hex).unwrap();
840    let mut buf = [0u8; 32];
841    buf.copy_from_slice(&inv2_bytes);
842    let mut inv2 = Scalar::zero();
843    inv2.set_b32(&buf);
844    let mut two = Scalar::zero();
845    two.set_int(2);
846    let mut l = [0u64; 8];
847    scalar_mul_512(&mut l, &inv2, &two);
848    let mut r = Scalar::zero();
849    scalar_reduce_512(&mut r, &l);
850    assert!(r.is_one(), "inv2*2 mod n = 1");
851}
852
853fn scalar_mul_shift_var(r: &mut Scalar, a: &Scalar, b: &Scalar, shift: u32) {
854    assert!(shift >= 256);
855    let mut l = [0u64; 8];
856    scalar_mul_512(&mut l, a, b);
857    let shiftlimbs = (shift >> 6) as usize;
858    let shiftlow = shift & 0x3F;
859    let shifthigh = 64 - shiftlow;
860    r.d[0] = if shift < 512 {
861        (l[shiftlimbs] >> shiftlow)
862            | (if shift < 448 && shiftlow != 0 {
863                l[1 + shiftlimbs] << shifthigh
864            } else {
865                0
866            })
867    } else {
868        0
869    };
870    r.d[1] = if shift < 448 {
871        (l[1 + shiftlimbs] >> shiftlow)
872            | (if shift < 384 && shiftlow != 0 {
873                l[2 + shiftlimbs] << shifthigh
874            } else {
875                0
876            })
877    } else {
878        0
879    };
880    r.d[2] = if shift < 384 {
881        (l[2 + shiftlimbs] >> shiftlow)
882            | (if shift < 320 && shiftlow != 0 {
883                l[3 + shiftlimbs] << shifthigh
884            } else {
885                0
886            })
887    } else {
888        0
889    };
890    r.d[3] = if shift < 320 {
891        l[3 + shiftlimbs] >> shiftlow
892    } else {
893        0
894    };
895    let bit = (l[(shift - 1) as usize >> 6] >> ((shift - 1) & 0x3F)) & 1;
896    scalar_cadd_bit(r, 0, bit != 0);
897}
898
899fn scalar_cadd_bit(r: &mut Scalar, bit: u32, flag: bool) {
900    let bit = if flag { bit } else { bit + 256 };
901    if bit >= 256 {
902        return;
903    }
904    let mut t: u128 = r.d[0] as u128
905        + if (bit >> 6) == 0 {
906            1u128 << (bit & 0x3F)
907        } else {
908            0
909        };
910    r.d[0] = t as u64;
911    t >>= 64;
912    t += r.d[1] as u128
913        + if (bit >> 6) == 1 {
914            1u128 << (bit & 0x3F)
915        } else {
916            0
917        };
918    r.d[1] = t as u64;
919    t >>= 64;
920    t += r.d[2] as u128
921        + if (bit >> 6) == 2 {
922            1u128 << (bit & 0x3F)
923        } else {
924            0
925        };
926    r.d[2] = t as u64;
927    t >>= 64;
928    t += r.d[3] as u128
929        + if (bit >> 6) == 3 {
930            1u128 << (bit & 0x3F)
931        } else {
932            0
933        };
934    r.d[3] = t as u64;
935}
936
937#[cfg(test)]
938mod tests {
939    use super::*;
940
941    #[test]
942    fn test_split_lambda_identity() {
943        // r1 + lambda*r2 == k (mod n)
944        let mut k = Scalar::zero();
945        k.set_int(42);
946
947        let mut r1 = Scalar::zero();
948        let mut r2 = Scalar::zero();
949        Scalar::split_lambda(&mut r1, &mut r2, &k);
950
951        let mut lambda_r2 = Scalar::zero();
952        lambda_r2.mul(&r2, &LAMBDA);
953        let mut check = Scalar::zero();
954        check.add(&r1, &lambda_r2);
955        assert!(bool::from(check.ct_eq(&k)), "r1 + lambda*r2 should equal k");
956    }
957
958    #[test]
959    fn test_split_lambda_neg_three() {
960        let mut three = Scalar::zero();
961        three.set_int(3);
962        let mut k = Scalar::zero();
963        k.negate(&three); // k = -3 mod n
964
965        let mut r1 = Scalar::zero();
966        let mut r2 = Scalar::zero();
967        Scalar::split_lambda(&mut r1, &mut r2, &k);
968
969        let mut lambda_r2 = Scalar::zero();
970        lambda_r2.mul(&r2, &LAMBDA);
971        let mut check = Scalar::zero();
972        check.add(&r1, &lambda_r2);
973        assert!(
974            bool::from(check.ct_eq(&k)),
975            "r1 + lambda*r2 should equal k for k=-3"
976        );
977    }
978
979    #[test]
980    fn test_split_lambda_ecdsa_scalar() {
981        let mut k = Scalar::zero();
982        k.d = [
983            11125243483441707226,
984            2149109665766520832,
985            14302025600096445326,
986            4162584031737161978,
987        ];
988
989        let n_big = scalar_to_biguint(&N);
990
991        let mut r1 = Scalar::zero();
992        let mut r2 = Scalar::zero();
993        Scalar::split_lambda(&mut r1, &mut r2, &k);
994
995        let r1_big = scalar_to_biguint(&r1);
996        let r2_big = scalar_to_biguint(&r2);
997        let n_half = &n_big / BigUint::from(2u64);
998        let r1_abs = if r1_big > n_half {
999            &n_big - &r1_big
1000        } else {
1001            r1_big.clone()
1002        };
1003        let r2_abs = if r2_big > n_half {
1004            &n_big - &r2_big
1005        } else {
1006            r2_big.clone()
1007        };
1008        assert!(
1009            r1_abs.bits() <= 128,
1010            "|r1| exceeds 128 bits: {}",
1011            r1_abs.bits()
1012        );
1013        assert!(
1014            r2_abs.bits() <= 128,
1015            "|r2| exceeds 128 bits: {}",
1016            r2_abs.bits()
1017        );
1018
1019        let mut lambda_r2 = Scalar::zero();
1020        lambda_r2.mul(&r2, &LAMBDA);
1021        let mut check = Scalar::zero();
1022        check.add(&r1, &lambda_r2);
1023        assert!(bool::from(check.ct_eq(&k)), "r1 + lambda*r2 should equal k");
1024    }
1025
1026    #[test]
1027    fn test_split_128_identity() {
1028        // r1 + 2^128*r2 == k
1029        let mut k = Scalar::zero();
1030        k.set_int(0x1234_5678);
1031
1032        let mut r1 = Scalar::zero();
1033        let mut r2 = Scalar::zero();
1034        Scalar::split_128(&mut r1, &mut r2, &k);
1035
1036        let mut two_128 = Scalar::zero();
1037        two_128.d[2] = 1;
1038        let mut r2_shifted = Scalar::zero();
1039        r2_shifted.mul(&r2, &two_128);
1040        let mut check = Scalar::zero();
1041        check.add(&r1, &r2_shifted);
1042        assert!(bool::from(check.ct_eq(&k)), "r1 + 2^128*r2 should equal k");
1043    }
1044}