use crate::{
ristretto::{RistrettoPublicKey, RistrettoSecretKey},
signatures::{CompressedSchnorrSignature, SchnorrSigChallenge, SchnorrSignature},
};
pub type RistrettoSchnorr = SchnorrSignature<RistrettoPublicKey, RistrettoSecretKey, SchnorrSigChallenge>;
pub type CompressedRistrettoSchnorr =
CompressedSchnorrSignature<RistrettoPublicKey, RistrettoSecretKey, SchnorrSigChallenge>;
pub type RistrettoSchnorrWithDomain<H> = SchnorrSignature<RistrettoPublicKey, RistrettoSecretKey, H>;
#[cfg(test)]
mod test {
use blake2::Blake2b;
use digest::{Digest, consts::U64};
use tari_utilities::{
ByteArray,
hex::{Hex, to_hex},
};
use crate::{
hash_domain,
keys::{PublicKey, SecretKey},
ristretto::{
RistrettoPublicKey,
RistrettoSchnorr,
RistrettoSecretKey,
ristretto_sig::RistrettoSchnorrWithDomain,
},
signatures::{SchnorrSigChallenge, SchnorrSignature},
};
#[test]
fn default() {
let sig = RistrettoSchnorr::default();
assert_eq!(sig.get_signature(), &RistrettoSecretKey::default());
assert_eq!(sig.get_public_nonce(), &RistrettoPublicKey::default());
}
#[test]
#[allow(non_snake_case)]
fn raw_sign_and_verify_challenge() {
let mut rng = rand::rng();
let (k, P) = RistrettoPublicKey::random_keypair(&mut rng);
let (r, R) = RistrettoPublicKey::random_keypair(&mut rng);
let e = Blake2b::<U64>::new()
.chain_update(P.as_bytes())
.chain_update(R.as_bytes())
.chain_update(b"Small Gods")
.finalize();
let e_key = RistrettoSecretKey::from_uniform_bytes(&e).unwrap();
let s = &r + &e_key * &k;
let sig = RistrettoSchnorr::sign_raw_uniform(&k, r, &e).unwrap();
let R_calc = sig.get_public_nonce();
assert_eq!(R, *R_calc);
assert_eq!(sig.get_signature(), &s);
assert!(sig.verify_raw_uniform(&P, &e));
assert!(!sig.verify_raw_uniform(&R, &e));
let wrong_challenge = Blake2b::<U64>::digest(b"Guards! Guards!");
assert!(!sig.verify_raw_uniform(&P, &wrong_challenge));
}
#[test]
#[allow(non_snake_case)]
fn test_signature_addition() {
let mut rng = rand::rng();
let (k1, P1) = RistrettoPublicKey::random_keypair(&mut rng);
let (r1, R1) = RistrettoPublicKey::random_keypair(&mut rng);
let (k2, P2) = RistrettoPublicKey::random_keypair(&mut rng);
let (r2, R2) = RistrettoPublicKey::random_keypair(&mut rng);
let e = Blake2b::<U64>::new()
.chain_update(R1.as_bytes())
.chain_update(R2.as_bytes())
.chain_update(P1.as_bytes())
.chain_update(P2.as_bytes())
.chain_update(b"Moving Pictures")
.finalize();
let s1 = RistrettoSchnorr::sign_raw_uniform(&k1, r1, &e).unwrap();
let s2 = RistrettoSchnorr::sign_raw_uniform(&k2, r2, &e).unwrap();
let s_agg = &s1 + &s2;
assert!(s_agg.verify_raw_uniform(&(P1 + P2), &e));
}
#[test]
#[allow(non_snake_case)]
fn domain_separated_challenge() {
let P =
RistrettoPublicKey::from_hex("74896a30c89186b8194e25f8c1382f8d3081c5a182fb8f8a6d34f27fbefbfc70").unwrap();
let R =
RistrettoPublicKey::from_hex("fa14cb581ce5717248444721242e6b195a482d503a853dea4acb513074d8d803").unwrap();
let msg = "Moving Pictures";
let hash = SchnorrSignature::<_, _, SchnorrSigChallenge>::construct_domain_separated_challenge::<_, Blake2b<U64>>(
&R, &P, msg,
);
let naiive = Blake2b::<U64>::new()
.chain_update(R.as_bytes())
.chain_update(P.as_bytes())
.chain_update(msg)
.finalize()
.to_vec();
assert_ne!(hash.as_ref(), naiive.as_bytes());
assert_eq!(
to_hex(hash.as_ref()),
"2db0656c9dd1482bf61d32f157726b05a88d567c31107bed9a5c60a02119518af35929f360726bffd846439ab12e7c9f4983cf5fab5ea735422e05e0f560ddfd"
);
}
#[test]
#[allow(non_snake_case)]
fn custom_hash_domain() {
hash_domain!(TestDomain, "test.signature.com");
let mut rng = rand::rng();
let (k, P) = RistrettoPublicKey::random_keypair(&mut rng);
let (r, _) = RistrettoPublicKey::random_keypair(&mut rng);
let msg = "Moving Pictures";
let sig1 = RistrettoSchnorr::sign_with_nonce_and_message(&k, r.clone(), msg).unwrap();
let sig2 = RistrettoSchnorrWithDomain::<TestDomain>::sign_with_nonce_and_message(&k, r, msg).unwrap();
assert_eq!(sig1.get_public_nonce(), sig2.get_public_nonce());
assert!(sig1.verify(&P, msg));
assert!(sig2.verify(&P, msg));
assert_ne!(sig1.get_signature(), sig2.get_signature());
}
#[test]
#[allow(non_snake_case)]
fn sign_and_verify_message() {
let mut rng = rand::rng();
let (k, P) = RistrettoPublicKey::random_keypair(&mut rng);
let sig = RistrettoSchnorr::sign(&k, "Queues are things that happen to other people", &mut rng).unwrap();
assert!(sig.verify(&P, "Queues are things that happen to other people"));
assert!(!sig.verify(&P, "Qs are things that happen to other people"));
assert!(!sig.verify(&(&P + &P), "Queues are things that happen to other people"));
}
#[test]
fn zero_public_key() {
let mut rng = rand::rng();
let secret_key = RistrettoSecretKey::default();
let public_key = RistrettoPublicKey::from_secret_key(&secret_key);
assert_eq!(public_key, RistrettoPublicKey::default());
let message = "A secret message";
let sig = RistrettoSchnorr::sign(&secret_key, message, &mut rng).unwrap();
assert!(!sig.verify(&public_key, message,));
}
}