Nova/src/provider/keccak.rs
src/provider/keccak.rsRaw
1//! This module provides an implementation of TranscriptEngineTrait using keccak256
2use crate::traits::PrimeFieldExt;
3use crate::{
4 errors::NovaError,
5 traits::{Group, TranscriptEngineTrait, TranscriptReprTrait},
6};
7use core::marker::PhantomData;
8use sha3::{Digest, Keccak256};
9
10const PERSONA_TAG: &[u8] = b"NoTR";
11const DOM_SEP_TAG: &[u8] = b"NoDS";
12const KECCAK256_STATE_SIZE: usize = 64;
13const KECCAK256_PREFIX_CHALLENGE_LO: u8 = 0;
14const KECCAK256_PREFIX_CHALLENGE_HI: u8 = 1;
15
16/// Provides an implementation of TranscriptEngine
17#[derive(Debug, Clone)]
18pub struct Keccak256Transcript<G: Group> {
19 round: u16,
20 state: [u8; KECCAK256_STATE_SIZE],
21 transcript: Vec<u8>,
22 _p: PhantomData<G>,
23}
24
25fn compute_updated_state(input: &[u8]) -> [u8; KECCAK256_STATE_SIZE] {
26 let input_lo = [input, &[KECCAK256_PREFIX_CHALLENGE_LO]].concat();
27 let input_hi = [input, &[KECCAK256_PREFIX_CHALLENGE_HI]].concat();
28
29 let mut hasher_lo = Keccak256::new();
30 let mut hasher_hi = Keccak256::new();
31
32 hasher_lo.input(&input_lo);
33 hasher_hi.input(&input_hi);
34
35 let output_lo = hasher_lo.result();
36 let output_hi = hasher_hi.result();
37
38 [output_lo, output_hi]
39 .concat()
40 .as_slice()
41 .try_into()
42 .unwrap()
43}
44
45impl<G: Group> TranscriptEngineTrait<G> for Keccak256Transcript<G> {
46 fn new(label: &'static [u8]) -> Self {
47 let input = [PERSONA_TAG, label].concat();
48 let output = compute_updated_state(&input);
49
50 Self {
51 round: 0u16,
52 state: output,
53 transcript: vec![],
54 _p: Default::default(),
55 }
56 }
57
58 fn squeeze(&mut self, label: &'static [u8]) -> Result<G::Scalar, NovaError> {
59 let input = [
60 DOM_SEP_TAG,
61 self.round.to_le_bytes().as_ref(),
62 self.state.as_ref(),
63 self.transcript.as_ref(),
64 label,
65 ]
66 .concat();
67 let output = compute_updated_state(&input);
68
69 // update state
70 self.round = {
71 if let Some(v) = self.round.checked_add(1) {
72 v
73 } else {
74 return Err(NovaError::InternalTranscriptError);
75 }
76 };
77 self.state.copy_from_slice(&output);
78 self.transcript = vec![];
79
80 // squeeze out a challenge
81 Ok(G::Scalar::from_uniform(&output))
82 }
83
84 fn absorb<T: TranscriptReprTrait<G>>(&mut self, label: &'static [u8], o: &T) {
85 self.transcript.extend_from_slice(label);
86 self.transcript.extend_from_slice(&o.to_transcript_bytes());
87 }
88
89 fn dom_sep(&mut self, bytes: &'static [u8]) {
90 self.transcript.extend_from_slice(DOM_SEP_TAG);
91 self.transcript.extend_from_slice(bytes);
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use crate::{
98 provider::bn256_grumpkin::bn256,
99 provider::keccak::Keccak256Transcript,
100 traits::{Group, TranscriptEngineTrait},
101 };
102 use ff::PrimeField;
103 use sha3::{Digest, Keccak256};
104
105 fn test_keccak_transcript_with<G: Group>(expected_h1: &'static str, expected_h2: &'static str) {
106 let mut transcript: Keccak256Transcript<G> = Keccak256Transcript::new(b"test");
107
108 // two scalars
109 let s1 = <G as Group>::Scalar::from(2u64);
110 let s2 = <G as Group>::Scalar::from(5u64);
111
112 // add the scalars to the transcript
113 transcript.absorb(b"s1", &s1);
114 transcript.absorb(b"s2", &s2);
115
116 // make a challenge
117 let c1: <G as Group>::Scalar = transcript.squeeze(b"c1").unwrap();
118 assert_eq!(hex::encode(c1.to_repr().as_ref()), expected_h1);
119
120 // a scalar
121 let s3 = <G as Group>::Scalar::from(128u64);
122
123 // add the scalar to the transcript
124 transcript.absorb(b"s3", &s3);
125
126 // make a challenge
127 let c2: <G as Group>::Scalar = transcript.squeeze(b"c2").unwrap();
128 assert_eq!(hex::encode(c2.to_repr().as_ref()), expected_h2);
129 }
130
131 #[test]
132 fn test_keccak_transcript() {
133 test_keccak_transcript_with::<pasta_curves::pallas::Point>(
134 "432d5811c8be3d44d47f52108a8749ae18482efd1a37b830f966456b5d75340c",
135 "65f7908d53abcd18f3b1d767456ef9009b91c7566a635e9ca7be26e21d4d7a10",
136 );
137
138 test_keccak_transcript_with::<bn256::Point>(
139 "93f9160d5501865b399ee4ff0ffe17b697a4023e33e931e2597d36e6cc4ac602",
140 "bca8bdb96608a8277a7cb34bd493dfbc5baf2a080d1d6c9d32d7ab4f238eb803",
141 );
142 }
143
144 #[test]
145 fn test_keccak_example() {
146 let mut hasher = Keccak256::new();
147 hasher.input(0xffffffff_u32.to_le_bytes());
148 let output: [u8; 32] = hasher.result().try_into().unwrap();
149 assert_eq!(
150 hex::encode(output),
151 "29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238"
152 );
153 }
154}