halo2curves_axiom/secp256r1/
fq.rsuse crate::arithmetic::{adc, mac, macx, sbb};
use crate::extend_field_legendre;
use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup};
use core::fmt;
use core::ops::{Add, Mul, Neg, Sub};
use rand::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Fq(pub(crate) [u64; 4]);
#[cfg(feature = "derive_serde")]
crate::serialize_deserialize_32_byte_primefield!(Fq);
const MODULUS: Fq = Fq([
0xf3b9cac2fc632551,
0xbce6faada7179e84,
0xffffffffffffffff,
0xffffffff00000000,
]);
#[cfg(not(target_pointer_width = "64"))]
const MODULUS_LIMBS_32: [u32; 8] = [
0xfc63_2551,
0xf3b9_cac2,
0xa717_9e84,
0xbce6_faad,
0xffff_ffff,
0xffff_ffff,
0x0000_0000,
0xffff_ffff,
];
const MODULUS_STR: &str = "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551";
const INV: u64 = 0xccd1c8aaee00bc4f;
const R: Fq = Fq([
0x0c46353d039cdaaf,
0x4319055258e8617b,
0x0000000000000000,
0xffffffff,
]);
const R2: Fq = Fq([
0x83244c95be79eea2,
0x4699799c49bd6fa6,
0x2845b2392b6bec59,
0x66e12d94f3d95620,
]);
const R3: Fq = Fq([
0xac8ebec90b65a624,
0x111f28ae0c0555c9,
0x2543b9246ba5e93f,
0x503a54e76407be65,
]);
const GENERATOR: Fq = Fq::from_raw([0x07, 0x00, 0x00, 0x00]);
const ROOT_OF_UNITY: Fq = Fq::from_raw([
0x0592d7fbb41e6602,
0x1546cad004378daf,
0xba807ace842a3dfc,
0xffc97f062a770992,
]);
const ROOT_OF_UNITY_INV: Fq = Fq::from_raw([
0x379c7f0657c73764,
0xe3ac117c794c4137,
0xc645fa0458131cae,
0xa0a66a5562d46f2a,
]);
const TWO_INV: Fq = Fq::from_raw([
0x79dce5617e3192a9,
0xde737d56d38bcf42,
0x7fffffffffffffff,
0x7fffffff80000000,
]);
const ZETA: Fq = Fq::from_raw([
0x7cbf87ff12884e21,
0x9405335ce9c83e1d,
0x4e786d0777fd6aef,
0x52891d43d946a035,
]);
const DELTA: Fq = Fq::from_raw([0x1e39a5057d81, 0, 0, 0]);
use crate::{
field_arithmetic, field_common, field_specific, impl_add_binop_specify_output,
impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative,
impl_binops_multiplicative_mixed, impl_from_u64, impl_sub_binop_specify_output, impl_sum_prod,
};
impl_binops_additive!(Fq, Fq);
impl_binops_multiplicative!(Fq, Fq);
field_common!(
Fq,
MODULUS,
INV,
MODULUS_STR,
TWO_INV,
ROOT_OF_UNITY_INV,
DELTA,
ZETA,
R,
R2,
R3
);
impl_from_u64!(Fq, R2);
field_arithmetic!(Fq, MODULUS, INV, dense);
impl_sum_prod!(Fq);
impl Fq {
pub const fn size() -> usize {
32
}
}
impl ff::Field for Fq {
const ZERO: Self = Self::zero();
const ONE: Self = Self::one();
fn random(mut rng: impl RngCore) -> Self {
Self::from_u512([
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
])
}
fn double(&self) -> Self {
self.double()
}
#[inline(always)]
fn square(&self) -> Self {
self.square()
}
fn invert(&self) -> CtOption<Self> {
self.invert()
}
fn pow_vartime<S: AsRef<[u64]>>(&self, exp: S) -> Self {
let mut res = Self::one();
let mut found_one = false;
for e in exp.as_ref().iter().rev() {
for i in (0..64).rev() {
if found_one {
res = res.square();
}
if ((*e >> i) & 1) == 1 {
found_one = true;
res *= self;
}
}
}
res
}
fn sqrt(&self) -> CtOption<Self> {
let tm1d2 = [
0x279dce5617e3192a,
0xfde737d56d38bcf4,
0x07ffffffffffffff,
0x7fffffff8000000,
];
ff::helpers::sqrt_tonelli_shanks(self, tm1d2)
}
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
ff::helpers::sqrt_ratio_generic(num, div)
}
}
impl ff::PrimeField for Fq {
type Repr = [u8; 32];
const NUM_BITS: u32 = 256;
const CAPACITY: u32 = 255;
const MODULUS: &'static str = MODULUS_STR;
const MULTIPLICATIVE_GENERATOR: Self = GENERATOR;
const ROOT_OF_UNITY: Self = ROOT_OF_UNITY;
const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV;
const TWO_INV: Self = TWO_INV;
const DELTA: Self = DELTA;
const S: u32 = 4;
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
let mut tmp = Fq([0, 0, 0, 0]);
tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap());
tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap());
tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap());
tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap());
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
let is_some = (borrow as u8) & 1;
tmp *= &R2;
CtOption::new(tmp, Choice::from(is_some))
}
fn to_repr(&self) -> Self::Repr {
let tmp: [u64; 4] = (*self).into();
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp[3].to_le_bytes());
res
}
fn is_odd(&self) -> Choice {
Choice::from(self.to_repr()[0] & 1)
}
}
impl FromUniformBytes<64> for Fq {
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
Self::from_u512([
u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
u64::from_le_bytes(bytes[32..40].try_into().unwrap()),
u64::from_le_bytes(bytes[40..48].try_into().unwrap()),
u64::from_le_bytes(bytes[48..56].try_into().unwrap()),
u64::from_le_bytes(bytes[56..64].try_into().unwrap()),
])
}
}
impl WithSmallOrderMulGroup<3> for Fq {
const ZETA: Self = ZETA;
}
extend_field_legendre!(Fq);
#[cfg(test)]
mod test {
use super::*;
use ff::Field;
use rand_core::OsRng;
#[test]
fn test_zeta() {
assert_eq!(Fq::ZETA * Fq::ZETA * Fq::ZETA, Fq::ONE);
assert_ne!(Fq::ZETA * Fq::ZETA, Fq::ONE);
}
#[test]
fn test_sqrt() {
for _ in 0..10000 {
let a = Fq::random(OsRng);
let mut b = a;
b = b.square();
let b = b.sqrt().unwrap();
let mut negb = b;
negb = negb.neg();
assert!(a == b || a == negb);
}
}
#[test]
fn test_constants() {
assert_eq!(
Fq::MODULUS,
"0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
);
assert_eq!(Fq::from(2) * Fq::TWO_INV, Fq::ONE);
}
#[test]
fn test_delta() {
assert_eq!(Fq::DELTA, Fq::MULTIPLICATIVE_GENERATOR.pow([1u64 << Fq::S]));
}
#[test]
fn test_root_of_unity() {
assert_eq!(Fq::ROOT_OF_UNITY.pow_vartime([1 << Fq::S]), Fq::one());
}
#[test]
fn test_inv_root_of_unity() {
assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::ROOT_OF_UNITY.invert().unwrap());
}
#[test]
fn test_field() {
crate::tests::field::random_field_tests::<Fq>("secp256r1 scalar".to_string());
}
#[test]
fn test_conversion() {
crate::tests::field::random_conversion_tests::<Fq>("secp256r1 scalar".to_string());
}
#[test]
fn test_serialization() {
crate::tests::field::random_serialization_test::<Fq>("secp256r1 scalar".to_string());
#[cfg(feature = "derive_serde")]
crate::tests::field::random_serde_test::<Fq>("secp256r1 scalar".to_string());
}
#[test]
fn test_quadratic_residue() {
crate::tests::field::random_quadratic_residue_test::<Fq>();
}
}