use dashu_int::{fast_div::ConstDivisor, UBig};
use rand_core::RngCore;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
pub mod p256;
pub use p256::SchnorrP256Group;
pub trait Group: Clone {
type P: Clone;
type F: Clone;
type DeserializeError;
fn generator(&self) -> Self::P;
fn dot(&self, p1: &Self::P, p2: &Self::P) -> Self::P;
fn mul_by_generator(&self, scalar: &Self::F) -> Self::P;
fn mul(&self, p: &Self::P, scalar: &Self::F) -> Self::P;
fn add_mul_scalar(&self, s1: &Self::F, s2: &Self::F, s3: &Self::F) -> Self::F;
fn neg(&self, scalar: &Self::F) -> Self::F;
fn is_equivalent_scalars(s1: &Self::F, s2: &Self::F) -> bool;
fn is_equivalent_points(p1: &Self::P, p2: &Self::P) -> bool;
fn map_point(point: &Self::P) -> Vec<u8>;
fn map_to_scalar(bytes: &[u8]) -> Self::F;
fn serialize_scalar(scalar: &Self::F) -> Vec<u8>;
fn serialize_point(point: &Self::P) -> Vec<u8>;
fn deserialize_scalar(bytes: &[u8]) -> Result<Self::F, Self::DeserializeError>;
fn deserialize_point(bytes: &[u8]) -> Result<Self::P, Self::DeserializeError>;
fn random_scalar<R: RngCore>(&self, rng: &mut R) -> Self::F;
fn random_element<R: RngCore>(&self, rng: &mut R) -> Self::P;
}
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SchnorrGroup {
p: UBig,
q: UBig,
a: UBig,
}
impl SchnorrGroup {
pub(crate) fn from_str(p: &str, q: &str, a: &str) -> Option<Self> {
let p = UBig::from_str(p).ok()?;
let q = UBig::from_str(q).ok()?;
let a = UBig::from_str(a).ok()?;
let modp = ConstDivisor::new(p.clone());
if modp.reduce(a.clone()).pow(&q) != modp.reduce(1) {
return None;
}
Some(Self { p, q, a })
}
}
impl Group for SchnorrGroup {
type P = UBig;
type F = UBig;
type DeserializeError = ();
fn generator(&self) -> Self::P {
self.a.clone()
}
fn dot(&self, p1: &UBig, p2: &UBig) -> UBig {
ConstDivisor::new(self.p.clone()).reduce(p1 * p2).residue()
}
fn mul_by_generator(&self, scalar: &UBig) -> UBig {
ConstDivisor::new(self.p.clone())
.reduce(self.a.clone())
.pow(scalar)
.residue()
}
fn mul(&self, p: &UBig, scalar: &UBig) -> UBig {
ConstDivisor::new(self.p.clone())
.reduce(p.clone())
.pow(scalar)
.residue()
}
fn add_mul_scalar(&self, s1: &UBig, s2: &UBig, s3: &UBig) -> UBig {
ConstDivisor::new(self.q.clone())
.reduce(s1 + s2 * s3)
.residue()
}
fn neg(&self, scalar: &UBig) -> UBig {
&self.q - scalar
}
fn is_equivalent_scalars(s1: &UBig, s2: &UBig) -> bool {
s1 == s2
}
fn is_equivalent_points(p1: &UBig, p2: &UBig) -> bool {
p1 == p2
}
fn map_point(point: &UBig) -> Vec<u8> {
point.to_le_bytes().to_vec()
}
fn map_to_scalar(bytes: &[u8]) -> UBig {
UBig::from_le_bytes(bytes)
}
fn serialize_scalar(scalar: &UBig) -> Vec<u8> {
scalar.to_le_bytes().to_vec()
}
fn serialize_point(point: &UBig) -> Vec<u8> {
point.to_le_bytes().to_vec()
}
fn deserialize_scalar(bytes: &[u8]) -> Result<UBig, ()> {
if bytes.is_empty() {
return Err(());
}
Ok(UBig::from_le_bytes(bytes))
}
fn deserialize_point(bytes: &[u8]) -> Result<UBig, ()> {
if bytes.is_empty() {
return Err(());
}
Ok(UBig::from_le_bytes(bytes))
}
fn random_scalar<R: RngCore>(&self, rng: &mut R) -> UBig {
let mut buf = self.q.to_le_bytes().to_vec();
rng.fill_bytes(&mut buf);
UBig::from_le_bytes(&buf) % &self.q
}
fn random_element<R: RngCore>(&self, rng: &mut R) -> UBig {
let scalar = self.random_scalar(rng);
self.mul_by_generator(&scalar)
}
}