use super::arith::Field;
use ic_core::ct::Choice;
pub trait Curve: Sized {
type Field: Field;
type Scalar: Field;
const NAME: &'static str;
const FIELD_BYTES: usize;
const SCALAR_BYTES: usize;
const ORDER_BITS: usize;
const B: Self::Field;
const GX: Self::Field;
const GY: Self::Field;
fn sqrt(x: &Self::Field) -> Self::Field;
fn field_from_slice(bytes: &[u8]) -> Option<Self::Field>;
fn scalar_from_slice(bytes: &[u8]) -> Option<Self::Scalar>;
fn scalar_reduce_slice(bytes: &[u8]) -> Self::Scalar;
}
pub struct Point<C: Curve> {
x: C::Field,
y: C::Field,
z: C::Field,
}
impl<C: Curve> Clone for Point<C> {
fn clone(&self) -> Self {
*self
}
}
impl<C: Curve> Copy for Point<C> {}
pub struct AffinePoint<C: Curve> {
pub x: C::Field,
pub y: C::Field,
}
impl<C: Curve> Clone for AffinePoint<C> {
fn clone(&self) -> Self {
*self
}
}
impl<C: Curve> Copy for AffinePoint<C> {}
impl<C: Curve> Point<C> {
pub fn identity() -> Self {
Point {
x: C::Field::ONE,
y: C::Field::ONE,
z: C::Field::ZERO,
}
}
pub fn generator() -> Self {
Point {
x: C::GX,
y: C::GY,
z: C::Field::ONE,
}
}
pub fn from_affine(p: &AffinePoint<C>) -> Self {
Point {
x: p.x,
y: p.y,
z: C::Field::ONE,
}
}
#[inline]
pub fn is_identity(&self) -> Choice {
self.z.is_zero()
}
pub fn double(&self) -> Self {
let delta = self.z.square();
let gamma = self.y.square();
let beta = self.x.mul(&gamma);
let alpha = self.x.sub(&delta).mul(&self.x.add(&delta)).triple();
let beta4 = beta.double().double();
let beta8 = beta4.double();
let x3 = alpha.square().sub(&beta8);
let z3 = self.y.add(&self.z).square().sub(&gamma).sub(&delta);
let gamma2_8 = gamma.square().double().double().double();
let y3 = alpha.mul(&beta4.sub(&x3)).sub(&gamma2_8);
Point {
x: x3,
y: y3,
z: z3,
}
}
fn add_raw(&self, other: &Self) -> (Self, Choice, Choice) {
let z1z1 = self.z.square();
let z2z2 = other.z.square();
let u1 = self.x.mul(&z2z2);
let u2 = other.x.mul(&z1z1);
let s1 = self.y.mul(&other.z).mul(&z2z2);
let s2 = other.y.mul(&self.z).mul(&z1z1);
let h = u2.sub(&u1);
let r = s2.sub(&s1).double();
let h_is_zero = h.is_zero();
let r_is_zero = r.is_zero();
let i = h.double().square();
let j = h.mul(&i);
let v = u1.mul(&i);
let x3 = r.square().sub(&j).sub(&v.double());
let y3 = r.mul(&v.sub(&x3)).sub(&s1.mul(&j).double());
let z3 = self.z.add(&other.z).square().sub(&z1z1).sub(&z2z2).mul(&h);
(
Point {
x: x3,
y: y3,
z: z3,
},
h_is_zero,
r_is_zero,
)
}
pub fn add(&self, other: &Self) -> Self {
let (sum, h_zero, r_zero) = self.add_raw(other);
let doubled = self.double();
let self_inf = self.is_identity();
let other_inf = other.is_identity();
let same_point = h_zero.and(r_zero);
let opposite = h_zero.and(r_zero.not());
let mut result = sum;
Self::cmov(&mut result, &doubled, same_point);
Self::cmov(&mut result, &Self::identity(), opposite);
Self::cmov(&mut result, self, other_inf);
Self::cmov(&mut result, other, self_inf);
result
}
#[inline]
pub(crate) fn conditional_negate(&mut self, choice: Choice) {
let ny = self.y.neg();
<C::Field as Field>::cmov(&mut self.y, &ny, choice);
}
pub(crate) fn cmov(a: &mut Self, b: &Self, choice: Choice) {
C::Field::cmov(&mut a.x, &b.x, choice);
C::Field::cmov(&mut a.y, &b.y, choice);
C::Field::cmov(&mut a.z, &b.z, choice);
}
pub fn neg(&self) -> Self {
Point {
x: self.x,
y: self.y.neg(),
z: self.z,
}
}
pub fn mul_scalar(&self, scalar: &C::Scalar) -> Self {
let bytes = scalar.to_bytes();
let bytes = bytes.as_ref();
let mut acc = Self::identity();
for byte in bytes.iter() {
for bit in (0..8).rev() {
acc = acc.double();
let sum = acc.add(self);
let b = Choice::from_u8((byte >> bit) & 1);
Self::cmov(&mut acc, &sum, b);
}
}
acc
}
fn negate(&self) -> Self {
Self {
x: self.x,
y: self.y.neg(),
z: self.z,
}
}
pub fn mul_scalar_vartime(&self, scalar: &C::Scalar) -> Self {
let twice = self.double();
let mut odd = [*self; 8];
for i in 1..8 {
odd[i] = odd[i - 1].add(&twice);
}
let bytes = scalar.to_bytes();
let (naf, len) = wnaf5(bytes.as_ref());
let mut acc = Self::identity();
for i in (0..len).rev() {
acc = acc.double();
let digit = naf[i];
if digit != 0 {
let entry = &odd[(digit.unsigned_abs() as usize) / 2];
acc = if digit > 0 {
acc.add(entry)
} else {
acc.add(&entry.negate())
};
}
}
acc
}
pub fn mul_double(a: &C::Scalar, p: &Self, b: &C::Scalar) -> Self
where
C: super::gentable::HasGeneratorTable,
{
Self::mul_generator(a).add(&p.mul_scalar_vartime(b))
}
pub fn mul_generator(scalar: &C::Scalar) -> Self
where
C: super::gentable::HasGeneratorTable,
{
C::mul_generator(scalar)
}
}
const WNAF_LIMBS: usize = 10;
const WNAF_DIGITS: usize = 8 * 66 + 2;
fn wnaf5(bytes: &[u8]) -> ([i8; WNAF_DIGITS], usize) {
let mut naf = [0i8; WNAF_DIGITS];
let mut k = [0u64; WNAF_LIMBS];
for (i, byte) in bytes.iter().rev().enumerate() {
k[i / 8] |= (*byte as u64) << ((i % 8) * 8);
}
let mut i = 0;
while k.iter().any(|&x| x != 0) {
if k[0] & 1 == 1 {
let mut d = (k[0] & 0x1f) as i64;
if d >= 16 {
d -= 32;
}
naf[i] = d as i8;
if d > 0 {
wnaf_sub(&mut k, d as u64);
} else {
wnaf_add(&mut k, d.unsigned_abs());
}
}
wnaf_shr1(&mut k);
i += 1;
}
(naf, i)
}
fn wnaf_sub(k: &mut [u64; WNAF_LIMBS], v: u64) {
let (d, mut borrow) = k[0].overflowing_sub(v);
k[0] = d;
for limb in k.iter_mut().skip(1) {
if !borrow {
break;
}
let (d, b) = limb.overflowing_sub(1);
*limb = d;
borrow = b;
}
}
fn wnaf_add(k: &mut [u64; WNAF_LIMBS], v: u64) {
let (d, mut carry) = k[0].overflowing_add(v);
k[0] = d;
for limb in k.iter_mut().skip(1) {
if !carry {
break;
}
let (d, c) = limb.overflowing_add(1);
*limb = d;
carry = c;
}
}
fn wnaf_shr1(k: &mut [u64; WNAF_LIMBS]) {
for i in 0..WNAF_LIMBS - 1 {
k[i] = (k[i] >> 1) | (k[i + 1] << 63);
}
k[WNAF_LIMBS - 1] >>= 1;
}
impl<C: Curve> Point<C> {
pub fn to_affine(&self) -> Option<AffinePoint<C>> {
if bool::from(self.is_identity()) {
return None;
}
let z_inv = self.z.invert();
let z_inv2 = z_inv.square();
let z_inv3 = z_inv2.mul(&z_inv);
Some(AffinePoint {
x: self.x.mul(&z_inv2),
y: self.y.mul(&z_inv3),
})
}
pub fn ct_eq(&self, other: &Self) -> Choice {
let z1z1 = self.z.square();
let z2z2 = other.z.square();
let x_eq = self.x.mul(&z2z2).ct_eq(&other.x.mul(&z1z1));
let y_eq = self
.y
.mul(&z2z2.mul(&other.z))
.ct_eq(&other.y.mul(&z1z1.mul(&self.z)));
let both_inf = self.is_identity().and(other.is_identity());
let neither_inf = self.is_identity().or(other.is_identity()).not();
both_inf.or(neither_inf.and(x_eq).and(y_eq))
}
}
impl<C: Curve> AffinePoint<C> {
pub fn is_on_curve(&self) -> Choice {
let lhs = self.y.square();
let rhs = self
.x
.square()
.mul(&self.x)
.sub(&self.x.triple())
.add(&C::B);
lhs.ct_eq(&rhs)
}
#[must_use = "a false return means nothing was written"]
pub fn write_uncompressed(&self, out: &mut [u8]) -> bool {
if out.len() != 1 + 2 * C::FIELD_BYTES {
return false;
}
out[0] = 0x04;
out[1..1 + C::FIELD_BYTES].copy_from_slice(self.x.to_bytes().as_ref());
out[1 + C::FIELD_BYTES..].copy_from_slice(self.y.to_bytes().as_ref());
true
}
#[must_use = "a false return means nothing was written"]
pub fn write_compressed(&self, out: &mut [u8]) -> bool {
if out.len() != 1 + C::FIELD_BYTES {
return false;
}
out[0] = 0x02 | self.y.is_odd().unwrap_u8();
out[1..].copy_from_slice(self.x.to_bytes().as_ref());
true
}
pub fn from_sec1(bytes: &[u8]) -> Option<Self> {
let f = C::FIELD_BYTES;
if bytes.len() == 1 + 2 * f && bytes[0] == 0x04 {
let x = C::field_from_slice(&bytes[1..1 + f])?;
let y = C::field_from_slice(&bytes[1 + f..])?;
let p = AffinePoint { x, y };
return bool::from(p.is_on_curve()).then_some(p);
}
if bytes.len() == 1 + f && (bytes[0] == 0x02 || bytes[0] == 0x03) {
let x = C::field_from_slice(&bytes[1..])?;
let y2 = x.square().mul(&x).sub(&x.triple()).add(&C::B);
let y = C::sqrt(&y2);
if y.square() != y2 {
return None;
}
let want_odd = Choice::from_u8(bytes[0] & 1);
let flip = Choice::from_u8(y.is_odd().unwrap_u8() ^ want_odd.unwrap_u8());
let mut chosen = y;
C::Field::cmov(&mut chosen, &y.neg(), flip);
return Some(AffinePoint { x, y: chosen });
}
None
}
}