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