use super::gf2m163::FieldElement;
fn b() -> FieldElement {
FieldElement::from_be_bytes(&[
0x05, 0xFF, 0x61, 0x08, 0x46, 0x2A, 0x2D, 0xC8, 0x21, 0x0A, 0xB4, 0x03, 0x92, 0x5E, 0x63,
0x8A, 0x19, 0xC1, 0x45, 0x5D, 0x21,
])
}
fn gx() -> FieldElement {
FieldElement::from_be_bytes(&[
0x07, 0x2D, 0x86, 0x7F, 0x93, 0xA9, 0x3A, 0xC2, 0x7D, 0xF9, 0xFF, 0x01, 0xAF, 0xFE, 0x74,
0x88, 0x5C, 0x8C, 0x54, 0x04, 0x20,
])
}
fn gy() -> FieldElement {
FieldElement::from_be_bytes(&[
0x00, 0x22, 0x4A, 0x9C, 0x39, 0x47, 0x85, 0x2B, 0x97, 0xC5, 0x59, 0x9D, 0x5F, 0x4A, 0xB8,
0x11, 0x22, 0xAD, 0xC3, 0xFD, 0x9B,
])
}
#[must_use]
pub fn order() -> [u8; 21] {
[
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xBE, 0xC1, 0x2B, 0xE2,
0x26, 0x2D, 0x39, 0xBC, 0xF1, 0x4D,
]
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Point {
Infinity,
Affine(FieldElement, FieldElement),
}
impl Point {
#[must_use]
pub fn generator() -> Self {
Point::Affine(gx(), gy())
}
#[must_use]
pub fn negate(self) -> Self {
match self {
Point::Infinity => Point::Infinity,
Point::Affine(x, y) => Point::Affine(x, x + y),
}
}
#[must_use]
pub fn is_on_curve(self) -> bool {
match self {
Point::Infinity => false,
Point::Affine(x, y) => {
let lhs = y.square() + x.multiply(y);
let rhs = x.multiply(x.square()) + x.square() + b();
lhs == rhs
}
}
}
#[must_use]
pub fn double(self) -> Self {
match self {
Point::Infinity => Point::Infinity,
Point::Affine(x1, y1) => {
if x1 == FieldElement::ZERO {
return Point::Infinity;
}
let lambda = x1 + y1.multiply(x1.invert());
let x3 = lambda.square() + lambda + FieldElement::ONE;
let y3 = x1.square() + (lambda + FieldElement::ONE).multiply(x3);
Point::Affine(x3, y3)
}
}
}
#[must_use]
pub fn scalar_multiply(self, k: &[u8; 21]) -> Self {
match self {
Point::Infinity => Point::Infinity,
Point::Affine(x, y) => {
let mut x1 = FieldElement::ONE;
let mut z1 = FieldElement::ZERO; let mut x2 = x;
let mut z2 = FieldElement::ONE;
for i in (0..163u32).rev() {
let bit = bit_at(k, i);
let swap = bit ^ 1;
cswap(swap, &mut x1, &mut x2);
cswap(swap, &mut z1, &mut z2);
let t1 = z1;
z1 = (x1.multiply(z2) + x2.multiply(z1)).square();
x1 = x.multiply(z1) + x1.multiply(x2).multiply(t1).multiply(z2);
let t2 = x2;
x2 = x2.square().square() + b().multiply(z2.square().square());
z2 = t2.square().multiply(z2.square());
cswap(swap, &mut x1, &mut x2);
cswap(swap, &mut z1, &mut z2);
}
if is_zero_mask(z1) != 0 {
return Point::Infinity;
}
let x1_affine = x1.multiply(z1.invert());
let x2_affine = x2.multiply(z2.invert());
let t1 = x1_affine + x;
let t2 = x2_affine + x;
let inner = t1.multiply(t2) + x.square() + y;
let y1_affine_formula = x.invert().multiply(t1).multiply(inner) + y;
let y1_affine = select(is_zero_mask(z2), x + y, y1_affine_formula);
Point::Affine(x1_affine, y1_affine)
}
}
}
}
impl core::ops::Add for Point {
type Output = Self;
fn add(self, other: Self) -> Self {
match (self, other) {
(Point::Infinity, q) => q,
(p, Point::Infinity) => p,
(Point::Affine(x1, y1), Point::Affine(x2, y2)) => {
if x1 == x2 {
if y1 == y2 {
return self.double();
}
return Point::Infinity;
}
let lambda = (y1 + y2).multiply((x1 + x2).invert());
let x3 = lambda.square() + lambda + x1 + x2 + FieldElement::ONE;
let y3 = lambda.multiply(x1 + x3) + x3 + y1;
Point::Affine(x3, y3)
}
}
}
}
fn bit_at(bytes: &[u8; 21], i: u32) -> u64 {
let byte_index = 20 - (i / 8) as usize;
let bit_in_byte = i % 8;
u64::from((bytes[byte_index] >> bit_in_byte) & 1)
}
fn cswap(swap: u64, a: &mut FieldElement, b: &mut FieldElement) {
let mask = 0u64.wrapping_sub(swap);
for i in 0..3 {
let t = mask & (a.0[i] ^ b.0[i]);
a.0[i] ^= t;
b.0[i] ^= t;
}
}
fn is_zero_mask(a: FieldElement) -> u64 {
let combined = a.0[0] | a.0[1] | a.0[2];
let is_nonzero = (combined | combined.wrapping_neg()) >> 63;
0u64.wrapping_sub(1 ^ is_nonzero)
}
fn select(mask: u64, if_mask: FieldElement, otherwise: FieldElement) -> FieldElement {
let mut out = [0u64; 3];
for ((out_limb, a), b) in out.iter_mut().zip(if_mask.0.iter()).zip(otherwise.0.iter()) {
*out_limb = b ^ (mask & (a ^ b));
}
FieldElement(out)
}
#[cfg(not(feature = "small-tables"))]
#[derive(Clone, Copy)]
struct ProjectivePoint {
x: FieldElement,
y: FieldElement,
z: FieldElement,
}
#[cfg(not(feature = "small-tables"))]
impl ProjectivePoint {
fn from_affine(p: Point) -> Self {
match p {
Point::Infinity => ProjectivePoint {
x: FieldElement::ONE,
y: FieldElement::ONE,
z: FieldElement::ZERO,
},
Point::Affine(x, y) => ProjectivePoint {
x,
y,
z: FieldElement::ONE,
},
}
}
fn double(self) -> Self {
if self.z == FieldElement::ZERO {
return self; }
let a = self.z.square();
let big_b = b().multiply(a.square());
let c = self.x.square();
let z3 = a.multiply(c);
let x3 = c.square() + big_b;
let y3 = (self.y.square() + z3 + big_b).multiply(x3) + z3.multiply(big_b);
ProjectivePoint {
x: x3,
y: y3,
z: z3,
}
}
fn mixed_add(self, other: Point) -> Self {
let (x2, y2) = match other {
Point::Infinity => return self,
Point::Affine(x, y) => (x, y),
};
if self.z == FieldElement::ZERO {
return ProjectivePoint::from_affine(other);
}
let z1_sq = self.z.square();
let a = self.y + y2.multiply(z1_sq);
let b_val = self.x + x2.multiply(self.z);
if b_val == FieldElement::ZERO {
return if a == FieldElement::ZERO {
self.double() } else {
ProjectivePoint {
x: FieldElement::ONE,
y: FieldElement::ONE,
z: FieldElement::ZERO,
}
};
}
let c = b_val.multiply(self.z);
let z3 = c.square();
let d = x2.multiply(z3);
let b_sq = b_val.square();
let x3 = a.square() + c.multiply(a + b_sq + c);
let y3 = (d + x3).multiply(a.multiply(c) + z3) + (y2 + x2).multiply(z3.square());
ProjectivePoint {
x: x3,
y: y3,
z: z3,
}
}
fn to_affine(self) -> Point {
if self.z == FieldElement::ZERO {
return Point::Infinity;
}
let z_inv = self.z.invert();
let x = self.x.multiply(z_inv);
let y = self.y.multiply(z_inv.square());
Point::Affine(x, y)
}
}
#[cfg(not(feature = "small-tables"))]
fn shamir_double_scalar_multiply(g: Point, s: &[u8; 21], q: Point, r: &[u8; 21]) -> Point {
let g_plus_q = g + q; let table = [Point::Infinity, q, g, g_plus_q];
let top = (0..163u32)
.rev()
.find(|&i| bit_at(s, i) != 0 || bit_at(r, i) != 0);
let Some(top) = top else {
return Point::Infinity; };
let entry_at = |i: u32| -> Point {
#[allow(clippy::cast_possible_truncation)]
let index = ((bit_at(s, i) << 1) | bit_at(r, i)) as usize;
table[index]
};
let mut acc = ProjectivePoint::from_affine(entry_at(top));
for i in (0..top).rev() {
acc = acc.double();
let entry = entry_at(i);
if entry != Point::Infinity {
acc = acc.mixed_add(entry);
}
}
acc.to_affine()
}
#[cfg(not(feature = "small-tables"))]
#[must_use]
pub fn verify_combine(g: Point, s: &[u8; 21], q: Point, r: &[u8; 21]) -> Point {
shamir_double_scalar_multiply(g, s, q, r)
}
#[cfg(feature = "small-tables")]
#[must_use]
pub fn verify_combine(g: Point, s: &[u8; 21], q: Point, r: &[u8; 21]) -> Point {
g.scalar_multiply(s) + q.scalar_multiply(r)
}