use crate::ec::p521::constants::{P521_FIELD_ELEMENT_SIZE, P521_LIMBS};
use crate::error::{Error, Result};
use dcrypt_internal::constant_time::{Choice, ConditionallySelectable};
use dcrypt_internal::zeroing::{Zeroize, Zeroizing};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FieldElement(pub(crate) [u32; P521_LIMBS]);
impl Default for FieldElement {
fn default() -> Self {
Self::zero()
}
}
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 = Zeroizing::new([0u32; P521_LIMBS]);
limbs[0] = 1;
Self(*limbs)
}
}
impl FieldElement {
pub fn from_bytes(bytes: &[u8; P521_FIELD_ELEMENT_SIZE]) -> Result<Self> {
let mut limbs = Zeroizing::new([0u32; P521_LIMBS]);
for i in 0..16 {
let offset = P521_FIELD_ELEMENT_SIZE - 4 - i * 4;
limbs[i] = ((bytes[offset] as u32) << 24)
| ((bytes[offset + 1] as u32) << 16)
| ((bytes[offset + 2] as u32) << 8)
| bytes[offset + 3] as u32;
}
limbs[16] = ((bytes[0] as u32) << 8) | bytes[1] as u32;
let fe = Zeroizing::new(FieldElement(*limbs));
if !fe.is_valid() {
return Err(Error::param("FieldElement P-521", "Value >= modulus"));
}
Ok(fe.into_inner())
}
pub fn to_bytes(&self) -> [u8; P521_FIELD_ELEMENT_SIZE] {
let mut bytes = [0u8; P521_FIELD_ELEMENT_SIZE];
self.write_bytes(&mut bytes);
bytes
}
pub(crate) fn write_bytes(&self, bytes: &mut [u8]) {
debug_assert_eq!(bytes.len(), P521_FIELD_ELEMENT_SIZE);
for (i, &limb) in self.0.iter().take(16).enumerate() {
let offset = P521_FIELD_ELEMENT_SIZE - 4 - i * 4;
bytes[offset] = (limb >> 24) as u8;
bytes[offset + 1] = (limb >> 16) as u8;
bytes[offset + 2] = (limb >> 8) as u8;
bytes[offset + 3] = limb as u8;
}
let most_significant = self.0[16] & 0x1ff;
bytes[0] = (most_significant >> 8) as u8;
bytes[1] = most_significant as u8;
}
#[inline(always)]
pub fn is_zero(&self) -> bool {
let mut any = 0u32;
for &limb in &self.0 {
any |= limb;
}
any == 0
}
#[inline(always)]
pub fn is_odd(&self) -> bool {
(self.0[0] & 1) == 1
}
#[inline(always)]
pub fn is_valid(&self) -> bool {
let (_difference, 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]) -> (Zeroizing<[u32; N]>, u32) {
let mut out = Zeroizing::new([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]) -> (Zeroizing<[u32; N]>, u32) {
let mut out = Zeroizing::new([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)]
pub(crate) fn conditional_select(a: &Self, b: &Self, flag: Choice) -> Self {
Self::select_limbs(&a.0, &b.0, flag)
}
#[inline(never)]
fn select_limbs(a: &[u32; P521_LIMBS], b: &[u32; P521_LIMBS], flag: Choice) -> Self {
let mut out = Zeroizing::new([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 mut 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;
tmp.zeroize();
}
}
}
impl FieldElement {
fn reduce_wide(t: &[u32; 34]) -> Self {
let mut first = Zeroizing::new([0u32; 18]);
let mut carry = 0u64;
for i in 0..16 {
let high = ((t[i + 16] >> 9) | (t[i + 17] << 23)) as u64;
let value = t[i] as u64 + high + carry;
first[i] = value as u32;
carry = value >> 32;
}
let high_16 = ((t[32] >> 9) | (t[33] << 23)) as u64;
let value_16 = (t[16] & 0x1ff) as u64 + high_16 + carry;
first[16] = value_16 as u32;
carry = value_16 >> 32;
let value_17 = ((t[33] as u64) >> 9) + carry;
first[17] = value_17 as u32;
let extra = ((first[16] >> 9) as u64) | ((first[17] as u64) << 23);
let mut limbs = Zeroizing::new([0u32; P521_LIMBS]);
carry = extra;
for i in 0..P521_LIMBS {
let low = if i == 16 { first[i] & 0x1ff } else { first[i] };
let value = low as u64 + carry;
limbs[i] = value as u32;
carry = value >> 32;
}
let (sub, borrow) = Self::sbb_n(&limbs, &Self::MOD_LIMBS);
Self::select_limbs(&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::select_limbs(&sum, &sub, need_sub)
}
pub fn sub(&self, other: &Self) -> Self {
let (diff, borrow) = Self::sbb_n(&self.0, &other.0);
let (sum, _carry) = Self::adc_n(&diff, &Self::MOD_LIMBS);
Self::select_limbs(&diff, &sum, Choice::from(borrow as u8))
}
pub fn mul(&self, other: &Self) -> Self {
let mut wide = Zeroizing::new([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 = Zeroizing::new([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;
}
let _ = carry;
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 = Zeroizing::new([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 = Zeroizing::new(FieldElement::one());
let base = Zeroizing::new(self.clone());
for byte in exp.iter() {
for bit in (0..8).rev() {
let squared = Zeroizing::new(result.square());
result.zeroize();
*result = squared.into_inner();
if (byte >> bit) & 1 == 1 {
let next = Zeroizing::new(result.mul(&base));
result.zeroize();
*result = next.into_inner();
}
}
}
Ok(result.into_inner())
}
pub fn sqrt(&self) -> Option<Self> {
if self.is_zero() {
return Some(Self::zero());
}
let mut res = Zeroizing::new(self.clone());
for _ in 0..519 {
let squared = Zeroizing::new(res.square());
res.zeroize();
*res = squared.into_inner();
}
let verification = Zeroizing::new(res.square());
if *verification == *self {
Some(res.into_inner())
} else {
None
}
}
pub(crate) fn get_modulus() -> Self {
FieldElement(Self::MOD_LIMBS)
}
}