use std::fmt::Display;
use std::ops::Deref;
use bls12_381_plus::elliptic_curve::bigint;
use bls12_381_plus::elliptic_curve::ops::MulByGenerator;
use bls12_381_plus::ff::Field;
use bls12_381_plus::group::prime::PrimeCurveAffine;
use bls12_381_plus::group::Curve;
use bls12_381_plus::group::GroupEncoding;
use bls12_381_plus::G1Affine;
use bls12_381_plus::{G1Projective, Scalar};
use rand::rngs::ThreadRng;
use secrecy::{ExposeSecret, Secret};
use sha2::{Digest, Sha256};
use crate::error;
use crate::error::Error;
use crate::keypair::NymProof;
use crate::utils::try_decompress_g1;
use crate::utils::try_into_scalar;
pub type Challenge = Scalar;
#[derive(PartialEq, Eq, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Nonce(pub(crate) Scalar);
impl Nonce {
pub fn new(bytes: impl AsRef<[u8]>) -> Self {
let chash = Sha256::digest(bytes);
Self(Scalar::from(bigint::U256::from_be_slice(&chash)))
}
}
impl Default for Nonce {
fn default() -> Self {
Self(Scalar::random(ThreadRng::default()))
}
}
impl TryFrom<[u8; 32]> for Nonce {
type Error = Error;
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
let maybe_coverted = Scalar::from_be_bytes(&bytes);
if maybe_coverted.is_some().into() {
Ok(Self(maybe_coverted.unwrap()))
} else {
Err(Error::NonceConversionError)
}
}
}
impl TryFrom<&[u8; 32]> for Nonce {
type Error = Error;
fn try_from(bytes: &[u8; 32]) -> Result<Self, Self::Error> {
let maybe_coverted = Scalar::from_be_bytes(bytes);
if maybe_coverted.is_some().into() {
Ok(Self(maybe_coverted.unwrap()))
} else {
Err(Error::NonceConversionError)
}
}
}
impl From<Vec<u8>> for Nonce {
fn from(bytes: Vec<u8>) -> Self {
Self::new(bytes.as_slice())
}
}
impl From<&Vec<u8>> for Nonce {
fn from(bytes: &Vec<u8>) -> Self {
Self::new(bytes.as_slice())
}
}
impl From<&[u8]> for Nonce {
fn from(bytes: &[u8]) -> Self {
Self::new(bytes)
}
}
impl Deref for Nonce {
type Target = Scalar;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl PartialEq<bls12_381_plus::Scalar> for &Nonce {
fn eq(&self, other: &bls12_381_plus::Scalar) -> bool {
self.0 == Scalar::from(*other)
}
}
impl From<Option<&[u8]>> for Nonce {
fn from(bytes: Option<&[u8]>) -> Self {
match bytes {
Some(bytes) => Self::new(bytes),
None => Self::default(),
}
}
}
impl From<Nonce> for Vec<u8> {
fn from(nonce: Nonce) -> Self {
nonce.0.to_be_bytes().to_vec()
}
}
impl From<&Nonce> for Vec<u8> {
fn from(nonce: &Nonce) -> Self {
nonce.0.to_be_bytes().to_vec()
}
}
impl From<Nonce> for [u8; 32] {
fn from(nonce: Nonce) -> Self {
nonce.0.to_be_bytes()
}
}
impl From<&Nonce> for [u8; 32] {
fn from(nonce: &Nonce) -> Self {
nonce.0.to_be_bytes()
}
}
#[cfg(feature = "zkp")]
use anyhow::Result;
#[cfg(feature = "zkp")]
use bls12_381_plus::G2Affine;
#[cfg(feature = "zkp")]
use bls12_381_plus::G2Projective;
#[derive(Clone, Debug)]
pub struct ChallengeState<T: PrimeCurveAffine> {
pub name: String,
pub g: T,
pub statement: Vec<T>,
pub hash: [u8; 32],
}
impl<T: PrimeCurveAffine + GroupEncoding> ChallengeState<T> {
pub fn new(statement: Vec<T>, announcement: impl AsRef<[u8]>) -> Self {
Self {
name: crate::config::CHALLENGE_STATE_NAME.to_string(),
g: <T as PrimeCurveAffine>::generator(),
statement,
hash: Sha256::digest(announcement).into(),
}
}
}
pub mod zkp_schnorr_fiat_shamir {
#[cfg(feature = "zkp")]
use super::*;
#[cfg(feature = "zkp")]
pub fn challenge<T: PrimeCurveAffine + GroupEncoding<Repr = impl AsRef<[u8]>>>(
state: &ChallengeState<T>,
) -> Scalar {
let mut state_bytes = Vec::new();
state_bytes.extend_from_slice(state.name.as_bytes());
state_bytes.extend_from_slice(state.g.to_bytes().as_ref());
for stmt in &state.statement {
state_bytes.extend_from_slice(stmt.to_bytes().as_ref());
}
state_bytes.extend_from_slice(&state.hash);
let digest = Sha256::digest(&state_bytes);
bigint::U256::from_be_slice(&digest).into()
}
#[cfg(feature = "zkp")]
pub fn non_interact_prove(
stms: &Vec<G2Affine>,
secret_wit: &Vec<Secret<Scalar>>,
) -> Result<(Vec<Scalar>, Challenge)> {
let w_list = (0..stms.len())
.map(|_| Scalar::random(ThreadRng::default()))
.collect::<Vec<Scalar>>();
let witness_list = w_list
.iter()
.map(G2Projective::mul_by_generator)
.collect::<Vec<G2Projective>>();
let announcement = witness_list
.iter()
.fold(G2Projective::IDENTITY, |acc, x| acc + x);
let state = ChallengeState::new(stms.to_vec(), &announcement.to_bytes());
let c = self::challenge(&state);
let r = (0..secret_wit.len())
.map(|i| {
let a = w_list[i];
let b = c * secret_wit[i].expose_secret();
a - b
})
.collect::<Vec<Scalar>>();
Ok((r, c))
}
#[cfg(feature = "zkp")]
pub fn non_interact_verify(stms: &[G2Affine], proof_list: &(Vec<Scalar>, Challenge)) -> bool {
let (r, c) = proof_list;
let w_list = (0..r.len())
.map(|i| G2Projective::mul_by_generator(&r[i]) + c * stms[i])
.collect::<Vec<G2Projective>>();
let announcement = w_list.iter().fold(G2Projective::IDENTITY, |acc, x| acc + x);
let state = ChallengeState::new(stms.to_vec(), &announcement.to_bytes());
let hash = self::challenge(&state);
hash == *c
}
}
pub trait Schnorr {
fn new() -> Self;
fn challenge<T: PrimeCurveAffine + GroupEncoding<Repr = impl AsRef<[u8]>> + Display>(
state: &ChallengeState<T>,
) -> Scalar {
let mut state_bytes = Vec::new();
state_bytes.extend_from_slice(state.name.as_bytes());
state_bytes.extend_from_slice(state.g.to_bytes().as_ref());
for stmt in &state.statement {
state_bytes.extend_from_slice(stmt.to_bytes().as_ref());
}
state_bytes.extend_from_slice(&state.hash);
let digest = Sha256::digest(&state_bytes);
Scalar::from(bigint::U256::from_be_slice(&digest))
}
fn response(
challenge: &Scalar,
announce_randomness: &Scalar,
stm: &G1Affine,
secret_wit: &Secret<Scalar>,
) -> Scalar {
assert!(G1Projective::mul_by_generator(secret_wit.expose_secret()).to_affine() == *stm);
Scalar::from(*announce_randomness + Scalar::from(*challenge) * secret_wit.expose_secret())
}
}
pub struct ZKPSchnorr {}
impl Schnorr for ZKPSchnorr {
fn new() -> Self {
Self {}
}
}
#[cfg(feature = "zkp")]
impl ZKPSchnorr {
pub fn verify(
challenge: &Challenge,
announce_element: &G1Projective,
stm: &G1Projective,
response: &Scalar,
) -> bool {
let left_side = G1Projective::mul_by_generator(response);
let right_side = announce_element + challenge * stm;
left_side == right_side
}
pub fn announce() -> (G1Projective, Scalar) {
let w_random = Scalar::random(ThreadRng::default());
let w_element = G1Projective::mul_by_generator(&w_random);
(w_element, w_random)
}
}
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct DamgardTransform {
pub pedersen: Pedersen,
}
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DamgardTransformCompressed {
pub pedersen: PedersenCompressed,
}
impl From<DamgardTransform> for DamgardTransformCompressed {
fn from(damgard: DamgardTransform) -> Self {
Self {
pedersen: damgard.pedersen.into(),
}
}
}
impl TryFrom<DamgardTransformCompressed> for DamgardTransform {
type Error = error::Error;
fn try_from(damgard_compressed: DamgardTransformCompressed) -> Result<Self, Self::Error> {
Ok(Self {
pedersen: Pedersen::try_from(damgard_compressed.pedersen)?,
})
}
}
impl DamgardTransform {
pub fn announce(&self, nonce: &Nonce) -> (PedersenCommit, PedersenOpen) {
let w_random = Scalar::random(ThreadRng::default());
let w_element = G1Projective::mul_by_generator(&w_random).to_affine();
let (pedersen_commit, mut pedersen_open) = self.pedersen.commit(nonce, w_random);
pedersen_open.element(w_element);
(pedersen_commit, pedersen_open)
}
pub fn verify(nym_proof: &NymProof, nonce: Option<&Nonce>) -> bool {
if let Some(nonce) = nonce {
if nym_proof.pedersen_open.open_randomness != *nonce {
return false;
}
}
let left_side = G1Projective::mul_by_generator(&nym_proof.response);
let right_side = nym_proof.pedersen_open.announce_element.as_ref().unwrap()
+ nym_proof.challenge * nym_proof.public_key;
let decommit = nym_proof
.damgard
.pedersen
.decommit(&nym_proof.pedersen_open, &nym_proof.pedersen_commit.into());
(left_side == right_side) && decommit
}
}
impl Schnorr for DamgardTransform {
fn new() -> Self {
let pedersen = Pedersen::new();
Self { pedersen }
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Pedersen {
pub h: G1Projective,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PedersenCompressed {
h: Vec<u8>,
}
impl PedersenCompressed {
pub fn h(&self) -> Vec<u8> {
self.h.clone()
}
}
impl From<Vec<u8>> for PedersenCompressed {
fn from(h: Vec<u8>) -> Self {
Self { h }
}
}
impl From<Pedersen> for PedersenCompressed {
fn from(pedersen: Pedersen) -> Self {
Self {
h: pedersen.h.to_compressed().to_vec(),
}
}
}
impl TryFrom<PedersenCompressed> for Pedersen {
type Error = error::Error;
fn try_from(pedersen_compressed: PedersenCompressed) -> Result<Self, Self::Error> {
try_decompress_g1(pedersen_compressed.h).map(|h| Pedersen { h: h.into() })
}
}
pub type PedersenCommit = G1Affine;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PedersenOpen {
pub open_randomness: Nonce,
pub announce_randomness: Scalar,
pub announce_element: Option<G1Affine>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PedersenOpenCompressed {
pub open_randomness: Vec<u8>,
pub announce_randomness: Vec<u8>,
pub announce_element: Option<Vec<u8>>,
}
impl From<PedersenOpen> for PedersenOpenCompressed {
fn from(pedersen_open: PedersenOpen) -> Self {
let announce_element = pedersen_open
.announce_element
.map(|elem| elem.to_compressed().to_vec());
Self {
open_randomness: pedersen_open.open_randomness.into(),
announce_randomness: pedersen_open.announce_randomness.into(),
announce_element,
}
}
}
impl From<&PedersenOpen> for PedersenOpenCompressed {
fn from(pedersen_open: &PedersenOpen) -> Self {
let announce_element = pedersen_open
.announce_element
.as_ref()
.map(|elem| elem.to_compressed().to_vec());
Self {
open_randomness: pedersen_open.open_randomness.clone().into(),
announce_randomness: pedersen_open.announce_randomness.into(),
announce_element,
}
}
}
impl std::convert::TryFrom<PedersenOpenCompressed> for PedersenOpen {
type Error = error::Error;
fn try_from(pedersen_open_compressed: PedersenOpenCompressed) -> Result<Self, Self::Error> {
let announce_element = pedersen_open_compressed
.announce_element
.map(|elem| {
let mut bytes = [0u8; G1Affine::COMPRESSED_BYTES];
bytes.copy_from_slice(&elem);
let maybe_g1 = G1Affine::from_compressed(&bytes);
if maybe_g1.is_none().into() {
return Err(Self::Error::InvalidG1Point);
}
Ok(maybe_g1.expect("G1Affine is Some"))
})
.transpose()?;
Ok(Self {
open_randomness: Nonce(try_into_scalar(pedersen_open_compressed.open_randomness)?),
announce_randomness: try_into_scalar(pedersen_open_compressed.announce_randomness)?,
announce_element,
})
}
}
impl PedersenOpen {
pub fn element(&mut self, elem: G1Affine) {
self.announce_element = Some(elem);
}
}
impl Default for Pedersen {
fn default() -> Self {
Self::new()
}
}
impl Pedersen {
pub fn new() -> Self {
let d = Secret::new(Scalar::random(ThreadRng::default())); let h = G1Projective::mul_by_generator(d.expose_secret());
Pedersen { h: h.into() }
}
pub fn commit(&self, nonce: &Nonce, msg: Scalar) -> (PedersenCommit, PedersenOpen) {
let r: Scalar = **nonce;
let pedersen_commit = r * self.h + G1Projective::mul_by_generator(&msg);
let pedersen_open = PedersenOpen {
open_randomness: nonce.clone(),
announce_randomness: msg,
announce_element: None,
};
(pedersen_commit.into(), pedersen_open)
}
pub fn decommit(&self, pedersen_open: &PedersenOpen, pedersen_commit: &PedersenCommit) -> bool {
let c2 = self.h * (*pedersen_open.open_randomness)
+ G1Projective::mul_by_generator(&pedersen_open.announce_randomness);
&c2.to_affine() == pedersen_commit
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "zkp")]
fn test_non_interact_prove() {
let nonce: Nonce = Nonce::default();
let secret_x = Secret::new(Scalar::random(ThreadRng::default()));
let public_h = G2Projective::mul_by_generator(secret_x.expose_secret());
let secrets = (0..5)
.map(|_| Secret::new(Scalar::random(ThreadRng::default())))
.collect::<Vec<Secret<Scalar>>>();
let stm = secrets
.iter()
.map(|s| G2Projective::mul_by_generator(s.expose_secret()).to_affine())
.collect();
let proof_list = zkp_schnorr_fiat_shamir::non_interact_prove(&stm, &secrets).unwrap();
let result = zkp_schnorr_fiat_shamir::non_interact_verify(&stm, &proof_list);
assert!(result);
let proof_single =
zkp_schnorr_fiat_shamir::non_interact_prove(&vec![public_h.into()], &vec![secret_x])
.unwrap();
let result_single =
zkp_schnorr_fiat_shamir::non_interact_verify(&[public_h.into()], &proof_single);
assert!(result_single);
}
#[test]
#[cfg(feature = "zkp")]
fn test_interact_prove() {
let secret_x = Secret::new(Scalar::random(ThreadRng::default()));
let stm = G1Projective::mul_by_generator(secret_x.expose_secret()).to_affine();
let announce = ZKPSchnorr::announce();
let state = ChallengeState::new(vec![stm], &announce.0.to_bytes());
let challenge = ZKPSchnorr::challenge(&state);
let response = ZKPSchnorr::response(&challenge, &announce.1, &stm, &secret_x);
assert!(ZKPSchnorr::verify(
&challenge,
&announce.0,
&stm.into(),
&response
));
}
#[test]
fn test_damgard_transform() {
let nonce: Nonce = Nonce::default();
let damgard = DamgardTransform::new();
let secret = Secret::new(Scalar::random(ThreadRng::default())); let statement = G1Projective::mul_by_generator(secret.expose_secret()).to_affine();
let (pedersen_commit, pedersen_open) = damgard.announce(&nonce);
let state = ChallengeState::new(vec![statement], &pedersen_commit.to_bytes());
let challenge = DamgardTransform::challenge(&state);
let response = DamgardTransform::response(
&challenge,
&pedersen_open.announce_randomness,
&statement,
&secret,
);
let proof_nym = NymProof {
challenge,
pedersen_open,
pedersen_commit,
public_key: statement.into(),
response,
damgard,
};
assert!(DamgardTransform::verify(&proof_nym, Some(&nonce)));
}
#[test]
fn test_roundtrip_nonce() {
let b = [42u8; 32];
let scalar = Scalar::from_be_bytes(&b).expect("bytes to be canonical");
let bytes = scalar.to_be_bytes();
let scalar2 = Scalar::from_be_bytes(&bytes).expect("bytes to be canonical");
assert_eq!(scalar, scalar2);
let nonce = Nonce::try_from(&b).expect("bytes to be canonical");
let bytes: [u8; 32] = nonce.clone().try_into().expect("nonce to be 32 bytes");
let nonce2 = Nonce::try_from(bytes).expect("bytes to be canonical");
assert_eq!(nonce, nonce2);
let nonce = Nonce::new(b);
let bytes: Vec<u8> = nonce.clone().into();
let nonce2 = Nonce(try_into_scalar(bytes).unwrap());
assert_eq!(nonce, nonce2);
}
#[test]
fn test_roundtrip_damgard_transform() {
let damgard = DamgardTransform::new();
let damgard_compressed = DamgardTransformCompressed::from(damgard.clone());
let damgard2 =
DamgardTransform::try_from(damgard_compressed).expect("compressed to be canonical");
assert_eq!(damgard, damgard2);
}
}