secp256kfun_k256_backend/arithmetic/
field.rs1use cfg_if::cfg_if;
4
5cfg_if! {
6 if #[cfg(feature = "field-montgomery")] {
7 mod field_montgomery;
8 } else if #[cfg(any(target_pointer_width = "32", feature = "force-32-bit"))] {
9 mod field_10x26;
10 } else if #[cfg(target_pointer_width = "64")] {
11 mod field_5x52;
12 }
13}
14
15cfg_if! {
16 if #[cfg(all(debug_assertions, not(feature = "field-montgomery")))] {
17 mod field_impl;
18 use field_impl::FieldElementImpl;
19 } else {
20 cfg_if! {
21 if #[cfg(feature = "field-montgomery")] {
22 use field_montgomery::FieldElementMontgomery as FieldElementImpl;
23 } else if #[cfg(any(target_pointer_width = "32", feature = "force-32-bit"))] {
24 use field_10x26::FieldElement10x26 as FieldElementImpl;
25 } else if #[cfg(target_pointer_width = "64")] {
26 use field_5x52::FieldElement5x52 as FieldElementImpl;
27 }
28 }
29 }
30}
31
32use crate::FieldBytes;
33use core::ops::{Add, AddAssign, Mul, MulAssign};
34use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
35
36#[cfg(feature = "zeroize")]
37use elliptic_curve::zeroize::Zeroize;
38
39#[cfg(test)]
40use num_bigint::{BigUint, ToBigUint};
41
42#[derive(Clone, Copy, Debug)]
44pub struct FieldElement(FieldElementImpl);
45
46impl FieldElement {
47 pub const fn zero() -> Self {
49 Self(FieldElementImpl::zero())
50 }
51
52 pub const fn one() -> Self {
54 Self(FieldElementImpl::one())
55 }
56
57 pub fn is_zero(&self) -> Choice {
63 self.0.is_zero()
64 }
65
66 pub fn is_odd(&self) -> Choice {
72 self.0.is_odd()
73 }
74
75 pub const fn from_bytes_unchecked(bytes: &[u8; 32]) -> Self {
78 Self(FieldElementImpl::from_bytes_unchecked(bytes))
79 }
80
81 pub fn from_bytes(bytes: &FieldBytes) -> CtOption<Self> {
86 FieldElementImpl::from_bytes(bytes).map(Self)
87 }
88
89 pub fn to_bytes(&self) -> FieldBytes {
91 self.0.normalize().to_bytes()
92 }
93
94 pub fn negate(&self, magnitude: u32) -> Self {
97 Self(self.0.negate(magnitude))
98 }
99
100 pub fn normalize(&self) -> Self {
103 Self(self.0.normalize())
104 }
105
106 pub fn normalize_weak(&self) -> Self {
109 Self(self.0.normalize_weak())
110 }
111
112 pub fn normalizes_to_zero(&self) -> Choice {
114 self.0.normalizes_to_zero()
115 }
116
117 pub fn mul_single(&self, rhs: u32) -> Self {
120 Self(self.0.mul_single(rhs))
121 }
122
123 pub fn double(&self) -> Self {
126 Self(self.0.add(&(self.0)))
127 }
128
129 pub fn mul(&self, rhs: &Self) -> Self {
133 Self(self.0.mul(&(rhs.0)))
134 }
135
136 pub fn square(&self) -> Self {
140 Self(self.0.square())
141 }
142
143 fn pow2k(&self, k: usize) -> Self {
145 let mut x = *self;
146 for _j in 0..k {
147 x = x.square();
148 }
149 x
150 }
151
152 pub fn invert(&self) -> CtOption<Self> {
155 let x2 = self.pow2k(1).mul(self);
160 let x3 = x2.pow2k(1).mul(self);
161 let x6 = x3.pow2k(3).mul(&x3);
162 let x9 = x6.pow2k(3).mul(&x3);
163 let x11 = x9.pow2k(2).mul(&x2);
164 let x22 = x11.pow2k(11).mul(&x11);
165 let x44 = x22.pow2k(22).mul(&x22);
166 let x88 = x44.pow2k(44).mul(&x44);
167 let x176 = x88.pow2k(88).mul(&x88);
168 let x220 = x176.pow2k(44).mul(&x44);
169 let x223 = x220.pow2k(3).mul(&x3);
170
171 let res = x223
173 .pow2k(23)
174 .mul(&x22)
175 .pow2k(5)
176 .mul(self)
177 .pow2k(3)
178 .mul(&x2)
179 .pow2k(2)
180 .mul(self);
181
182 CtOption::new(res, !self.normalizes_to_zero())
183 }
184
185 pub fn sqrt(&self) -> CtOption<Self> {
188 let x2 = self.pow2k(1).mul(&self);
204 let x3 = x2.pow2k(1).mul(&self);
205 let x6 = x3.pow2k(3).mul(&x3);
206 let x9 = x6.pow2k(3).mul(&x3);
207 let x11 = x9.pow2k(2).mul(&x2);
208 let x22 = x11.pow2k(11).mul(&x11);
209 let x44 = x22.pow2k(22).mul(&x22);
210 let x88 = x44.pow2k(44).mul(&x44);
211 let x176 = x88.pow2k(88).mul(&x88);
212 let x220 = x176.pow2k(44).mul(&x44);
213 let x223 = x220.pow2k(3).mul(&x3);
214
215 let res = x223.pow2k(23).mul(&x22).pow2k(6).mul(&x2).pow2k(2);
217
218 let is_root = (res.mul(&res).negate(1) + self).normalizes_to_zero();
219
220 CtOption::new(res, is_root)
222 }
223
224 #[cfg(test)]
225 pub fn modulus_as_biguint() -> BigUint {
226 Self::one().negate(1).to_biguint().unwrap() + 1.to_biguint().unwrap()
227 }
228}
229
230impl PartialEq for FieldElement {
231 fn eq(&self, other: &Self) -> bool {
232 self.0.ct_eq(&(other.0)).into()
233 }
234}
235
236impl Default for FieldElement {
237 fn default() -> Self {
238 Self::zero()
239 }
240}
241
242impl ConditionallySelectable for FieldElement {
243 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
244 Self(FieldElementImpl::conditional_select(&(a.0), &(b.0), choice))
245 }
246}
247
248impl ConstantTimeEq for FieldElement {
249 fn ct_eq(&self, other: &Self) -> Choice {
250 self.0.ct_eq(&(other.0))
251 }
252}
253
254impl Add<&FieldElement> for &FieldElement {
255 type Output = FieldElement;
256
257 fn add(self, other: &FieldElement) -> FieldElement {
258 FieldElement(self.0.add(&(other.0)))
259 }
260}
261
262impl Add<&FieldElement> for FieldElement {
263 type Output = FieldElement;
264
265 fn add(self, other: &FieldElement) -> FieldElement {
266 FieldElement(self.0.add(&(other.0)))
267 }
268}
269
270impl AddAssign<FieldElement> for FieldElement {
271 fn add_assign(&mut self, rhs: FieldElement) {
272 *self = *self + &rhs;
273 }
274}
275
276impl Mul<&FieldElement> for &FieldElement {
277 type Output = FieldElement;
278
279 fn mul(self, other: &FieldElement) -> FieldElement {
280 FieldElement(self.0.mul(&(other.0)))
281 }
282}
283
284impl Mul<&FieldElement> for FieldElement {
285 type Output = FieldElement;
286
287 fn mul(self, other: &FieldElement) -> FieldElement {
288 FieldElement(self.0.mul(&(other.0)))
289 }
290}
291
292impl MulAssign<FieldElement> for FieldElement {
293 fn mul_assign(&mut self, rhs: FieldElement) {
294 *self = *self * &rhs;
295 }
296}
297
298#[cfg(feature = "zeroize")]
299impl Zeroize for FieldElement {
300 fn zeroize(&mut self) {
301 self.0.zeroize();
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use num_bigint::{BigUint, ToBigUint};
308 use proptest::prelude::*;
309
310 use super::FieldElement;
311 use crate::{
312 arithmetic::util::{biguint_to_bytes, bytes_to_biguint},
313 test_vectors::field::DBL_TEST_VECTORS,
314 FieldBytes,
315 };
316
317 impl From<&BigUint> for FieldElement {
318 fn from(x: &BigUint) -> Self {
319 let bytes = biguint_to_bytes(x);
320 Self::from_bytes(&bytes.into()).unwrap()
321 }
322 }
323
324 impl ToBigUint for FieldElement {
325 fn to_biguint(&self) -> Option<BigUint> {
326 Some(bytes_to_biguint(self.to_bytes().as_ref()))
327 }
328 }
329
330 #[test]
331 fn zero_is_additive_identity() {
332 let zero = FieldElement::zero();
333 let one = FieldElement::one();
334 assert_eq!((zero + &zero).normalize(), zero);
335 assert_eq!((one + &zero).normalize(), one);
336 }
337
338 #[test]
339 fn one_is_multiplicative_identity() {
340 let one = FieldElement::one();
341 assert_eq!((one * &one).normalize(), one);
342 }
343
344 #[test]
345 fn from_bytes() {
346 assert_eq!(
347 FieldElement::from_bytes(&FieldBytes::default()).unwrap(),
348 FieldElement::zero()
349 );
350 assert_eq!(
351 FieldElement::from_bytes(
352 &[
353 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
354 0, 0, 0, 0, 0, 1
355 ]
356 .into()
357 )
358 .unwrap(),
359 FieldElement::one()
360 );
361 assert!(bool::from(
362 FieldElement::from_bytes(&[0xff; 32].into()).is_none()
363 ));
364 }
365
366 #[test]
367 fn to_bytes() {
368 assert_eq!(FieldElement::zero().to_bytes(), [0; 32].into());
369 assert_eq!(
370 FieldElement::one().to_bytes(),
371 [
372 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
373 0, 0, 0, 1
374 ]
375 .into()
376 );
377 }
378
379 #[test]
380 fn repeated_add() {
381 let mut r = FieldElement::one();
382 for i in 0..DBL_TEST_VECTORS.len() {
383 assert_eq!(r.to_bytes(), DBL_TEST_VECTORS[i].into());
384 r = (r + &r).normalize();
385 }
386 }
387
388 #[test]
389 fn repeated_double() {
390 let mut r = FieldElement::one();
391 for i in 0..DBL_TEST_VECTORS.len() {
392 assert_eq!(r.to_bytes(), DBL_TEST_VECTORS[i].into());
393 r = r.double().normalize();
394 }
395 }
396
397 #[test]
398 fn repeated_mul() {
399 let mut r = FieldElement::one();
400 let two = r + &r;
401 for i in 0..DBL_TEST_VECTORS.len() {
402 assert_eq!(r.normalize().to_bytes(), DBL_TEST_VECTORS[i].into());
403 r = r * &two;
404 }
405 }
406
407 #[test]
408 fn negation() {
409 let two = FieldElement::one().double();
410 let neg_two = two.negate(2);
411 assert_eq!((two + &neg_two).normalize(), FieldElement::zero());
412 assert_eq!(neg_two.negate(3).normalize(), two.normalize());
413 }
414
415 #[test]
416 fn invert() {
417 assert!(bool::from(FieldElement::zero().invert().is_none()));
418
419 let one = FieldElement::one();
420 assert_eq!(one.invert().unwrap().normalize(), one);
421
422 let two = one + &one;
423 let inv_two = two.invert().unwrap();
424 assert_eq!((two * &inv_two).normalize(), one);
425 }
426
427 #[test]
428 fn sqrt() {
429 let one = FieldElement::one();
430 let two = one + &one;
431 let four = two.square();
432 assert_eq!(four.sqrt().unwrap().normalize(), two.normalize());
433 }
434
435 prop_compose! {
436 fn field_element()(bytes in any::<[u8; 32]>()) -> FieldElement {
437 let mut res = bytes_to_biguint(&bytes);
438 let m = FieldElement::modulus_as_biguint();
439 if res >= m {
442 res -= m;
443 }
444 FieldElement::from(&res)
445 }
446 }
447
448 proptest! {
449
450 #[test]
451 fn fuzzy_add(
452 a in field_element(),
453 b in field_element()
454 ) {
455 let a_bi = a.to_biguint().unwrap();
456 let b_bi = b.to_biguint().unwrap();
457 let res_bi = (&a_bi + &b_bi) % FieldElement::modulus_as_biguint();
458 let res_ref = FieldElement::from(&res_bi);
459 let res_test = (&a + &b).normalize();
460 assert_eq!(res_test, res_ref);
461 }
462
463 #[test]
464 fn fuzzy_mul(
465 a in field_element(),
466 b in field_element()
467 ) {
468 let a_bi = a.to_biguint().unwrap();
469 let b_bi = b.to_biguint().unwrap();
470 let res_bi = (&a_bi * &b_bi) % FieldElement::modulus_as_biguint();
471 let res_ref = FieldElement::from(&res_bi);
472 let res_test = (&a * &b).normalize();
473 assert_eq!(res_test, res_ref);
474 }
475
476 #[test]
477 fn fuzzy_square(
478 a in field_element()
479 ) {
480 let a_bi = a.to_biguint().unwrap();
481 let res_bi = (&a_bi * &a_bi) % FieldElement::modulus_as_biguint();
482 let res_ref = FieldElement::from(&res_bi);
483 let res_test = a.square().normalize();
484 assert_eq!(res_test, res_ref);
485 }
486
487 #[test]
488 fn fuzzy_negate(
489 a in field_element()
490 ) {
491 let m = FieldElement::modulus_as_biguint();
492 let a_bi = a.to_biguint().unwrap();
493 let res_bi = (&m - &a_bi) % &m;
494 let res_ref = FieldElement::from(&res_bi);
495 let res_test = a.negate(1).normalize();
496 assert_eq!(res_test, res_ref);
497 }
498
499 #[test]
500 fn fuzzy_sqrt(
501 a in field_element()
502 ) {
503 let m = FieldElement::modulus_as_biguint();
504 let a_bi = a.to_biguint().unwrap();
505 let sqr_bi = (&a_bi * &a_bi) % &m;
506 let sqr = FieldElement::from(&sqr_bi);
507
508 let res_ref1 = a;
509 let possible_sqrt = (&m - &a_bi) % &m;
510 let res_ref2 = FieldElement::from(&possible_sqrt);
511 let res_test = sqr.sqrt().unwrap().normalize();
512 assert!(res_test == res_ref1 || res_test == res_ref2);
514 }
515
516 #[test]
517 fn fuzzy_invert(
518 a in field_element()
519 ) {
520 let a = if bool::from(a.is_zero()) { FieldElement::one() } else { a };
521 let a_bi = a.to_biguint().unwrap();
522 let inv = a.invert().unwrap().normalize();
523 let inv_bi = inv.to_biguint().unwrap();
524 let m = FieldElement::modulus_as_biguint();
525 assert_eq!((&inv_bi * &a_bi) % &m, 1.to_biguint().unwrap());
526 }
527 }
528}