arnaucube
arnaucube/NovaPublic Self-hosted repository
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}