dcrypt_algorithms/ec/p384/
field.rs

1//! P-384 field arithmetic implementation
2
3use crate::ec::p384::constants::P384_FIELD_ELEMENT_SIZE;
4use crate::error::{Error, Result};
5use subtle::{Choice, ConditionallySelectable};
6
7/// P-384 field element representing values in F_p
8///
9/// Internally stored as 12 little-endian 32-bit limbs for efficient arithmetic.
10/// All operations maintain the invariant that values are reduced modulo p.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct FieldElement(pub(crate) [u32; 12]);
13
14impl FieldElement {
15    /* -------------------------------------------------------------------- */
16    /*  NIST P-384 Field Constants (stored as little-endian 32-bit limbs)  */
17    /* -------------------------------------------------------------------- */
18
19    /// The NIST P-384 prime modulus: p = 2^384 - 2^128 - 2^96 + 2^32 - 1
20    /// Stored as 12 little-endian 32-bit limbs where limbs[0] is least significant
21    pub(crate) const MOD_LIMBS: [u32; 12] = [
22        0xFFFF_FFFF, // 2⁰ … 2³¹
23        0x0000_0000, // 2³² … 2⁶³
24        0x0000_0000, // 2⁶⁴ … 2⁹⁵
25        0xFFFF_FFFF, // 2⁹⁶ … 2¹²⁷
26        0xFFFF_FFFE, // 2¹²⁸ … 2¹⁵⁹
27        0xFFFF_FFFF, // 2¹⁶⁰ … 2¹⁹¹
28        0xFFFF_FFFF, // 2¹⁹² … 2²²³
29        0xFFFF_FFFF, // 2²²⁴ … 2²⁵⁵
30        0xFFFF_FFFF, // 2²⁵⁶ … 2²⁸⁷
31        0xFFFF_FFFF, // 2²⁸⁸ … 2³¹⁹
32        0xFFFF_FFFF, // 2³²⁰ … 2³⁵¹
33        0xFFFF_FFFF, // 2³⁵² … 2³⁸³
34    ];
35
36    /// The curve parameter a = -3 mod p, used in the curve equation y² = x³ + ax + b
37    /// For P-384: a = p - 3
38    pub(crate) const A_M3: [u32; 12] = [
39        0xFFFF_FFFC, // (2³² - 1) - 3 = 2³² - 4
40        0x0000_0000,
41        0x0000_0000,
42        0xFFFF_FFFF,
43        0xFFFF_FFFE,
44        0xFFFF_FFFF,
45        0xFFFF_FFFF,
46        0xFFFF_FFFF,
47        0xFFFF_FFFF,
48        0xFFFF_FFFF,
49        0xFFFF_FFFF,
50        0xFFFF_FFFF, // Most significant limb
51    ];
52
53    /// The additive identity element: 0
54    pub fn zero() -> Self {
55        FieldElement([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
56    }
57
58    /// The multiplicative identity element: 1
59    pub fn one() -> Self {
60        FieldElement([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
61    }
62
63    /// Create a field element from big-endian byte representation
64    ///
65    /// Validates that the input represents a value less than the field modulus p.
66    /// Returns an error if the value is >= p.
67    pub fn from_bytes(bytes: &[u8; P384_FIELD_ELEMENT_SIZE]) -> Result<Self> {
68        let mut limbs = [0u32; 12];
69
70        // Convert from big-endian bytes to little-endian limbs
71        // limbs[0] = least-significant 4 bytes (bytes[44..48])
72        // limbs[11] = most-significant 4 bytes (bytes[0..4])
73        for (i, limb) in limbs.iter_mut().enumerate() {
74            let offset = (11 - i) * 4; // Byte offset: 44, 40, 36, ..., 0
75            *limb = u32::from_be_bytes([
76                bytes[offset],
77                bytes[offset + 1],
78                bytes[offset + 2],
79                bytes[offset + 3],
80            ]);
81        }
82
83        // Validate that the value is in the field (< p)
84        let fe = FieldElement(limbs);
85        if !fe.is_valid() {
86            return Err(Error::param(
87                "FieldElement",
88                "Value must be less than the field modulus",
89            ));
90        }
91
92        Ok(fe)
93    }
94
95    /// Convert field element to big-endian byte representation
96    pub fn to_bytes(&self) -> [u8; P384_FIELD_ELEMENT_SIZE] {
97        let mut bytes = [0u8; P384_FIELD_ELEMENT_SIZE];
98
99        // Convert from little-endian limbs to big-endian bytes
100        for i in 0..12 {
101            let limb_bytes = self.0[i].to_be_bytes();
102            let offset = (11 - i) * 4; // Byte offset: 44, 40, 36, ..., 0
103            bytes[offset..offset + 4].copy_from_slice(&limb_bytes);
104        }
105        bytes
106    }
107
108    /// Constant-time validation that the field element is in canonical form (< p)
109    ///
110    /// Uses constant-time subtraction to check if self < p without branching.
111    /// Returns true if the element is valid (< p), false otherwise.
112    #[inline(always)]
113    pub fn is_valid(&self) -> bool {
114        // Attempt to subtract p from self
115        // If subtraction requires a borrow, then self < p (valid)
116        let (_, borrow) = Self::sbb12(self.0, Self::MOD_LIMBS);
117        borrow == 1
118    }
119
120    /// Constant-time field addition: (self + other) mod p
121    ///
122    /// Algorithm:
123    /// 1. Perform full 384-bit addition with carry detection
124    /// 2. Conditionally subtract p if result >= p
125    /// 3. Ensure result is in canonical form
126    #[inline(always)]
127    pub fn add(&self, other: &Self) -> Self {
128        // Step 1: Full 384-bit addition
129        let (sum, carry) = Self::adc12(self.0, other.0);
130
131        // Step 2: Attempt conditional reduction by subtracting p
132        let (sum_minus_p, borrow) = Self::sbb12(sum, Self::MOD_LIMBS);
133
134        // Step 3: Choose reduced value if:
135        //   - Addition overflowed (carry == 1), OR
136        //   - Subtraction didn't borrow (borrow == 0), meaning sum >= p
137        let need_reduce = (carry | (borrow ^ 1)) & 1;
138        let reduced = Self::conditional_select(&sum, &sum_minus_p, Choice::from(need_reduce as u8));
139
140        // Step 4: Final canonical reduction
141        reduced.conditional_sub_p()
142    }
143
144    /// Constant-time field subtraction: (self - other) mod p
145    ///
146    /// Algorithm:
147    /// 1. Perform limb-wise subtraction
148    /// 2. If subtraction borrows, add p to get the correct positive result
149    pub fn sub(&self, other: &Self) -> Self {
150        // Step 1: Raw subtraction
151        let (diff, borrow) = Self::sbb12(self.0, other.0);
152
153        // Step 2: If we borrowed, add p to get the correct positive result
154        let (candidate, _) = Self::adc12(diff, Self::MOD_LIMBS);
155
156        // Step 3: Constant-time select based on borrow flag
157        Self::conditional_select(&diff, &candidate, Choice::from(borrow as u8))
158    }
159
160    /// Field multiplication: (self * other) mod p
161    ///
162    /// Algorithm:
163    /// 1. Compute the full 768-bit product using schoolbook multiplication
164    /// 2. Perform carry propagation to get proper limb representation
165    /// 3. Apply Barrett reduction for P-384
166    pub fn mul(&self, other: &Self) -> Self {
167        // Phase 1: Accumulate partial products in 128-bit temporaries
168        // This prevents overflow during the schoolbook multiplication
169        let mut t = [0u128; 24];
170        for i in 0..12 {
171            for j in 0..12 {
172                t[i + j] += (self.0[i] as u128) * (other.0[j] as u128);
173            }
174        }
175
176        // Phase 2: Carry propagation to convert to 32-bit limb representation
177        let mut prod = [0u32; 24];
178        let mut carry: u128 = 0;
179        for i in 0..24 {
180            let v = t[i] + carry;
181            prod[i] = (v & 0xffff_ffff) as u32;
182            carry = v >> 32;
183        }
184
185        // Phase 3: Apply P-384 reduction
186        Self::reduce_wide(prod)
187    }
188
189    /// Field squaring: self² mod p
190    ///
191    /// Optimized version of multiplication for the case where both operands
192    /// are the same. Currently implemented as self.mul(self) but could be
193    /// optimized further with dedicated squaring algorithms.
194    #[inline(always)]
195    pub fn square(&self) -> Self {
196        self.mul(self)
197    }
198
199    /// Compute the modular multiplicative inverse using Fermat's Little Theorem
200    ///
201    /// For prime fields, a^(p-1) ≡ 1 (mod p), so a^(p-2) ≡ a^(-1) (mod p).
202    /// Uses binary exponentiation (square-and-multiply) for efficiency.
203    ///
204    /// Returns an error if attempting to invert zero (which has no inverse).
205    pub fn invert(&self) -> Result<Self> {
206        if self.is_zero() {
207            return Err(Error::param(
208                "FieldElement",
209                "Inversion of zero is undefined",
210            ));
211        }
212
213        // The exponent p-2 for NIST P-384 in big-endian byte format
214        const P_MINUS_2: [u8; 48] = [
215            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
216            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
217            0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218            0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFD,
219        ];
220
221        // Binary exponentiation: compute self^(p-2) mod p
222        let mut result = FieldElement::one();
223        let mut base = self.clone();
224
225        // Process each bit of the exponent from least to most significant
226        for &byte in P_MINUS_2.iter().rev() {
227            for bit in 0..8 {
228                if (byte >> bit) & 1 == 1 {
229                    result = result.mul(&base);
230                }
231                base = base.square();
232            }
233        }
234
235        Ok(result)
236    }
237
238    /// Check if the field element represents zero
239    ///
240    /// Constant-time check across all limbs to determine if the
241    /// field element is the additive identity.
242    pub fn is_zero(&self) -> bool {
243        for limb in self.0.iter() {
244            if *limb != 0 {
245                return false;
246            }
247        }
248        true
249    }
250
251    /// Return `true` when the element is odd (LSB set)
252    ///
253    /// Used for point compression to determine the sign of the y-coordinate.
254    /// The parity is determined by the least significant bit of the canonical
255    /// representation.
256    pub fn is_odd(&self) -> bool {
257        (self.0[0] & 1) == 1
258    }
259
260    /// Modular square root using the (p+1)/4 shortcut (p ≡ 3 mod 4).
261    ///
262    /// Because the P-384 prime satisfies p ≡ 3 (mod 4), we can compute
263    /// sqrt(a) = a^((p+1)/4) mod p. This is more efficient than the
264    /// general Tonelli-Shanks algorithm.
265    ///
266    /// Returns `None` when the input is a quadratic non-residue (i.e.,
267    /// when no square root exists in the field).
268    ///
269    /// # Algorithm
270    /// For p ≡ 3 (mod 4), if a has a square root, then:
271    /// - sqrt(a) = ±a^((p+1)/4) mod p
272    /// - We return the principal square root (the smaller of the two)
273    pub fn sqrt(&self) -> Option<Self> {
274        if self.is_zero() {
275            return Some(Self::zero());
276        }
277
278        // (p + 1) / 4 for P-384 as big-endian bytes
279        // p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff
280        // (p + 1) / 4 = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbffffffffc000000000000000400000000
281        const EXP: [u8; 48] = [
282            0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
283            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
284            0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
285            0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
286        ];
287
288        let mut result = FieldElement::one();
289        let mut base = self.clone();
290
291        // Binary exponentiation from LSB to MSB
292        for &byte in EXP.iter().rev() {
293            for bit in 0..8 {
294                if ((byte >> bit) & 1) == 1 {
295                    result = result.mul(&base);
296                }
297                base = base.square();
298            }
299        }
300
301        // Verify that result^2 = self (constant-time check)
302        if result.square() == *self {
303            Some(result)
304        } else {
305            None
306        }
307    }
308
309    // Private helper methods
310
311    /// Constant-time conditional selection between two limb arrays
312    ///
313    /// Returns a if flag == 0, returns b if flag == 1
314    /// Used for branchless operations to maintain constant-time guarantees.
315    fn conditional_select(a: &[u32; 12], b: &[u32; 12], flag: Choice) -> Self {
316        let mut out = [0u32; 12];
317        for i in 0..12 {
318            out[i] = u32::conditional_select(&a[i], &b[i], flag);
319        }
320        FieldElement(out)
321    }
322
323    /// 12-limb addition with carry propagation
324    ///
325    /// Performs full-width addition across all limbs, returning both
326    /// the sum and the final carry bit for overflow detection.
327    #[inline(always)]
328    fn adc12(a: [u32; 12], b: [u32; 12]) -> ([u32; 12], u32) {
329        let mut r = [0u32; 12];
330        let mut carry = 0;
331
332        for i in 0..12 {
333            // Add corresponding limbs plus carry from previous iteration
334            let (sum1, carry1) = a[i].overflowing_add(b[i]);
335            let (sum2, carry2) = sum1.overflowing_add(carry);
336
337            r[i] = sum2;
338            carry = (carry1 as u32) | (carry2 as u32);
339        }
340
341        (r, carry)
342    }
343
344    /// 12-limb subtraction with borrow propagation
345    ///
346    /// Performs full-width subtraction across all limbs, returning both
347    /// the difference and the final borrow bit for underflow detection.
348    #[inline(always)]
349    fn sbb12(a: [u32; 12], b: [u32; 12]) -> ([u32; 12], u32) {
350        let mut r = [0u32; 12];
351        let mut borrow = 0;
352
353        for i in 0..12 {
354            // Subtract corresponding limbs minus borrow from previous iteration
355            let (diff1, borrow1) = a[i].overflowing_sub(b[i]);
356            let (diff2, borrow2) = diff1.overflowing_sub(borrow);
357
358            r[i] = diff2;
359            borrow = (borrow1 as u32) | (borrow2 as u32);
360        }
361        (r, borrow)
362    }
363
364    /// Conditionally subtract p if the current value is >= p
365    ///
366    /// Ensures the field element is in canonical reduced form.
367    /// Used as a final step in arithmetic operations.
368    fn conditional_sub_p(&self) -> Self {
369        let needs_sub = Choice::from((!self.is_valid() as u8) & 1);
370        Self::conditional_sub(self.0, needs_sub)
371    }
372
373    /// Conditionally subtract the field modulus p based on a boolean condition
374    ///
375    /// Uses constant-time selection to avoid branching while maintaining
376    /// the option to perform the subtraction.
377    fn conditional_sub(limbs: [u32; 12], condition: Choice) -> Self {
378        let mut result = [0u32; 12];
379        let (diff, _) = Self::sbb12(limbs, Self::MOD_LIMBS);
380
381        // Constant-time select between original limbs and difference
382        for i in 0..12 {
383            result[i] = u32::conditional_select(&limbs[i], &diff[i], condition);
384        }
385
386        Self(result)
387    }
388
389    /// NIST P-384 specific reduction for 768-bit values using Solinas method
390    /// Fully constant-time Solinas reduction with two carry-folds.
391    /// For P-384: 2^384 ≡ 2^128 + 2^96 - 2^32 + 1 (mod p)
392    pub(crate) fn reduce_wide(t: [u32; 24]) -> FieldElement {
393        // 1) load into signed 128-bit
394        let mut s = [0i128; 24];
395        for i in 0..24 {
396            s[i] = t[i] as i128;
397        }
398
399        // 2) fold high limbs 12..23 into 0..11 via
400        //    2^384 ≡ 2^128 + 2^96 - 2^32 + 1 (mod p)
401        for i in (12..24).rev() {
402            let v = s[i];
403            s[i] = 0;
404            s[i - 12] = s[i - 12].wrapping_add(v); // +1 (2^0 term)
405            s[i - 11] = s[i - 11].wrapping_sub(v); // -2^32 term
406            s[i - 9] = s[i - 9].wrapping_add(v); // +2^96 term
407            s[i - 8] = s[i - 8].wrapping_add(v); // +2^128 term
408        }
409
410        // 2b) the previous step can leave non-zero words in slots 12..15
411        //     (it happens when i = 20..23). Fold them once more so that
412        //     all non-zero limbs are now in 0..11.
413        for i in (12..16).rev() {
414            let v = s[i];
415            s[i] = 0;
416            s[i - 12] = s[i - 12].wrapping_add(v); // +1
417            s[i - 11] = s[i - 11].wrapping_sub(v); // -2^32
418            s[i - 9] = s[i - 9].wrapping_add(v); // +2^96
419            s[i - 8] = s[i - 8].wrapping_add(v); // +2^128
420        }
421
422        // 3) first signed carry-propagate
423        let mut carry1: i128 = 0;
424        for limb in s.iter_mut().take(12) {
425            let tmp = *limb + carry1;
426            *limb = tmp & 0xffff_ffff;
427            carry1 = tmp >> 32; // arithmetic shift
428        }
429
430        // 4) fold carry1 back down using same relation
431        let c1 = carry1;
432        s[0] = s[0].wrapping_add(c1); // +1
433        s[1] = s[1].wrapping_sub(c1); // -2^32
434        s[3] = s[3].wrapping_add(c1); // +2^96
435        s[4] = s[4].wrapping_add(c1); // +2^128
436
437        // 5) second signed carry-propagate
438        let mut carry2: i128 = 0;
439        for limb in s.iter_mut().take(12) {
440            let tmp = *limb + carry2;
441            *limb = tmp & 0xffff_ffff;
442            carry2 = tmp >> 32;
443        }
444
445        // 6) fold carry2 back down
446        let c2 = carry2;
447        s[0] = s[0].wrapping_add(c2);
448        s[1] = s[1].wrapping_sub(c2);
449        s[3] = s[3].wrapping_add(c2);
450        s[4] = s[4].wrapping_add(c2);
451
452        // 7) final signed carry-propagate into 32-bit limbs
453        let mut out = [0u32; 12];
454        let mut carry3: i128 = 0;
455        for i in 0..12 {
456            let tmp = s[i] + carry3;
457            out[i] = (tmp & 0xffff_ffff) as u32;
458            carry3 = tmp >> 32;
459        }
460
461        // 8) one last constant-time subtract if ≥ p
462        let (subbed, borrow) = Self::sbb12(out, Self::MOD_LIMBS);
463        let need_sub = Choice::from((borrow ^ 1) as u8); // borrow==0 ⇒ out>=p
464        Self::conditional_select(&out, &subbed, need_sub)
465    }
466
467    /// Get the field modulus p as a FieldElement
468    ///
469    /// Returns the NIST P-384 prime modulus for use in reduction operations.
470    pub(crate) fn get_modulus() -> Self {
471        FieldElement(Self::MOD_LIMBS)
472    }
473}