Skip to main content

dcrypt_algorithms/ec/bls12_381/
scalar.rs

1//! BLS12-381 scalar field F_q where q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
2
3use crate::error::{Error, Result};
4use crate::types::{ByteSerializable, ConstantTimeEq as DcryptConstantTimeEq, SecureZeroingType};
5use core::fmt;
6use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
7use dcrypt_internal::constant_time::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
8use dcrypt_internal::zeroing::{zeroizing_bytes_from_slice, Zeroize, Zeroizing, ZeroizingBytes};
9
10// Arithmetic helpers
11/// Compute a + b + carry, returning (result, carry)
12#[inline(always)]
13const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) {
14    let ret = (a as u128) + (b as u128) + (carry as u128);
15    (ret as u64, (ret >> 64) as u64)
16}
17
18/// Compute a - (b + borrow), returning (result, borrow)
19#[inline(always)]
20const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) {
21    let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128));
22    (ret as u64, (ret >> 64) as u64)
23}
24
25/// Compute a + (b * c) + carry, returning (result, carry)
26#[inline(always)]
27const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) {
28    let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128);
29    (ret as u64, (ret >> 64) as u64)
30}
31
32/// Scalar field element of BLS12-381 for public arithmetic.
33///
34/// This generic field element is `Copy` and is not a protected secret-key
35/// container. BLS secret keys remain encoded in exact-size zeroizing storage
36/// and use the dedicated `multiply_secret_be_bytes` group operation.
37///
38/// Internal representation: four 64-bit limbs in little-endian Montgomery form.
39#[derive(Clone, Copy, Eq)]
40pub struct Scalar(pub(crate) [u64; 4]);
41
42impl fmt::Debug for Scalar {
43    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44        let tmp = self.to_bytes();
45        write!(f, "0x")?;
46        for &b in tmp.iter().rev() {
47            write!(f, "{:02x}", b)?;
48        }
49        Ok(())
50    }
51}
52
53impl fmt::Display for Scalar {
54    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55        write!(f, "{:?}", self)
56    }
57}
58
59impl From<u64> for Scalar {
60    fn from(val: u64) -> Scalar {
61        Scalar([val, 0, 0, 0]) * R2
62    }
63}
64
65impl ConstantTimeEq for Scalar {
66    fn ct_eq(&self, other: &Self) -> Choice {
67        self.0[0].ct_eq(&other.0[0])
68            & self.0[1].ct_eq(&other.0[1])
69            & self.0[2].ct_eq(&other.0[2])
70            & self.0[3].ct_eq(&other.0[3])
71    }
72}
73
74impl PartialEq for Scalar {
75    #[inline]
76    fn eq(&self, other: &Self) -> bool {
77        bool::from(ConstantTimeEq::ct_eq(self, other))
78    }
79}
80
81impl ConditionallySelectable for Scalar {
82    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
83        Scalar([
84            u64::conditional_select(&a.0[0], &b.0[0], choice),
85            u64::conditional_select(&a.0[1], &b.0[1], choice),
86            u64::conditional_select(&a.0[2], &b.0[2], choice),
87            u64::conditional_select(&a.0[3], &b.0[3], choice),
88        ])
89    }
90}
91
92// Constants
93const MODULUS: Scalar = Scalar([
94    0xffff_ffff_0000_0001,
95    0x53bd_a402_fffe_5bfe,
96    0x3339_d808_09a1_d805,
97    0x73ed_a753_299d_7d48,
98]);
99
100/// Scalar modulus encoded as a canonical 32-byte big-endian integer.
101const MODULUS_BE: [u8; 32] = [
102    0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05,
103    0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
104];
105
106/// Validate a borrowed big-endian secret-key scalar without constructing a
107/// `Copy` field element or duplicating the secret byte array.
108pub(crate) fn secret_be_bytes_are_valid(bytes: &[u8; 32]) -> Choice {
109    let mut nonzero = 0u8;
110    let mut borrow = 0u16;
111    for index in (0..bytes.len()).rev() {
112        nonzero |= bytes[index];
113        let rhs = (MODULUS_BE[index] as u16) + borrow;
114        let difference = (bytes[index] as u16).wrapping_sub(rhs);
115        borrow = (difference >> 15) & 1;
116    }
117
118    Choice::from(borrow as u8) & !nonzero.ct_eq(&0u8)
119}
120
121/// INV = -(q^{-1} mod 2^64) mod 2^64
122const INV: u64 = 0xffff_fffe_ffff_ffff;
123
124/// R = 2^256 mod q
125const R: Scalar = Scalar([
126    0x0000_0001_ffff_fffe,
127    0x5884_b7fa_0003_4802,
128    0x998c_4fef_ecbc_4ff5,
129    0x1824_b159_acc5_056f,
130]);
131
132/// R^2 = 2^512 mod q
133const R2: Scalar = Scalar([
134    0xc999_e990_f3f2_9c6d,
135    0x2b6c_edcb_8792_5c23,
136    0x05d3_1496_7254_398f,
137    0x0748_d9d9_9f59_ff11,
138]);
139
140/// R^3 = 2^768 mod q
141const R3: Scalar = Scalar([
142    0xc62c_1807_439b_73af,
143    0x1b3e_0d18_8cf0_6990,
144    0x73d1_3c71_c7b5_f418,
145    0x6e2a_5bb9_c8db_33e9,
146]);
147
148// Constants for Tonelli-Shanks square root algorithm
149// 2-adicity of (r - 1)
150const S: u32 = 32;
151
152// T = (r - 1) / 2^S  (odd part)
153const TONELLI_T: [u64; 4] = [
154    0xfffe_5bfe_ffff_ffff,
155    0x09a1_d805_53bd_a402,
156    0x299d_7d48_3339_d808,
157    0x0000_0000_73ed_a753,
158];
159
160// (T + 1)/2, used to initialize x = a^((T+1)/2)
161const TONELLI_TP1_DIV2: [u64; 4] = [
162    0x7fff_2dff_8000_0000,
163    0x04d0_ec02_a9de_d201,
164    0x94ce_bea4_199c_ec04,
165    0x0000_0000_39f6_d3a9,
166];
167
168// Exponent (r-1)/2, the Legendre exponent
169#[allow(dead_code)]
170const LEGENDRE_EXP: [u64; 4] = [
171    0x7fff_ffff_8000_0000,
172    0xa9de_d201_7fff_2dff,
173    0x199c_ec04_04d0_ec02,
174    0x39f6_d3a9_94ce_bea4,
175];
176
177impl<'a> Neg for &'a Scalar {
178    type Output = Scalar;
179
180    #[inline]
181    fn neg(self) -> Scalar {
182        self.neg()
183    }
184}
185
186impl Neg for Scalar {
187    type Output = Scalar;
188
189    #[inline]
190    fn neg(self) -> Scalar {
191        -&self
192    }
193}
194
195impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar {
196    type Output = Scalar;
197
198    #[inline]
199    fn sub(self, rhs: &'b Scalar) -> Scalar {
200        self.sub(rhs)
201    }
202}
203
204impl<'a, 'b> Add<&'b Scalar> for &'a Scalar {
205    type Output = Scalar;
206
207    #[inline]
208    fn add(self, rhs: &'b Scalar) -> Scalar {
209        self.add(rhs)
210    }
211}
212
213impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar {
214    type Output = Scalar;
215
216    #[inline]
217    fn mul(self, rhs: &'b Scalar) -> Scalar {
218        self.mul(rhs)
219    }
220}
221
222// Binop implementations
223impl<'b> Add<&'b Scalar> for Scalar {
224    type Output = Scalar;
225    #[inline]
226    fn add(self, rhs: &'b Scalar) -> Scalar {
227        &self + rhs
228    }
229}
230
231impl<'a> Add<Scalar> for &'a Scalar {
232    type Output = Scalar;
233    #[inline]
234    fn add(self, rhs: Scalar) -> Scalar {
235        self + &rhs
236    }
237}
238
239impl Add<Scalar> for Scalar {
240    type Output = Scalar;
241    #[inline]
242    fn add(self, rhs: Scalar) -> Scalar {
243        &self + &rhs
244    }
245}
246
247impl<'b> Sub<&'b Scalar> for Scalar {
248    type Output = Scalar;
249    #[inline]
250    fn sub(self, rhs: &'b Scalar) -> Scalar {
251        &self - rhs
252    }
253}
254
255impl<'a> Sub<Scalar> for &'a Scalar {
256    type Output = Scalar;
257    #[inline]
258    fn sub(self, rhs: Scalar) -> Scalar {
259        self - &rhs
260    }
261}
262
263impl Sub<Scalar> for Scalar {
264    type Output = Scalar;
265    #[inline]
266    fn sub(self, rhs: Scalar) -> Scalar {
267        &self - &rhs
268    }
269}
270
271impl SubAssign<Scalar> for Scalar {
272    #[inline]
273    fn sub_assign(&mut self, rhs: Scalar) {
274        *self = &*self - &rhs;
275    }
276}
277
278impl AddAssign<Scalar> for Scalar {
279    #[inline]
280    fn add_assign(&mut self, rhs: Scalar) {
281        *self = &*self + &rhs;
282    }
283}
284
285impl<'b> SubAssign<&'b Scalar> for Scalar {
286    #[inline]
287    fn sub_assign(&mut self, rhs: &'b Scalar) {
288        *self = &*self - rhs;
289    }
290}
291
292impl<'b> AddAssign<&'b Scalar> for Scalar {
293    #[inline]
294    fn add_assign(&mut self, rhs: &'b Scalar) {
295        *self = &*self + rhs;
296    }
297}
298
299impl<'b> Mul<&'b Scalar> for Scalar {
300    type Output = Scalar;
301    #[inline]
302    fn mul(self, rhs: &'b Scalar) -> Scalar {
303        &self * rhs
304    }
305}
306
307impl<'a> Mul<Scalar> for &'a Scalar {
308    type Output = Scalar;
309    #[inline]
310    fn mul(self, rhs: Scalar) -> Scalar {
311        self * &rhs
312    }
313}
314
315impl Mul<Scalar> for Scalar {
316    type Output = Scalar;
317    #[inline]
318    fn mul(self, rhs: Scalar) -> Scalar {
319        &self * &rhs
320    }
321}
322
323impl MulAssign<Scalar> for Scalar {
324    #[inline]
325    fn mul_assign(&mut self, rhs: Scalar) {
326        *self = &*self * &rhs;
327    }
328}
329
330impl<'b> MulAssign<&'b Scalar> for Scalar {
331    #[inline]
332    fn mul_assign(&mut self, rhs: &'b Scalar) {
333        *self = &*self * rhs;
334    }
335}
336
337impl Default for Scalar {
338    #[inline]
339    fn default() -> Self {
340        Self::zero()
341    }
342}
343
344impl Zeroize for Scalar {
345    fn zeroize(&mut self) {
346        self.0.zeroize();
347    }
348}
349
350impl ByteSerializable for Scalar {
351    type Bytes = ZeroizingBytes;
352
353    fn to_bytes(&self) -> ZeroizingBytes {
354        let mut encoded = Zeroizing::new(self.to_bytes());
355        let output = zeroizing_bytes_from_slice(&encoded[..]);
356        encoded.zeroize();
357        output
358    }
359
360    fn from_bytes(bytes: &[u8]) -> Result<Self> {
361        if bytes.len() != 32 {
362            return Err(Error::Length {
363                context: "Scalar::from_bytes",
364                expected: 32,
365                actual: bytes.len(),
366            });
367        }
368
369        let mut array = Zeroizing::new([0u8; 32]);
370        array.copy_from_slice(bytes);
371
372        Scalar::from_bytes(&array)
373            .into_option()
374            .ok_or_else(|| Error::param("scalar_bytes", "non-canonical scalar"))
375    }
376}
377
378impl DcryptConstantTimeEq for Scalar {
379    fn ct_eq(&self, other: &Self) -> bool {
380        bool::from(ConstantTimeEq::ct_eq(self, other))
381    }
382}
383
384impl SecureZeroingType for Scalar {
385    fn zeroed() -> Self {
386        Self::zero()
387    }
388}
389
390impl Scalar {
391    /// Additive identity
392    #[inline]
393    pub const fn zero() -> Scalar {
394        Scalar([0, 0, 0, 0])
395    }
396
397    /// Multiplicative identity
398    #[inline]
399    pub const fn one() -> Scalar {
400        R
401    }
402
403    /// Check if element is zero.
404    #[inline]
405    pub fn is_zero(&self) -> Choice {
406        (self.0[0] | self.0[1] | self.0[2] | self.0[3]).ct_eq(&0)
407    }
408
409    /// Validate canonical, nonzero big-endian secret-key bytes without
410    /// constructing a field element or copying the input.
411    pub fn secret_key_bytes_are_valid(bytes: &[u8; 32]) -> Choice {
412        secret_be_bytes_are_valid(bytes)
413    }
414
415    /// Double this element
416    #[inline]
417    pub const fn double(&self) -> Scalar {
418        self.add(self)
419    }
420
421    /// Create from little-endian bytes if canonical
422    pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Scalar> {
423        let mut tmp = Scalar([0, 0, 0, 0]);
424
425        tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap());
426        tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap());
427        tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap());
428        tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap());
429
430        // Check canonical by subtracting modulus
431        let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
432        let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
433        let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
434        let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
435
436        let is_some = (borrow as u8) & 1;
437
438        // Convert to Montgomery: (a * R^2) / R = aR
439        tmp *= &R2;
440
441        CtOption::new(tmp, Choice::from(is_some))
442    }
443
444    /// Convert to little-endian bytes
445    pub fn to_bytes(&self) -> [u8; 32] {
446        // Remove Montgomery: (aR) / R = a
447        let tmp = Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
448
449        let mut res = [0; 32];
450        res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
451        res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
452        res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
453        res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
454
455        res
456    }
457
458    /// Create a scalar from a canonical 32-byte big-endian integer.
459    ///
460    /// This is the byte order used by the `OS2IP`/`I2OSP` notation in BLS
461    /// ciphersuite specifications. Zero is a canonical field element; callers
462    /// constructing a secret key must reject it.
463    pub fn from_be_bytes(bytes: &[u8; 32]) -> CtOption<Scalar> {
464        let mut little_endian = Zeroizing::new(*bytes);
465        little_endian.reverse();
466        Self::from_bytes(&little_endian)
467    }
468
469    /// Convert this scalar to its canonical 32-byte big-endian integer.
470    pub fn to_be_bytes(&self) -> [u8; 32] {
471        let mut bytes = self.to_bytes();
472        bytes.reverse();
473        bytes
474    }
475
476    /// Convert this scalar to canonical big-endian bytes in a clearing owner.
477    ///
478    /// Secret-key code should prefer this method to [`Self::to_be_bytes`],
479    /// whose plain array return type is intended for public field elements.
480    pub fn to_be_bytes_zeroizing(&self) -> Zeroizing<[u8; 32]> {
481        let mut bytes = Zeroizing::new(self.to_bytes());
482        bytes.reverse();
483        bytes
484    }
485
486    /// Create from 512-bit little-endian integer mod q
487    pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar {
488        let limbs = Zeroizing::new([
489            u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()),
490            u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()),
491            u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()),
492            u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()),
493            u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()),
494            u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()),
495            u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()),
496            u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()),
497        ]);
498        Scalar::from_u512(&limbs)
499    }
500
501    /// Reduce a big-endian integer of at most 64 bytes modulo the scalar-field
502    /// order.
503    ///
504    /// This is suitable for the 48-byte `OS2IP(OKM) mod r` step used by BLS
505    /// key generation. It is intentionally distinct from canonical decoding:
506    /// reduction accepts values greater than or equal to the modulus and may
507    /// return zero, which a secret-key generation procedure must reject.
508    pub fn from_be_bytes_mod_order(bytes: &[u8]) -> Result<Scalar> {
509        if bytes.len() > 64 {
510            return Err(Error::param(
511                "scalar_bytes",
512                "big-endian reduction input exceeds 64 bytes",
513            ));
514        }
515
516        let mut wide = Zeroizing::new([0u8; 64]);
517        for (destination, source) in wide.iter_mut().zip(bytes.iter().rev()) {
518            *destination = *source;
519        }
520        Ok(Self::from_bytes_wide(&wide))
521    }
522
523    /// Hashes arbitrary data to a scalar field element using SHA-256.
524    ///
525    /// This function implements one invocation of RFC 9380 `hash_to_field` for the
526    /// BLS12-381 scalar field, using `expand_message_xmd` with SHA-256 and a
527    /// 48-byte field element input (`L = 48`).
528    ///
529    /// The output may be zero. Protocols deriving a secret key must apply their
530    /// own ciphersuite's rejection or key-generation procedure.
531    ///
532    /// # Arguments
533    /// * `data`: The input data to hash.
534    /// * `dst`: A Domain Separation Tag (DST) to ensure hashes are unique per application context.
535    ///
536    /// # Returns
537    /// A `Result` containing the `Scalar` or an error.
538    pub fn hash_to_field(data: &[u8], dst: &[u8]) -> Result<Self> {
539        let expanded = super::hash_to_curve::expand_message_xmd(data, dst, 48)?;
540        Self::from_be_bytes_mod_order(&expanded)
541    }
542
543    fn from_u512(limbs: &[u64; 8]) -> Scalar {
544        let d0 = Zeroizing::new(Scalar([limbs[0], limbs[1], limbs[2], limbs[3]]));
545        let d1 = Zeroizing::new(Scalar([limbs[4], limbs[5], limbs[6], limbs[7]]));
546        let low = Zeroizing::new(&*d0 * &R2);
547        let high = Zeroizing::new(&*d1 * &R3);
548        &*low + &*high
549    }
550
551    /// Creates a scalar from four `u64` limbs (little-endian). This function will
552    /// convert the raw integer into Montgomery form.
553    pub const fn from_raw(val: [u64; 4]) -> Self {
554        (&Scalar(val)).mul(&R2)
555    }
556
557    /// Computes the square of this scalar.
558    #[inline]
559    pub const fn square(&self) -> Scalar {
560        let (r1, carry) = mac(0, self.0[0], self.0[1], 0);
561        let (r2, carry) = mac(0, self.0[0], self.0[2], carry);
562        let (r3, r4) = mac(0, self.0[0], self.0[3], carry);
563
564        let (r3, carry) = mac(r3, self.0[1], self.0[2], 0);
565        let (r4, r5) = mac(r4, self.0[1], self.0[3], carry);
566
567        let (r5, r6) = mac(r5, self.0[2], self.0[3], 0);
568
569        let r7 = r6 >> 63;
570        let r6 = (r6 << 1) | (r5 >> 63);
571        let r5 = (r5 << 1) | (r4 >> 63);
572        let r4 = (r4 << 1) | (r3 >> 63);
573        let r3 = (r3 << 1) | (r2 >> 63);
574        let r2 = (r2 << 1) | (r1 >> 63);
575        let r1 = r1 << 1;
576
577        let (r0, carry) = mac(0, self.0[0], self.0[0], 0);
578        let (r1, carry) = adc(0, r1, carry);
579        let (r2, carry) = mac(r2, self.0[1], self.0[1], carry);
580        let (r3, carry) = adc(0, r3, carry);
581        let (r4, carry) = mac(r4, self.0[2], self.0[2], carry);
582        let (r5, carry) = adc(0, r5, carry);
583        let (r6, carry) = mac(r6, self.0[3], self.0[3], carry);
584        let (r7, _) = adc(0, r7, carry);
585
586        Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7)
587    }
588
589    /// Computes `x` raised to the power of `2^k`.
590    #[inline]
591    pub fn pow2k(mut x: Scalar, mut k: u32) -> Scalar {
592        while k > 0 {
593            x = x.square();
594            k -= 1;
595        }
596        x
597    }
598
599    /// Variable-time exponentiation by a 256-bit little-endian exponent.
600    fn pow_vartime(&self, by: &[u64; 4]) -> Self {
601        let mut res = Self::one();
602        for limb in by.iter().rev() {
603            for i in (0..64).rev() {
604                res = res.square();
605                if ((limb >> i) & 1) == 1 {
606                    res *= self;
607                }
608            }
609        }
610        res
611    }
612
613    /// Computes the square root of this scalar using Tonelli-Shanks.
614    /// Returns `Some(s)` with `s^2 = self` if a square root exists, else `None`.
615    pub fn sqrt(&self) -> CtOption<Self> {
616        // Trivial case: sqrt(0) = 0
617        if bool::from(self.is_zero()) {
618            return CtOption::new(Scalar::zero(), Choice::from(1));
619        }
620
621        // Choose a fixed quadratic non-residue. For this field, 5 works.
622        let z = Scalar::from(5u64);
623
624        // Precompute values per Tonelli-Shanks
625        let mut c = z.pow_vartime(&TONELLI_T); // c = z^T
626        let mut t = self.pow_vartime(&TONELLI_T); // t = a^T
627        let mut x = self.pow_vartime(&TONELLI_TP1_DIV2); // x = a^((T+1)/2)
628        let mut m = S;
629
630        // If t == 1, we guessed the root correctly.
631        if bool::from(ConstantTimeEq::ct_eq(&t, &Scalar::one())) {
632            return CtOption::new(x, ConstantTimeEq::ct_eq(&x.square(), self));
633        }
634
635        // Main Tonelli-Shanks loop
636        loop {
637            // Find smallest i in [1, m) with t^(2^i) == 1
638            let mut i = 1u32;
639            let mut t2i = t.square();
640            while i < m && !bool::from(ConstantTimeEq::ct_eq(&t2i, &Scalar::one())) {
641                t2i = t2i.square();
642                i += 1;
643            }
644
645            // If i == m, then a is not a square root
646            if i == m {
647                return CtOption::new(Scalar::zero(), Choice::from(0));
648            }
649
650            // b = c^{2^(m - i - 1)}
651            let b = Scalar::pow2k(c, m - i - 1);
652
653            // Update variables
654            x = x * b;
655            let b2 = b.square();
656            t = t * b2;
657            c = b2;
658            m = i;
659
660            // If t is now 1, we are done
661            if bool::from(ConstantTimeEq::ct_eq(&t, &Scalar::one())) {
662                break;
663            }
664        }
665
666        // Final constant-time check to ensure correctness
667        CtOption::new(x, ConstantTimeEq::ct_eq(&x.square(), self))
668    }
669
670    /// Computes the multiplicative inverse of this scalar, if it is non-zero.
671    pub fn invert(&self) -> CtOption<Self> {
672        #[inline(always)]
673        fn square_assign_multi(n: &mut Scalar, num_times: usize) {
674            for _ in 0..num_times {
675                *n = n.square();
676            }
677        }
678        // Addition chain from github.com/kwantam/addchain
679        let mut t0 = self.square();
680        let mut t1 = t0 * self;
681        let mut t16 = t0.square();
682        let mut t6 = t16.square();
683        let mut t5 = t6 * t0;
684        t0 = t6 * t16;
685        let mut t12 = t5 * t16;
686        let mut t2 = t6.square();
687        let mut t7 = t5 * t6;
688        let mut t15 = t0 * t5;
689        let mut t17 = t12.square();
690        t1 *= t17;
691        let mut t3 = t7 * t2;
692        let t8 = t1 * t17;
693        let t4 = t8 * t2;
694        let t9 = t8 * t7;
695        t7 = t4 * t5;
696        let t11 = t4 * t17;
697        t5 = t9 * t17;
698        let t14 = t7 * t15;
699        let t13 = t11 * t12;
700        t12 = t11 * t17;
701        t15 *= &t12;
702        t16 *= &t15;
703        t3 *= &t16;
704        t17 *= &t3;
705        t0 *= &t17;
706        t6 *= &t0;
707        t2 *= &t6;
708        square_assign_multi(&mut t0, 8);
709        t0 *= &t17;
710        square_assign_multi(&mut t0, 9);
711        t0 *= &t16;
712        square_assign_multi(&mut t0, 9);
713        t0 *= &t15;
714        square_assign_multi(&mut t0, 9);
715        t0 *= &t15;
716        square_assign_multi(&mut t0, 7);
717        t0 *= &t14;
718        square_assign_multi(&mut t0, 7);
719        t0 *= &t13;
720        square_assign_multi(&mut t0, 10);
721        t0 *= &t12;
722        square_assign_multi(&mut t0, 9);
723        t0 *= &t11;
724        square_assign_multi(&mut t0, 8);
725        t0 *= &t8;
726        square_assign_multi(&mut t0, 8);
727        t0 *= self;
728        square_assign_multi(&mut t0, 14);
729        t0 *= &t9;
730        square_assign_multi(&mut t0, 10);
731        t0 *= &t8;
732        square_assign_multi(&mut t0, 15);
733        t0 *= &t7;
734        square_assign_multi(&mut t0, 10);
735        t0 *= &t6;
736        square_assign_multi(&mut t0, 8);
737        t0 *= &t5;
738        square_assign_multi(&mut t0, 16);
739        t0 *= &t3;
740        square_assign_multi(&mut t0, 8);
741        t0 *= &t2;
742        square_assign_multi(&mut t0, 7);
743        t0 *= &t4;
744        square_assign_multi(&mut t0, 9);
745        t0 *= &t2;
746        square_assign_multi(&mut t0, 8);
747        t0 *= &t3;
748        square_assign_multi(&mut t0, 8);
749        t0 *= &t2;
750        square_assign_multi(&mut t0, 8);
751        t0 *= &t2;
752        square_assign_multi(&mut t0, 8);
753        t0 *= &t2;
754        square_assign_multi(&mut t0, 8);
755        t0 *= &t3;
756        square_assign_multi(&mut t0, 8);
757        t0 *= &t2;
758        square_assign_multi(&mut t0, 8);
759        t0 *= &t2;
760        square_assign_multi(&mut t0, 5);
761        t0 *= &t1;
762        square_assign_multi(&mut t0, 5);
763        t0 *= &t1;
764
765        CtOption::new(t0, !ConstantTimeEq::ct_eq(self, &Self::zero()))
766    }
767
768    #[inline(always)]
769    const fn montgomery_reduce(
770        r0: u64,
771        r1: u64,
772        r2: u64,
773        r3: u64,
774        r4: u64,
775        r5: u64,
776        r6: u64,
777        r7: u64,
778    ) -> Self {
779        let k = r0.wrapping_mul(INV);
780        let (_, carry) = mac(r0, k, MODULUS.0[0], 0);
781        let (r1, carry) = mac(r1, k, MODULUS.0[1], carry);
782        let (r2, carry) = mac(r2, k, MODULUS.0[2], carry);
783        let (r3, carry) = mac(r3, k, MODULUS.0[3], carry);
784        let (r4, carry2) = adc(r4, 0, carry);
785
786        let k = r1.wrapping_mul(INV);
787        let (_, carry) = mac(r1, k, MODULUS.0[0], 0);
788        let (r2, carry) = mac(r2, k, MODULUS.0[1], carry);
789        let (r3, carry) = mac(r3, k, MODULUS.0[2], carry);
790        let (r4, carry) = mac(r4, k, MODULUS.0[3], carry);
791        let (r5, carry2) = adc(r5, carry2, carry);
792
793        let k = r2.wrapping_mul(INV);
794        let (_, carry) = mac(r2, k, MODULUS.0[0], 0);
795        let (r3, carry) = mac(r3, k, MODULUS.0[1], carry);
796        let (r4, carry) = mac(r4, k, MODULUS.0[2], carry);
797        let (r5, carry) = mac(r5, k, MODULUS.0[3], carry);
798        let (r6, carry2) = adc(r6, carry2, carry);
799
800        let k = r3.wrapping_mul(INV);
801        let (_, carry) = mac(r3, k, MODULUS.0[0], 0);
802        let (r4, carry) = mac(r4, k, MODULUS.0[1], carry);
803        let (r5, carry) = mac(r5, k, MODULUS.0[2], carry);
804        let (r6, carry) = mac(r6, k, MODULUS.0[3], carry);
805        let (r7, _) = adc(r7, carry2, carry);
806
807        (&Scalar([r4, r5, r6, r7])).sub(&MODULUS)
808    }
809
810    /// Multiplies this scalar by another.
811    #[inline]
812    pub const fn mul(&self, rhs: &Self) -> Self {
813        let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0);
814        let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry);
815        let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry);
816        let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry);
817
818        let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0);
819        let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry);
820        let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry);
821        let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry);
822
823        let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0);
824        let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry);
825        let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry);
826        let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry);
827
828        let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0);
829        let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry);
830        let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry);
831        let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry);
832
833        Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7)
834    }
835
836    /// Subtracts another scalar from this one.
837    #[inline]
838    pub const fn sub(&self, rhs: &Self) -> Self {
839        let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0);
840        let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow);
841        let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow);
842        let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow);
843
844        let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0);
845        let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry);
846        let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry);
847        let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry);
848
849        Scalar([d0, d1, d2, d3])
850    }
851
852    /// Adds another scalar to this one.
853    #[inline]
854    pub const fn add(&self, rhs: &Self) -> Self {
855        let (d0, carry) = adc(self.0[0], rhs.0[0], 0);
856        let (d1, carry) = adc(self.0[1], rhs.0[1], carry);
857        let (d2, carry) = adc(self.0[2], rhs.0[2], carry);
858        let (d3, _) = adc(self.0[3], rhs.0[3], carry);
859
860        (&Scalar([d0, d1, d2, d3])).sub(&MODULUS)
861    }
862
863    /// Computes the additive negation of this scalar.
864    #[inline]
865    pub const fn neg(&self) -> Self {
866        let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0);
867        let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow);
868        let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow);
869        let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow);
870
871        let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1);
872
873        Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask])
874    }
875}
876
877impl From<Scalar> for [u8; 32] {
878    fn from(value: Scalar) -> [u8; 32] {
879        value.to_bytes()
880    }
881}
882
883impl<'a> From<&'a Scalar> for [u8; 32] {
884    fn from(value: &'a Scalar) -> [u8; 32] {
885        value.to_bytes()
886    }
887}
888
889impl<T> core::iter::Sum<T> for Scalar
890where
891    T: core::borrow::Borrow<Scalar>,
892{
893    fn sum<I>(iter: I) -> Self
894    where
895        I: Iterator<Item = T>,
896    {
897        iter.fold(Self::zero(), |acc, item| acc + item.borrow())
898    }
899}
900
901impl<T> core::iter::Product<T> for Scalar
902where
903    T: core::borrow::Borrow<Scalar>,
904{
905    fn product<I>(iter: I) -> Self
906    where
907        I: Iterator<Item = T>,
908    {
909        iter.fold(Self::one(), |acc, item| acc * item.borrow())
910    }
911}
912
913// Tests
914#[test]
915fn test_inv() {
916    // Verify INV constant
917    let mut inv = 1u64;
918    for _ in 0..63 {
919        inv = inv.wrapping_mul(inv);
920        inv = inv.wrapping_mul(MODULUS.0[0]);
921    }
922    inv = inv.wrapping_neg();
923    assert_eq!(inv, INV);
924}
925
926#[cfg(feature = "std")]
927#[test]
928fn test_debug() {
929    assert_eq!(
930        format!("{:?}", Scalar::zero()),
931        "0x0000000000000000000000000000000000000000000000000000000000000000"
932    );
933    assert_eq!(
934        format!("{:?}", Scalar::one()),
935        "0x0000000000000000000000000000000000000000000000000000000000000001"
936    );
937    // R is the Montgomery representation of 1. The Debug trait should perform the
938    // conversion, so it should also format to "1".
939    assert_eq!(
940        format!("{:?}", R),
941        "0x0000000000000000000000000000000000000000000000000000000000000001"
942    );
943}
944
945#[test]
946fn test_equality() {
947    assert_eq!(Scalar::zero(), Scalar::zero());
948    assert_eq!(Scalar::one(), Scalar::one());
949    #[allow(clippy::eq_op)]
950    {
951        assert_eq!(R2, R2);
952    }
953
954    assert!(Scalar::zero() != Scalar::one());
955    assert!(Scalar::one() != R2);
956}
957
958#[test]
959fn test_to_bytes() {
960    assert_eq!(
961        Scalar::zero().to_bytes(),
962        [
963            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
964            0, 0, 0
965        ]
966    );
967
968    assert_eq!(
969        Scalar::one().to_bytes(),
970        [
971            1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
972            0, 0, 0
973        ]
974    );
975
976    // R is the Montgomery representation of 1. to_bytes() should perform the
977    // conversion, so it should also produce the bytes for "1".
978    assert_eq!(
979        R.to_bytes(),
980        [
981            1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
982            0, 0, 0
983        ]
984    );
985
986    assert_eq!(
987        (-&Scalar::one()).to_bytes(),
988        [
989            0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8,
990            216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115
991        ]
992    );
993}
994
995#[test]
996fn test_from_bytes() {
997    let mut a = R2;
998
999    for _ in 0..100 {
1000        let bytes = a.to_bytes();
1001        let b = Scalar::from_bytes(&bytes).unwrap();
1002        assert_eq!(a, b);
1003
1004        // Test negation roundtrip
1005        let bytes = (-a).to_bytes();
1006        let b = Scalar::from_bytes(&bytes).unwrap();
1007        assert_eq!(-a, b);
1008
1009        a = a.square();
1010    }
1011}
1012
1013#[test]
1014fn test_from_bytes_enforces_canonical_scalar_encoding() {
1015    let mut modulus =
1016        hex::decode("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001").unwrap();
1017    modulus.reverse();
1018    let modulus: [u8; 32] = modulus.try_into().unwrap();
1019    assert!(bool::from(Scalar::from_bytes(&modulus).is_none()));
1020    assert!(<Scalar as ByteSerializable>::from_bytes(&modulus).is_err());
1021
1022    let mut largest =
1023        hex::decode("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000").unwrap();
1024    largest.reverse();
1025    let largest: [u8; 32] = largest.try_into().unwrap();
1026    assert!(bool::from(Scalar::from_bytes(&largest).is_some()));
1027
1028    assert!(bool::from(Scalar::from_bytes(&[0u8; 32]).is_some()));
1029    assert!(bool::from(Scalar::from_bytes(&[0xff; 32]).is_none()));
1030
1031    let mut modulus_be = modulus;
1032    modulus_be.reverse();
1033    assert!(bool::from(Scalar::from_be_bytes(&modulus_be).is_none()));
1034    assert!(bool::from(
1035        Scalar::from_be_bytes_mod_order(&modulus_be)
1036            .unwrap()
1037            .is_zero()
1038    ));
1039    assert!(Scalar::from_be_bytes_mod_order(&[0u8; 65]).is_err());
1040    let forty_two = Scalar::from(42u64);
1041    assert_eq!(
1042        Scalar::from_be_bytes(&forty_two.to_be_bytes()).unwrap(),
1043        forty_two
1044    );
1045}
1046
1047#[test]
1048fn borrowed_secret_validation_matches_canonical_scalar_decoder() {
1049    let mut bytes = [0u8; 32];
1050    for counter in 0u16..2048 {
1051        for (index, byte) in bytes.iter_mut().enumerate() {
1052            *byte = (counter as u8)
1053                .wrapping_mul(73)
1054                .wrapping_add((counter >> 8) as u8)
1055                .wrapping_add((index as u8).wrapping_mul(19));
1056        }
1057
1058        let decoded = Scalar::from_be_bytes(&bytes).into_option();
1059        let expected = decoded
1060            .as_ref()
1061            .map(|scalar| !bool::from(scalar.is_zero()))
1062            .unwrap_or(false);
1063        assert_eq!(
1064            bool::from(Scalar::secret_key_bytes_are_valid(&bytes)),
1065            expected,
1066            "mismatch at corpus item {counter}",
1067        );
1068    }
1069}
1070
1071#[cfg(test)]
1072const LARGEST: Scalar = Scalar([
1073    0xffff_ffff_0000_0000,
1074    0x53bd_a402_fffe_5bfe,
1075    0x3339_d808_09a1_d805,
1076    0x73ed_a753_299d_7d48,
1077]);
1078
1079#[test]
1080fn test_addition() {
1081    let mut tmp = LARGEST;
1082    tmp += &LARGEST;
1083
1084    assert_eq!(
1085        tmp,
1086        Scalar([
1087            0xffff_fffe_ffff_ffff,
1088            0x53bd_a402_fffe_5bfe,
1089            0x3339_d808_09a1_d805,
1090            0x73ed_a753_299d_7d48,
1091        ])
1092    );
1093
1094    let mut tmp = LARGEST;
1095    tmp += &Scalar([1, 0, 0, 0]);
1096
1097    assert_eq!(tmp, Scalar::zero());
1098}
1099
1100#[test]
1101fn test_inversion() {
1102    assert!(bool::from(Scalar::zero().invert().is_none()));
1103    assert_eq!(Scalar::one().invert().unwrap(), Scalar::one());
1104    assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one());
1105
1106    let mut tmp = R2;
1107
1108    for _ in 0..100 {
1109        let mut tmp2 = tmp.invert().unwrap();
1110        tmp2.mul_assign(&tmp);
1111
1112        assert_eq!(tmp2, Scalar::one());
1113
1114        tmp.add_assign(&R2);
1115    }
1116}
1117
1118#[test]
1119fn test_sqrt() {
1120    // Test with zero
1121    assert_eq!(Scalar::zero().sqrt().unwrap(), Scalar::zero());
1122
1123    // Test with one
1124    assert_eq!(Scalar::one().sqrt().unwrap(), Scalar::one());
1125
1126    // Test with a known square
1127    let four = Scalar::from(4u64);
1128    let two = Scalar::from(2u64);
1129    let neg_two = -two;
1130
1131    let sqrt_four = four.sqrt().unwrap();
1132    assert!(sqrt_four == two || sqrt_four == neg_two);
1133    assert_eq!(sqrt_four.square(), four);
1134
1135    // Test with a random square
1136    let s = Scalar::from(123456789u64);
1137    let s_sq = s.square();
1138    let s_sqrt = s_sq.sqrt().unwrap();
1139    assert!(s_sqrt == s || s_sqrt == -s);
1140    assert_eq!(s_sqrt.square(), s_sq);
1141
1142    // Test with a non-residue.
1143    // For this field, 5 is a quadratic non-residue.
1144    let five = Scalar::from(5u64);
1145    assert!(bool::from(five.sqrt().is_none()));
1146
1147    // Test with a residue.
1148    // For a prime q where q = 1 mod 4, -1 is a residue.
1149    let neg_one = -Scalar::one();
1150    let neg_one_sqrt = neg_one.sqrt().unwrap();
1151    assert_eq!(neg_one_sqrt.square(), neg_one);
1152
1153    // Test roundtrip for many values
1154    let mut val = R2;
1155    for _ in 0..100 {
1156        let sq = val.square();
1157        let sqrt = sq.sqrt().unwrap();
1158        assert!(sqrt == val || sqrt == -val);
1159        val += R;
1160    }
1161}
1162
1163#[test]
1164fn test_from_raw() {
1165    assert_eq!(
1166        Scalar::from_raw([
1167            0x0001_ffff_fffd,
1168            0x5884_b7fa_0003_4802,
1169            0x998c_4fef_ecbc_4ff5,
1170            0x1824_b159_acc5_056f,
1171        ]),
1172        Scalar::from_raw([0xffff_ffff_ffff_ffff; 4])
1173    );
1174
1175    assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero());
1176
1177    assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R);
1178}
1179
1180#[test]
1181fn test_scalar_hash_to_field() {
1182    let data1 = b"some input data";
1183    let data2 = b"different input data";
1184    let dst1 = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"; // Standard DST format
1185    let dst2 = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
1186
1187    // 1. Different data should produce different scalars
1188    let s1 = Scalar::hash_to_field(data1, dst1).unwrap();
1189    let s2 = Scalar::hash_to_field(data2, dst1).unwrap();
1190    assert_ne!(s1, s2);
1191
1192    // 2. Same data with different DSTs should produce different scalars
1193    let s3 = Scalar::hash_to_field(data1, dst1).unwrap();
1194    let s4 = Scalar::hash_to_field(data1, dst2).unwrap();
1195    assert_ne!(s3, s4);
1196
1197    // 3. Hashing should be deterministic
1198    let s5 = Scalar::hash_to_field(data1, dst1).unwrap();
1199    assert_eq!(s3, s5);
1200
1201    // 4. Verify output is always valid scalar (less than modulus)
1202    for test_case in &[b"" as &[u8], b"a", b"test", &[0xFF; 100], &[0x00; 64]] {
1203        let scalar = Scalar::hash_to_field(test_case, dst1).unwrap();
1204        // The scalar should already be reduced, so converting to/from bytes should work
1205        let bytes = scalar.to_bytes();
1206        let scalar2 = Scalar::from_bytes(&bytes).unwrap();
1207        assert_eq!(scalar, scalar2, "Output should be a valid reduced scalar");
1208    }
1209
1210    // 5. Test that the expansion reduces bias appropriately. RFC 9380 uses a
1211    // 48-byte input for this field, providing the required security margin.
1212    let mut scalars = Vec::new();
1213    for i in 0u32..100 {
1214        let data = i.to_le_bytes();
1215        let s = Scalar::hash_to_field(&data, dst1).unwrap();
1216        scalars.push(s);
1217    }
1218    // All should be different (no collisions in small sample)
1219    for i in 0..scalars.len() {
1220        for j in i + 1..scalars.len() {
1221            assert_ne!(
1222                scalars[i], scalars[j],
1223                "Unexpected collision at {} and {}",
1224                i, j
1225            );
1226        }
1227    }
1228
1229    // 6. Test empty DST and empty data edge cases
1230    let s_empty = Scalar::hash_to_field(b"", b"").unwrap();
1231    let s_empty2 = Scalar::hash_to_field(b"", b"").unwrap();
1232    assert_eq!(
1233        s_empty, s_empty2,
1234        "Empty input should still be deterministic"
1235    );
1236
1237    // 7. Verify that DST length is properly included (catches common implementation bugs)
1238    let dst_short = b"A";
1239    let dst_long = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; // 50 A's
1240    let s_short = Scalar::hash_to_field(data1, dst_short).unwrap();
1241    let s_long = Scalar::hash_to_field(data1, dst_long).unwrap();
1242    assert_ne!(s_short, s_long, "DST length should affect output");
1243
1244    // 8. Test mathematical properties: hash(data) should be uniformly distributed
1245    // We can't test true uniformity easily, but we can check it's not always even/odd
1246    let mut has_odd = false;
1247    let mut has_even = false;
1248    for i in 0u8..20 {
1249        let s = Scalar::hash_to_field(&[i], dst1).unwrap();
1250        // Check the least significant bit
1251        if s.to_bytes()[0] & 1 == 0 {
1252            has_even = true;
1253        } else {
1254            has_odd = true;
1255        }
1256    }
1257    assert!(
1258        has_odd && has_even,
1259        "Hash output should have both odd and even values"
1260    );
1261}
1262
1263#[test]
1264fn test_zeroize() {
1265    use dcrypt_internal::zeroing::Zeroize;
1266
1267    let mut a = Scalar::from_raw([
1268        0x1fff_3231_233f_fffd,
1269        0x4884_b7fa_0003_4802,
1270        0x998c_4fef_ecbc_4ff3,
1271        0x1824_b159_acc5_0562,
1272    ]);
1273    a.zeroize();
1274    assert!(bool::from(
1275        dcrypt_internal::constant_time::ConstantTimeEq::ct_eq(&a, &Scalar::zero())
1276    ));
1277}