diff --git a/crypto/transcript/Cargo.toml b/crypto/transcript/Cargo.toml index b75d7173..b7a6d9d0 100644 --- a/crypto/transcript/Cargo.toml +++ b/crypto/transcript/Cargo.toml @@ -18,6 +18,10 @@ digest = "0.10" blake2 = { version = "0.10", optional = true } merlin = { version = "3", optional = true } +[dev-dependencies] +blake2 = "0.10" + [features] recommended = ["blake2"] merlin = ["dep:merlin"] +tests = [] diff --git a/crypto/transcript/src/lib.rs b/crypto/transcript/src/lib.rs index 455b3df0..2f4ef615 100644 --- a/crypto/transcript/src/lib.rs +++ b/crypto/transcript/src/lib.rs @@ -6,6 +6,9 @@ mod merlin; #[cfg(feature = "merlin")] pub use crate::merlin::MerlinTranscript; +#[cfg(any(test, feature = "tests"))] +pub mod tests; + use digest::{ typenum::{ consts::U32, marker_traits::NonZero, type_operators::IsGreaterOrEqual, operator_aliases::GrEq, diff --git a/crypto/transcript/src/tests.rs b/crypto/transcript/src/tests.rs new file mode 100644 index 00000000..0a461ba5 --- /dev/null +++ b/crypto/transcript/src/tests.rs @@ -0,0 +1,95 @@ +use crate::Transcript; + +pub fn test_transcript() +where + T::Challenge: PartialEq, +{ + // Ensure distinct names cause distinct challenges + { + let mut t1 = T::new(b"1"); + let mut t2 = T::new(b"2"); + assert!(t1.challenge(b"c") != t2.challenge(b"c")); + } + + // Ensure names can't lead into labels + { + let mut t1 = T::new(b"12"); + let c1 = t1.challenge(b"c"); + let mut t2 = T::new(b"1"); + let c2 = t2.challenge(b"2c"); + assert!(c1 != c2); + } + + let t = || T::new(b"name"); + let c = |mut t: T| t.challenge(b"c"); + + // Ensure domain separators do something + { + let mut t1 = t(); + t1.domain_separate(b"d"); + assert!(c(t1) != c(t())); + } + + // Ensure distinct domain separators create distinct challenges + { + let mut t1 = t(); + let mut t2 = t(); + t1.domain_separate(b"d1"); + t2.domain_separate(b"d2"); + assert!(c(t1) != c(t2)); + } + + // Ensure distinct messages create distinct challenges + { + // By label + { + let mut t1 = t(); + let mut t2 = t(); + t1.append_message(b"msg", b"a"); + t2.append_message(b"msg", b"b"); + assert!(c(t1) != c(t2)); + } + + // By value + { + let mut t1 = t(); + let mut t2 = t(); + t1.append_message(b"a", b"val"); + t2.append_message(b"b", b"val"); + assert!(c(t1) != c(t2)); + } + } + + // Ensure challenges advance the transcript + { + let mut t = t(); + let c1 = t.challenge(b"c"); + let c2 = t.challenge(b"c"); + assert!(c1 != c2); + } + + // Ensure distinct challenge labels produce distinct challenges + assert!(t().challenge(b"a") != t().challenge(b"b")); + + // Ensure RNG seed calls advance the transcript + { + let mut t = t(); + let s1 = t.rng_seed(b"s"); + let s2 = t.rng_seed(b"s"); + assert!(s1 != s2); + } + + // Ensure distinct RNG seed labels produce distinct seeds + assert!(t().rng_seed(b"a") != t().rng_seed(b"b")); +} + +#[test] +fn test_digest() { + test_transcript::>(); +} + +#[cfg(feature = "merlin")] +#[test] +fn test_merlin() { + test_transcript::(); +}