Skip to main content

zinc_transcript/
lib.rs

1pub mod traits;
2
3use crate::traits::{ConstTranscribable, GenTranscribable, Transcript};
4use crypto_primitives::{ConstIntSemiring, PrimeField};
5use zinc_primality::PrimalityTest;
6use zinc_utils::add;
7
8/// A cryptographic transcript implementation using the BLAKE3 hash
9/// function. Used for Fiat-Shamir transformations in zero-knowledge proof
10/// systems.
11#[derive(Debug, Clone)]
12pub struct Blake3Transcript {
13    /// The underlying BLAKE3 hasher that maintains the transcript state.
14    hasher: blake3::Hasher,
15}
16
17impl Default for Blake3Transcript {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl Blake3Transcript {
24    pub fn new() -> Self {
25        Self {
26            hasher: blake3::Hasher::new(),
27        }
28    }
29
30    /// Generates a specified number of pseudorandom bytes based on the current
31    /// transcript state. Uses a counter-based approach to generate enough
32    /// bytes from the hasher.
33    ///
34    /// Note that this does NOT update the internal state of the hasher
35    #[allow(clippy::arithmetic_side_effects)]
36    fn fill_with_random_bytes(&mut self, buf: &mut [u8]) {
37        self.hasher.finalize_xof().fill(buf);
38    }
39
40    fn gen_random<R: ConstTranscribable>(&mut self, buf: &mut [u8]) -> R {
41        self.fill_with_random_bytes(buf);
42        self.absorb_inner(buf);
43        R::read_transcription_bytes_exact(buf)
44    }
45}
46
47impl Transcript for Blake3Transcript {
48    fn get_challenge<T: ConstTranscribable>(&mut self) -> T {
49        let mut buf = vec![0u8; T::NUM_BYTES];
50        self.fill_with_random_bytes(&mut buf);
51        self.hasher.update(&[0x12]);
52        self.hasher.update(&buf);
53        self.hasher.update(&[0x34]);
54        T::read_transcription_bytes_exact(&buf)
55    }
56
57    #[allow(clippy::arithmetic_side_effects)]
58    fn get_prime<R: ConstIntSemiring + ConstTranscribable, T: PrimalityTest<R>>(&mut self) -> R {
59        let buf = &mut vec![0u8; R::NUM_BYTES];
60        loop {
61            let mut prime_candidate: R = self.gen_random(buf);
62            if prime_candidate.is_zero() {
63                continue;
64            }
65            if prime_candidate.is_even() {
66                prime_candidate -= R::ONE;
67            }
68            if T::is_probably_prime(&prime_candidate) {
69                return prime_candidate;
70            }
71        }
72    }
73
74    fn absorb_inner(&mut self, v: &[u8]) {
75        self.hasher.update(v);
76    }
77}
78
79pub fn read_field_cfg<F>(bytes: &[u8]) -> F::Config
80where
81    F: PrimeField,
82    F::Modulus: ConstTranscribable,
83{
84    let mod_size = F::Modulus::NUM_BYTES;
85    let modulus = F::Modulus::read_transcription_bytes_exact(&bytes[..mod_size]);
86    F::make_cfg(&modulus).expect("valid field modulus in proof transcription")
87}
88
89pub fn read_field_vec_with_cfg<F>(bytes: &[u8], field_cfg: &F::Config) -> Vec<F>
90where
91    F: PrimeField,
92    F::Inner: ConstTranscribable,
93{
94    let inner_size = F::Inner::NUM_BYTES;
95    bytes
96        .chunks_exact(inner_size)
97        .map(F::Inner::read_transcription_bytes_exact)
98        .map(|inner| F::new_unchecked_with_cfg(inner, field_cfg))
99        .collect()
100}
101
102pub fn append_field_cfg<'a, F>(buf: &'a mut [u8], modulus: &F::Modulus) -> &'a mut [u8]
103where
104    F: PrimeField,
105    F::Modulus: ConstTranscribable,
106{
107    let mod_size = F::Modulus::NUM_BYTES;
108    let (buf, rest) = buf.split_at_mut(mod_size);
109    modulus.write_transcription_bytes_exact(buf);
110    rest
111}
112
113pub fn append_field_vec_inner<'a, F>(buf: &'a mut [u8], slice: &[F]) -> &'a mut [u8]
114where
115    F: PrimeField,
116    F::Inner: ConstTranscribable,
117{
118    let inner_size = F::Inner::NUM_BYTES;
119    let mut offset = 0;
120    for elem in slice {
121        let offset_end = add!(offset, inner_size);
122        elem.inner()
123            .write_transcription_bytes_exact(&mut buf[offset..offset_end]);
124        offset = offset_end;
125    }
126    &mut buf[offset..]
127}