Skip to main content

arcis_compiler/utils/zkp/
transcript.rs

1//! Arcis implementation of https://github.com/dalek-cryptography/merlin/blob/master/src/transcript.rs
2
3use crate::{
4    core::{
5        circuits::boolean::{
6            boolean_value::{Boolean, BooleanValue},
7            byte::Byte,
8        },
9        global_value::{curve_value::CompressedCurveValue, value::FieldValue},
10    },
11    traits::{FromLeBits, ToLeBytes},
12    utils::{
13        field::ScalarField,
14        zkp::{
15            elgamal::ElGamalCiphertext,
16            grouped_elgamal::{
17                GroupedElGamalCiphertext2Handles,
18                GroupedElGamalCiphertext3Handles,
19                GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES_LEN,
20                GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES_LEN,
21            },
22            strobe::Strobe128,
23        },
24    },
25};
26use zk_elgamal_proof::encryption::{
27    DECRYPT_HANDLE_LEN,
28    ELGAMAL_CIPHERTEXT_LEN,
29    PEDERSEN_COMMITMENT_LEN,
30};
31
32/// Domain separation label to initialize the STROBE context.
33///
34/// This is not to be confused with the crate's semver string:
35/// the latter applies to the API, while this label defines the protocol.
36/// E.g. it is possible that crate 2.0 will have an incompatible API,
37/// but implement the same 1.0 protocol.
38pub const MERLIN_PROTOCOL_LABEL: &[u8] = b"Merlin v1.0";
39
40fn encode_u64(x: u64) -> [u8; 8] {
41    use byteorder::{ByteOrder, LittleEndian};
42
43    let mut buf = [0; 8];
44    LittleEndian::write_u64(&mut buf, x);
45    buf
46}
47
48fn encode_usize_as_u32(x: usize) -> [u8; 4] {
49    use byteorder::{ByteOrder, LittleEndian};
50
51    assert!(x <= (u32::MAX as usize));
52
53    let mut buf = [0; 4];
54    LittleEndian::write_u32(&mut buf, x as u32);
55    buf
56}
57
58/// A transcript of a public-coin argument.
59///
60/// The prover's messages are added to the transcript using
61/// [`append_message`](Transcript::append_message), and the verifier's
62/// challenges can be computed using
63/// [`challenge_bytes`](Transcript::challenge_bytes).
64///
65/// # Creating and using a Merlin transcript
66///
67/// To create a Merlin transcript, use [`Transcript::new()`].  This
68/// function takes a domain separation label which should be unique to
69/// the application.
70///
71/// To use the transcript with a Merlin-based proof implementation,
72/// the prover's side creates a Merlin transcript with an
73/// application-specific domain separation label, and passes a `&mut`
74/// reference to the transcript to the proving function(s).
75///
76/// To verify the resulting proof, the verifier creates their own
77/// Merlin transcript using the same domain separation label, then
78/// passes a `&mut` reference to the verifier's transcript to the
79/// verification function.
80///
81/// # Implementing proofs using Merlin
82///
83/// For information on the design of Merlin and how to use it to
84/// implement a proof system, see the documentation at
85/// [merlin.cool](https://merlin.cool), particularly the [Using
86/// Merlin](https://merlin.cool/use/index.html) section.
87#[derive(Clone)]
88pub struct Transcript<B: Boolean> {
89    strobe: Strobe128<B>,
90}
91
92impl<B: Boolean> Transcript<B> {
93    /// Initialize a new transcript with the supplied `label`, which
94    /// is used as a domain separator.
95    ///
96    /// # Note
97    ///
98    /// This function should be called by a proof library's API
99    /// consumer (i.e., the application using the proof library), and
100    /// **not by the proof implementation**.  See the [Passing
101    /// Transcripts](https://merlin.cool/use/passing.html) section of
102    /// the Merlin website for more details on why.
103    pub fn new(label: &'static [u8]) -> Transcript<B> {
104        let mut transcript = Transcript {
105            strobe: Strobe128::new(MERLIN_PROTOCOL_LABEL),
106        };
107        transcript.append_message(
108            b"dom-sep",
109            &label
110                .iter()
111                .copied()
112                .map(Byte::<B>::from)
113                .collect::<Vec<Byte<B>>>(),
114        );
115
116        transcript
117    }
118
119    /// Append a prover's `message` to the transcript.
120    ///
121    /// The `label` parameter is metadata about the message, and is
122    /// also appended to the transcript.  See the [Transcript
123    /// Protocols](https://merlin.cool/use/protocol.html) section of
124    /// the Merlin website for details on labels.
125    pub fn append_message(&mut self, label: &'static [u8], message: &[Byte<B>]) {
126        let data_len = encode_usize_as_u32(message.len());
127        self.strobe.meta_ad(label, false);
128        self.strobe.meta_ad(&data_len, true);
129        self.strobe.ad(message, false);
130    }
131
132    /// Convenience method for appending a `u64` to the transcript.
133    ///
134    /// The `label` parameter is metadata about the message, and is
135    /// also appended to the transcript.  See the [Transcript
136    /// Protocols](https://merlin.cool/use/protocol.html) section of
137    /// the Merlin website for details on labels.
138    ///
139    /// # Implementation
140    ///
141    /// Calls `append_message` with the 8-byte little-endian encoding
142    /// of `x`.
143    fn append_u64(&mut self, label: &'static [u8], x: u64) {
144        self.append_message(
145            label,
146            &encode_u64(x)
147                .into_iter()
148                .map(Byte::<B>::from)
149                .collect::<Vec<Byte<B>>>(),
150        );
151    }
152
153    /// Fill the supplied buffer with the verifier's challenge bytes.
154    ///
155    /// The `label` parameter is metadata about the challenge, and is
156    /// also appended to the transcript.  See the [Transcript
157    /// Protocols](https://merlin.cool/use/protocol.html) section of
158    /// the Merlin website for details on labels.
159    pub fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [Byte<B>]) {
160        let data_len = encode_usize_as_u32(dest.len());
161        self.strobe.meta_ad(label, false);
162        self.strobe.meta_ad(&data_len, true);
163        self.strobe.prf(dest, false);
164    }
165
166    pub fn range_proof_domain_separator(&mut self, n: u64) {
167        self.append_message(
168            b"dom-sep",
169            &b"range-proof"
170                .iter()
171                .copied()
172                .map(Byte::<B>::from)
173                .collect::<Vec<Byte<B>>>(),
174        );
175        self.append_u64(b"n", n);
176    }
177
178    pub fn inner_product_proof_domain_separator(&mut self, n: u64) {
179        self.append_message(
180            b"dom-sep",
181            &b"inner-product"
182                .iter()
183                .copied()
184                .map(Byte::<B>::from)
185                .collect::<Vec<Byte<B>>>(),
186        );
187        self.append_u64(b"n", n);
188    }
189
190    pub fn ciphertext_commitment_equality_proof_domain_separator(&mut self) {
191        self.append_message(
192            b"dom-sep",
193            &b"ciphertext-commitment-equality-proof"
194                .iter()
195                .copied()
196                .map(Byte::<B>::from)
197                .collect::<Vec<Byte<B>>>(),
198        )
199    }
200
201    pub fn zero_ciphertext_proof_domain_separator(&mut self) {
202        self.append_message(
203            b"dom-sep",
204            &b"zero-ciphertext-proof"
205                .iter()
206                .copied()
207                .map(Byte::<B>::from)
208                .collect::<Vec<Byte<B>>>(),
209        )
210    }
211
212    pub fn grouped_ciphertext_validity_proof_domain_separator(&mut self, handles: u64) {
213        self.append_message(
214            b"dom-sep",
215            &b"validity-proof"
216                .iter()
217                .copied()
218                .map(Byte::<B>::from)
219                .collect::<Vec<Byte<B>>>(),
220        );
221        self.append_u64(b"handles", handles);
222    }
223
224    pub fn batched_grouped_ciphertext_validity_proof_domain_separator(&mut self, handles: u64) {
225        self.append_message(
226            b"dom-sep",
227            &b"batched-validity-proof"
228                .iter()
229                .copied()
230                .map(Byte::<B>::from)
231                .collect::<Vec<Byte<B>>>(),
232        );
233        self.append_u64(b"handles", handles);
234    }
235
236    pub fn pubkey_proof_domain_separator(&mut self) {
237        self.append_message(
238            b"dom-sep",
239            &b"pubkey-proof"
240                .iter()
241                .copied()
242                .map(Byte::<B>::from)
243                .collect::<Vec<Byte<B>>>(),
244        )
245    }
246}
247
248impl Transcript<BooleanValue> {
249    pub fn append_scalar(&mut self, label: &'static [u8], scalar: &FieldValue<ScalarField>) {
250        self.append_message(label, &scalar.to_le_bytes());
251    }
252
253    pub fn append_point(&mut self, label: &'static [u8], point: &CompressedCurveValue) {
254        self.append_message(label, &point.to_bytes());
255    }
256
257    pub fn append_elgamal_ciphertext(
258        &mut self,
259        label: &'static [u8],
260        ciphertext: &ElGamalCiphertext,
261    ) {
262        let mut bytes = [Byte::<BooleanValue>::from(0u8); ELGAMAL_CIPHERTEXT_LEN];
263        bytes[..PEDERSEN_COMMITMENT_LEN]
264            .copy_from_slice(&ciphertext.commitment.get_point().compress().to_bytes());
265        bytes[PEDERSEN_COMMITMENT_LEN..]
266            .copy_from_slice(&ciphertext.handle.get_point().compress().to_bytes());
267        self.append_message(label, &bytes);
268    }
269
270    pub fn append_elgamal_ciphertext_2_handles(
271        &mut self,
272        label: &'static [u8],
273        ciphertext: &GroupedElGamalCiphertext2Handles,
274    ) {
275        let mut bytes = [Byte::<BooleanValue>::from(0u8); GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES_LEN];
276        bytes[..PEDERSEN_COMMITMENT_LEN]
277            .copy_from_slice(&ciphertext.0.commitment.get_point().compress().to_bytes());
278        let mut offset = PEDERSEN_COMMITMENT_LEN;
279        for i in 0..2 {
280            bytes[offset..offset + DECRYPT_HANDLE_LEN]
281                .copy_from_slice(&ciphertext.0.handles[i].get_point().compress().to_bytes());
282            offset += DECRYPT_HANDLE_LEN;
283        }
284        self.append_message(label, &bytes);
285    }
286
287    pub fn append_elgamal_ciphertext_3_handles(
288        &mut self,
289        label: &'static [u8],
290        ciphertext: &GroupedElGamalCiphertext3Handles,
291    ) {
292        let mut bytes = [Byte::<BooleanValue>::from(0u8); GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES_LEN];
293        bytes[..PEDERSEN_COMMITMENT_LEN]
294            .copy_from_slice(&ciphertext.0.commitment.get_point().compress().to_bytes());
295        let mut offset = PEDERSEN_COMMITMENT_LEN;
296        for i in 0..3 {
297            bytes[offset..offset + DECRYPT_HANDLE_LEN]
298                .copy_from_slice(&ciphertext.0.handles[i].get_point().compress().to_bytes());
299            offset += DECRYPT_HANDLE_LEN;
300        }
301        self.append_message(label, &bytes);
302    }
303
304    pub fn challenge_scalar(&mut self, label: &'static [u8]) -> FieldValue<ScalarField> {
305        let mut buf = [Byte::from(0u8); 64];
306        self.challenge_bytes(label, &mut buf);
307        let buf_bits = buf
308            .into_iter()
309            .flat_map(|byte| byte.to_vec())
310            .collect::<Vec<BooleanValue>>();
311
312        FieldValue::<ScalarField>::from_le_bits(buf_bits, false)
313    }
314}