1use 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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub struct FieldElement(pub(crate) [u32; 8]);
14
15impl 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 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 pub fn zero() -> Self {
55 FieldElement([0; 8])
56 }
57
58 pub fn one() -> Self {
60 let mut limbs = Zeroizing::new([0; 8]);
61 limbs[0] = 1;
62 FieldElement(limbs.into_inner())
63 }
64
65 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 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 #[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 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 pub fn is_odd(&self) -> bool {
118 (self.0[0] & 1) == 1
120 }
121
122 #[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 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 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 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 #[inline(always)]
179 pub fn square(&self) -> Self {
180 self.mul(self)
181 }
182
183 pub fn double(&self) -> Self {
185 self.add(self)
186 }
187
188 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 pub fn sqrt(&self) -> Option<Self> {
210 if self.is_zero() {
211 return Some(Self::zero());
212 }
213 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 fn reduce_wide(t: Zeroizing<[u32; 16]>) -> Self {
279 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 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 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 let mut result = Zeroizing::new([0u64; 9]);
307
308 for i in 0..8 {
310 result[i] += t_low[i] as u64;
311 }
312
313 for i in 0..8 {
315 result[i + 1] += t_high[i] as u64;
316 }
317
318 for i in 0..9 {
320 result[i] += t_high_977[i];
321 }
322
323 for i in 0..8 {
325 result[i + 1] += result[i] >> 32;
326 result[i] &= 0xFFFF_FFFF;
327 }
328
329 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 let mut r = Zeroizing::new([0u32; 8]);
343 for i in 0..8 {
344 r[i] = result[i] as u32;
345 }
346
347 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 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 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 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}