sigma_protocols/
transcript.rs1use sha3::{
2 digest::{ExtendableOutput, Update, XofReader},
3 Shake128,
4};
5
6#[derive(Clone)]
7pub struct Transcript {
8 hasher: Shake128,
9 #[allow(dead_code)]
10 domain_separator: Vec<u8>,
11}
12
13impl Transcript {
14 pub fn new(domain_separator: &[u8]) -> Self {
15 let mut hasher = Shake128::default();
16 hasher.update(domain_separator);
17 hasher.update(&(domain_separator.len() as u64).to_le_bytes());
18
19 Self {
20 hasher,
21 domain_separator: domain_separator.to_vec(),
22 }
23 }
24
25 pub fn append_message(&mut self, label: &[u8], message: &[u8]) {
26 self.hasher.update(label);
27 self.hasher.update(&(label.len() as u64).to_le_bytes());
28 self.hasher.update(message);
29 self.hasher.update(&(message.len() as u64).to_le_bytes());
30 }
31
32 pub fn challenge_bytes(&mut self, label: &[u8], dest: &mut [u8]) {
33 self.hasher.update(label);
34 self.hasher.update(&(label.len() as u64).to_le_bytes());
35
36 let mut reader = self.hasher.clone().finalize_xof();
37 reader.read(dest);
38
39 self.hasher.update(dest);
40 }
41}
42
43#[derive(Clone)]
44pub struct DuplexSponge {
45 state: Shake128,
46}
47
48impl DuplexSponge {
49 pub fn new(initialization_vector: &[u8; 32]) -> Self {
50 let mut state = Shake128::default();
51 state.update(initialization_vector);
52 Self { state }
53 }
54
55 pub fn absorb(&mut self, data: &[u8]) {
56 self.state.update(data);
57 self.state.update(&(data.len() as u64).to_le_bytes());
58 }
59
60 pub fn squeeze(&mut self, output_len: usize) -> Vec<u8> {
61 let mut output = vec![0u8; output_len];
62 let mut reader = self.state.clone().finalize_xof();
63 reader.read(&mut output);
64
65 self.state.update(&output);
66
67 output
68 }
69
70 pub fn prover_message(&mut self, commitment: &[u8]) -> &mut Self {
71 self.absorb(b"prover");
72 self.absorb(commitment);
73 self
74 }
75
76 pub fn verifier_challenge(&mut self, challenge_len: usize) -> Vec<u8> {
77 self.absorb(b"verifier");
78 self.squeeze(challenge_len)
79 }
80}