use alloc::vec::Vec;
use core::{
cmp::Ordering,
hash::{Hash, Hasher},
ops::{Add, Mul},
};
use rand_core::{CryptoRng, Rng};
use snafu::prelude::*;
use tari_utilities::ByteArray;
use crate::{
alloc::borrow::ToOwned,
commitment::{HomomorphicCommitment, HomomorphicCommitmentFactory},
keys::{PublicKey, SecretKey},
signatures::SchnorrSignature,
};
#[derive(Clone, Debug, Snafu, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[allow(missing_docs)]
pub enum CommitmentAndPublicKeySignatureError {
#[snafu(display("An invalid challenge was provided"))]
InvalidChallenge,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CommitmentAndPublicKeySignature<P, K> {
pub(crate) ephemeral_commitment: HomomorphicCommitment<P>,
pub(crate) ephemeral_pubkey: P,
pub(crate) u_a: K,
pub(crate) u_x: K,
pub(crate) u_y: K,
}
impl<P, K> CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
K: SecretKey,
{
pub fn new(ephemeral_commitment: HomomorphicCommitment<P>, ephemeral_pubkey: P, u_a: K, u_x: K, u_y: K) -> Self {
CommitmentAndPublicKeySignature {
ephemeral_commitment,
ephemeral_pubkey,
u_a,
u_x,
u_y,
}
}
#[allow(clippy::too_many_arguments)]
pub fn sign<C>(
a: &K,
x: &K,
y: &K,
r_a: &K,
r_x: &K,
r_y: &K,
challenge: &[u8],
factory: &C,
) -> Result<Self, CommitmentAndPublicKeySignatureError>
where
K: Mul<P, Output = P>,
for<'a> &'a K: Add<&'a K, Output = K>,
for<'a> &'a K: Mul<&'a K, Output = K>,
C: HomomorphicCommitmentFactory<P = P>,
{
let e = match K::from_uniform_bytes(challenge) {
Ok(e) => e,
Err(_) => return Err(CommitmentAndPublicKeySignatureError::InvalidChallenge),
};
if e == K::default() {
return Err(CommitmentAndPublicKeySignatureError::InvalidChallenge);
}
let ea = &e * a;
let ex = &e * x;
let ey = &e * y;
let u_a = r_a + &ea;
let u_x = r_x + &ex;
let u_y = r_y + &ey;
let ephemeral_commitment = factory.commit(r_x, r_a);
let ephemeral_pubkey = P::from_secret_key(r_y);
Ok(Self::new(ephemeral_commitment, ephemeral_pubkey, u_a, u_x, u_y))
}
pub fn verify_challenge<'a, C, R>(
&self,
commitment: &'a HomomorphicCommitment<P>,
pubkey: &'a P,
challenge: &[u8],
factory: &C,
rng: &mut R,
) -> bool
where
for<'b> &'a HomomorphicCommitment<P>: Mul<&'b K, Output = HomomorphicCommitment<P>>,
for<'b> &'b P: Mul<&'b K, Output = P>,
for<'b> &'b HomomorphicCommitment<P>: Add<&'b HomomorphicCommitment<P>, Output = HomomorphicCommitment<P>>,
for<'b> &'b P: Add<&'b P, Output = P>,
for<'b> &'b K: Mul<&'b K, Output = K>,
for<'b> &'b K: Add<&'b K, Output = K>,
C: HomomorphicCommitmentFactory<P = P>,
R: Rng + CryptoRng,
{
let e = match K::from_uniform_bytes(challenge) {
Ok(e) => e,
Err(_) => return false,
};
self.verify(commitment, pubkey, &e, factory, rng)
}
pub fn verify<'a, C, R>(
&self,
commitment: &'a HomomorphicCommitment<P>,
pubkey: &'a P,
challenge: &K,
factory: &C,
rng: &mut R,
) -> bool
where
for<'b> &'a HomomorphicCommitment<P>: Mul<&'b K, Output = HomomorphicCommitment<P>>,
for<'b> &'b P: Mul<&'b K, Output = P>,
for<'b> &'b HomomorphicCommitment<P>: Add<&'b HomomorphicCommitment<P>, Output = HomomorphicCommitment<P>>,
for<'b> &'b P: Add<&'b P, Output = P>,
for<'b> &'b K: Mul<&'b K, Output = K>,
for<'b> &'b K: Add<&'b K, Output = K>,
C: HomomorphicCommitmentFactory<P = P>,
R: Rng + CryptoRng,
{
if commitment.as_public_key() == &P::default() || pubkey == &P::default() {
return false;
}
if *challenge == K::default() {
return false;
}
let w = K::random(rng);
let verifier_lhs = factory
.commit(&(&self.u_x + &(&w * &self.u_y)), &self.u_a)
.as_public_key()
.to_owned();
let verifier_rhs_unweighted =
self.ephemeral_commitment.as_public_key() + (commitment * challenge).as_public_key();
let verifier_rhs_weighted = &self.ephemeral_pubkey * &w + pubkey * &(&w * challenge);
verifier_lhs == verifier_rhs_unweighted + verifier_rhs_weighted
}
pub fn complete_signature_tuple(&self) -> (&HomomorphicCommitment<P>, &P, &K, &K, &K) {
(
&self.ephemeral_commitment,
&self.ephemeral_pubkey,
&self.u_a,
&self.u_x,
&self.u_y,
)
}
pub fn u_a(&self) -> &K {
&self.u_a
}
pub fn u_x(&self) -> &K {
&self.u_x
}
pub fn u_y(&self) -> &K {
&self.u_y
}
pub fn ephemeral_commitment(&self) -> &HomomorphicCommitment<P> {
&self.ephemeral_commitment
}
pub fn ephemeral_pubkey(&self) -> &P {
&self.ephemeral_pubkey
}
pub fn to_vec(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(2 * P::key_length() + 3 * K::key_length());
buf.extend_from_slice(self.ephemeral_commitment().as_bytes());
buf.extend_from_slice(self.ephemeral_pubkey().as_bytes());
buf.extend_from_slice(self.u_a().as_bytes());
buf.extend_from_slice(self.u_x().as_bytes());
buf.extend_from_slice(self.u_y().as_bytes());
buf
}
}
impl<'a, 'b, P, K> Add<&'b CommitmentAndPublicKeySignature<P, K>> for &'a CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
&'a HomomorphicCommitment<P>: Add<&'b HomomorphicCommitment<P>, Output = HomomorphicCommitment<P>>,
&'a P: Add<&'b P, Output = P>,
K: SecretKey,
&'a K: Add<&'b K, Output = K>,
{
type Output = CommitmentAndPublicKeySignature<P, K>;
fn add(self, rhs: &'b CommitmentAndPublicKeySignature<P, K>) -> CommitmentAndPublicKeySignature<P, K> {
let ephemeral_commitment_sum = self.ephemeral_commitment() + rhs.ephemeral_commitment();
let ephemeral_pubkey_sum_sum = self.ephemeral_pubkey() + rhs.ephemeral_pubkey();
let u_a_sum = self.u_a() + rhs.u_a();
let u_x_sum = self.u_x() + rhs.u_x();
let u_y_sum = self.u_y() + rhs.u_y();
CommitmentAndPublicKeySignature::new(
ephemeral_commitment_sum,
ephemeral_pubkey_sum_sum,
u_a_sum,
u_x_sum,
u_y_sum,
)
}
}
impl<'a, P, K> Add<CommitmentAndPublicKeySignature<P, K>> for &'a CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
for<'b> &'a HomomorphicCommitment<P>: Add<&'b HomomorphicCommitment<P>, Output = HomomorphicCommitment<P>>,
for<'b> &'a P: Add<&'b P, Output = P>,
K: SecretKey,
for<'b> &'a K: Add<&'b K, Output = K>,
{
type Output = CommitmentAndPublicKeySignature<P, K>;
fn add(self, rhs: CommitmentAndPublicKeySignature<P, K>) -> CommitmentAndPublicKeySignature<P, K> {
let ephemeral_commitment_sum = self.ephemeral_commitment() + rhs.ephemeral_commitment();
let ephemeral_pubkey_sum_sum = self.ephemeral_pubkey() + rhs.ephemeral_pubkey();
let u_a_sum = self.u_a() + rhs.u_a();
let u_x_sum = self.u_x() + rhs.u_x();
let u_y_sum = self.u_y() + rhs.u_y();
CommitmentAndPublicKeySignature::new(
ephemeral_commitment_sum,
ephemeral_pubkey_sum_sum,
u_a_sum,
u_x_sum,
u_y_sum,
)
}
}
impl<'a, 'b, P, K> Add<&'b SchnorrSignature<P, K>> for &'a CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
&'a HomomorphicCommitment<P>: Add<&'b HomomorphicCommitment<P>, Output = HomomorphicCommitment<P>>,
&'a P: Add<&'b P, Output = P>,
K: SecretKey,
&'a K: Add<&'b K, Output = K>,
{
type Output = CommitmentAndPublicKeySignature<P, K>;
fn add(self, rhs: &'b SchnorrSignature<P, K>) -> CommitmentAndPublicKeySignature<P, K> {
let ephemeral_commitment_sum = self.ephemeral_commitment().clone();
let ephemeral_pubkey_sum_sum = self.ephemeral_pubkey() + rhs.get_public_nonce();
let u_a_sum = self.u_a().clone();
let u_x_sum = self.u_x().clone();
let u_y_sum = self.u_y() + rhs.get_signature();
CommitmentAndPublicKeySignature::new(
ephemeral_commitment_sum,
ephemeral_pubkey_sum_sum,
u_a_sum,
u_x_sum,
u_y_sum,
)
}
}
impl<'a, P, K> Add<SchnorrSignature<P, K>> for &'a CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
for<'b> &'a HomomorphicCommitment<P>: Add<&'b HomomorphicCommitment<P>, Output = HomomorphicCommitment<P>>,
for<'b> &'a P: Add<&'b P, Output = P>,
K: SecretKey,
for<'b> &'a K: Add<&'b K, Output = K>,
{
type Output = CommitmentAndPublicKeySignature<P, K>;
fn add(self, rhs: SchnorrSignature<P, K>) -> CommitmentAndPublicKeySignature<P, K> {
let ephemeral_commitment_sum = self.ephemeral_commitment().clone();
let ephemeral_pubkey_sum_sum = self.ephemeral_pubkey() + rhs.get_public_nonce();
let u_a_sum = self.u_a().clone();
let u_x_sum = self.u_x().clone();
let u_y_sum = self.u_y() + rhs.get_signature();
CommitmentAndPublicKeySignature::new(
ephemeral_commitment_sum,
ephemeral_pubkey_sum_sum,
u_a_sum,
u_x_sum,
u_y_sum,
)
}
}
impl<P, K> Default for CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
K: SecretKey,
{
fn default() -> Self {
CommitmentAndPublicKeySignature::new(
HomomorphicCommitment::<P>::default(),
P::default(),
K::default(),
K::default(),
K::default(),
)
}
}
impl<P, K> Ord for CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
K: SecretKey,
{
fn cmp(&self, other: &Self) -> Ordering {
let mut compare = self.ephemeral_commitment().cmp(other.ephemeral_commitment());
if compare != Ordering::Equal {
return compare;
}
compare = self.ephemeral_pubkey().cmp(other.ephemeral_pubkey());
if compare != Ordering::Equal {
return compare;
}
compare = self.u_a().as_bytes().cmp(other.u_a().as_bytes());
if compare != Ordering::Equal {
return compare;
}
compare = self.u_x().as_bytes().cmp(other.u_x().as_bytes());
if compare != Ordering::Equal {
return compare;
}
self.u_y().as_bytes().cmp(other.u_y().as_bytes())
}
}
impl<P, K> PartialOrd for CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
K: SecretKey,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<P, K> PartialEq for CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
K: SecretKey,
{
fn eq(&self, other: &Self) -> bool {
self.ephemeral_commitment().eq(other.ephemeral_commitment()) &&
self.ephemeral_pubkey().eq(other.ephemeral_pubkey()) &&
self.u_a().eq(other.u_a()) &&
self.u_x().eq(other.u_x()) &&
self.u_y().eq(other.u_y())
}
}
impl<P, K> Eq for CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
K: SecretKey,
{
}
impl<P, K> Hash for CommitmentAndPublicKeySignature<P, K>
where
P: PublicKey<K = K>,
K: SecretKey,
{
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.to_vec())
}
}