diff --git a/CHANGELOG.md b/CHANGELOG.md index 3481d28..9d86477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - [BREAKING] Updated Winterfell dependency to v0.11 (#346). - Added RPO-STARK based DSA (#349). - Added benchmarks for DSA implementations (#354). +- Implemented deterministic RPO-STARK based DSA (#358). ## 0.12.0 (2024-10-30) diff --git a/src/dsa/rpo_stark/stark/mod.rs b/src/dsa/rpo_stark/stark/mod.rs index 80d409c..46aff1e 100644 --- a/src/dsa/rpo_stark/stark/mod.rs +++ b/src/dsa/rpo_stark/stark/mod.rs @@ -1,11 +1,12 @@ +use alloc::vec::Vec; use core::marker::PhantomData; use prover::RpoSignatureProver; -use rand::{distributions::Standard, prelude::Distribution, thread_rng, RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; -use winter_crypto::{ElementHasher, Hasher, SaltedMerkleTree}; +use winter_crypto::{ElementHasher, SaltedMerkleTree}; use winter_math::fields::f64::BaseElement; use winter_prover::{Proof, ProofOptions, Prover}; +use winter_utils::Serializable; use winter_verifier::{verify, AcceptableOptions, VerifierError}; use crate::{ @@ -24,10 +25,7 @@ pub struct RpoSignatureScheme { _h: PhantomData, } -impl + Sync> RpoSignatureScheme -where - Standard: Distribution<::Digest>, -{ +impl + Sync> RpoSignatureScheme { pub fn new(options: ProofOptions) -> Self { RpoSignatureScheme { options, _h: PhantomData } } @@ -40,9 +38,7 @@ where let trace = prover.build_trace(sk); // generate the initial seed for the PRNG used for zero-knowledge - let mut seed = ::Seed::default(); - let mut rng = thread_rng(); - rng.fill_bytes(&mut seed); + let seed: [u8; 32] = generate_seed(sk, msg); // generate the proof prover.prove(trace, Some(seed)).expect("failed to generate the signature") @@ -67,3 +63,36 @@ where ) } } + +/// Deterministically generates a seed for seeding the PRNG used for zero-knowledge. +/// +/// This uses the argument described in [RFC 6979](https://datatracker.ietf.org/doc/html/rfc6979#section-3.5) +/// ยง 3.5 where the concatenation of the private key and the hashed message, i.e., sk || H(m), is +/// used in order to construct the initial seed of a PRNG. +/// +/// Note that we hash in also a context string in order to domain separate between different +/// instantiations of the signature scheme. +#[inline] +pub fn generate_seed(sk: [BaseElement; DIGEST_SIZE], msg: [BaseElement; DIGEST_SIZE]) -> [u8; 32] { + let context_bytes = " + Seed for PRNG used for Zero-knowledge in RPO-STARK signature scheme: + 1. Version: Conjectured security + 2. FRI queries: 30 + 3. Blowup factor: 8 + 4. Grinding bits: 12 + 5. Field extension degree: 2 + 6. FRI folding factor: 4 + 7. FRI remainder polynomial max degree: 7 + " + .to_bytes(); + let sk_bytes = sk.to_bytes(); + let msg_bytes = msg.to_bytes(); + + let total_length = context_bytes.len() + sk_bytes.len() + msg_bytes.len(); + let mut buffer = Vec::with_capacity(total_length); + buffer.extend_from_slice(&context_bytes); + buffer.extend_from_slice(&sk_bytes); + buffer.extend_from_slice(&msg_bytes); + + blake3::hash(&buffer).into() +}