Skip to main content

dcrypt_algorithms/ec/k256/
field.rs

1//! secp256k1 field arithmetic implementation.
2//! Field prime p = 2^256 - 2^32 - 977.
3
4use crate::ec::k256::constants::K256_FIELD_ELEMENT_SIZE;
5use crate::error::{Error, Result};
6use dcrypt_internal::{
7    constant_time::{Choice, ConditionallySelectable},
8    Zeroize, Zeroizing,
9};
10
11/// secp256k1 field element representing values in F_p
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub struct FieldElement(pub(crate) [u32; 8]);
14
15// `ConditionallySelectable` currently requires `Copy`. Safe Rust therefore
16// cannot erase compiler- or register-created copies. All explicit arithmetic
17// byte and limb owners below use `Zeroizing` and are cleared on every exit.
18impl Default for FieldElement {
19    fn default() -> Self {
20        Self::zero()
21    }
22}
23
24impl Zeroize for FieldElement {
25    fn zeroize(&mut self) {
26        self.0.zeroize();
27    }
28}
29
30impl ConditionallySelectable for FieldElement {
31    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
32        let mut out = Zeroizing::new([0u32; 8]);
33        for i in 0..8 {
34            out[i] = u32::conditional_select(&a.0[i], &b.0[i], choice);
35        }
36        FieldElement(out.into_inner())
37    }
38}
39
40impl FieldElement {
41    /// The secp256k1 prime modulus: p = 2^256 - 2^32 - 977
42    pub(crate) const MOD_LIMBS: [u32; 8] = [
43        0xFFFF_FC2F,
44        0xFFFF_FFFE,
45        0xFFFF_FFFF,
46        0xFFFF_FFFF,
47        0xFFFF_FFFF,
48        0xFFFF_FFFF,
49        0xFFFF_FFFF,
50        0xFFFF_FFFF,
51    ];
52
53    /// The additive identity element: 0
54    pub fn zero() -> Self {
55        FieldElement([0; 8])
56    }
57
58    /// The multiplicative identity element: 1
59    pub fn one() -> Self {
60        let mut limbs = Zeroizing::new([0; 8]);
61        limbs[0] = 1;
62        FieldElement(limbs.into_inner())
63    }
64
65    /// Create a field element from its canonical byte representation.
66    ///
67    /// Returns an error if the value is greater than or equal to the field modulus.
68    pub fn from_bytes(bytes: &[u8; K256_FIELD_ELEMENT_SIZE]) -> Result<Self> {
69        let mut limbs = Zeroizing::new([0u32; 8]);
70        for (i, limb) in limbs.iter_mut().enumerate() {
71            let offset = (7 - i) * 4;
72            *limb = u32::from_be_bytes([
73                bytes[offset],
74                bytes[offset + 1],
75                bytes[offset + 2],
76                bytes[offset + 3],
77            ]);
78        }
79        let fe = Zeroizing::new(FieldElement(limbs.into_inner()));
80        if !fe.is_valid() {
81            return Err(Error::param(
82                "FieldElement K256",
83                "Value must be less than the field modulus",
84            ));
85        }
86        Ok(fe.into_inner())
87    }
88
89    /// Convert this field element to its canonical byte representation.
90    pub fn to_bytes(&self) -> [u8; K256_FIELD_ELEMENT_SIZE] {
91        let mut bytes = Zeroizing::new([0u8; K256_FIELD_ELEMENT_SIZE]);
92        for i in 0..8 {
93            let limb_bytes = Zeroizing::new(self.0[i].to_be_bytes());
94            let offset = (7 - i) * 4;
95            bytes[offset..offset + 4].copy_from_slice(&limb_bytes[..]);
96        }
97        bytes.into_inner()
98    }
99
100    /// Check if this field element is less than the field modulus.
101    #[inline(always)]
102    pub fn is_valid(&self) -> bool {
103        let (_difference, borrow) = Self::sbb8(&self.0, &Self::MOD_LIMBS);
104        borrow == 1
105    }
106
107    /// Check if this field element is zero.
108    pub fn is_zero(&self) -> bool {
109        let mut any = Zeroizing::new(0u32);
110        for &limb in &self.0 {
111            *any |= limb;
112        }
113        *any == 0
114    }
115
116    /// Check if this field element is odd (least significant bit is 1).
117    pub fn is_odd(&self) -> bool {
118        // limbs[0] contains the least significant 32 bits
119        (self.0[0] & 1) == 1
120    }
121
122    /// Add two field elements modulo p.
123    #[inline(always)]
124    pub fn add(&self, other: &Self) -> Self {
125        let (sum, carry) = Self::adc8(&self.0, &other.0);
126        let (sum_minus_p, borrow) = Self::sbb8(&sum, &Self::MOD_LIMBS);
127        let needs_reduce = (carry | (borrow ^ 1)) & 1;
128        let result = Zeroizing::new(Self::conditional_select(
129            &sum,
130            &sum_minus_p,
131            Choice::from(needs_reduce as u8),
132        ));
133        result.into_inner()
134    }
135
136    /// Subtract two field elements modulo p.
137    pub fn sub(&self, other: &Self) -> Self {
138        let (diff, borrow) = Self::sbb8(&self.0, &other.0);
139        let (candidate, _carry) = Self::adc8(&diff, &Self::MOD_LIMBS);
140        let result = Zeroizing::new(Self::conditional_select(
141            &diff,
142            &candidate,
143            Choice::from(borrow as u8),
144        ));
145        result.into_inner()
146    }
147
148    /// Negate a field element modulo p.
149    pub fn negate(&self) -> Self {
150        let negated = Zeroizing::new(FieldElement(Self::MOD_LIMBS).sub(self));
151        let result = Zeroizing::new(Self::conditional_select(
152            &negated.0,
153            &Self::zero().0,
154            Choice::from(self.is_zero() as u8),
155        ));
156        result.into_inner()
157    }
158
159    /// Multiply two field elements modulo p.
160    pub fn mul(&self, other: &Self) -> Self {
161        let mut t = Zeroizing::new([0u128; 16]);
162        for i in 0..8 {
163            for j in 0..8 {
164                t[i + j] += (self.0[i] as u128) * (other.0[j] as u128);
165            }
166        }
167        let mut prod = Zeroizing::new([0u32; 16]);
168        let mut carry: u128 = 0;
169        for i in 0..16 {
170            let v = t[i] + carry;
171            prod[i] = (v & 0xffff_ffff) as u32;
172            carry = v >> 32;
173        }
174        Self::reduce_wide(prod)
175    }
176
177    /// Square a field element modulo p.
178    #[inline(always)]
179    pub fn square(&self) -> Self {
180        self.mul(self)
181    }
182
183    /// Double a field element (multiply by 2) modulo p.
184    pub fn double(&self) -> Self {
185        self.add(self)
186    }
187
188    /// Compute the multiplicative inverse of a field element.
189    ///
190    /// Returns an error if the element is zero.
191    pub fn invert(&self) -> Result<Self> {
192        if self.is_zero() {
193            return Err(Error::param(
194                "FieldElement K256",
195                "Inversion of zero is undefined",
196            ));
197        }
198        const P_MINUS_2: [u8; 32] = [
199            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
200            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
201            0xFF, 0xFF, 0xFC, 0x2D,
202        ];
203        self.pow(&P_MINUS_2)
204    }
205
206    /// Compute the square root of a field element.
207    ///
208    /// Returns None if the element is not a quadratic residue.
209    pub fn sqrt(&self) -> Option<Self> {
210        if self.is_zero() {
211            return Some(Self::zero());
212        }
213        // p mod 4 = 3, so sqrt(a) = a^((p+1)/4)
214        const P_PLUS_1_DIV_4: [u8; 32] = [
215            0x3F, 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            0xBF, 0xFF, 0xFF, 0x0C,
218        ];
219        let root = Zeroizing::new(self.pow(&P_PLUS_1_DIV_4).ok()?);
220        let squared = Zeroizing::new(root.square());
221        if *squared == *self {
222            Some(root.into_inner())
223        } else {
224            None
225        }
226    }
227
228    fn pow(&self, exp_be: &[u8]) -> Result<Self> {
229        let mut result = Zeroizing::new(Self::one());
230        let base = Zeroizing::new(*self);
231        for &byte in exp_be.iter() {
232            for i in (0..8).rev() {
233                let squared = Zeroizing::new(result.square());
234                result.zeroize();
235                *result = squared.into_inner();
236                if (byte >> i) & 1 == 1 {
237                    let product = Zeroizing::new(result.mul(&base));
238                    result.zeroize();
239                    *result = product.into_inner();
240                }
241            }
242        }
243        Ok(result.into_inner())
244    }
245
246    fn conditional_select(a: &[u32; 8], b: &[u32; 8], flag: Choice) -> Self {
247        let mut out = Zeroizing::new([0u32; 8]);
248        for i in 0..8 {
249            out[i] = u32::conditional_select(&a[i], &b[i], flag);
250        }
251        FieldElement(out.into_inner())
252    }
253
254    fn adc8(a: &[u32; 8], b: &[u32; 8]) -> (Zeroizing<[u32; 8]>, u32) {
255        let mut r = Zeroizing::new([0u32; 8]);
256        let mut carry: u64 = 0;
257        for i in 0..8 {
258            let tmp = (a[i] as u64) + (b[i] as u64) + carry;
259            r[i] = tmp as u32;
260            carry = tmp >> 32;
261        }
262        (r, carry as u32)
263    }
264
265    fn sbb8(a: &[u32; 8], b: &[u32; 8]) -> (Zeroizing<[u32; 8]>, u32) {
266        let mut r = Zeroizing::new([0u32; 8]);
267        let mut borrow: i64 = 0;
268        for i in 0..8 {
269            let tmp = (a[i] as i64) - (b[i] as i64) - borrow;
270            r[i] = tmp as u32;
271            borrow = (tmp >> 63) & 1;
272        }
273        (r, borrow as u32)
274    }
275
276    /// Reduce a 512-bit number modulo p = 2^256 - 2^32 - 977
277    /// Uses the special form of secp256k1's prime for efficient reduction
278    fn reduce_wide(t: Zeroizing<[u32; 16]>) -> Self {
279        // For p = 2^256 - 2^32 - 977, we can use the fact that
280        // 2^256 ≡ 2^32 + 977 (mod p)
281        // This allows us to reduce the high 256 bits efficiently
282
283        // Split t into low 256 bits (t_low) and high 256 bits (t_high)
284        let mut t_low = Zeroizing::new([0u32; 8]);
285        let mut t_high = Zeroizing::new([0u32; 8]);
286        t_low.copy_from_slice(&t[..8]);
287        t_high.copy_from_slice(&t[8..]);
288
289        // We need to compute: t_low + t_high * 2^256
290        // Since 2^256 ≡ 2^32 + 977 (mod p), we compute:
291        // t_low + t_high * (2^32 + 977)
292        // = t_low + (t_high << 32) + t_high * 977
293
294        // First, compute t_high * 977
295        let mut t_high_977 = Zeroizing::new([0u64; 9]);
296        for i in 0..8 {
297            t_high_977[i] += (t_high[i] as u64) * 977u64;
298        }
299        // Propagate carries
300        for i in 0..8 {
301            t_high_977[i + 1] += t_high_977[i] >> 32;
302            t_high_977[i] &= 0xFFFF_FFFF;
303        }
304
305        // Now add: t_low + (t_high << 32) + t_high_977
306        let mut result = Zeroizing::new([0u64; 9]);
307
308        // Add t_low
309        for i in 0..8 {
310            result[i] += t_low[i] as u64;
311        }
312
313        // Add t_high << 32 (which means t_high[i] goes to position i+1)
314        for i in 0..8 {
315            result[i + 1] += t_high[i] as u64;
316        }
317
318        // Add t_high_977
319        for i in 0..9 {
320            result[i] += t_high_977[i];
321        }
322
323        // Propagate all carries
324        for i in 0..8 {
325            result[i + 1] += result[i] >> 32;
326            result[i] &= 0xFFFF_FFFF;
327        }
328
329        // Fold the ninth limb unconditionally so reduction does not branch on
330        // secret field values.
331        let overflow = Zeroizing::new(result[8]);
332        result[8] = 0;
333        result[0] += *overflow * 977;
334        result[1] += *overflow;
335        for i in 0..7 {
336            result[i + 1] += result[i] >> 32;
337            result[i] &= 0xFFFF_FFFF;
338        }
339        result[7] &= 0xFFFF_FFFF;
340
341        // Convert back to u32 array
342        let mut r = Zeroizing::new([0u32; 8]);
343        for i in 0..8 {
344            r[i] = result[i] as u32;
345        }
346
347        // Final reduction if r >= p
348        let (reduced, borrow) = Self::sbb8(&r, &Self::MOD_LIMBS);
349        let selected = Zeroizing::new(Self::conditional_select(
350            &r,
351            &reduced,
352            Choice::from((borrow ^ 1) as u8),
353        ));
354        selected.into_inner()
355    }
356}
357
358#[cfg(test)]
359mod field_constants_tests {
360    use super::*;
361
362    #[test]
363    fn test_modulus_is_correct() {
364        // The correct secp256k1 prime in hex:
365        // p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
366
367        // Convert MOD_LIMBS to bytes for comparison
368        let mut mod_bytes = [0u8; 32];
369        for (i, &limb) in FieldElement::MOD_LIMBS.iter().enumerate() {
370            let limb_bytes = limb.to_be_bytes();
371            let offset = (7 - i) * 4;
372            mod_bytes[offset..offset + 4].copy_from_slice(&limb_bytes);
373        }
374
375        // Expected prime as bytes
376        let expected_bytes: [u8; 32] = [
377            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
378            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
379            0xFF, 0xFF, 0xFC, 0x2F,
380        ];
381
382        assert_eq!(
383            mod_bytes, expected_bytes,
384            "MOD_LIMBS does not encode the correct secp256k1 prime"
385        );
386    }
387
388    #[test]
389    fn zeroize_is_owner_local_under_required_copy_semantics() {
390        // `ConditionallySelectable` requires `Copy`: this tests the precise
391        // owner-local guarantee and documents that an earlier copy remains.
392        let original = FieldElement::one();
393        let mut owned_copy = original;
394
395        owned_copy.zeroize();
396
397        assert!(owned_copy.is_zero());
398        assert!(!original.is_zero());
399    }
400}