use fiat_crypto::curve25519_64::{
fiat_25519_add, fiat_25519_carry, fiat_25519_carry_mul, fiat_25519_carry_square,
fiat_25519_from_bytes, fiat_25519_loose_field_element, fiat_25519_opp, fiat_25519_sub,
fiat_25519_tight_field_element, fiat_25519_to_bytes,
};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
#[derive(Clone, Copy)]
pub struct Fe25519(fiat_25519_tight_field_element);
impl core::fmt::Debug for Fe25519 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Fe25519({:?})", self.to_bytes())
}
}
impl Default for Fe25519 {
fn default() -> Self {
Self::ZERO
}
}
impl ConstantTimeEq for Fe25519 {
fn ct_eq(&self, other: &Self) -> Choice {
let a = self.to_bytes();
let b = other.to_bytes();
a.ct_eq(&b)
}
}
impl ConditionallySelectable for Fe25519 {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut result = fiat_25519_tight_field_element([0u64; 5]);
for i in 0..5 {
result.0[i] = u64::conditional_select(&a.0 .0[i], &b.0 .0[i], choice);
}
Fe25519(result)
}
}
impl Fe25519 {
pub const ZERO: Fe25519 = Fe25519(fiat_25519_tight_field_element([0, 0, 0, 0, 0]));
pub const ONE: Fe25519 = Fe25519(fiat_25519_tight_field_element([1, 0, 0, 0, 0]));
pub const CURVE25519_A: Fe25519 = Fe25519(fiat_25519_tight_field_element([486662, 0, 0, 0, 0]));
pub const SQRT_AM2: Fe25519 = Fe25519(fiat_25519_tight_field_element([
1693982333959686,
608509411481997,
2235573344831311,
947681270984193,
266558006233600,
]));
pub const SQRT_M1: Fe25519 = Fe25519(fiat_25519_tight_field_element([
1718705420411056,
234908883556509,
2233514472574048,
2117202627021982,
765476049583133,
]));
#[inline]
pub fn from_bytes(bytes: &[u8; 32]) -> Self {
let mut result = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_from_bytes(&mut result, bytes);
Fe25519(result)
}
#[inline]
pub fn to_bytes(&self) -> [u8; 32] {
let mut result = [0u8; 32];
fiat_25519_to_bytes(&mut result, &self.0);
result
}
#[inline]
pub fn is_zero(&self) -> Choice {
self.ct_eq(&Self::ZERO)
}
pub fn is_negative(&self) -> Choice {
let bytes = self.to_bytes();
Choice::from(bytes[0] & 1)
}
#[inline]
pub fn add(&self, other: &Self) -> Self {
let mut loose = fiat_25519_loose_field_element([0u64; 5]);
fiat_25519_add(&mut loose, &self.0, &other.0);
let mut result = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_carry(&mut result, &loose);
Fe25519(result)
}
#[inline]
pub fn sub(&self, other: &Self) -> Self {
let mut loose = fiat_25519_loose_field_element([0u64; 5]);
fiat_25519_sub(&mut loose, &self.0, &other.0);
let mut result = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_carry(&mut result, &loose);
Fe25519(result)
}
#[inline]
pub fn neg(&self) -> Self {
let mut loose = fiat_25519_loose_field_element([0u64; 5]);
fiat_25519_opp(&mut loose, &self.0);
let mut result = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_carry(&mut result, &loose);
Fe25519(result)
}
#[inline]
fn as_loose(&self) -> fiat_25519_loose_field_element {
fiat_25519_loose_field_element(self.0 .0)
}
#[inline]
pub fn mul(&self, other: &Self) -> Self {
let mut result = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_carry_mul(&mut result, &self.as_loose(), &other.as_loose());
Fe25519(result)
}
#[inline]
pub fn square(&self) -> Self {
let mut result = fiat_25519_tight_field_element([0u64; 5]);
fiat_25519_carry_square(&mut result, &self.as_loose());
Fe25519(result)
}
#[inline]
pub fn sq2(&self) -> Self {
self.square().add(&self.square())
}
fn pow_internal(&self, mut n: [u64; 4]) -> Self {
let mut result = Self::ONE;
let mut base = *self;
for _ in 0..256 {
if n[0] & 1 == 1 {
result = result.mul(&base);
}
base = base.square();
n[0] = (n[0] >> 1) | (n[1] << 63);
n[1] = (n[1] >> 1) | (n[2] << 63);
n[2] = (n[2] >> 1) | (n[3] << 63);
n[3] >>= 1;
}
result
}
pub fn invert(&self) -> Self {
let exp: [u64; 4] = [
0xffffffffffffffeb,
0xffffffffffffffff,
0xffffffffffffffff,
0x7fffffffffffffff,
];
self.pow_internal(exp)
}
pub fn is_square(&self) -> Choice {
let exp: [u64; 4] = [
0xfffffffffffffff6,
0xffffffffffffffff,
0xffffffffffffffff,
0x3fffffffffffffff,
];
let result = self.pow_internal(exp);
result.ct_eq(&Self::ONE)
}
pub fn is_not_square(&self) -> Choice {
!self.is_square()
}
pub fn sqrt(&self) -> (Choice, Self) {
let exp: [u64; 4] = [
0xfffffffffffffffe,
0xffffffffffffffff,
0xffffffffffffffff,
0x0fffffffffffffff,
];
let beta = self.pow_internal(exp); let beta_sq = beta.square();
let is_correct = beta_sq.ct_eq(self);
let neg_self = self.neg();
let needs_sqrt_m1 = beta_sq.ct_eq(&neg_self);
let sqrt_m1_beta = beta.mul(&Self::SQRT_M1);
let result = Self::conditional_select(&beta, &sqrt_m1_beta, needs_sqrt_m1);
let exists = is_correct | needs_sqrt_m1;
(exists, result)
}
pub fn cmov(&mut self, other: &Self, choice: Choice) {
*self = Self::conditional_select(self, other, choice);
}
pub fn conditional_negate(&mut self, choice: Choice) {
let negated = self.neg();
self.cmov(&negated, choice);
}
pub fn abs(&self) -> Self {
let negated = self.neg();
Self::conditional_select(self, &negated, self.is_negative())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zero_one() {
let zero = Fe25519::ZERO;
let one = Fe25519::ONE;
assert!(bool::from(zero.is_zero()));
assert!(!bool::from(one.is_zero()));
let zero_bytes = zero.to_bytes();
let one_bytes = one.to_bytes();
assert_eq!(zero_bytes, [0u8; 32]);
assert_eq!(one_bytes[0], 1);
for byte in &one_bytes[1..32] {
assert_eq!(*byte, 0);
}
}
#[test]
fn test_add_sub() {
let a = Fe25519::from_bytes(&[42u8; 32]);
let b = Fe25519::from_bytes(&[17u8; 32]);
let c = a.add(&b);
let d = c.sub(&b);
assert!(bool::from(d.ct_eq(&a)));
}
#[test]
fn test_mul() {
let two = Fe25519::ONE.add(&Fe25519::ONE);
let four = two.mul(&two);
let also_four = two.add(&two);
assert!(bool::from(four.ct_eq(&also_four)));
}
#[test]
fn test_square() {
let three = Fe25519::ONE.add(&Fe25519::ONE).add(&Fe25519::ONE);
let nine_mul = three.mul(&three);
let nine_sq = three.square();
assert!(bool::from(nine_mul.ct_eq(&nine_sq)));
}
#[test]
fn test_invert() {
let a = Fe25519::from_bytes(&[42u8; 32]);
let a_inv = a.invert();
let product = a.mul(&a_inv);
assert!(bool::from(product.ct_eq(&Fe25519::ONE)));
}
#[test]
fn test_sqrt() {
let four = Fe25519::ONE
.add(&Fe25519::ONE)
.add(&Fe25519::ONE)
.add(&Fe25519::ONE);
let (exists, sqrt_four) = four.sqrt();
assert!(bool::from(exists));
let should_be_four = sqrt_four.square();
assert!(bool::from(should_be_four.ct_eq(&four)));
}
#[test]
fn test_is_negative() {
assert!(bool::from(Fe25519::ONE.is_negative()));
let two = Fe25519::ONE.add(&Fe25519::ONE);
assert!(!bool::from(two.is_negative()));
}
#[test]
fn test_curve25519_a() {
let a = Fe25519::CURVE25519_A;
let a_bytes = a.to_bytes();
assert_eq!(a_bytes[0], 0x06);
assert_eq!(a_bytes[1], 0x6D);
assert_eq!(a_bytes[2], 0x07);
for byte in &a_bytes[3..32] {
assert_eq!(*byte, 0);
}
}
}