1use crate::{BatchVerifier, Signer, Verifier};
7use blake3::BLOCK_LEN;
8use bytes::Buf;
9use commonware_codec::{
10 EncodeSize, FixedArray, FixedSize, Read, ReadExt, Write,
11 varint::{MAX_U64_VARINT_SIZE, UInt},
12};
13use commonware_math::algebra::Random;
14#[commonware_macros::stability(ALPHA)]
15use commonware_utils::NZU64;
16use commonware_utils::{Array, Span};
17#[commonware_macros::stability(ALPHA)]
18use core::num::NonZeroU64;
19use core::{convert::Infallible, fmt::Display, ops::Deref};
20use rand_core::{CryptoRng, TryCryptoRng, TryRng};
21use zeroize::ZeroizeOnDrop;
22
23#[derive(ZeroizeOnDrop)]
28struct Rng {
29 inner: blake3::OutputReader,
30 buf: [u8; BLOCK_LEN],
31 start: usize,
32}
33
34impl Rng {
35 const fn new(inner: blake3::OutputReader) -> Self {
36 Self {
37 inner,
38 buf: [0u8; BLOCK_LEN],
39 start: BLOCK_LEN,
40 }
41 }
42}
43
44impl TryRng for Rng {
45 type Error = Infallible;
46
47 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
48 let mut bytes = [0u8; 4];
49 self.try_fill_bytes(&mut bytes)?;
50 Ok(u32::from_le_bytes(bytes))
51 }
52
53 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
54 let mut bytes = [0u8; 8];
55 self.try_fill_bytes(&mut bytes)?;
56 Ok(u64::from_le_bytes(bytes))
57 }
58
59 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
60 let dest_len = dest.len();
61 let remaining = &self.buf[self.start..];
62 if remaining.len() >= dest_len {
63 dest.copy_from_slice(&remaining[..dest_len]);
64 self.start += dest_len;
65 return Ok(());
66 }
67
68 let (start, mut dest) = dest.split_at_mut(remaining.len());
69 start.copy_from_slice(remaining);
70 self.start = BLOCK_LEN;
71
72 while dest.len() >= BLOCK_LEN {
73 let (block, rest) = dest.split_at_mut(BLOCK_LEN);
74 self.inner.fill(block);
75 dest = rest;
76 }
77
78 let dest_len = dest.len();
79 if dest_len > 0 {
80 self.inner.fill(&mut self.buf[..]);
81 dest.copy_from_slice(&self.buf[..dest_len]);
82 self.start = dest_len;
83 }
84
85 Ok(())
86 }
87}
88
89impl TryCryptoRng for Rng {}
90
91fn flush(hasher: &mut blake3::Hasher, pending: u64, version: Version) {
92 let mut pending_bytes = [0u8; MAX_U64_VARINT_SIZE];
93 let pending = UInt(pending);
94 pending.write(&mut &mut pending_bytes[..]);
95 let length = &mut pending_bytes[..pending.encode_size()];
96 version.frame_length(length);
97 hasher.update(length);
98}
99
100#[repr(u8)]
102#[derive(Clone, Copy)]
103enum StartTag {
104 New = 0,
105 Resume = 1,
106 Fork = 2,
107 Noise = 3,
108}
109
110#[non_exhaustive]
117#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
118pub enum Version {
119 V0,
178 V1,
212}
213
214impl Version {
215 const fn frame_length(self, length: &mut [u8]) {
217 match self {
218 Self::V0 => {}
219 Self::V1 => length.reverse(),
220 }
221 }
222}
223
224#[derive(ZeroizeOnDrop)]
231pub struct Transcript {
232 hasher: blake3::Hasher,
233 pending: u64,
234 #[zeroize(skip)]
235 version: Version,
236}
237
238impl Transcript {
239 fn start(tag: StartTag, summary: Option<Summary>, version: Version) -> Self {
240 let mut hasher = summary.map_or_else(blake3::Hasher::new, |s| {
245 blake3::Hasher::new_keyed(s.hash.as_bytes())
246 });
247 hasher.update(&[tag as u8]);
248 Self {
249 hasher,
250 pending: 0,
251 version,
252 }
253 }
254
255 fn flush(&mut self) {
256 flush(&mut self.hasher, self.pending, self.version);
257 self.pending = 0;
258 }
259
260 const fn unflushed(&self) -> bool {
261 self.pending != 0
262 }
263}
264
265impl Transcript {
266 pub fn new(namespace: &[u8], version: Version) -> Self {
277 let mut out = Self::start(StartTag::New, None, version);
278 out.commit(namespace);
279 out
280 }
281
282 pub fn resume(summary: Summary, version: Version) -> Self {
293 Self::start(StartTag::Resume, Some(summary), version)
294 }
295
296 pub fn commit(&mut self, data: impl Buf) -> &mut Self {
316 self.append(data);
317 self.flush();
318 self
319 }
320
321 pub fn append(&mut self, mut data: impl Buf) -> &mut Self {
337 let length =
338 u64::try_from(data.remaining()).expect("transcript packet length does not fit in u64");
339 let pending = self
340 .pending
341 .checked_add(length)
342 .expect("transcript packet exceeds u64::MAX bytes");
343
344 while data.has_remaining() {
345 let chunk = data.chunk();
346 self.hasher.update(chunk);
347 data.advance(chunk.len());
348 }
349 self.pending = pending;
350 self
351 }
352
353 pub fn fork(&self, label: &'static [u8]) -> Self {
364 let mut out = Self::start(StartTag::Fork, Some(self.summarize()), self.version);
365 out.commit(label);
366 out
367 }
368
369 pub fn noise(&self, label: &'static [u8]) -> impl CryptoRng + use<> {
378 let mut out = Self::start(StartTag::Noise, Some(self.summarize()), self.version);
379 out.commit(label);
380 Rng::new(out.hasher.finalize_xof())
381 }
382
383 #[commonware_macros::stability(ALPHA)]
400 pub fn shuffle<T>(&self, label: &'static [u8], items: &mut [T]) {
401 let mut rng = self.noise(label);
402 for i in (1..items.len()).rev() {
403 let j = sample(&mut rng, NZU64!(i as u64 + 1));
404 items.swap(i, j as usize);
405 }
406 }
407
408 #[commonware_macros::stability(ALPHA)]
420 pub fn sample(&self, label: &'static [u8], bound: NonZeroU64) -> u64 {
421 sample(self.noise(label), bound)
422 }
423
424 pub fn summarize(&self) -> Summary {
434 let hash = if self.unflushed() {
435 let mut hasher = self.hasher.clone();
436 flush(&mut hasher, self.pending, self.version);
437 hasher.finalize()
438 } else {
439 self.hasher.finalize()
440 };
441 Summary { hash }
442 }
443}
444
445#[commonware_macros::stability(ALPHA)]
447fn sample(mut rng: impl CryptoRng, bound: NonZeroU64) -> u64 {
448 let bound = bound.get();
449
450 let zone = bound * (u64::MAX / bound);
453 loop {
454 let v = rng.next_u64();
455 if v < zone {
456 return v % bound;
457 }
458 }
459}
460
461impl Transcript {
463 pub fn sign<S: Signer>(&self, s: &S) -> <S as Signer>::Signature {
469 self.summarize().sign(s)
470 }
471
472 pub fn verify<V: Verifier>(&self, v: &V, sig: &<V as Verifier>::Signature) -> bool {
474 self.summarize().verify(v, sig)
475 }
476
477 pub fn add_to_batch<B: BatchVerifier>(
479 &self,
480 batch: &mut B,
481 public_key: &B::PublicKey,
482 signature: &<B::PublicKey as Verifier>::Signature,
483 ) -> bool {
484 self.summarize().add_to_batch(batch, public_key, signature)
485 }
486}
487
488impl Summary {
489 pub fn sign<S: Signer>(&self, s: &S) -> <S as Signer>::Signature {
491 s.sign(b"", self.as_ref())
494 }
495
496 pub fn verify<V: Verifier>(&self, v: &V, sig: &<V as Verifier>::Signature) -> bool {
498 v.verify(b"", self.as_ref(), sig)
501 }
502
503 pub fn add_to_batch<B: BatchVerifier>(
505 &self,
506 batch: &mut B,
507 public_key: &B::PublicKey,
508 signature: &<B::PublicKey as Verifier>::Signature,
509 ) -> bool {
510 batch.add(b"", self.as_ref(), public_key, signature)
513 }
514}
515
516#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FixedArray)]
522pub struct Summary {
523 hash: blake3::Hash,
524}
525
526impl FixedSize for Summary {
527 const SIZE: usize = blake3::OUT_LEN;
528}
529
530impl Write for Summary {
531 fn write(&self, buf: &mut impl bytes::BufMut) {
532 self.hash.as_bytes().write(buf)
533 }
534}
535
536impl Read for Summary {
537 type Cfg = ();
538
539 fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
540 Ok(Self {
541 hash: blake3::Hash::from_bytes(ReadExt::read(buf)?),
542 })
543 }
544}
545
546impl AsRef<[u8]> for Summary {
547 fn as_ref(&self) -> &[u8] {
548 self.hash.as_bytes().as_slice()
549 }
550}
551
552impl Deref for Summary {
553 type Target = [u8];
554
555 fn deref(&self) -> &Self::Target {
556 self.as_ref()
557 }
558}
559
560impl PartialOrd for Summary {
561 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
562 Some(self.cmp(other))
563 }
564}
565
566impl Ord for Summary {
567 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
568 self.as_ref().cmp(other.as_ref())
569 }
570}
571
572impl Display for Summary {
573 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
574 write!(f, "{}", commonware_formatting::Hex(self.as_ref()))
575 }
576}
577
578impl Span for Summary {}
579
580impl Array for Summary {}
581
582impl crate::Digest for Summary {
583 const EMPTY: Self = Self {
584 hash: blake3::Hash::from_bytes([0u8; blake3::OUT_LEN]),
585 };
586}
587
588impl Random for Summary {
589 fn random(mut rng: impl CryptoRng) -> Self {
590 let mut bytes = [0u8; blake3::OUT_LEN];
591 rng.fill_bytes(&mut bytes[..]);
592 Self {
593 hash: blake3::Hash::from_bytes(bytes),
594 }
595 }
596}
597
598#[cfg(any(test, feature = "arbitrary"))]
599impl arbitrary::Arbitrary<'_> for Summary {
600 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
601 let bytes: [u8; blake3::OUT_LEN] = u.arbitrary()?;
602 Ok(Self {
603 hash: blake3::Hash::from_bytes(bytes),
604 })
605 }
606}
607
608#[cfg(test)]
609mod test {
610 use super::*;
611 use crate::ed25519;
612 use commonware_codec::{DecodeExt as _, Encode};
613 use commonware_parallel::Sequential;
614 use commonware_utils::test_rng;
615 use rand_core::Rng;
616
617 const V0_VERSION: Version = Version::V0;
618
619 fn v0(namespace: &[u8]) -> Transcript {
620 Transcript::new(namespace, V0_VERSION)
621 }
622
623 fn v1(namespace: &[u8]) -> Transcript {
624 Transcript::new(namespace, Version::V1)
625 }
626
627 #[test]
628 fn test_namespace_affects_summary() {
629 let s1 = v0(b"Test-A").summarize();
630 let s2 = v0(b"Test-B").summarize();
631 assert_ne!(s1, s2);
632 }
633
634 #[test]
635 fn test_namespace_doesnt_leak_into_data() {
636 let s1 = v0(b"Test-A").summarize();
637 let s2 = v0(b"Test-").commit(b"".as_slice()).summarize();
638 assert_ne!(s1, s2);
639 }
640
641 #[test]
642 fn test_commit_separates_data() {
643 let s1 = v0(b"").commit(b"AB".as_slice()).summarize();
644 let s2 = v0(b"")
645 .commit(b"A".as_slice())
646 .commit(b"B".as_slice())
647 .summarize();
648 assert_ne!(s1, s2);
649 }
650
651 #[test]
652 fn test_v1_separates_adversarial_data() {
653 let zeros = [0u8; 127];
654 let split_v0 = v0(b"")
655 .commit(zeros.as_slice())
656 .commit([0x80].as_slice())
657 .summarize();
658
659 let mut merged = zeros.to_vec();
660 merged.push(0x7f);
661 let merged_v0 = v0(b"").commit(merged.as_slice()).summarize();
662 assert_eq!(split_v0, merged_v0);
663
664 let split_v1 = v1(b"")
665 .commit(zeros.as_slice())
666 .commit([0x80].as_slice())
667 .summarize();
668 let merged_v1 = v1(b"").commit(merged.as_slice()).summarize();
669
670 assert_ne!(split_v1, merged_v1);
671 }
672
673 #[test]
674 fn test_flush_supports_maximum_packet_length() {
675 for version in [V0_VERSION, Version::V1] {
676 let mut actual = blake3::Hasher::new();
677 flush(&mut actual, u64::MAX, version);
678
679 let mut length = [0u8; MAX_U64_VARINT_SIZE];
680 let pending = UInt(u64::MAX);
681 pending.write(&mut &mut length[..]);
682 let length = &mut length[..pending.encode_size()];
683 match version {
684 Version::V0 => {}
685 Version::V1 => length.reverse(),
686 }
687
688 let mut expected = blake3::Hasher::new();
689 expected.update(length);
690 assert_eq!(actual.finalize(), expected.finalize());
691 }
692 }
693
694 #[test]
695 fn test_start_tags() {
696 for (tag, expected) in [
697 (StartTag::New, 0),
698 (StartTag::Resume, 1),
699 (StartTag::Fork, 2),
700 (StartTag::Noise, 3),
701 ] {
702 assert_eq!(tag as u8, expected);
703 }
704 }
705
706 #[test]
707 fn test_versions_match_for_single_byte_lengths() {
708 let v0 = v0(b"test");
709 let v1 = v1(b"test");
710 assert_eq!(v0.summarize(), v1.summarize());
711 assert_eq!(v0.fork(b"fork").summarize(), v1.fork(b"fork").summarize());
712
713 let summary = v0.summarize();
714 let resumed_v0 = Transcript::resume(summary, Version::V0)
715 .commit(b"x".as_slice())
716 .summarize();
717 let resumed_v1 = Transcript::resume(summary, Version::V1)
718 .commit(b"x".as_slice())
719 .summarize();
720 assert_eq!(resumed_v0, resumed_v1);
721
722 let mut noise_v0 = [0u8; 32];
723 let mut noise_v1 = [0u8; 32];
724 v0.noise(b"noise").fill_bytes(&mut noise_v0);
725 v1.noise(b"noise").fill_bytes(&mut noise_v1);
726 assert_eq!(noise_v0, noise_v1);
727 }
728
729 #[test]
730 fn test_version_frames_derived_labels() {
731 const LONG_LABEL: &[u8] = &[0; 128];
732
733 let v0 = v0(b"test");
734 let v1 = v1(b"test");
735 assert_ne!(
736 v0.fork(LONG_LABEL).summarize(),
737 v1.fork(LONG_LABEL).summarize()
738 );
739
740 let mut noise_v0 = [0u8; 32];
741 let mut noise_v1 = [0u8; 32];
742 v0.noise(LONG_LABEL).fill_bytes(&mut noise_v0);
743 v1.noise(LONG_LABEL).fill_bytes(&mut noise_v1);
744 assert_ne!(noise_v0, noise_v1);
745 }
746
747 #[test]
748 fn test_append_overflow_does_not_hash() {
749 let mut transcript = Transcript::start(StartTag::New, None, Version::V1);
750 transcript.pending = u64::MAX - 1;
751 let before = transcript.hasher.finalize();
752
753 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
754 transcript.append(b"A".as_slice().chain(b"B".as_slice()));
755 }));
756
757 assert!(result.is_err());
758 assert_eq!(transcript.pending, u64::MAX - 1);
759 assert_eq!(transcript.hasher.finalize(), before);
760 }
761
762 #[test]
763 fn test_append_commit_works() {
764 let s1 = v0(b"")
765 .append(b"A".as_slice())
766 .commit(b"B".as_slice())
767 .summarize();
768 let s2 = v0(b"").commit(b"AB".as_slice()).summarize();
769 assert_eq!(s1, s2);
770 }
771
772 #[test]
773 fn test_fork_returns_different_result() {
774 let t1 = v0(b"");
775 let t2 = t1.fork(b"");
776 assert_ne!(t1.summarize(), t2.summarize());
777 }
778
779 #[test]
780 fn test_fork_label_matters() {
781 let t1 = v0(b"");
782 let t2 = t1.fork(b"A");
783 let t3 = t2.fork(b"B");
784 assert_ne!(t2.summarize(), t3.summarize());
785 }
786
787 #[test]
788 fn test_noise_and_summarize_are_different() {
789 let t1 = v0(b"");
790 let mut s1_bytes = [0u8; 32];
791 t1.noise(b"foo").fill_bytes(&mut s1_bytes[..]);
792 let s1 = Summary {
793 hash: blake3::Hash::from_bytes(s1_bytes),
794 };
795 let s2 = t1.summarize();
796 assert_ne!(s1, s2);
797 }
798
799 #[test]
800 fn test_noise_stream_chunking_doesnt_matter() {
801 let mut s = [0u8; 2 * BLOCK_LEN];
802 v0(b"test").noise(b"NOISE").fill_bytes(&mut s[..]);
803 for i in 0..s.len() {
805 let mut s_prime = [0u8; 2 * BLOCK_LEN];
806 let mut noise = v0(b"test").noise(b"NOISE");
807 noise.fill_bytes(&mut s_prime[..i]);
808 noise.fill_bytes(&mut s_prime[i..]);
809 assert_eq!(s, s_prime);
810 }
811 }
812
813 #[test]
814 fn test_noise_label_matters() {
815 let mut s1 = [0u8; 32];
816 let mut s2 = [0u8; 32];
817 let t1 = v0(b"test");
818 t1.noise(b"A").fill_bytes(&mut s1);
819 t1.noise(b"B").fill_bytes(&mut s2);
820 assert_ne!(s1, s2);
821 }
822
823 #[test]
824 fn test_summarize_resume_is_different_than_new() {
825 let s = v0(b"test").summarize();
826 let s1 = v0(s.hash.as_bytes()).summarize();
827 let s2 = Transcript::resume(s, V0_VERSION).summarize();
828 assert_ne!(s1, s2);
829 }
830
831 #[test]
832 fn test_summary_encode_roundtrip() {
833 let s = v0(b"test").summarize();
834 assert_eq!(&s, &Summary::decode(s.encode()).unwrap());
835 }
836
837 #[test]
838 fn test_summary_sign_verify_matches_transcript() {
839 let sk = ed25519::PrivateKey::from_seed(7);
840 let pk = sk.public_key();
841 let mut transcript = v0(b"test");
842 transcript.commit(b"DATA".as_slice());
843 let summary = transcript.summarize();
844
845 let sig = summary.sign(&sk);
846 assert_eq!(sig, transcript.sign(&sk));
847 assert!(summary.verify(&pk, &sig));
848 assert!(transcript.verify(&pk, &sig));
849 }
850
851 #[test]
852 fn test_summary_add_to_batch_matches_transcript() {
853 let sk = ed25519::PrivateKey::from_seed(7);
854 let pk = sk.public_key();
855 let mut transcript = v0(b"test");
856 transcript.commit(b"DATA".as_slice());
857 let summary = transcript.summarize();
858 let sig = transcript.sign(&sk);
859
860 let mut summary_batch = ed25519::Batch::new(1);
861 assert!(summary.add_to_batch(&mut summary_batch, &pk, &sig));
862 let mut transcript_batch = ed25519::Batch::new(1);
863 assert!(transcript.add_to_batch(&mut transcript_batch, &pk, &sig));
864
865 assert!(summary_batch.verify(&mut test_rng(), &Sequential));
866 assert!(transcript_batch.verify(&mut test_rng(), &Sequential));
867 }
868
869 #[test]
870 fn test_shuffle_is_permutation() {
871 let t = v0(b"test");
872 let mut items: Vec<u32> = (0..1000).collect();
873 t.shuffle(b"shuffle", &mut items);
874 assert_ne!(items, (0..1000).collect::<Vec<_>>());
875 items.sort_unstable();
876 assert_eq!(items, (0..1000).collect::<Vec<_>>());
877 }
878
879 #[test]
880 fn test_shuffle_is_deterministic() {
881 let mut t = v0(b"test");
882 t.commit(b"DATA".as_slice());
883 let mut s1: Vec<u32> = (0..100).collect();
884 let mut s2 = s1.clone();
885 t.shuffle(b"shuffle", &mut s1);
886 t.shuffle(b"shuffle", &mut s2);
887 assert_eq!(s1, s2);
888 }
889
890 #[test]
891 fn test_shuffle_label_and_history_matter() {
892 let t1 = v0(b"test");
893 let mut t2 = v0(b"test");
894 t2.commit(b"DATA".as_slice());
895 let mut base: Vec<u32> = (0..100).collect();
896 let (mut a, mut b, mut c) = (base.clone(), base.clone(), base.clone());
897 t1.shuffle(b"A", &mut a);
898 t1.shuffle(b"B", &mut b);
899 t2.shuffle(b"A", &mut c);
900 base.clear();
901 assert_ne!(a, b);
902 assert_ne!(a, c);
903 }
904
905 #[test]
906 fn test_sample_within_bound() {
907 let t = v0(b"test");
908 let mut rng = t.noise(b"sample");
909 for bound in [1, 2, 3, 7, 100, 1 << 40, u64::MAX] {
910 assert!(sample(&mut rng, NZU64!(bound)) < bound);
911 }
912 assert_eq!(t.sample(b"sample", NZU64!(1)), 0);
913 assert_eq!(
914 t.sample(b"one shot", NZU64!(1000)),
915 sample(t.noise(b"one shot"), NZU64!(1000))
916 );
917 }
918
919 #[test]
920 fn test_missing_append() {
921 let s1 = v0(b"foo").append(b"AB".as_slice()).summarize();
922 let s2 = v0(b"foo")
923 .append(b"A".as_slice())
924 .commit(b"B".as_slice())
925 .summarize();
926 assert_eq!(s1, s2)
927 }
928
929 #[cfg(feature = "arbitrary")]
930 mod conformance {
931 use super::*;
932 use commonware_codec::conformance::CodecConformance;
933 use commonware_conformance::Conformance;
934
935 #[allow(clippy::unused_async)]
936 async fn transcript_ops(seed: u64, version: Version) -> Vec<u8> {
937 let seed_bytes = seed.to_le_bytes();
938 let namespace = seed_bytes[..(seed as usize % seed_bytes.len()) + 1].to_vec();
939 let data: Vec<_> = (0..seed as usize % 256)
940 .map(|i| (seed as u8).wrapping_add((3 * i) as u8))
941 .collect();
942 let split = data.len() / 2;
943
944 let mut transcript = Transcript::new(&namespace, version);
945 transcript.append(&data[..split]);
946 transcript.commit(&data[split..]);
947
948 let mut log = transcript.summarize().encode().to_vec();
949 log.extend(
950 Transcript::new(&namespace, version)
951 .commit(&data[..split])
952 .commit(&data[split..])
953 .summarize()
954 .encode(),
955 );
956 log.extend(
957 Transcript::new(&namespace, version)
958 .append(data.as_slice())
959 .commit([].as_slice())
960 .summarize()
961 .encode(),
962 );
963 let resumed = Transcript::resume(transcript.summarize(), version);
964 log.extend(resumed.summarize().encode());
965 log.extend(transcript.fork(b"left").summarize().encode());
966 log.extend(transcript.fork(b"right").summarize().encode());
967
968 let mut noise = [0u8; 80];
969 let mut rng = transcript.noise(b"noise");
970 log.extend(rng.next_u32().encode());
971 log.extend(rng.next_u64().encode());
972 rng.fill_bytes(&mut noise[..31]);
973 rng.fill_bytes(&mut noise[31..]);
974 log.extend(noise);
975
976 let mut indices: Vec<u32> = (0..(seed % 100) as u32).collect();
977 transcript.shuffle(b"shuffle", &mut indices);
978 for index in &indices {
979 log.extend(index.encode());
980 }
981 log.extend(transcript.sample(b"sample", NZU64!(seed | 1)).encode());
982
983 let private_key = ed25519::PrivateKey::from_seed(seed);
984 let public_key = private_key.public_key();
985 let summary = transcript.summarize();
986 let summary_sig = summary.sign(&private_key);
987 let transcript_sig = transcript.sign(&private_key);
988 log.extend(summary_sig.encode());
989 log.extend(transcript_sig.encode());
990 log.extend(summary.verify(&public_key, &summary_sig).encode());
991 log.extend(transcript.verify(&public_key, &transcript_sig).encode());
992
993 let mut summary_batch = ed25519::Batch::new(1);
994 log.extend(
995 summary
996 .add_to_batch(&mut summary_batch, &public_key, &summary_sig)
997 .encode(),
998 );
999 log.extend(
1000 summary_batch
1001 .verify(&mut transcript.noise(b"summary batch"), &Sequential)
1002 .encode(),
1003 );
1004
1005 let mut transcript_batch = ed25519::Batch::new(1);
1006 log.extend(
1007 transcript
1008 .add_to_batch(&mut transcript_batch, &public_key, &transcript_sig)
1009 .encode(),
1010 );
1011 log.extend(
1012 transcript_batch
1013 .verify(&mut transcript.noise(b"transcript batch"), &Sequential)
1014 .encode(),
1015 );
1016
1017 let mut pending = Transcript::new(&namespace, version);
1018 pending.append(data.as_slice());
1019 let pending_summary = pending.summarize();
1020 log.extend(pending_summary.encode());
1021 log.extend(pending.fork(b"pending fork").summarize().encode());
1022
1023 let mut pending_noise = [0u8; 37];
1024 pending
1025 .noise(b"pending noise")
1026 .fill_bytes(&mut pending_noise);
1027 log.extend(pending_noise);
1028
1029 let pending_sig = pending.sign(&private_key);
1030 log.extend(pending_sig.encode());
1031 log.extend(pending.verify(&public_key, &pending_sig).encode());
1032
1033 log
1034 }
1035
1036 struct TranscriptV0Ops;
1037
1038 impl Conformance for TranscriptV0Ops {
1039 async fn commit(seed: u64) -> Vec<u8> {
1040 transcript_ops(seed, V0_VERSION).await
1041 }
1042 }
1043
1044 struct TranscriptV1Ops;
1045
1046 impl Conformance for TranscriptV1Ops {
1047 async fn commit(seed: u64) -> Vec<u8> {
1048 transcript_ops(seed, Version::V1).await
1049 }
1050 }
1051
1052 commonware_conformance::conformance_tests! {
1053 TranscriptV0Ops => 4096,
1054 TranscriptV1Ops => 4096,
1055 CodecConformance<Summary>,
1056 }
1057 }
1058}