use crate::ec::p521::constants::{
p521_bytes_to_limbs, p521_limbs_to_bytes, P521_FIELD_ELEMENT_SIZE, P521_LIMBS,
};
use crate::error::{Error, Result};
use subtle::{Choice, ConditionallySelectable};
use zeroize::Zeroize;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FieldElement(pub(crate) [u32; P521_LIMBS]);
impl Zeroize for FieldElement {
fn zeroize(&mut self) {
self.0.zeroize();
}
}
impl FieldElement {
pub(crate) const MOD_LIMBS: [u32; P521_LIMBS] = [
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0x0000_01FF, ];
pub(crate) const A_M3: [u32; P521_LIMBS] = [
0xFFFF_FFFC,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0xFFFF_FFFF,
0x0000_01FF,
];
#[inline]
pub fn zero() -> Self {
FieldElement([0u32; P521_LIMBS])
}
#[inline]
pub fn one() -> Self {
let mut limbs = [0u32; P521_LIMBS];
limbs[0] = 1;
Self(limbs)
}
}
impl FieldElement {
pub fn from_bytes(bytes: &[u8; P521_FIELD_ELEMENT_SIZE]) -> Result<Self> {
let limbs = p521_bytes_to_limbs(bytes);
let fe = FieldElement(limbs);
if !fe.is_valid() {
return Err(Error::param("FieldElement P-521", "Value >= modulus"));
}
Ok(fe)
}
pub fn to_bytes(&self) -> [u8; P521_FIELD_ELEMENT_SIZE] {
p521_limbs_to_bytes(&self.0)
}
#[inline(always)]
pub fn is_zero(&self) -> bool {
self.0.iter().all(|&w| w == 0)
}
#[inline(always)]
pub fn is_odd(&self) -> bool {
(self.0[0] & 1) == 1
}
#[inline(always)]
pub fn is_valid(&self) -> bool {
let (_, borrow) = Self::sbb_n(self.0, Self::MOD_LIMBS);
borrow == 1 }
}
impl FieldElement {
#[inline(always)]
pub(crate) fn adc_n<const N: usize>(a: [u32; N], b: [u32; N]) -> ([u32; N], u32) {
let mut out = [0u32; N];
let mut carry = 0u64;
for i in 0..N {
let t = a[i] as u64 + b[i] as u64 + carry;
out[i] = t as u32;
carry = t >> 32;
}
(out, carry as u32)
}
#[inline(always)]
pub(crate) fn sbb_n<const N: usize>(a: [u32; N], b: [u32; N]) -> ([u32; N], u32) {
let mut out = [0u32; N];
let mut borrow = 0i64;
for i in 0..N {
let t = a[i] as i64 - b[i] as i64 - borrow;
out[i] = t as u32;
borrow = (t >> 63) & 1; }
(out, borrow as u32)
}
#[inline(always)]
fn conditional_select(a: &[u32; P521_LIMBS], b: &[u32; P521_LIMBS], flag: Choice) -> Self {
let mut out = [0u32; P521_LIMBS];
for i in 0..P521_LIMBS {
out[i] = u32::conditional_select(&a[i], &b[i], flag);
}
FieldElement(out)
}
#[inline(always)]
pub fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
for i in 0..P521_LIMBS {
let tmp = u32::conditional_select(&a.0[i], &b.0[i], choice);
b.0[i] = u32::conditional_select(&b.0[i], &a.0[i], choice);
a.0[i] = tmp;
}
}
}
impl FieldElement {
fn reduce_wide(t: [u32; 34]) -> Self {
let mut acc = [0u64; 18]; let mut c: u64 = 0;
for i in 0..17 {
let l = t[i] as u64;
let h = t[17 + i] as u64;
let lo = (h << 23) & 0xFFFF_FFFF;
let hi = h >> 9;
let tmp = l + lo + c;
acc[i] = tmp & 0xFFFF_FFFF;
c = (tmp >> 32) + hi; }
acc[17] = c;
let top = acc[17];
if top != 0 {
let tmp = acc[0] + ((top << 23) & 0xFFFF_FFFF);
acc[0] = tmp & 0xFFFF_FFFF;
let mut c = (tmp >> 32) + (top >> 9);
for i in 1..=17 {
let idx = i % 17;
let tmp = acc[idx] + c;
acc[idx] = tmp & 0xFFFF_FFFF;
c = tmp >> 32;
if c == 0 {
break;
}
}
acc[17] = 0; }
let extra = acc[16] >> 9; acc[16] &= 0x1FF;
if extra != 0 {
let mut i = 0;
let mut c = extra;
loop {
let tmp = acc[i] + c;
acc[i] = tmp & 0xFFFF_FFFF;
c = tmp >> 32;
if c == 0 {
break;
}
i = (i + 1) % 17;
}
}
let mut limbs = [0u32; 17];
for i in 0..17 {
limbs[i] = acc[i] as u32;
}
let (sub, borrow) = Self::sbb_n(limbs, Self::MOD_LIMBS);
Self::conditional_select(&limbs, &sub, Choice::from((borrow ^ 1) as u8))
}
}
impl FieldElement {
pub fn add(&self, other: &Self) -> Self {
let (sum, carry) = Self::adc_n(self.0, other.0);
let (sub, borrow) = Self::sbb_n(sum, Self::MOD_LIMBS);
let need_sub = Choice::from(((carry | (borrow ^ 1)) & 1) as u8);
Self::conditional_select(&sum, &sub, need_sub)
}
pub fn sub(&self, other: &Self) -> Self {
let (diff, borrow) = Self::sbb_n(self.0, other.0);
let (sum, _) = Self::adc_n(diff, Self::MOD_LIMBS);
Self::conditional_select(&diff, &sum, Choice::from(borrow as u8))
}
pub fn mul(&self, other: &Self) -> Self {
let mut wide = [0u128; 34];
for i in 0..17 {
for j in 0..17 {
wide[i + j] += (self.0[i] as u128) * (other.0[j] as u128);
}
}
let mut limbs = [0u32; 34];
let mut carry: u128 = 0;
for i in 0..34 {
let v = wide[i] + carry;
limbs[i] = (v & 0xFFFF_FFFF) as u32;
carry = v >> 32;
}
if carry != 0 {
let extra = carry as u32;
let (new, of) = limbs[0].overflowing_add(extra);
limbs[0] = new;
if of {
let mut k = 1;
while k < 17 {
let (n, o) = limbs[k].overflowing_add(1);
limbs[k] = n;
if !o {
break;
}
k += 1;
}
}
}
Self::reduce_wide(limbs)
}
#[inline(always)]
pub fn square(&self) -> Self {
self.mul(self)
}
pub fn invert(&self) -> Result<Self> {
if self.is_zero() {
return Err(Error::param("FieldElement P-521", "Inverse of zero"));
}
let mut exp = [0u8; P521_FIELD_ELEMENT_SIZE];
exp[0] = 0x01;
for byte in exp.iter_mut().skip(1) {
*byte = 0xFF;
}
let mut borrow = 2u16;
for i in (0..66).rev() {
let v = exp[i] as i16 - borrow as i16;
exp[i] = if v < 0 { (v + 256) as u8 } else { v as u8 };
borrow = if v < 0 { 1 } else { 0 };
}
let mut result = FieldElement::one();
let base = self.clone();
for byte in exp.iter() {
for bit in (0..8).rev() {
result = result.square();
if (byte >> bit) & 1 == 1 {
result = result.mul(&base);
}
}
}
Ok(result)
}
pub fn sqrt(&self) -> Option<Self> {
if self.is_zero() {
return Some(Self::zero());
}
let mut res = self.clone();
for _ in 0..519 {
res = res.square();
}
if res.square() == *self {
Some(res)
} else {
None
}
}
pub(crate) fn get_modulus() -> Self {
FieldElement(Self::MOD_LIMBS)
}
}