use crate::{
ristretto::{RistrettoPublicKey, RistrettoSecretKey},
signatures::{CommitmentAndPublicKeySignature, CompressedCommitmentAndPublicKeySignature},
};
pub type RistrettoComAndPubSig = CommitmentAndPublicKeySignature<RistrettoPublicKey, RistrettoSecretKey>;
pub type CompressedRistrettoComAndPubSig =
CompressedCommitmentAndPublicKeySignature<RistrettoPublicKey, RistrettoSecretKey>;
#[cfg(test)]
mod test {
use blake2::Blake2b;
use digest::{Digest, consts::U64};
use rand_core::Rng;
use tari_utilities::ByteArray;
use crate::{
commitment::{HomomorphicCommitment, HomomorphicCommitmentFactory},
keys::{PublicKey, SecretKey},
ristretto::{
RistrettoComAndPubSig,
RistrettoPublicKey,
RistrettoSchnorr,
RistrettoSecretKey,
pedersen::{PedersenCommitment, commitment_factory::PedersenCommitmentFactory},
},
};
#[test]
fn default() {
let sig = RistrettoComAndPubSig::default();
assert_eq!(
sig.complete_signature_tuple(),
(
&PedersenCommitment::default(),
&RistrettoPublicKey::default(),
&RistrettoSecretKey::default(),
&RistrettoSecretKey::default(),
&RistrettoSecretKey::default()
)
);
assert_eq!(sig.ephemeral_commitment(), &PedersenCommitment::default());
assert_eq!(sig.ephemeral_pubkey(), &RistrettoPublicKey::default());
assert_eq!(sig.u_a(), &RistrettoSecretKey::default());
assert_eq!(sig.u_x(), &RistrettoSecretKey::default());
assert_eq!(sig.u_y(), &RistrettoSecretKey::default());
}
#[test]
fn sign_and_verify_message() {
let mut rng = rand::rng();
let a_value = RistrettoSecretKey::random(&mut rng);
let x_value = RistrettoSecretKey::random(&mut rng);
let y_value = RistrettoSecretKey::random(&mut rng);
let factory = PedersenCommitmentFactory::default();
let commitment = factory.commit(&x_value, &a_value);
let pubkey = RistrettoPublicKey::from_secret_key(&y_value);
let r_a = RistrettoSecretKey::random(&mut rng);
let r_x = RistrettoSecretKey::random(&mut rng);
let r_y = RistrettoSecretKey::random(&mut rng);
let ephemeral_commitment = factory.commit(&r_x, &r_a);
let ephemeral_pubkey = RistrettoPublicKey::from_secret_key(&r_y);
let challenge = Blake2b::<U64>::new()
.chain_update(commitment.as_bytes())
.chain_update(pubkey.as_bytes())
.chain_update(ephemeral_commitment.as_bytes())
.chain_update(ephemeral_pubkey.as_bytes())
.chain_update(b"Small Gods")
.finalize();
let e_key = RistrettoSecretKey::from_uniform_bytes(&challenge).unwrap();
let u_a = &r_a + e_key.clone() * &a_value;
let u_x = &r_x + e_key.clone() * &x_value;
let u_y = &r_y + e_key * &y_value;
let sig =
RistrettoComAndPubSig::sign(&a_value, &x_value, &y_value, &r_a, &r_x, &r_y, &challenge, &factory).unwrap();
assert_eq!(*sig.ephemeral_commitment(), ephemeral_commitment);
assert_eq!(*sig.ephemeral_pubkey(), ephemeral_pubkey);
assert_eq!(*sig.u_a(), u_a);
assert_eq!(*sig.u_x(), u_x);
assert_eq!(*sig.u_y(), u_y);
assert_eq!(
sig.complete_signature_tuple(),
(&ephemeral_commitment, &ephemeral_pubkey, &u_a, &u_x, &u_y)
);
assert!(sig.verify_challenge(&commitment, &pubkey, &challenge, &factory, &mut rng));
let evil_a = RistrettoSecretKey::random(&mut rng);
let evil_x = RistrettoSecretKey::random(&mut rng);
let evil_commitment = factory.commit(&evil_x, &evil_a);
let evil_y = RistrettoSecretKey::random(&mut rng);
let evil_pubkey = RistrettoPublicKey::from_secret_key(&evil_y);
assert!(!sig.verify_challenge(&evil_commitment, &pubkey, &challenge, &factory, &mut rng));
assert!(!sig.verify_challenge(&commitment, &evil_pubkey, &challenge, &factory, &mut rng));
let evil_challenge = Blake2b::<U64>::digest(b"Guards! Guards!");
assert!(!sig.verify_challenge(&commitment, &pubkey, &evil_challenge, &factory, &mut rng));
}
#[test]
fn sign_and_verify_message_partial() {
let mut rng = rand::rng();
let a_value = RistrettoSecretKey::random(&mut rng);
let x_value = RistrettoSecretKey::random(&mut rng);
let y_value = RistrettoSecretKey::random(&mut rng);
let factory = PedersenCommitmentFactory::default();
let commitment = factory.commit(&x_value, &a_value);
let pubkey = RistrettoPublicKey::from_secret_key(&y_value);
let r_a = RistrettoSecretKey::random(&mut rng);
let r_x = RistrettoSecretKey::random(&mut rng);
let r_y = RistrettoSecretKey::random(&mut rng);
let ephemeral_commitment = factory.commit(&r_x, &r_a);
let ephemeral_pubkey = RistrettoPublicKey::from_secret_key(&r_y);
let challenge = Blake2b::<U64>::new()
.chain_update(commitment.as_bytes())
.chain_update(pubkey.as_bytes())
.chain_update(ephemeral_commitment.as_bytes())
.chain_update(ephemeral_pubkey.as_bytes())
.chain_update(b"Small Gods")
.finalize();
let sig_total =
RistrettoComAndPubSig::sign(&a_value, &x_value, &y_value, &r_a, &r_x, &r_y, &challenge, &factory).unwrap();
let default_pk = RistrettoSecretKey::default();
let sig_p_1 = RistrettoComAndPubSig::sign(
&a_value,
&x_value,
&default_pk,
&r_a,
&r_x,
&default_pk,
&challenge,
&factory,
)
.unwrap();
assert!(!sig_p_1.verify_challenge(&commitment, &pubkey, &challenge, &factory, &mut rng));
let sig_p_2 = RistrettoComAndPubSig::sign(
&default_pk,
&default_pk,
&y_value,
&default_pk,
&default_pk,
&r_y,
&challenge,
&factory,
)
.unwrap();
assert!(!sig_p_2.verify_challenge(&commitment, &pubkey, &challenge, &factory, &mut rng));
let sig_p_total = &sig_p_1 + &sig_p_2;
assert_eq!(sig_p_total, sig_total);
assert!(sig_p_total.verify_challenge(&commitment, &pubkey, &challenge, &factory, &mut rng));
}
#[test]
fn sign_and_verify_combined_schnorr() {
let mut rng = rand::rng();
let challenge = Blake2b::<U64>::new().chain_update(b"Small Gods").finalize();
let a_value = RistrettoSecretKey::random(&mut rng);
let x_value = RistrettoSecretKey::random(&mut rng);
let y_value = RistrettoSecretKey::random(&mut rng);
let factory = PedersenCommitmentFactory::default();
let commitment = factory.commit(&x_value, &a_value);
let pubkey = RistrettoPublicKey::from_secret_key(&y_value);
let r_a = RistrettoSecretKey::random(&mut rng);
let r_x = RistrettoSecretKey::random(&mut rng);
let r_y = RistrettoSecretKey::random(&mut rng);
let sig_capk =
RistrettoComAndPubSig::sign(&a_value, &x_value, &y_value, &r_a, &r_x, &r_y, &challenge, &factory).unwrap();
assert!(sig_capk.verify_challenge(&commitment, &pubkey, &challenge, &factory, &mut rng));
let (k, pub_k) = RistrettoPublicKey::random_keypair(&mut rng);
let r = RistrettoSecretKey::random(&mut rng);
let total_y_value = &y_value + &k;
let total_r_y = &r_y + &r;
let sig = RistrettoSchnorr::sign_raw_uniform(&k, r, &challenge).unwrap();
assert!(sig.verify_raw_uniform(&pub_k, &challenge));
let total_pubkey = RistrettoPublicKey::from_secret_key(&total_y_value);
let total_sig_capk = RistrettoComAndPubSig::sign(
&a_value,
&x_value,
&total_y_value,
&r_a,
&r_x,
&total_r_y,
&challenge,
&factory,
)
.unwrap();
assert!(total_sig_capk.verify_challenge(&commitment, &total_pubkey, &challenge, &factory, &mut rng));
let agg_sig = &sig_capk + &sig;
assert!(agg_sig.verify_challenge(&commitment, &total_pubkey, &challenge, &factory, &mut rng));
assert_eq!(agg_sig, total_sig_capk);
}
#[test]
fn test_signature_addition() {
let mut rng = rand::rng();
let factory = PedersenCommitmentFactory::default();
let a_value_alice = RistrettoSecretKey::random(&mut rng);
let x_value_alice = RistrettoSecretKey::random(&mut rng);
let commitment_alice = factory.commit(&x_value_alice, &a_value_alice);
let y_value_alice = RistrettoSecretKey::random(&mut rng);
let pubkey_alice = RistrettoPublicKey::from_secret_key(&y_value_alice);
let r_a_alice = RistrettoSecretKey::random(&mut rng);
let r_x_alice = RistrettoSecretKey::random(&mut rng);
let r_y_alice = RistrettoSecretKey::random(&mut rng);
let ephemeral_commitment_alice = factory.commit(&r_x_alice, &r_a_alice);
let ephemeral_pubkey_alice = RistrettoPublicKey::from_secret_key(&r_y_alice);
let a_value_bob = RistrettoSecretKey::random(&mut rng);
let x_value_bob = RistrettoSecretKey::random(&mut rng);
let commitment_bob = factory.commit(&x_value_bob, &a_value_bob);
let y_value_bob = RistrettoSecretKey::random(&mut rng);
let pubkey_bob = RistrettoPublicKey::from_secret_key(&y_value_bob);
let r_a_bob = RistrettoSecretKey::random(&mut rng);
let r_x_bob = RistrettoSecretKey::random(&mut rng);
let r_y_bob = RistrettoSecretKey::random(&mut rng);
let ephemeral_commitment_bob = factory.commit(&r_x_bob, &r_a_bob);
let ephemeral_pubkey_bob = RistrettoPublicKey::from_secret_key(&r_y_bob);
let challenge = Blake2b::<U64>::digest(b"Test challenge");
let sig_alice = RistrettoComAndPubSig::sign(
&a_value_alice,
&x_value_alice,
&y_value_alice,
&r_a_alice,
&r_x_alice,
&r_y_alice,
&challenge,
&factory,
)
.unwrap();
let sig_bob = RistrettoComAndPubSig::sign(
&a_value_bob,
&x_value_bob,
&y_value_bob,
&r_a_bob,
&r_x_bob,
&r_y_bob,
&challenge,
&factory,
)
.unwrap();
let sig_sum = &sig_alice + &sig_bob;
assert_eq!(
*sig_sum.ephemeral_commitment(),
&ephemeral_commitment_alice + &ephemeral_commitment_bob
);
assert_eq!(
*sig_sum.ephemeral_pubkey(),
&ephemeral_pubkey_alice + &ephemeral_pubkey_bob
);
let commitment_sum = &commitment_alice + &commitment_bob;
let pubkey_sum = &pubkey_alice + &pubkey_bob;
assert!(sig_sum.verify_challenge(&commitment_sum, &pubkey_sum, &challenge, &factory, &mut rng))
}
#[test]
fn to_vec() {
let sig = RistrettoComAndPubSig::default();
let bytes = sig.to_vec();
assert_eq!(
bytes.capacity(),
2 * RistrettoPublicKey::key_length() + 3 * RistrettoSecretKey::key_length()
);
assert_eq!(bytes.capacity(), bytes.len());
assert!(bytes.iter().all(|b| *b == 0x00));
}
#[test]
fn zero_commitment() {
let mut rng = rand::rng();
let factory = PedersenCommitmentFactory::default();
let a = RistrettoSecretKey::default();
let x = RistrettoSecretKey::default();
let commitment = factory.commit(&x, &a);
assert_eq!(commitment, HomomorphicCommitment::<RistrettoPublicKey>::default());
let y = RistrettoSecretKey::random(&mut rng);
let public_key = RistrettoPublicKey::from_secret_key(&y);
let mut challenge = [0u8; RistrettoSecretKey::WIDE_REDUCTION_LEN];
rng.fill_bytes(&mut challenge);
let sig = RistrettoComAndPubSig::sign(
&a,
&x,
&y,
&RistrettoSecretKey::random(&mut rng),
&RistrettoSecretKey::random(&mut rng),
&RistrettoSecretKey::random(&mut rng),
&challenge,
&factory,
)
.unwrap();
assert!(!sig.verify_challenge(&commitment, &public_key, &challenge, &factory, &mut rng));
}
#[test]
fn zero_public_key() {
let mut rng = rand::rng();
let factory = PedersenCommitmentFactory::default();
let a = RistrettoSecretKey::random(&mut rng);
let x = RistrettoSecretKey::random(&mut rng);
let commitment = factory.commit(&x, &a);
let y = RistrettoSecretKey::default();
let public_key = RistrettoPublicKey::from_secret_key(&y);
assert_eq!(public_key, RistrettoPublicKey::default());
let mut challenge = [0u8; RistrettoSecretKey::WIDE_REDUCTION_LEN];
rng.fill_bytes(&mut challenge);
let sig = RistrettoComAndPubSig::sign(
&a,
&x,
&y,
&RistrettoSecretKey::random(&mut rng),
&RistrettoSecretKey::random(&mut rng),
&RistrettoSecretKey::random(&mut rng),
&challenge,
&factory,
)
.unwrap();
assert!(!sig.verify_challenge(&commitment, &public_key, &challenge, &factory, &mut rng));
}
}