Skip to content

Commit

Permalink
Refactor hashing domains (tari-project#107)
Browse files Browse the repository at this point in the history
This PR moves hashing domains to a common module, in order to reduce the risk of collisions. It also ensures that all hashing operations are versioned.

BREAKING CHANGE: Updates how some hashing operations are performed, which will break existing proofs.
  • Loading branch information
AaronFeickert authored Aug 11, 2024
1 parent a0760a0 commit bf074cb
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 78 deletions.
31 changes: 29 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,6 @@ extern crate alloc;

pub use merlin::Transcript;

pub(crate) const TRANSCRIPT_HASH_BYTES: usize = 32;

/// Iterated arbitrary-base Gray code functionality.
pub(crate) mod gray;
/// Public parameters used for generating and verifying Triptych proofs.
Expand All @@ -139,3 +137,32 @@ pub use witness::TriptychWitness;

/// Parallel Triptych functionality.
pub mod parallel;

/// Domain separators used for hashing operations
pub(crate) mod domains {
// Version
pub(crate) const VERSION: u64 = 0;

// Number of bytes in a transcript hash
pub(crate) const TRANSCRIPT_HASH_BYTES: usize = 32;

// Parameters
pub(crate) const TRANSCRIPT_PARAMETERS: &str = "Triptych parameters";
pub(crate) const TRANSCRIPT_PARALLEL_PARAMETERS: &str = "Parallel Triptych parameters";
pub(crate) const POINT_G1: &str = "Triptych G1";
pub(crate) const POINT_U: &str = "Triptych U";
pub(crate) const POINT_COMMITMENT_G: &str = "Triptych CommitmentG";
pub(crate) const POINT_COMMITMENT_H: &str = "Triptych CommitmentH";

// Statement
pub(crate) const TRANSCRIPT_INPUT_SET: &str = "Triptych input set";
pub(crate) const TRANSCRIPT_PARALLEL_INPUT_SET: &str = "Parallel Triptych input set";
pub(crate) const TRANSCRIPT_STATEMENT: &str = "Triptych statement";
pub(crate) const TRANSCRIPT_PARALLEL_STATEMENT: &str = "Parallel Triptych statement";

// Proof
pub(crate) const TRANSCRIPT_PROOF: &str = "Triptych proof";
pub(crate) const TRANSCRIPT_PARALLEL_PROOF: &str = "Parallel Triptych proof";
pub(crate) const TRANSCRIPT_VERIFIER_WEIGHTS: &str = "Triptych verifier weights";
pub(crate) const TRANSCRIPT_PARALLEL_VERIFIER_WEIGHTS: &str = "Parallel Triptych verifier weights";
}
25 changes: 12 additions & 13 deletions src/parallel/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use curve25519_dalek::{
};
use snafu::prelude::*;

use crate::{util::OperationTiming, Transcript, TRANSCRIPT_HASH_BYTES};
use crate::{domains, util::OperationTiming, Transcript};

/// Public parameters used for generating and verifying Triptych proofs.
///
Expand Down Expand Up @@ -46,11 +46,6 @@ pub enum ParameterError {
}

impl TriptychParameters {
// Domain separator used for hashing
const DOMAIN: &'static str = "Parallel Triptych parameters";
// Version identifier used for hashing
const VERSION: u64 = 0;

/// Generate new [`TriptychParameters`] for Triptych proofs.
///
/// The base `n > 1` and exponent `m > 1` define the size of verification key vectors, so it must be the case that
Expand All @@ -66,14 +61,16 @@ impl TriptychParameters {
// Use `BLAKE3` to generate `G1`
let mut G1_bytes = [0u8; 64];
let mut hasher = Hasher::new();
hasher.update(b"Triptych G1");
hasher.update(domains::POINT_G1.as_bytes());
hasher.update(&domains::VERSION.to_le_bytes());
hasher.finalize_xof().fill(&mut G1_bytes);
let G1 = RistrettoPoint::from_uniform_bytes(&G1_bytes);

// Use `BLAKE3` to generate `U`
let mut U_bytes = [0u8; 64];
let mut hasher = Hasher::new();
hasher.update(b"Triptych U");
hasher.update(domains::POINT_U.as_bytes());
hasher.update(&domains::VERSION.to_le_bytes());
hasher.finalize_xof().fill(&mut U_bytes);
let U = RistrettoPoint::from_uniform_bytes(&U_bytes);

Expand Down Expand Up @@ -118,13 +115,15 @@ impl TriptychParameters {
// Use `BLAKE3` to generate `CommitmentH`
let mut CommitmentH_bytes = [0u8; 64];
let mut hasher = Hasher::new();
hasher.update(b"Triptych CommitmentH");
hasher.update(domains::POINT_COMMITMENT_H.as_bytes());
hasher.update(&domains::VERSION.to_le_bytes());
hasher.finalize_xof().fill(&mut CommitmentH_bytes);
let CommitmentH = RistrettoPoint::from_uniform_bytes(&CommitmentH_bytes);

// Use `BLAKE3` for the commitment matrix generators
let mut hasher = Hasher::new();
hasher.update(b"Triptych CommitmentG");
hasher.update(domains::POINT_COMMITMENT_G.as_bytes());
hasher.update(&domains::VERSION.to_le_bytes());
hasher.update(&n.to_le_bytes());
hasher.update(&m.to_le_bytes());
let mut hasher_xof = hasher.finalize_xof();
Expand All @@ -139,8 +138,8 @@ impl TriptychParameters {
.collect::<Vec<RistrettoPoint>>();

// Use Merlin for the transcript hash
let mut transcript = Transcript::new(Self::DOMAIN.as_bytes());
transcript.append_u64(b"version", Self::VERSION);
let mut transcript = Transcript::new(domains::TRANSCRIPT_PARALLEL_PARAMETERS.as_bytes());
transcript.append_u64(b"version", domains::VERSION);
transcript.append_message(b"n", &n.to_le_bytes());
transcript.append_message(b"m", &m.to_le_bytes());
transcript.append_message(b"G", G.compress().as_bytes());
Expand All @@ -150,7 +149,7 @@ impl TriptychParameters {
transcript.append_message(b"CommitmentG", item.compress().as_bytes());
}
transcript.append_message(b"CommitmentH", CommitmentH.compress().as_bytes());
let mut hash = vec![0u8; TRANSCRIPT_HASH_BYTES];
let mut hash = vec![0u8; domains::TRANSCRIPT_HASH_BYTES];
transcript.challenge_bytes(b"hash", &mut hash);

Ok(TriptychParameters {
Expand Down
4 changes: 3 additions & 1 deletion src/parallel/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use subtle::{ConditionallySelectable, ConstantTimeEq};
use zeroize::Zeroizing;

use crate::{
domains,
gray::GrayIterator,
parallel::{transcript::ProofTranscript, TriptychStatement, TriptychWitness},
util::{delta, NullRng, OperationTiming},
Expand Down Expand Up @@ -722,7 +723,8 @@ impl TriptychProof {
let mut U_scalar = Scalar::ZERO;

// Set up a transcript generator for use in weighting
let mut transcript_weights = Transcript::new(b"Triptych verifier weights");
let mut transcript_weights = Transcript::new(domains::TRANSCRIPT_PARALLEL_VERIFIER_WEIGHTS.as_bytes());
transcript_weights.append_u64(b"version", domains::VERSION);

let mut null_rng = NullRng;

Expand Down
24 changes: 7 additions & 17 deletions src/parallel/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use alloc::{sync::Arc, vec, vec::Vec};
use curve25519_dalek::{traits::Identity, RistrettoPoint};
use snafu::prelude::*;

use crate::{parallel::TriptychParameters, Transcript, TRANSCRIPT_HASH_BYTES};
use crate::{domains, parallel::TriptychParameters, Transcript};

/// A Triptych input set.
///
Expand All @@ -21,11 +21,6 @@ pub struct TriptychInputSet {
}

impl TriptychInputSet {
// Domain separator used for hashing
const DOMAIN: &'static str = "Parallel Triptych input set";
// Version identifier used for hashing
const VERSION: u64 = 0;

/// Generate a new [`TriptychInputSet`] from a slice `M` of verification keys and slice `M1` of auxiliary
/// verification keys.
#[allow(non_snake_case)]
Expand Down Expand Up @@ -98,16 +93,16 @@ impl TriptychInputSet {
})?;

// Use Merlin for the transcript hash
let mut transcript = Transcript::new(Self::DOMAIN.as_bytes());
transcript.append_u64(b"version", Self::VERSION);
let mut transcript = Transcript::new(domains::TRANSCRIPT_PARALLEL_INPUT_SET.as_bytes());
transcript.append_u64(b"version", domains::VERSION);
transcript.append_message(b"unpadded_size", &unpadded_size.to_le_bytes());
for item in M {
transcript.append_message(b"M", item.compress().as_bytes());
}
for item in M1 {
transcript.append_message(b"M1", item.compress().as_bytes());
}
let mut hash = vec![0u8; TRANSCRIPT_HASH_BYTES];
let mut hash = vec![0u8; domains::TRANSCRIPT_HASH_BYTES];
transcript.challenge_bytes(b"hash", &mut hash);

Ok(Self {
Expand Down Expand Up @@ -160,11 +155,6 @@ pub enum StatementError {
}

impl TriptychStatement {
// Domain separator used for hashing
const DOMAIN: &'static str = "Parallel Triptych statement";
// Version identifier used for hashing
const VERSION: u64 = 0;

/// Generate a new [`TriptychStatement`].
///
/// The [`TriptychInputSet`] `input_set` must have a verification key vector whose size matches that specified by
Expand Down Expand Up @@ -205,13 +195,13 @@ impl TriptychStatement {
}

// Use Merlin for the transcript hash
let mut transcript = Transcript::new(Self::DOMAIN.as_bytes());
transcript.append_u64(b"version", Self::VERSION);
let mut transcript = Transcript::new(domains::TRANSCRIPT_PARALLEL_STATEMENT.as_bytes());
transcript.append_u64(b"version", domains::VERSION);
transcript.append_message(b"params", params.get_hash());
transcript.append_message(b"input_set", input_set.get_hash());
transcript.append_message(b"offset", offset.compress().as_bytes());
transcript.append_message(b"J", J.compress().as_bytes());
let mut hash = vec![0u8; TRANSCRIPT_HASH_BYTES];
let mut hash = vec![0u8; domains::TRANSCRIPT_HASH_BYTES];
transcript.challenge_bytes(b"hash", &mut hash);

Ok(Self {
Expand Down
10 changes: 3 additions & 7 deletions src/parallel/transcript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use merlin::TranscriptRng;
use rand_core::CryptoRngCore;

use crate::{
domains,
parallel::{proof::ProofError, TriptychParameters, TriptychStatement, TriptychWitness},
Transcript,
};
Expand All @@ -21,11 +22,6 @@ pub(crate) struct ProofTranscript<'a, R: CryptoRngCore> {
}

impl<'a, R: CryptoRngCore> ProofTranscript<'a, R> {
// Domain separator used for hashing
const DOMAIN: &'static str = "Parallel Triptych proof";
// Version identifier used for hashing
const VERSION: u64 = 0;

/// Initialize a transcript.
pub(crate) fn new(
transcript: &'a mut Transcript,
Expand All @@ -34,8 +30,8 @@ impl<'a, R: CryptoRngCore> ProofTranscript<'a, R> {
witness: Option<&'a TriptychWitness>,
) -> Self {
// Update the transcript
transcript.append_message(b"dom-sep", Self::DOMAIN.as_bytes());
transcript.append_u64(b"version", Self::VERSION);
transcript.append_message(b"dom-sep", domains::TRANSCRIPT_PARALLEL_PROOF.as_bytes());
transcript.append_u64(b"version", domains::VERSION);
transcript.append_message(b"statement", statement.get_hash());

// Set up the transcript generator
Expand Down
22 changes: 10 additions & 12 deletions src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use curve25519_dalek::{
};
use snafu::prelude::*;

use crate::{util::OperationTiming, Transcript, TRANSCRIPT_HASH_BYTES};
use crate::{domains, util::OperationTiming, Transcript};

/// Public parameters used for generating and verifying Triptych proofs.
///
Expand Down Expand Up @@ -45,11 +45,6 @@ pub enum ParameterError {
}

impl TriptychParameters {
// Domain separator used for hashing
const DOMAIN: &'static str = "Triptych parameters";
// Version identifier used for hashing
const VERSION: u64 = 0;

/// Generate new [`TriptychParameters`] for Triptych proofs.
///
/// The base `n > 1` and exponent `m > 1` define the size of verification key vectors, so it must be the case that
Expand All @@ -65,7 +60,8 @@ impl TriptychParameters {
// Use `BLAKE3` to generate `U`
let mut U_bytes = [0u8; 64];
let mut hasher = Hasher::new();
hasher.update(b"Triptych U");
hasher.update(domains::POINT_U.as_bytes());
hasher.update(&domains::VERSION.to_le_bytes());
hasher.finalize_xof().fill(&mut U_bytes);
let U = RistrettoPoint::from_uniform_bytes(&U_bytes);

Expand Down Expand Up @@ -103,13 +99,15 @@ impl TriptychParameters {
// Use `BLAKE3` to generate `CommitmentH`
let mut CommitmentH_bytes = [0u8; 64];
let mut hasher = Hasher::new();
hasher.update(b"Triptych CommitmentH");
hasher.update(domains::POINT_COMMITMENT_H.as_bytes());
hasher.update(&domains::VERSION.to_le_bytes());
hasher.finalize_xof().fill(&mut CommitmentH_bytes);
let CommitmentH = RistrettoPoint::from_uniform_bytes(&CommitmentH_bytes);

// Use `BLAKE3` for the commitment matrix generators
let mut hasher = Hasher::new();
hasher.update(b"Triptych CommitmentG");
hasher.update(domains::POINT_COMMITMENT_G.as_bytes());
hasher.update(&domains::VERSION.to_le_bytes());
hasher.update(&n.to_le_bytes());
hasher.update(&m.to_le_bytes());
let mut hasher_xof = hasher.finalize_xof();
Expand All @@ -124,8 +122,8 @@ impl TriptychParameters {
.collect::<Vec<RistrettoPoint>>();

// Use Merlin for the transcript hash
let mut transcript = Transcript::new(Self::DOMAIN.as_bytes());
transcript.append_u64(b"version", Self::VERSION);
let mut transcript = Transcript::new(domains::TRANSCRIPT_PARAMETERS.as_bytes());
transcript.append_u64(b"version", domains::VERSION);
transcript.append_message(b"n", &n.to_le_bytes());
transcript.append_message(b"m", &m.to_le_bytes());
transcript.append_message(b"G", G.compress().as_bytes());
Expand All @@ -134,7 +132,7 @@ impl TriptychParameters {
transcript.append_message(b"CommitmentG", item.compress().as_bytes());
}
transcript.append_message(b"CommitmentH", CommitmentH.compress().as_bytes());
let mut hash = vec![0u8; TRANSCRIPT_HASH_BYTES];
let mut hash = vec![0u8; domains::TRANSCRIPT_HASH_BYTES];
transcript.challenge_bytes(b"hash", &mut hash);

Ok(TriptychParameters {
Expand Down
4 changes: 3 additions & 1 deletion src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use subtle::{ConditionallySelectable, ConstantTimeEq};
use zeroize::Zeroizing;

use crate::{
domains,
gray::GrayIterator,
transcript::ProofTranscript,
util::{delta, NullRng, OperationTiming},
Expand Down Expand Up @@ -668,7 +669,8 @@ impl TriptychProof {
let mut U_scalar = Scalar::ZERO;

// Set up a transcript generator for use in weighting
let mut transcript_weights = Transcript::new(b"Triptych verifier weights");
let mut transcript_weights = Transcript::new(domains::TRANSCRIPT_VERIFIER_WEIGHTS.as_bytes());
transcript_weights.append_u64(b"version", domains::VERSION);

let mut null_rng = NullRng;

Expand Down
24 changes: 7 additions & 17 deletions src/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use alloc::{sync::Arc, vec, vec::Vec};
use curve25519_dalek::{traits::Identity, RistrettoPoint};
use snafu::prelude::*;

use crate::{Transcript, TriptychParameters, TRANSCRIPT_HASH_BYTES};
use crate::{domains, Transcript, TriptychParameters};

/// A Triptych input set.
///
Expand All @@ -20,11 +20,6 @@ pub struct TriptychInputSet {
}

impl TriptychInputSet {
// Domain separator used for hashing
const DOMAIN: &'static str = "Triptych input set";
// Version identifier used for hashing
const VERSION: u64 = 0;

/// Generate a new [`TriptychInputSet`] from a slice `M` of verification keys.
#[allow(non_snake_case)]
pub fn new(M: &[RistrettoPoint]) -> Result<Self, StatementError> {
Expand Down Expand Up @@ -71,13 +66,13 @@ impl TriptychInputSet {
})?;

// Use Merlin for the transcript hash
let mut transcript = Transcript::new(Self::DOMAIN.as_bytes());
transcript.append_u64(b"version", Self::VERSION);
let mut transcript = Transcript::new(domains::TRANSCRIPT_INPUT_SET.as_bytes());
transcript.append_u64(b"version", domains::VERSION);
transcript.append_message(b"unpadded_size", &unpadded_size.to_le_bytes());
for item in M {
transcript.append_message(b"M", item.compress().as_bytes());
}
let mut hash = vec![0u8; TRANSCRIPT_HASH_BYTES];
let mut hash = vec![0u8; domains::TRANSCRIPT_HASH_BYTES];
transcript.challenge_bytes(b"hash", &mut hash);

Ok(Self {
Expand Down Expand Up @@ -122,11 +117,6 @@ pub enum StatementError {
}

impl TriptychStatement {
// Domain separator used for hashing
const DOMAIN: &'static str = "Triptych statement";
// Version identifier used for hashing
const VERSION: u64 = 0;

/// Generate a new [`TriptychStatement`].
///
/// The [`TriptychInputSet`] `input_set` must have a verification key vector whose size matches that specified by
Expand Down Expand Up @@ -155,12 +145,12 @@ impl TriptychStatement {
}

// Use Merlin for the transcript hash
let mut transcript = Transcript::new(Self::DOMAIN.as_bytes());
transcript.append_u64(b"version", Self::VERSION);
let mut transcript = Transcript::new(domains::TRANSCRIPT_STATEMENT.as_bytes());
transcript.append_u64(b"version", domains::VERSION);
transcript.append_message(b"params", params.get_hash());
transcript.append_message(b"input_set", input_set.get_hash());
transcript.append_message(b"J", J.compress().as_bytes());
let mut hash = vec![0u8; TRANSCRIPT_HASH_BYTES];
let mut hash = vec![0u8; domains::TRANSCRIPT_HASH_BYTES];
transcript.challenge_bytes(b"hash", &mut hash);

Ok(Self {
Expand Down
Loading

0 comments on commit bf074cb

Please sign in to comment.