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