mod field_backend;
mod group;
use crate::bignum::MontModulus;
use crate::ct::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeLess};
use crate::ec::Error;
use field_backend::{Fe, FieldBackend, Secp256k1Field, fe_from_hex};
use group::Point;
const GX_HEX: &str = "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
const GY_HEX: &str = "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8";
const N_HEX: &str = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141";
type Backend = Secp256k1Field;
#[inline]
fn field() -> Backend {
Secp256k1Field::new()
}
#[derive(Clone)]
pub struct Scalar(Fe);
impl Scalar {
pub const ZERO: Scalar = Scalar(Fe::ZERO);
pub const ONE: Scalar = Scalar(Fe::ONE);
#[inline]
fn modulus() -> MontModulus<4> {
MontModulus::new(fe_from_hex(N_HEX))
}
#[inline]
fn order() -> Fe {
fe_from_hex(N_HEX)
}
pub fn from_bytes_be(bytes: &[u8; 32]) -> Result<Scalar, Error> {
let v = Fe::from_be_bytes(bytes);
if bool::from(v.ct_lt(&Self::order())) {
Ok(Scalar(v))
} else {
Err(Error::InvalidInput)
}
}
pub fn from_bytes_be_reduce(bytes: &[u8; 32]) -> Scalar {
let v = Fe::from_be_bytes(bytes);
Scalar(v.reduce(&Self::order()))
}
pub fn to_bytes_be(&self) -> [u8; 32] {
let mut out = [0u8; 32];
self.0.write_be_bytes(&mut out);
out
}
pub fn add(&self, rhs: &Scalar) -> Scalar {
Scalar(Self::modulus().add_mod(&self.0, &rhs.0))
}
pub fn sub(&self, rhs: &Scalar) -> Scalar {
Scalar(Self::modulus().sub_mod(&self.0, &rhs.0))
}
pub fn mul(&self, rhs: &Scalar) -> Scalar {
Scalar(Self::modulus().mul_mod(&self.0, &rhs.0))
}
pub fn negate(&self) -> Scalar {
Scalar(Self::modulus().sub_mod(&Fe::ZERO, &self.0))
}
pub fn invert(&self) -> Scalar {
let n_minus_2 = Self::order().wrapping_sub(&Fe::from_u64(2));
Scalar(Self::modulus().pow(&self.0, &n_minus_2))
}
pub fn is_zero(&self) -> Choice {
self.0.is_zero()
}
pub fn ct_eq(&self, other: &Scalar) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl Drop for Scalar {
fn drop(&mut self) {
self.0 = Fe::ZERO;
let _ = core::hint::black_box(&self.0);
}
}
#[derive(Clone, Copy)]
pub struct ProjectivePoint(Point);
#[derive(Clone, Copy)]
pub struct AffinePoint {
x: Fe,
y: Fe,
}
impl ProjectivePoint {
pub fn identity() -> ProjectivePoint {
ProjectivePoint(Point::identity(&field()))
}
pub fn generator() -> ProjectivePoint {
let f = field();
ProjectivePoint(Point::from_affine(
&f,
&fe_from_hex(GX_HEX),
&fe_from_hex(GY_HEX),
))
}
pub fn is_identity(&self) -> Choice {
self.0.is_identity()
}
pub fn add(&self, rhs: &ProjectivePoint) -> ProjectivePoint {
ProjectivePoint(Point::add(&field(), &self.0, &rhs.0))
}
pub fn double(&self) -> ProjectivePoint {
ProjectivePoint(Point::double(&field(), &self.0))
}
pub fn negate(&self) -> ProjectivePoint {
ProjectivePoint(Point::negate(&field(), &self.0))
}
pub fn mul(&self, scalar: &Scalar) -> ProjectivePoint {
ProjectivePoint(Point::mul(&field(), scalar.0.as_limbs(), &self.0))
}
pub fn mul_generator(scalar: &Scalar) -> ProjectivePoint {
Self::generator().mul(scalar)
}
pub fn ct_eq(&self, other: &ProjectivePoint) -> Choice {
self.0.ct_eq(&field(), &other.0)
}
pub fn to_affine(&self) -> Option<AffinePoint> {
self.0
.to_affine(&field())
.map(|(x, y)| AffinePoint { x, y })
}
}
impl ConditionallySelectable for ProjectivePoint {
#[inline]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
ProjectivePoint(Point::conditional_select(&a.0, &b.0, choice))
}
}
impl AffinePoint {
pub fn generator() -> AffinePoint {
AffinePoint {
x: fe_from_hex(GX_HEX),
y: fe_from_hex(GY_HEX),
}
}
pub fn to_projective(&self) -> ProjectivePoint {
ProjectivePoint(Point::from_affine(&field(), &self.x, &self.y))
}
pub fn x_bytes(&self) -> [u8; 32] {
field().to_bytes_be(&self.x)
}
pub fn y_bytes(&self) -> [u8; 32] {
field().to_bytes_be(&self.y)
}
fn is_on_curve(f: &Backend, x: &Fe, y: &Fe) -> Choice {
let y2 = f.square(y);
let x3 = f.mul(&f.square(x), x);
let rhs = f.add(&x3, &Fe::from_u64(7));
y2.ct_eq(&rhs)
}
pub fn to_sec1_compressed(&self) -> [u8; 33] {
let mut out = [0u8; 33];
let x = self.x_bytes();
let y = self.y_bytes();
let y_odd = y[31] & 1;
out[0] = 0x02 | y_odd;
out[1..].copy_from_slice(&x);
out
}
pub fn to_sec1_uncompressed(&self) -> [u8; 65] {
let mut out = [0u8; 65];
out[0] = 0x04;
out[1..33].copy_from_slice(&self.x_bytes());
out[33..].copy_from_slice(&self.y_bytes());
out
}
pub fn from_sec1(bytes: &[u8]) -> Result<AffinePoint, Error> {
let f = field();
match bytes.first().copied() {
Some(tag @ (0x02 | 0x03)) => {
if bytes.len() != 33 {
return Err(Error::Malformed);
}
let mut xb = [0u8; 32];
xb.copy_from_slice(&bytes[1..33]);
let x = f
.from_bytes_be(&xb)
.into_option()
.ok_or(Error::InvalidInput)?;
let x3 = f.mul(&f.square(&x), &x);
let rhs = f.add(&x3, &Fe::from_u64(7));
let y = f.sqrt(&rhs).into_option().ok_or(Error::InvalidInput)?;
let y_bytes = f.to_bytes_be(&y);
let want_odd = tag & 1;
let have_odd = y_bytes[31] & 1;
let y = if have_odd == want_odd {
y
} else {
f.negate(&y)
};
let pt = AffinePoint { x, y };
if bool::from(pt.x.is_zero() & pt.y.is_zero()) {
return Err(Error::InvalidInput);
}
Ok(pt)
}
Some(0x04) => {
if bytes.len() != 65 {
return Err(Error::Malformed);
}
let mut xb = [0u8; 32];
let mut yb = [0u8; 32];
xb.copy_from_slice(&bytes[1..33]);
yb.copy_from_slice(&bytes[33..65]);
let x = f
.from_bytes_be(&xb)
.into_option()
.ok_or(Error::InvalidInput)?;
let y = f
.from_bytes_be(&yb)
.into_option()
.ok_or(Error::InvalidInput)?;
if !bool::from(Self::is_on_curve(&f, &x, &y)) {
return Err(Error::InvalidInput);
}
if bool::from(x.is_zero() & y.is_zero()) {
return Err(Error::InvalidInput);
}
Ok(AffinePoint { x, y })
}
_ => Err(Error::Malformed),
}
}
}
pub fn xonly_tweak_add(internal: &[u8; 32], tweak: &[u8; 32]) -> Result<([u8; 32], bool), Error> {
let mut compressed = [0u8; 33];
compressed[0] = 0x02;
compressed[1..].copy_from_slice(internal);
let p = AffinePoint::from_sec1(&compressed)?;
let t = Scalar::from_bytes_be(tweak)?;
let q = p
.to_projective()
.add(&ProjectivePoint::mul_generator(&t))
.to_affine()
.ok_or(Error::InvalidInput)?;
let parity = q.y_bytes()[31] & 1 == 1;
Ok((q.x_bytes(), parity))
}
#[cfg(test)]
mod tests;