#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct U160 {
pub limbs: [u32; 5],
}
impl Ord for U160 {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
for i in (0..5).rev() {
match self.limbs[i].cmp(&other.limbs[i]) {
core::cmp::Ordering::Equal => continue,
ord => return ord,
}
}
core::cmp::Ordering::Equal
}
}
impl PartialOrd for U160 {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl U160 {
pub const ZERO: U160 = U160 { limbs: [0; 5] };
pub const ONE: U160 = U160 {
limbs: [1, 0, 0, 0, 0],
};
pub fn from_be_bytes(b: &[u8; 20]) -> Self {
let mut limbs = [0u32; 5];
for (i, limb) in limbs.iter_mut().enumerate() {
let off = 16 - 4 * i;
*limb = ((b[off] as u32) << 24)
| ((b[off + 1] as u32) << 16)
| ((b[off + 2] as u32) << 8)
| (b[off + 3] as u32);
}
U160 { limbs }
}
pub fn to_be_bytes(self) -> [u8; 20] {
let mut out = [0u8; 20];
for (i, &limb) in self.limbs.iter().enumerate() {
let off = 16 - 4 * i;
out[off] = (limb >> 24) as u8;
out[off + 1] = (limb >> 16) as u8;
out[off + 2] = (limb >> 8) as u8;
out[off + 3] = limb as u8;
}
out
}
pub fn is_zero(&self) -> bool {
self.limbs == [0u32; 5]
}
pub fn bit(&self, i: usize) -> bool {
if i >= 160 {
return false;
}
(self.limbs[i / 32] >> (i % 32)) & 1 == 1
}
pub fn bit_len(&self) -> usize {
for i in (0..5).rev() {
if self.limbs[i] != 0 {
return i * 32 + (32 - self.limbs[i].leading_zeros() as usize);
}
}
0
}
fn adc(&self, other: &U160) -> (U160, u32) {
let mut limbs = [0u32; 5];
let mut carry: u64 = 0;
for (i, out) in limbs.iter_mut().enumerate() {
let s = self.limbs[i] as u64 + other.limbs[i] as u64 + carry;
*out = s as u32;
carry = s >> 32;
}
(U160 { limbs }, carry as u32)
}
fn sbb(&self, other: &U160) -> (U160, u32) {
let mut limbs = [0u32; 5];
let mut borrow: i64 = 0;
for (i, out) in limbs.iter_mut().enumerate() {
let d = self.limbs[i] as i64 - other.limbs[i] as i64 - borrow;
if d < 0 {
*out = (d + (1i64 << 32)) as u32;
borrow = 1;
} else {
*out = d as u32;
borrow = 0;
}
}
(U160 { limbs }, borrow as u32)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Fp {
pub value: U160,
}
pub const P: U160 = U160 {
limbs: [
0x79a7_d7df,
0xf9ea_e7c4,
0x60bd_b09e,
0x55ec_ceb5,
0x9dc9_d813,
],
};
pub const B: U160 = U160 {
limbs: [
0xdaac_b1d8,
0x1245_e0c4,
0x5248_d68e,
0xc1cb_cd16,
0x402d_ad3e,
],
};
pub const GX: U160 = U160 {
limbs: [
0xc54c_cec6,
0x81d0_a4bd,
0xf4cc_a7eb,
0x5783_51e6,
0x2e64_fc22,
],
};
pub const GY: U160 = U160 {
limbs: [
0x07f5_cbb9,
0xf23c_9a07,
0x9db4_55c7,
0xd054_4288,
0x0914_a25d,
],
};
pub const N: U160 = U160 {
limbs: [
0x7f5a_b017,
0x5481_7b2c,
0x60bd_c44f,
0x55ec_ceb5,
0x9dc9_d813,
],
};
impl Fp {
pub const ZERO: Fp = Fp { value: U160::ZERO };
pub const ONE: Fp = Fp { value: U160::ONE };
pub fn from_u160(mut v: U160) -> Self {
while v.cmp(&P) != core::cmp::Ordering::Less {
let (r, _) = v.sbb(&P);
v = r;
}
Fp { value: v }
}
pub fn from_be_bytes(b: &[u8; 20]) -> Self {
Fp::from_u160(U160::from_be_bytes(b))
}
pub fn to_be_bytes(self) -> [u8; 20] {
self.value.to_be_bytes()
}
pub fn is_zero(&self) -> bool {
self.value.is_zero()
}
pub fn add(&self, other: &Fp) -> Fp {
let (sum, carry) = self.value.adc(&other.value);
if carry == 1 {
let (r, _) = sum.sbb(&P);
Fp::from_u160(r)
} else {
Fp::from_u160(sum)
}
}
pub fn sub(&self, other: &Fp) -> Fp {
let (diff, borrow) = self.value.sbb(&other.value);
if borrow == 1 {
let (r, _) = diff.adc(&P);
Fp { value: r }
} else {
Fp { value: diff }
}
}
pub fn mul(&self, other: &Fp) -> Fp {
let prod = mul_wide(&self.value, &other.value);
Fp {
value: reduce_wide(&prod, &P),
}
}
pub fn square(&self) -> Fp {
self.mul(self)
}
pub fn neg(&self) -> Fp {
if self.is_zero() {
*self
} else {
let (r, _) = P.sbb(&self.value);
Fp { value: r }
}
}
pub fn inv(&self) -> Fp {
if self.is_zero() {
return Fp::ZERO;
}
let (mut e, _) = P.sbb(&U160 {
limbs: [2, 0, 0, 0, 0],
});
let _ = &mut e;
self.pow(&e)
}
pub fn pow(&self, exp: &U160) -> Fp {
let mut result = Fp::ONE;
let bits = exp.bit_len();
for i in (0..bits).rev() {
result = result.square();
if exp.bit(i) {
result = result.mul(self);
}
}
result
}
}
fn mul_wide(a: &U160, b: &U160) -> [u32; 10] {
let mut out = [0u64; 10];
for i in 0..5 {
let mut carry: u64 = 0;
for j in 0..5 {
let cur = out[i + j] + a.limbs[i] as u64 * b.limbs[j] as u64 + carry;
out[i + j] = cur & 0xFFFF_FFFF;
carry = cur >> 32;
}
out[i + 5] += carry;
}
let mut limbs = [0u32; 10];
for i in 0..10 {
limbs[i] = out[i] as u32;
}
limbs
}
fn reduce_wide(wide: &[u32; 10], m: &U160) -> U160 {
let mut rem = U160::ZERO;
for bit in (0..320).rev() {
let mut carry = 0u32;
for limb in rem.limbs.iter_mut() {
let new_carry = *limb >> 31;
*limb = (*limb << 1) | carry;
carry = new_carry;
}
let wbit = (wide[bit / 32] >> (bit % 32)) & 1;
rem.limbs[0] |= wbit;
if carry == 1 || rem.cmp(m) != core::cmp::Ordering::Less {
let (r, _) = rem.sbb(m);
rem = r;
}
}
rem
}
pub fn scalar_reduce(mut v: U160) -> U160 {
while v.cmp(&N) != core::cmp::Ordering::Less {
let (r, _) = v.sbb(&N);
v = r;
}
v
}
pub fn scalar_reduce_wide(wide: &[u32; 10]) -> U160 {
reduce_wide(wide, &N)
}
pub fn scalar_add(a: &U160, b: &U160) -> U160 {
let (sum, carry) = a.adc(b);
if carry == 1 {
let (r, _) = sum.sbb(&N);
scalar_reduce(r)
} else {
scalar_reduce(sum)
}
}
pub fn scalar_mul(a: &U160, b: &U160) -> U160 {
let prod = mul_wide(a, b);
reduce_wide(&prod, &N)
}
pub fn scalar_inv(a: &U160) -> U160 {
if a.is_zero() {
return U160::ZERO;
}
let (exp, _) = N.sbb(&U160 {
limbs: [2, 0, 0, 0, 0],
});
scalar_pow(a, &exp)
}
fn scalar_pow(base: &U160, exp: &U160) -> U160 {
let mut result = U160::ONE;
let bits = exp.bit_len();
for i in (0..bits).rev() {
result = scalar_mul(&result, &result);
if exp.bit(i) {
result = scalar_mul(&result, base);
}
}
result
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Point {
Infinity,
Affine {
x: Fp,
y: Fp,
},
}
impl Point {
pub fn generator() -> Point {
Point::Affine {
x: Fp::from_u160(GX),
y: Fp::from_u160(GY),
}
}
pub fn is_infinity(&self) -> bool {
matches!(self, Point::Infinity)
}
pub fn from_coords(x: &[u8; 20], y: &[u8; 20]) -> Option<Point> {
let px = Fp::from_be_bytes(x);
let py = Fp::from_be_bytes(y);
let p = Point::Affine { x: px, y: py };
if p.is_on_curve() {
Some(p)
} else {
None
}
}
pub fn to_bytes(&self) -> [u8; 40] {
let mut out = [0u8; 40];
if let Point::Affine { x, y } = self {
out[..20].copy_from_slice(&x.to_be_bytes());
out[20..].copy_from_slice(&y.to_be_bytes());
}
out
}
pub fn is_on_curve(&self) -> bool {
match self {
Point::Infinity => true,
Point::Affine { x, y } => {
let three = Fp::from_u160(U160 {
limbs: [3, 0, 0, 0, 0],
});
let lhs = y.square();
let x3 = x.square().mul(x);
let rhs = x3.sub(&three.mul(x)).add(&Fp::from_u160(B));
lhs == rhs
}
}
}
pub fn double(&self) -> Point {
match self {
Point::Infinity => Point::Infinity,
Point::Affine { x, y } => {
if y.is_zero() {
return Point::Infinity;
}
let three = Fp::from_u160(U160 {
limbs: [3, 0, 0, 0, 0],
});
let two = Fp::from_u160(U160 {
limbs: [2, 0, 0, 0, 0],
});
let num = three.mul(&x.square()).sub(&three); let den = two.mul(y);
let lambda = num.mul(&den.inv());
let x3 = lambda.square().sub(x).sub(x);
let y3 = lambda.mul(&x.sub(&x3)).sub(y);
Point::Affine { x: x3, y: y3 }
}
}
}
pub fn add(&self, other: &Point) -> Point {
match (self, other) {
(Point::Infinity, _) => *other,
(_, Point::Infinity) => *self,
(Point::Affine { x: x1, y: y1 }, Point::Affine { x: x2, y: y2 }) => {
if x1 == x2 {
if y1 == y2 {
return self.double();
}
return Point::Infinity;
}
let lambda = y2.sub(y1).mul(&x2.sub(x1).inv());
let x3 = lambda.square().sub(x1).sub(x2);
let y3 = lambda.mul(&x1.sub(&x3)).sub(y1);
Point::Affine { x: x3, y: y3 }
}
}
}
pub fn mul_scalar(&self, k: &U160) -> Point {
let base = match self {
Point::Infinity => return Point::Infinity,
Point::Affine { x, y } => Jacobian {
x: *x,
y: *y,
z: Fp::ONE,
},
};
let mut acc = Jacobian::INFINITY;
let bits = k.bit_len();
for i in (0..bits).rev() {
acc = acc.double();
if k.bit(i) {
acc = acc.add(&base);
}
}
acc.to_affine()
}
pub fn x_u160(&self) -> U160 {
match self {
Point::Affine { x, .. } => x.value,
Point::Infinity => U160::ZERO,
}
}
}
#[derive(Debug, Clone, Copy)]
struct Jacobian {
x: Fp,
y: Fp,
z: Fp,
}
impl Jacobian {
const INFINITY: Jacobian = Jacobian {
x: Fp::ONE,
y: Fp::ONE,
z: Fp::ZERO,
};
fn is_infinity(&self) -> bool {
self.z.is_zero()
}
fn double(&self) -> Jacobian {
if self.is_infinity() || self.y.is_zero() {
return Jacobian::INFINITY;
}
let two = Fp::from_u160(U160 {
limbs: [2, 0, 0, 0, 0],
});
let three = Fp::from_u160(U160 {
limbs: [3, 0, 0, 0, 0],
});
let four = Fp::from_u160(U160 {
limbs: [4, 0, 0, 0, 0],
});
let eight = Fp::from_u160(U160 {
limbs: [8, 0, 0, 0, 0],
});
let zz = self.z.square();
let yy = self.y.square();
let m = three.mul(&self.x.sub(&zz)).mul(&self.x.add(&zz));
let s = four.mul(&self.x).mul(&yy);
let x3 = m.square().sub(&two.mul(&s));
let yyyy = yy.square();
let y3 = m.mul(&s.sub(&x3)).sub(&eight.mul(&yyyy));
let z3 = two.mul(&self.y).mul(&self.z);
Jacobian {
x: x3,
y: y3,
z: z3,
}
}
fn add(&self, other: &Jacobian) -> Jacobian {
if self.is_infinity() {
return *other;
}
if other.is_infinity() {
return *self;
}
let z1z1 = self.z.square();
let u2 = other.x.mul(&z1z1);
let s2 = other.y.mul(&z1z1).mul(&self.z);
let u1 = self.x;
let s1 = self.y;
let h = u2.sub(&u1);
let r = s2.sub(&s1);
if h.is_zero() {
if r.is_zero() {
return self.double();
}
return Jacobian::INFINITY;
}
let hh = h.square();
let hhh = hh.mul(&h);
let two = Fp::from_u160(U160 {
limbs: [2, 0, 0, 0, 0],
});
let u1hh = u1.mul(&hh);
let x3 = r.square().sub(&hhh).sub(&two.mul(&u1hh));
let y3 = r.mul(&u1hh.sub(&x3)).sub(&s1.mul(&hhh));
let z3 = self.z.mul(&h);
Jacobian {
x: x3,
y: y3,
z: z3,
}
}
fn to_affine(self) -> Point {
if self.is_infinity() {
return Point::Infinity;
}
let zinv = self.z.inv();
let zinv2 = zinv.square();
let zinv3 = zinv2.mul(&zinv);
Point::Affine {
x: self.x.mul(&zinv2),
y: self.y.mul(&zinv3),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn u160_from_small(v: u32) -> U160 {
U160 {
limbs: [v, 0, 0, 0, 0],
}
}
#[test]
fn curve_params_round_trip_bytes() {
for v in [P, B, GX, GY, N] {
let b = v.to_be_bytes();
assert_eq!(U160::from_be_bytes(&b), v);
}
}
#[test]
fn generator_is_on_curve() {
assert!(Point::generator().is_on_curve());
}
#[test]
fn fp_add_sub_inverse() {
let a = Fp::from_u160(u160_from_small(0x1234_5678));
let b = Fp::from_u160(u160_from_small(0x9abc_def0));
let c = a.add(&b);
assert_eq!(c.sub(&b), a);
assert_eq!(c.sub(&a), b);
}
#[test]
fn fp_mul_inv_is_one() {
let a = Fp::from_u160(GX);
let inv = a.inv();
let prod = a.mul(&inv);
assert_eq!(prod, Fp::ONE);
}
#[test]
fn scalar_inv_is_one() {
let a = U160 {
limbs: [0xdead_beef, 0x1234, 0x5678, 0x9abc, 0x0def],
};
let inv = scalar_inv(&a);
assert_eq!(scalar_mul(&a, &inv), U160::ONE);
}
#[test]
fn generator_order_n_is_infinity() {
let p = Point::generator().mul_scalar(&N);
assert!(p.is_infinity());
}
#[test]
fn double_matches_add_self() {
let g = Point::generator();
assert_eq!(g.double(), g.add(&g));
}
#[test]
fn scalar_mul_distributes() {
let a = u160_from_small(7);
let b = u160_from_small(11);
let g = Point::generator();
let lhs = g.mul_scalar(&scalar_add(&a, &b));
let rhs = g.mul_scalar(&a).add(&g.mul_scalar(&b));
assert_eq!(lhs, rhs);
}
#[test]
fn affine_round_trips_through_bytes() {
let g = Point::generator();
let bytes = g.to_bytes();
let mut x = [0u8; 20];
let mut y = [0u8; 20];
x.copy_from_slice(&bytes[..20]);
y.copy_from_slice(&bytes[20..]);
assert_eq!(Point::from_coords(&x, &y), Some(g));
}
}