#![no_std]
mod curve;
mod scalar;
mod sha512;
#[cfg(target_os = "solana")]
mod syscalls;
#[cfg(all(any(feature = "prove", test), not(target_os = "solana")))]
mod prove;
#[cfg(all(any(feature = "prove", test), not(target_os = "solana")))]
pub use prove::SecretKey;
#[cfg(test)]
mod tests;
pub const SUITE: u8 = 0x03;
pub const PUBLIC_KEY_LENGTH: usize = 32;
pub const PROOF_LENGTH: usize = 80;
pub const OUTPUT_LENGTH: usize = 64;
pub type Output = [u8; OUTPUT_LENGTH];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
InvalidPublicKey,
InvalidProof,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PublicKey(pub [u8; PUBLIC_KEY_LENGTH]);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Proof(pub [u8; PROOF_LENGTH]);
impl PublicKey {
#[inline]
pub fn validate(&self) -> Result<(), Error> {
if curve::validate(&self.0) && !curve::is_small_order(&self.0) {
Ok(())
} else {
Err(Error::InvalidPublicKey)
}
}
}
impl Proof {
#[inline(always)]
fn gamma(&self) -> &[u8; 32] {
self.0[..32].try_into().unwrap()
}
#[inline(always)]
fn c(&self) -> &[u8; 16] {
self.0[32..48].try_into().unwrap()
}
#[inline(always)]
fn s(&self) -> &[u8; 32] {
self.0[48..].try_into().unwrap()
}
#[inline]
pub fn verify(&self, public_key: &PublicKey, alpha: &[u8]) -> Result<Output, Error> {
let y = &public_key.0;
let gamma = self.gamma();
let c = self.c();
let s = self.s();
if curve::is_small_order(y) {
return Err(Error::InvalidPublicKey);
}
if !scalar::is_canonical(s) {
return Err(Error::InvalidProof);
}
let h = encode_to_curve(y, alpha).ok_or(Error::InvalidProof)?;
let neg_c = scalar::negate_challenge(c);
let u = curve::double_scalar_mul(s, &curve::BASEPOINT, &neg_c, y)
.ok_or(Error::InvalidPublicKey)?;
let v = curve::double_scalar_mul(s, &h, &neg_c, gamma).ok_or(Error::InvalidProof)?;
if challenge(y, &h, gamma, &u, &v) != *c {
return Err(Error::InvalidProof);
}
self.derive_output()
}
#[inline]
fn derive_output(&self) -> Result<Output, Error> {
let gamma8 = curve::mul_by_cofactor(self.gamma()).ok_or(Error::InvalidProof)?;
Ok(sha512::hashv(&[&[SUITE, 0x03], &gamma8, &[0x00]]))
}
}
#[inline]
pub(crate) fn encode_to_curve(y: &[u8; 32], alpha: &[u8]) -> Option<curve::Point> {
for ctr in 0..=u8::MAX {
let hash = sha512::hashv(&[&[SUITE, 0x01], y, alpha, &[ctr, 0x00]]);
let candidate: &[u8; 32] = hash[..32].try_into().unwrap();
if curve::validate(candidate) && !curve::is_small_order(candidate) {
return curve::mul_by_cofactor(candidate);
}
}
None
}
#[inline]
pub(crate) fn challenge(
y: &[u8; 32],
h: &[u8; 32],
gamma: &[u8; 32],
u: &[u8; 32],
v: &[u8; 32],
) -> [u8; 16] {
let hash = sha512::hashv(&[&[SUITE, 0x02], y, h, gamma, u, v, &[0x00]]);
hash[..16].try_into().unwrap()
}