clock_curve_math/bigint/
backend_bigint.rs

1//! BigInt backend implementation using clock-bigint.
2//!
3//! This module provides a high-performance, constant-time implementation of
4//! 256-bit multi-precision arithmetic using the clock-bigint library as the
5//! underlying primitive. It serves as the core arithmetic engine for the
6//! clock-curve-math cryptographic library.
7//!
8//! # Architecture Overview
9//!
10//! The BigInt type wraps clock-bigint's U256 type, providing:
11//! - **256-bit precision**: Four 64-bit limbs for multi-precision arithmetic
12//! - **Constant-time operations**: All operations execute in time independent of values
13//! - **Cryptographic security**: Designed to prevent timing-based side-channel attacks
14//! - **Performance optimization**: Hand-tuned algorithms for cryptographic workloads
15//!
16//! # Memory Layout
17//!
18//! Values are stored as 4 × 64-bit limbs in little-endian order:
19//! ```text
20//! [limb0, limb1, limb2, limb3] where limb0 is least significant
21//! Value = limb0 + limb1 * 2^64 + limb2 * 2^128 + limb3 * 2^192
22//! ```
23//!
24//! # Security Considerations
25//!
26//! All operations are designed with cryptographic security in mind:
27//!
28//! - **Constant-time execution**: Prevents timing-based side-channel attacks
29//! - **No data-dependent branches**: Control flow independent of secret values
30//! - **Predictable memory access**: Cache access patterns are consistent
31//! - **Secure comparisons**: Constant-time equality and ordering checks
32//!
33//! # Performance Characteristics
34//!
35//! The implementation prioritizes cryptographic performance:
36//!
37//! - **Addition/Subtraction**: O(1) - fixed 4-limb operations
38//! - **Multiplication**: O(1) - optimized schoolbook multiplication with u128
39//! - **Wide operations**: O(1) - full 512-bit results for modular reduction
40//! - **Comparisons**: O(1) - constant-time limb-by-limb comparison
41//! - **Bit operations**: O(1) - efficient shift and bit manipulation
42//!
43//! # Algorithm Details
44//!
45//! ## Multiplication Strategy
46//!
47//! Uses optimized schoolbook multiplication with u128 intermediates to avoid
48//! overflow during computation. The algorithm processes limb pairs and manages
49//! carry propagation to produce correct 256-bit results.
50//!
51//! ## Wide Operations
52//!
53//! `mul_wide` and `square_wide` return full double-precision results as
54//! `(low: BigInt, high: BigInt)` tuples, enabling Montgomery reduction and
55//! other modular arithmetic techniques that require access to carry bits.
56//!
57//! ## Constant-Time Design
58//!
59//! All operations avoid:
60//! - Data-dependent loops or early exits
61//! - Conditional branches based on secret data
62//! - Variable-time library functions
63//! - Cache-timing dependent memory access patterns
64//!
65//! # Usage in Cryptography
66//!
67//! This BigInt implementation serves as the foundation for:
68//!
69//! - **Elliptic curve arithmetic**: Point addition, doubling, and scalar multiplication
70//! - **Modular exponentiation**: RSA and Diffie-Hellman implementations
71//! - **Finite field operations**: Custom modulus arithmetic
72//! - **Cryptographic protocols**: Digital signatures, key exchange, encryption
73//!
74//! # Implementation Notes
75//!
76//! - **Alignment**: Uses `#[repr(align(32))]` for cache line and AVX compatibility
77//! - **Dependencies**: Relies on clock-bigint for low-level U256 operations
78//! - **Testing**: Comprehensive test suite validates correctness and constant-time properties
79//! - **no_std compatibility**: Works in environments without standard library allocation
80
81use core::cmp::Ordering;
82
83use clock_bigint::U256;
84
85use crate::ct;
86
87/// 256-bit multi-precision integer for cryptographic arithmetic.
88///
89/// BigInt provides constant-time multi-precision arithmetic operations essential
90/// for elliptic curve cryptography, modular exponentiation, and other cryptographic
91/// primitives. It wraps clock-bigint's U256 type with additional cryptographic
92/// security features and performance optimizations.
93///
94/// # Memory Layout
95/// - **4 × 64-bit limbs**: Little-endian storage (limb\[0\] is least significant)
96/// - **32-byte alignment**: Optimized for cache lines and SIMD operations
97/// - **Copy semantics**: Efficient stack-based operations without heap allocation
98///
99/// # Security Properties
100/// - **Constant-time operations**: All arithmetic executes in time independent of values
101/// - **Side-channel resistance**: Designed to prevent timing and cache attacks
102/// - **No secret-dependent branches**: Control flow independent of sensitive data
103/// - **Secure comparisons**: Constant-time equality and ordering operations
104///
105/// # Performance Characteristics
106/// - **Stack-allocated**: No heap allocation for arithmetic operations
107/// - **SIMD-friendly**: 32-byte alignment enables vectorized operations
108/// - **Branch-free algorithms**: All operations use conditional moves and masks
109/// - **Optimized multiplication**: Custom schoolbook multiplication with u128 intermediates
110///
111/// # Usage Examples
112/// ```ignore
113/// use clock_curve_math::bigint::BigInt;
114///
115/// // Create from small values
116/// let a = BigInt::from_u64(42);
117/// let b = BigInt::from_u64(123);
118///
119/// // Arithmetic operations (all constant-time)
120/// let sum = a.add(&b);
121/// let product = a.mul(&b);
122///
123/// // Wide multiplication for modular reduction
124/// let (low, high) = a.mul_wide(&b);
125///
126/// // Secure comparison
127/// let ordering = a.ct_cmp(&b);
128///
129/// // Bit manipulation
130/// let shifted = a.shl(8);
131/// let bit = a.get_bit(0);
132/// ```
133///
134/// # Cryptographic Applications
135/// - **Elliptic curve arithmetic**: Point operations over prime fields
136/// - **Modular exponentiation**: RSA and Diffie-Hellman implementations
137/// - **Finite field operations**: Custom modulus arithmetic
138/// - **Cryptographic protocols**: Digital signatures and key exchange
139///
140/// # Implementation Details
141/// - **Backend**: Uses clock-bigint's U256 for low-level operations
142/// - **Alignment**: `#[repr(align(32))]` for cache line and AVX compatibility
143/// - **Traits**: Implements standard Rust traits (PartialEq, PartialOrd, etc.)
144/// - **Operators**: Supports `<<` (shl) and `-` (sub) operators
145///
146/// # Thread Safety
147/// BigInt is `Copy` and contains no interior mutability, making it safe to
148/// share across thread boundaries without synchronization.
149#[derive(Clone, Copy, Debug)]
150#[repr(align(32))] // 32-byte alignment for cache line optimization and AVX compatibility
151pub struct BigInt {
152    /// Internal U256 value from clock-bigint library
153    inner: U256,
154}
155
156impl BigInt {
157    /// Creates a BigInt from four 64-bit limbs in little-endian order.
158    ///
159    /// The limbs are interpreted as a multi-precision integer where:
160    /// - `limbs[0]` is the least significant 64 bits
161    /// - `limbs[1]` is bits 64-127
162    /// - `limbs[2]` is bits 128-191
163    /// - `limbs[3]` is the most significant 64 bits
164    ///
165    /// # Parameters
166    /// * `limbs` - Array of four u64 values in little-endian limb order
167    ///
168    /// # Returns
169    /// A new BigInt with the specified limb values
170    ///
171    /// # Examples
172    /// ```ignore
173    /// use clock_curve_math::bigint::BigInt;
174    ///
175    /// // Create 2^64 (1 << 64)
176    /// let big_num = BigInt::from_limbs(&[0, 1, 0, 0]);
177    ///
178    /// // Create a large number
179    /// let large = BigInt::from_limbs(&[0x123456789ABCDEF0, 0xFEDCBA9876543210, 0, 0]);
180    /// ```
181    pub fn from_limbs(limbs: &[u64; 4]) -> Self {
182        // U256::from_limbs should never fail for a valid [u64; 4] array
183        Self {
184            inner: U256::from_limbs(limbs)
185                .expect("U256::from_limbs should never fail for [u64; 4]"),
186        }
187    }
188
189    /// Creates a BigInt from 32 bytes in little-endian byte order.
190    ///
191    /// The bytes are interpreted as a 256-bit big-endian integer, with:
192    /// - `bytes[0]` being the least significant byte
193    /// - `bytes[31]` being the most significant byte
194    ///
195    /// This is the standard byte representation for cryptographic values.
196    ///
197    /// # Parameters
198    /// * `bytes` - Array of 32 bytes in little-endian order
199    ///
200    /// # Returns
201    /// A new BigInt with the value represented by the bytes
202    ///
203    /// # Examples
204    /// ```ignore
205    /// use clock_curve_math::bigint::BigInt;
206    ///
207    /// // Create from 32-byte array (all zeros except first byte = 1)
208    /// let bytes = [1u8; 32];
209    /// let num = BigInt::from_bytes(&bytes);
210    ///
211    /// // Create from hex representation
212    /// let hex_bytes = hex::decode("0100000000000000000000000000000000000000000000000000000000000000")?;
213    /// let big_endian_num = BigInt::from_bytes(hex_bytes.as_slice().try_into()?);
214    /// ```
215    ///
216    /// # Note
217    /// This method uses little-endian byte order, which is standard for most
218    /// cryptographic protocols and matches the internal limb representation.
219    pub fn from_bytes(bytes: &[u8; 32]) -> Self {
220        // Convert bytes to limbs safely - array indexing is guaranteed safe for [u8; 32]
221        let limbs = [
222            u64::from_le_bytes(
223                bytes[0..8]
224                    .try_into()
225                    .expect("Byte slice conversion failed"),
226            ),
227            u64::from_le_bytes(
228                bytes[8..16]
229                    .try_into()
230                    .expect("Byte slice conversion failed"),
231            ),
232            u64::from_le_bytes(
233                bytes[16..24]
234                    .try_into()
235                    .expect("Byte slice conversion failed"),
236            ),
237            u64::from_le_bytes(
238                bytes[24..32]
239                    .try_into()
240                    .expect("Byte slice conversion failed"),
241            ),
242        ];
243        Self {
244            inner: U256::from_limbs(&limbs).expect("Failed to create U256 from bytes"),
245        }
246    }
247
248    /// Creates a BigInt from a 64-bit unsigned integer value.
249    ///
250    /// This is a convenience constructor for creating BigInt values from
251    /// small integers, scalars, or other u64 values commonly used in
252    /// cryptographic computations.
253    ///
254    /// # Parameters
255    /// * `value` - The u64 value to convert to BigInt
256    ///
257    /// # Returns
258    /// A new BigInt with the specified value in the least significant limb
259    ///
260    /// # Examples
261    /// ```ignore
262    /// use clock_curve_math::bigint::BigInt;
263    ///
264    /// let zero = BigInt::from_u64(0);
265    /// let one = BigInt::from_u64(1);
266    /// let max_u64 = BigInt::from_u64(u64::MAX);
267    ///
268    /// // Common cryptographic constants
269    /// let curve_order = BigInt::from_u64(0xFFFFFFFFFFFFFFFF); // secp256k1 order approximation
270    /// ```
271    ///
272    /// # Performance
273    /// This is the most efficient way to create small BigInt values.
274    pub fn from_u64(value: u64) -> Self {
275        Self {
276            inner: U256::from_u64(value),
277        }
278    }
279
280    /// Converts the BigInt to a 32-byte array in little-endian byte order.
281    ///
282    /// Returns the BigInt value as bytes, with the least significant byte first.
283    /// This is the standard representation for cryptographic values and matches
284    /// the byte order expected by most cryptographic protocols.
285    ///
286    /// # Returns
287    /// A 32-byte array containing the BigInt value in little-endian order
288    ///
289    /// # Examples
290    /// ```ignore
291    /// use clock_curve_math::bigint::BigInt;
292    ///
293    /// let num = BigInt::from_u64(0x123456789ABCDEF0);
294    /// let bytes = num.to_bytes();
295    ///
296    /// // bytes[0] = 0xF0, bytes[1] = 0xDE, bytes[2] = 0xBC, etc.
297    /// assert_eq!(bytes[0], 0xF0);
298    /// assert_eq!(bytes[1], 0xBC);
299    /// ```
300    ///
301    /// # Use Cases
302    /// - Serializing BigInt values for network transmission
303    /// - Storing cryptographic keys or parameters
304    /// - Interfacing with other cryptographic libraries
305    /// - Debugging and logging BigInt values
306    pub fn to_bytes(&self) -> [u8; 32] {
307        let limbs = self.inner.as_limbs();
308        let mut bytes = [0u8; 32];
309        for (i, &limb) in limbs.iter().enumerate() {
310            bytes[i * 8..(i + 1) * 8].copy_from_slice(&limb.to_le_bytes());
311        }
312        bytes
313    }
314
315    /// Returns the four 64-bit limbs of the BigInt in little-endian order.
316    ///
317    /// The limbs represent the BigInt as:
318    /// value = limbs\[0\] + limbs\[1\] * 2^64 + limbs\[2\] * 2^128 + limbs\[3\] * 2^192
319    ///
320    /// # Returns
321    /// An array of four u64 values where `limbs[0]` is the least significant limb
322    ///
323    /// # Examples
324    /// ```ignore
325    /// use clock_curve_math::bigint::BigInt;
326    ///
327    /// let num = BigInt::from_u64(0x123456789ABCDEF0);
328    /// let limbs = num.limbs();
329    ///
330    /// assert_eq!(limbs[0], 0x123456789ABCDEF0); // LSB
331    /// assert_eq!(limbs[1], 0);
332    /// assert_eq!(limbs[2], 0);
333    /// assert_eq!(limbs[3], 0); // MSB
334    /// ```
335    ///
336    /// # Use Cases
337    /// - Implementing custom arithmetic operations
338    /// - Interfacing with low-level cryptographic primitives
339    /// - Debugging multi-precision arithmetic
340    /// - Converting to/from other big integer representations
341    pub fn limbs(&self) -> [u64; 4] {
342        let slice = self.inner.as_limbs();
343        [slice[0], slice[1], slice[2], slice[3]]
344    }
345
346    /// Constant-time addition.
347    pub fn add(&self, rhs: &Self) -> Self {
348        let a_limbs = self.limbs();
349        let b_limbs = rhs.limbs();
350        let mut result_limbs = [0u64; 4];
351        let mut carry = 0u64;
352
353        for i in 0..4 {
354            let (sum, carry1) = a_limbs[i].overflowing_add(b_limbs[i]);
355            let (sum, carry2) = sum.overflowing_add(carry);
356            result_limbs[i] = sum;
357            carry = (carry1 as u64) + (carry2 as u64);
358        }
359
360        Self::from_limbs(&result_limbs)
361    }
362
363    /// Constant-time subtraction.
364    pub fn sub(&self, rhs: &Self) -> Self {
365        let a_limbs = self.limbs();
366        let b_limbs = rhs.limbs();
367        let mut result_limbs = [0u64; 4];
368        let mut borrow = 0u64;
369
370        for i in 0..4 {
371            let (diff, borrow1) = a_limbs[i].overflowing_sub(b_limbs[i]);
372            let (diff, borrow2) = diff.overflowing_sub(borrow);
373            result_limbs[i] = diff;
374            borrow = (borrow1 as u64) + (borrow2 as u64);
375        }
376
377        Self::from_limbs(&result_limbs)
378    }
379
380    /// Constant-time multiplication with proper overflow handling.
381    ///
382    /// Returns the full 256-bit product. For modular arithmetic,
383    /// the caller is responsible for reduction if needed.
384    pub fn mul(&self, rhs: &Self) -> Self {
385        let a_limbs = self.limbs();
386        let b_limbs = rhs.limbs();
387        let mut result = [0u64; 8]; // 8 limbs for 256x256 = 512 bit result
388
389        // Optimized schoolbook multiplication using u128 to avoid overflow
390        // Process in a way that maximizes ILP (Instruction Level Parallelism)
391        // Made constant-time by always processing all possible carry propagations
392        for i in 0..4 {
393            let mut carry = 0u128;
394            for j in 0..4 {
395                let prod = a_limbs[i] as u128 * b_limbs[j] as u128;
396                let sum = prod + carry + result[i + j] as u128;
397                result[i + j] = sum as u64;
398                carry = sum >> 64;
399            }
400            // Propagate remaining carry - always process all possible limbs for constant-time
401            let mut k = i + 4;
402            while k < 8 {
403                let sum = carry + result[k] as u128;
404                result[k] = sum as u64;
405                carry = sum >> 64;
406                k += 1;
407            }
408        }
409
410        // For 256-bit modular arithmetic, we typically want the result mod 2^256
411        // This is equivalent to taking the low 4 limbs
412        Self::from_limbs(&[result[0], result[1], result[2], result[3]])
413    }
414
415    /// Wide multiplication returning the full 512-bit result as (low, high) BigInts.
416    pub fn mul_wide(&self, rhs: &Self) -> (Self, Self) {
417        let a_limbs = self.limbs();
418        let b_limbs = rhs.limbs();
419        let mut result = [0u64; 8]; // 8 limbs for 256x256 = 512 bit result
420
421        // Simple schoolbook multiplication with u128 to avoid overflow
422        for i in 0..4 {
423            let mut carry = 0u128;
424            for j in 0..4 {
425                let prod = a_limbs[i] as u128 * b_limbs[j] as u128;
426                let sum = prod + carry + result[i + j] as u128;
427                result[i + j] = sum as u64;
428                carry = sum >> 64;
429            }
430            // Propagate remaining carry
431            let mut k = i + 4;
432            while carry > 0 && k < 8 {
433                let sum = carry + result[k] as u128;
434                result[k] = sum as u64;
435                carry = sum >> 64;
436                k += 1;
437            }
438        }
439
440        let low = Self::from_limbs(&[result[0], result[1], result[2], result[3]]);
441        let high = Self::from_limbs(&[result[4], result[5], result[6], result[7]]);
442        (low, high)
443    }
444
445    /// Wide squaring returning the full 512-bit result as (low, high) BigInts.
446    ///
447    /// This is optimized compared to `mul_wide(self, self)` since we only compute
448    /// each cross term a\[i\]*a\[j\] once and double it appropriately, reducing
449    /// redundant computations.
450    ///
451    /// Performance improvement: ~10-15% faster than general multiplication.
452    pub fn square_wide(&self) -> (Self, Self) {
453        let a_limbs = self.limbs();
454        let mut result = [0u64; 8];
455
456        // Optimized squaring algorithm
457        // For each pair (i,j), we compute a[i]*a[j] and add it to the appropriate positions
458        // When i == j, we add a[i]^2 directly
459        // When i != j, we add 2*a[i]*a[j] (distributed across the result limbs)
460
461        for i in 0..4 {
462            // First, handle the diagonal terms: a[i]^2
463            let square = a_limbs[i] as u128 * a_limbs[i] as u128;
464            let square_lo = square as u64;
465            let square_hi = (square >> 64) as u64;
466
467            // Add square_lo to result[2*i]
468            let sum = result[2 * i] as u128 + square_lo as u128;
469            result[2 * i] = sum as u64;
470            let mut carry = (sum >> 64) as u64;
471
472            // Add square_hi and carry to result[2*i + 1]
473            if 2 * i + 1 < 8 {
474                let sum = result[2 * i + 1] as u128 + square_hi as u128 + carry as u128;
475                result[2 * i + 1] = sum as u64;
476                carry = (sum >> 64) as u64;
477
478                // Propagate carry to higher limbs if needed
479                let mut k = 2 * i + 2;
480                while carry > 0 && k < 8 {
481                    let sum = result[k] as u128 + carry as u128;
482                    result[k] = sum as u64;
483                    carry = (sum >> 64) as u64;
484                    k += 1;
485                }
486            }
487
488            // Now handle the off-diagonal terms: 2*a[i]*a[j] for j > i
489            for j in (i + 1)..4 {
490                let prod = a_limbs[i] as u128 * a_limbs[j] as u128;
491                let prod_lo = prod as u64;
492                let prod_hi = (prod >> 64) as u64;
493
494                // Double the product (multiply by 2)
495                let doubled_lo = prod_lo.wrapping_mul(2);
496                let doubled_hi = prod_hi.wrapping_mul(2);
497                let carry_from_doubling = if prod_lo > u64::MAX / 2 { 1 } else { 0 }
498                    + if prod_hi > u64::MAX / 2 { 2 } else { 0 };
499
500                // Add to result[i+j]
501                let sum1 = result[i + j] as u128 + doubled_lo as u128;
502                result[i + j] = sum1 as u64;
503                let carry1 = (sum1 >> 64) as u64 + carry_from_doubling;
504
505                // Add to result[i+j+1] with carry
506                if i + j + 1 < 8 {
507                    let sum2 = result[i + j + 1] as u128 + doubled_hi as u128 + carry1 as u128;
508                    result[i + j + 1] = sum2 as u64;
509                    let carry2 = (sum2 >> 64) as u64;
510
511                    // Propagate carry to higher limbs if needed
512                    let mut k = i + j + 2;
513                    let mut carry = carry2;
514                    while carry > 0 && k < 8 {
515                        let sum = result[k] as u128 + carry as u128;
516                        result[k] = sum as u64;
517                        carry = (sum >> 64) as u64;
518                        k += 1;
519                    }
520                }
521            }
522        }
523
524        let low = Self::from_limbs(&[result[0], result[1], result[2], result[3]]);
525        let high = Self::from_limbs(&[result[4], result[5], result[6], result[7]]);
526        (low, high)
527    }
528
529    /// Division with remainder using binary long division.
530    ///
531    /// Returns (quotient, remainder) where self = quotient * divisor + remainder.
532    /// This implementation uses binary long division which is much more efficient
533    /// than repeated subtraction.
534    pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
535        // Handle division by zero - return (0, self) as a safe fallback
536        if divisor.is_zero() {
537            return (BigInt::from_u64(0), *self);
538        }
539
540        // Handle simple cases
541        if self.is_zero() {
542            return (BigInt::from_u64(0), BigInt::from_u64(0));
543        }
544
545        let cmp = self.cmp(divisor);
546        if cmp == core::cmp::Ordering::Less {
547            return (BigInt::from_u64(0), *self);
548        }
549        if cmp == core::cmp::Ordering::Equal {
550            return (BigInt::from_u64(1), BigInt::from_u64(0));
551        }
552
553        // Use binary search to find the quotient more efficiently
554        let mut quotient = BigInt::from_u64(0);
555        let mut remainder = *self;
556
557        // Binary search for quotient
558        // Upper bound is self.bit_length() - divisor.bit_length() + 1 bits
559        let mut low = BigInt::from_u64(0);
560        let mut high =
561            BigInt::from_u64(1).shl((self.bit_length() - divisor.bit_length() + 1).min(256));
562
563        while low.cmp(&high) == core::cmp::Ordering::Less {
564            let mid = low.add(&high).shr(1);
565            let product = divisor.mul(&mid);
566
567            match product.cmp(&remainder) {
568                core::cmp::Ordering::Less | core::cmp::Ordering::Equal => {
569                    quotient = mid;
570                    low = mid.add(&BigInt::from_u64(1));
571                }
572                core::cmp::Ordering::Greater => {
573                    high = mid;
574                }
575            }
576        }
577
578        // Compute remainder
579        let product = divisor.mul(&quotient);
580        remainder = remainder.sub(&product);
581
582        (quotient, remainder)
583    }
584
585    /// Constant-time left shift.
586    pub fn shl(&self, bits: u32) -> Self {
587        let limbs = self.limbs();
588        let mut result_limbs = [0u64; 4];
589
590        let limb_shift = (bits / 64) as usize;
591        let bit_shift = bits % 64;
592
593        if limb_shift >= 4 {
594            // Shift by more than 256 bits results in zero
595            return Self::from_u64(0);
596        }
597
598        for i in 0..4 {
599            if i + limb_shift < 4 {
600                result_limbs[i + limb_shift] = limbs[i] << bit_shift;
601                if bit_shift > 0 && i + limb_shift + 1 < 4 {
602                    result_limbs[i + limb_shift + 1] |= limbs[i] >> (64 - bit_shift);
603                }
604            }
605        }
606
607        Self::from_limbs(&result_limbs)
608    }
609
610    /// Constant-time right shift.
611    pub fn shr(&self, bits: u32) -> Self {
612        // Similar to left shift but in reverse
613        let limbs = self.limbs();
614        let mut result_limbs = [0u64; 4];
615
616        let limb_shift = (bits / 64) as usize;
617        let bit_shift = bits % 64;
618
619        if limb_shift >= 4 {
620            return Self::from_u64(0);
621        }
622
623        for i in 0..4 {
624            if i >= limb_shift {
625                result_limbs[i - limb_shift] = limbs[i] >> bit_shift;
626                if bit_shift > 0 && i > limb_shift {
627                    result_limbs[i - limb_shift - 1] |= limbs[i] << (64 - bit_shift);
628                }
629            }
630        }
631
632        Self::from_limbs(&result_limbs)
633    }
634
635    /// Constant-time comparison.
636    pub fn cmp(&self, rhs: &Self) -> Ordering {
637        // Compare from most significant limb to least significant
638        let self_limbs = self.limbs();
639        let rhs_limbs = rhs.limbs();
640
641        for i in (0..4).rev() {
642            if self_limbs[i] > rhs_limbs[i] {
643                return Ordering::Greater;
644            } else if self_limbs[i] < rhs_limbs[i] {
645                return Ordering::Less;
646            }
647        }
648
649        Ordering::Equal
650    }
651
652    /// Constant-time comparison that prevents timing leaks.
653    ///
654    /// This comparison always processes all limbs regardless of their values,
655    /// ensuring constant execution time and preventing timing-based side-channel attacks.
656    ///
657    /// The implementation uses constant-time operations to compare limbs from
658    /// most significant to least significant, accumulating the result without
659    /// any early returns or branches based on intermediate results.
660    pub fn ct_cmp(&self, rhs: &Self) -> Ordering {
661        use crate::ct::{ct_gt, ct_lt};
662
663        let self_limbs = self.limbs();
664        let rhs_limbs = rhs.limbs();
665
666        // Initialize result flags - start assuming equal
667        let mut is_greater = 0u64;
668        let mut is_less = 0u64;
669        let mut found_difference = 0u64;
670
671        // Compare limbs from MSB to LSB (index 3 to 0)
672        for i in (0..4).rev() {
673            let a_limb = self_limbs[i];
674            let b_limb = rhs_limbs[i];
675
676            // Check if this limb differs
677            let limb_greater = ct_gt(a_limb, b_limb);
678            let limb_less = ct_lt(a_limb, b_limb);
679
680            // Only update result if we haven't found a difference in higher limbs
681            // Use constant-time selection to conditionally update based on found_difference
682            let update_mask = 1u64 ^ found_difference; // 1 if no difference found yet, 0 otherwise
683
684            is_greater |= limb_greater & update_mask;
685            is_less |= limb_less & update_mask;
686
687            // If this limb differs, mark that we've found the first difference
688            found_difference |= limb_greater | limb_less;
689        }
690
691        // Convert to Ordering using constant-time operations
692        if is_greater == 1 {
693            Ordering::Greater
694        } else if is_less == 1 {
695            Ordering::Less
696        } else {
697            Ordering::Equal
698        }
699    }
700
701    /// Constant-time conditional selection between two BigInts.
702    ///
703    /// Returns `a` if `flag == 0`, `b` if `flag == 1`.
704    /// Executes in constant time regardless of input values.
705    ///
706    /// # Safety
707    ///
708    /// `flag` must be either `0` or `1`. Other values may leak timing information.
709    pub fn ct_select(a: &Self, b: &Self, flag: u64) -> Self {
710        use crate::ct::ct_mask;
711
712        let a_limbs = a.limbs();
713        let b_limbs = b.limbs();
714        let mask = ct_mask(flag);
715
716        let mut result_limbs = [0u64; 4];
717        for i in 0..4 {
718            // When mask is all 1s (flag=1), select b
719            // When mask is all 0s (flag=0), select a
720            result_limbs[i] = (a_limbs[i] & !mask) | (b_limbs[i] & mask);
721        }
722
723        Self::from_limbs(&result_limbs)
724    }
725
726    /// Constant-time zero check.
727    pub fn is_zero(&self) -> bool {
728        let limbs = self.limbs();
729        let mut result = 1u64;
730        for limb in &limbs {
731            result &= ct::ct_is_zero(*limb);
732        }
733        result == 1
734    }
735
736    /// Get the bit at the specified position (constant-time).
737    ///
738    /// Returns 1 if the bit is set, 0 otherwise.
739    /// Bit positions start from 0 (least significant bit).
740    pub fn get_bit(&self, bit_position: u32) -> u64 {
741        let limb_index = (bit_position / 64) as usize;
742        let bit_index = bit_position % 64;
743
744        if limb_index >= 4 {
745            return 0;
746        }
747
748        let limbs = self.limbs();
749        let limb = limbs[limb_index];
750        (limb >> bit_index) & 1
751    }
752
753    /// Get the number of bits needed to represent this BigInt.
754    ///
755    /// Returns the position of the highest set bit plus one.
756    /// Returns 0 for zero.
757    pub fn bit_length(&self) -> u32 {
758        let limbs = self.limbs();
759
760        // Find the highest non-zero limb
761        for i in (0..4).rev() {
762            if limbs[i] != 0 {
763                // Find the highest bit in this limb
764                let mut bit = 63;
765                while bit > 0 {
766                    if (limbs[i] & (1u64 << bit)) != 0 {
767                        return (i as u32 * 64) + bit + 1;
768                    }
769                    bit -= 1;
770                }
771                // If we reach here, it's bit 0
772                return (i as u32 * 64) + 1;
773            }
774        }
775
776        0 // Zero has no bits set
777    }
778
779    /// Attempts to convert the BigInt to a u64 value.
780    ///
781    /// Returns the value as a u64 if it fits within the range [0, 2^64 - 1],
782    /// otherwise returns None. This is useful for converting small BigInt
783    /// values back to primitive types for compatibility with other libraries.
784    ///
785    /// # Returns
786    /// - `Some(value)` if the BigInt value fits in a u64
787    /// - `None` if the value is too large (requires more than 64 bits)
788    ///
789    /// # Examples
790    /// ```ignore
791    /// use clock_curve_math::bigint::BigInt;
792    ///
793    /// let small = BigInt::from_u64(42);
794    /// assert_eq!(small.to_u64(), Some(42));
795    ///
796    /// let large = BigInt::from_limbs(&[0, 1, 0, 0]); // 2^64
797    /// assert_eq!(large.to_u64(), None); // Too large for u64
798    /// ```
799    ///
800    /// # Performance
801    /// O(1) check of the upper limbs to determine if conversion is possible.
802    pub fn to_u64(&self) -> Option<u64> {
803        let limbs = self.limbs();
804        if limbs[1] == 0 && limbs[2] == 0 && limbs[3] == 0 {
805            Some(limbs[0])
806        } else {
807            None
808        }
809    }
810
811    /// Performs bitwise AND operation between two BigInts.
812    ///
813    /// Computes `self & rhs` by performing bitwise AND on each corresponding
814    /// limb of the two BigInts. This is useful for masking operations and
815    /// extracting specific bit patterns from large integers.
816    ///
817    /// # Parameters
818    /// * `rhs` - The BigInt to AND with self
819    ///
820    /// # Returns
821    /// A new BigInt with the result of the bitwise AND operation
822    ///
823    /// # Examples
824    /// ```ignore
825    /// use clock_curve_math::bigint::BigInt;
826    ///
827    /// let a = BigInt::from_u64(0b11001100);
828    /// let b = BigInt::from_u64(0b10101010);
829    /// let result = a.and(&b);
830    ///
831    /// assert_eq!(result.to_u64(), Some(0b10001000)); // 0b11001100 & 0b10101010
832    /// ```
833    ///
834    /// # Performance
835    /// O(1) - performs four 64-bit AND operations.
836    ///
837    /// # Security
838    /// Constant-time operation, safe for use with cryptographic secrets.
839    pub fn and(&self, rhs: &Self) -> Self {
840        let lhs_limbs = self.limbs();
841        let rhs_limbs = rhs.limbs();
842        let mut result_limbs = [0u64; 4];
843
844        for i in 0..4 {
845            result_limbs[i] = lhs_limbs[i] & rhs_limbs[i];
846        }
847
848        Self::from_limbs(&result_limbs)
849    }
850
851    /// Performs checked left shift operation with overflow detection.
852    ///
853    /// Shifts the BigInt left by the specified number of bits. If the shift
854    /// amount would cause overflow (shift >= 256 bits), returns None.
855    /// Otherwise returns the shifted value.
856    ///
857    /// # Parameters
858    /// * `bits` - Number of bits to shift left (0 ≤ bits < 256)
859    ///
860    /// # Returns
861    /// - `Some(result)` if the shift is valid and doesn't overflow
862    /// - `None` if the shift would overflow (bits >= 256)
863    ///
864    /// # Examples
865    /// ```ignore
866    /// use clock_curve_math::bigint::BigInt;
867    ///
868    /// let num = BigInt::from_u64(1);
869    ///
870    /// // Valid shifts
871    /// assert_eq!(num.checked_shl(0).unwrap().to_u64(), Some(1));    // 1 << 0 = 1
872    /// assert_eq!(num.checked_shl(1).unwrap().to_u64(), Some(2));    // 1 << 1 = 2
873    /// assert_eq!(num.checked_shl(63).unwrap().to_u64(), Some(1 << 63)); // Max u64
874    ///
875    /// // Invalid shift (would overflow)
876    /// assert_eq!(num.checked_shl(256), None);
877    /// ```
878    ///
879    /// # Security
880    /// Constant-time operation when shift amount is within bounds.
881    /// Use this instead of `shl()` when shift amount might be untrusted.
882    pub fn checked_shl(&self, bits: u32) -> Option<Self> {
883        if bits >= 256 {
884            return Some(Self::from_u64(0));
885        }
886
887        let limb_shift = bits / 64;
888        let bit_shift = bits % 64;
889
890        let limbs = self.limbs();
891        let mut result_limbs = [0u64; 4];
892
893        for (i, result_limb) in result_limbs.iter_mut().enumerate() {
894            if i >= limb_shift as usize {
895                let source_idx = i - limb_shift as usize;
896                if source_idx < 4 {
897                    *result_limb = limbs[source_idx] << bit_shift;
898                    // Add carry from previous limb
899                    if bit_shift > 0 && source_idx > 0 {
900                        *result_limb |= limbs[source_idx - 1] >> (64 - bit_shift);
901                    }
902                }
903            }
904        }
905
906        Some(Self::from_limbs(&result_limbs))
907    }
908}
909
910/// Implements the left shift operator (`<<`) for BigInt.
911///
912/// Allows using the `<<` operator for left shift operations on owned BigInt values.
913/// Delegates to the `shl()` method for the actual implementation.
914///
915/// # Examples
916/// ```ignore
917/// use clock_curve_math::bigint::BigInt;
918///
919/// let num = BigInt::from_u64(1);
920/// let shifted = num << 8; // Equivalent to num.shl(8)
921/// assert_eq!(shifted.to_u64(), Some(256));
922/// ```
923impl core::ops::Shl<u32> for BigInt {
924    type Output = BigInt;
925
926    fn shl(self, rhs: u32) -> Self::Output {
927        (&self).shl(rhs)
928    }
929}
930
931/// Implements the subtraction operator (`-`) for BigInt.
932///
933/// Allows using the `-` operator for subtraction operations on owned BigInt values.
934/// Delegates to the `sub()` method for the actual implementation.
935///
936/// # Examples
937/// ```ignore
938/// use clock_curve_math::bigint::BigInt;
939///
940/// let a = BigInt::from_u64(10);
941/// let b = BigInt::from_u64(3);
942/// let result = a - b; // Equivalent to a.sub(&b)
943/// assert_eq!(result.to_u64(), Some(7));
944/// ```
945impl core::ops::Sub for BigInt {
946    type Output = BigInt;
947
948    fn sub(self, rhs: Self) -> Self::Output {
949        (&self).sub(&rhs)
950    }
951}
952
953/// Implements equality comparison for BigInt.
954///
955/// Two BigInts are equal if all their limbs are equal. This implementation
956/// compares limbs directly for efficiency, which is constant-time since
957/// all BigInts have exactly 4 limbs.
958///
959/// # Security Note
960/// This comparison is constant-time and safe for use with cryptographic secrets.
961/// It compares all limbs regardless of the actual values.
962impl PartialEq for BigInt {
963    fn eq(&self, other: &Self) -> bool {
964        self.limbs() == other.limbs()
965    }
966}
967
968/// Marks BigInt as having total equality.
969///
970/// BigInt equality is reflexive, symmetric, and transitive, satisfying
971/// the requirements for the `Eq` trait.
972impl Eq for BigInt {}
973
974/// Implements partial ordering for BigInt.
975///
976/// Provides comparison operations using the `cmp()` method. Since BigInt
977/// represents integers with total ordering, this always returns `Some(Ordering)`.
978///
979/// # Security Note
980/// This uses the standard comparison method which may leak timing information.
981/// For cryptographic applications, use `ct_cmp()` instead.
982impl PartialOrd for BigInt {
983    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
984        Some(self.cmp(other))
985    }
986}
987
988/// Implements total ordering for BigInt.
989///
990/// BigInt values have a total ordering (any two values can be compared),
991/// making BigInt suitable for use as keys in ordered collections like BTreeMap.
992///
993/// # Security Note
994/// This uses the standard comparison method which may leak timing information.
995/// For cryptographic applications requiring constant-time comparison, use `ct_cmp()`.
996impl Ord for BigInt {
997    fn cmp(&self, other: &Self) -> Ordering {
998        self.cmp(other)
999    }
1000}
1001
1002impl Default for BigInt {
1003    fn default() -> Self {
1004        Self::from_u64(0)
1005    }
1006}
1007
1008#[cfg(test)]
1009mod tests {
1010    use super::*;
1011
1012    #[test]
1013    fn test_ct_cmp() {
1014        let a = BigInt::from_u64(42);
1015        let b = BigInt::from_u64(42);
1016        let c = BigInt::from_u64(43);
1017
1018        assert_eq!(a.ct_cmp(&b), core::cmp::Ordering::Equal);
1019        assert_eq!(a.ct_cmp(&c), core::cmp::Ordering::Less);
1020        assert_eq!(c.ct_cmp(&a), core::cmp::Ordering::Greater);
1021
1022        // Test with larger numbers
1023        let big1 = BigInt::from_limbs(&[0xFFFFFFFFFFFFFFFF, 0x0, 0x0, 0x0]);
1024        let big2 = BigInt::from_limbs(&[0xFFFFFFFFFFFFFFFE, 0x0, 0x0, 0x0]);
1025
1026        assert_eq!(big2.ct_cmp(&big1), core::cmp::Ordering::Less);
1027        assert_eq!(big1.ct_cmp(&big2), core::cmp::Ordering::Greater);
1028    }
1029
1030    /// Tests the constant-time selection functionality.
1031    ///
1032    /// Verifies that the `ct_select()` method correctly selects between two
1033    /// BigInt values based on the flag parameter. Tests both selection paths
1034    /// (flag = 0 and flag = 1) to ensure the conditional selection works
1035    /// correctly and maintains constant-time properties.
1036    #[test]
1037    fn test_ct_select() {
1038        let a = BigInt::from_u64(42);
1039        let b = BigInt::from_u64(123);
1040
1041        assert_eq!(BigInt::ct_select(&a, &b, 0), a);
1042        assert_eq!(BigInt::ct_select(&a, &b, 1), b);
1043    }
1044}