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("ient);
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}