From e8118d926d76ea0e8d8cc6541981c7d9eaca0970 Mon Sep 17 00:00:00 2001 From: Aaron Feickert <66188213+AaronFeickert@users.noreply.github.com> Date: Mon, 8 Jan 2024 09:54:35 -0600 Subject: [PATCH] Move proof message into statement --- benches/triptych.rs | 20 ++++++------ src/lib.rs | 16 +++------- src/proof.rs | 78 ++++++++++++++++++++++++--------------------- src/statement.rs | 15 ++++++++- 4 files changed, 70 insertions(+), 59 deletions(-) diff --git a/benches/triptych.rs b/benches/triptych.rs index 2e6a218..a864053 100644 --- a/benches/triptych.rs +++ b/benches/triptych.rs @@ -54,13 +54,13 @@ fn generate_proof(c: &mut Criterion) { // Generate statement let J = witness.compute_linking_tag(); - let statement = Statement::new(¶ms, &input_set, &J).unwrap(); + let message = "Proof message".as_bytes(); + let statement = Statement::new(¶ms, &input_set, &J, Some(message)).unwrap(); // Start the benchmark b.iter(|| { // Generate the proof - let _proof = - Proof::prove(&witness, &statement, Some("Proof message".as_bytes()), &mut rng).unwrap(); + let _proof = Proof::prove(&witness, &statement, &mut rng).unwrap(); }) }); } @@ -103,13 +103,13 @@ fn generate_proof_vartime(c: &mut Criterion) { // Generate statement let J = witness.compute_linking_tag(); - let statement = Statement::new(¶ms, &input_set, &J).unwrap(); + let message = "Proof message".as_bytes(); + let statement = Statement::new(¶ms, &input_set, &J, Some(message)).unwrap(); // Start the benchmark b.iter(|| { // Generate the proof - let _proof = - Proof::prove_vartime(&witness, &statement, Some("Proof message".as_bytes()), &mut rng).unwrap(); + let _proof = Proof::prove_vartime(&witness, &statement, &mut rng).unwrap(); }) }); } @@ -147,14 +147,14 @@ fn verify_proof(c: &mut Criterion) { // Generate statement let J = witness.compute_linking_tag(); - let statement = Statement::new(¶ms, &input_set, &J).unwrap(); - let message = "Proof message".as_bytes(); - let proof = Proof::prove(&witness, &statement, Some(message), &mut rng).unwrap(); + let statement = Statement::new(¶ms, &input_set, &J, Some(message)).unwrap(); + + let proof = Proof::prove(&witness, &statement, &mut rng).unwrap(); // Start the benchmark b.iter(|| { - assert!(proof.verify(&statement, Some(message))); + assert!(proof.verify(&statement)); }) }); } diff --git a/src/lib.rs b/src/lib.rs index 0efbc0a..42db61d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,22 +88,16 @@ //! .collect::>(); //! let input_set = Arc::new(InputSet::new(&M)); //! -//! // Generate the statement, which includes the verification key vector and linking tag +//! // Generate the statement, which includes the verification key vector, linking tag, and optional message //! let J = witness.compute_linking_tag(); -//! let statement = Statement::new(¶ms, &input_set, &J).unwrap(); -//! -//! // We can optionally specify an arbitrary message to bind into the proof //! let message = "This message will be bound to the proof".as_bytes(); +//! let statement = Statement::new(¶ms, &input_set, &J, Some(message)).unwrap(); //! //! // Generate a proof from the witness -//! let proof = Proof::prove(&witness, &statement, Some(message), &mut rng).unwrap(); -//! -//! // The proof should verify against the statement and message -//! assert!(proof.verify(&statement, Some(message))); +//! let proof = Proof::prove(&witness, &statement, &mut rng).unwrap(); //! -//! // The proof should not verify against a different message -//! let evil_message = "Pure evil".as_bytes(); -//! assert!(!proof.verify(&statement, Some(evil_message))); +//! // The proof should verify against the same statement +//! assert!(proof.verify(&statement)); //! ``` #![no_std] diff --git a/src/proof.rs b/src/proof.rs index 5bf1630..9c50d79 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -91,18 +91,17 @@ impl Proof { /// /// You must also supply a cryptographically-secure random number generator `rng`. /// - /// You may optionally provide a byte slice `message` that is bound to the proof's Fiat-Shamir transcript. - /// The verifier must provide the same message in order for the proof to verify. + /// If the statement includes an optional message, it is bound to the proof's Fiat-Shamir transcript. + /// The verifier must provide the same message in its statement in order for the proof to verify. /// /// This function specifically avoids constant-time operations for efficiency. /// If you want any attempt at avoiding timing side-channel attacks, use `prove` instead. pub fn prove_vartime( witness: &Witness, statement: &Statement, - message: Option<&[u8]>, rng: &mut R, ) -> Result { - Self::prove_internal(witness, statement, message, rng, true) + Self::prove_internal(witness, statement, rng, true) } /// Generate a Triptych proof. @@ -113,18 +112,13 @@ impl Proof { /// /// You must also supply a cryptographically-secure random number generator `rng`. /// - /// You may optionally provide a byte slice `message` that is bound to the proof's Fiat-Shamir transcript. - /// The verifier must provide the same message in order for the proof to verify. + /// If the statement includes an optional message, it is bound to the proof's Fiat-Shamir transcript. + /// The verifier must provide the same message in its statement in order for the proof to verify. /// /// This function makes some attempt at avoiding timing side-channel attacks. /// If you know you don't need this, you can use `prove_vartime` for speedier operations. - pub fn prove( - witness: &Witness, - statement: &Statement, - message: Option<&[u8]>, - rng: &mut R, - ) -> Result { - Self::prove_internal(witness, statement, message, rng, false) + pub fn prove(witness: &Witness, statement: &Statement, rng: &mut R) -> Result { + Self::prove_internal(witness, statement, rng, false) } /// The actual prover functionality. @@ -132,7 +126,6 @@ impl Proof { fn prove_internal( witness: &Witness, statement: &Statement, - message: Option<&[u8]>, rng: &mut R, vartime: bool, ) -> Result { @@ -159,7 +152,7 @@ impl Proof { // Start the transcript let mut transcript = Transcript::new("Triptych proof".as_bytes()); transcript.append_u64("version".as_bytes(), VERSION); - if let Some(message) = message { + if let Some(message) = statement.get_message() { transcript.append_message("message".as_bytes(), message); } transcript.append_message("params".as_bytes(), params.get_hash()); @@ -347,12 +340,12 @@ impl Proof { /// Verify a Triptych proof. /// - /// Verification requires that the statement `statement` and optional byte slice `message` match those used when the + /// Verification requires that the statement `statement` match that used when the /// proof was generated. /// /// Returns a boolean that is `true` if and only if the proof is valid. #[allow(clippy::too_many_lines, non_snake_case)] - pub fn verify(&self, statement: &Statement, message: Option<&[u8]>) -> bool { + pub fn verify(&self, statement: &Statement) -> bool { // Extract statement values for convenience let M = statement.get_input_set().get_keys(); let params = statement.get_params(); @@ -377,7 +370,7 @@ impl Proof { // Generate the verifier challenge let mut transcript = Transcript::new("Triptych proof".as_bytes()); transcript.append_u64("version".as_bytes(), VERSION); - if let Some(message) = message { + if let Some(message) = statement.get_message() { transcript.append_message("message".as_bytes(), message); } transcript.append_message("params".as_bytes(), params.get_hash()); @@ -564,7 +557,8 @@ mod test { // Generate statement let J = witness.compute_linking_tag(); - let statement = Statement::new(¶ms, &input_set, &J).unwrap(); + let message = "Proof message".as_bytes(); + let statement = Statement::new(¶ms, &input_set, &J, Some(message)).unwrap(); (witness, statement) } @@ -579,9 +573,8 @@ mod test { let (witness, statement) = generate_data(n, m, &mut rng); // Generate and verify a proof - let message = "Proof messsage".as_bytes(); - let proof = Proof::prove(&witness, &statement, Some(message), &mut rng).unwrap(); - assert!(proof.verify(&statement, Some(message))); + let proof = Proof::prove(&witness, &statement, &mut rng).unwrap(); + assert!(proof.verify(&statement)); } #[test] @@ -594,9 +587,8 @@ mod test { let (witness, statement) = generate_data(n, m, &mut rng); // Generate and verify a proof - let message = "Proof messsage".as_bytes(); - let proof = Proof::prove_vartime(&witness, &statement, Some(message), &mut rng).unwrap(); - assert!(proof.verify(&statement, Some(message))); + let proof = Proof::prove_vartime(&witness, &statement, &mut rng).unwrap(); + assert!(proof.verify(&statement)); } #[test] @@ -609,12 +601,19 @@ mod test { let (witness, statement) = generate_data(n, m, &mut rng); // Generate a proof - let message = "Proof messsage".as_bytes(); - let proof = Proof::prove_vartime(&witness, &statement, Some(message), &mut rng).unwrap(); + let proof = Proof::prove_vartime(&witness, &statement, &mut rng).unwrap(); - // Attempt to verify the proof against a different message, which should fail - let evil_message = "Evil proof message".as_bytes(); - assert!(!proof.verify(&statement, Some(evil_message))); + // Generate a statement with a modified message + let evil_statement = Statement::new( + statement.get_params(), + statement.get_input_set(), + statement.get_J(), + Some("Evil proof message".as_bytes()), + ) + .unwrap(); + + // Attempt to verify the proof against the new statement, which should fail + assert!(!proof.verify(&evil_statement)); } #[test] @@ -627,18 +626,23 @@ mod test { let (witness, statement) = generate_data(n, m, &mut rng); // Generate a proof - let message = "Proof messsage".as_bytes(); - let proof = Proof::prove_vartime(&witness, &statement, Some(message), &mut rng).unwrap(); + let proof = Proof::prove_vartime(&witness, &statement, &mut rng).unwrap(); // Generate a statement with a modified input set let mut M = statement.get_input_set().get_keys().to_vec(); let index = ((witness.get_l() + 1) % witness.get_params().get_N()) as usize; M[index] = RistrettoPoint::random(&mut rng); let evil_input_set = Arc::new(InputSet::new(&M)); - let evil_statement = Statement::new(statement.get_params(), &evil_input_set, statement.get_J()).unwrap(); + let evil_statement = Statement::new( + statement.get_params(), + &evil_input_set, + statement.get_J(), + statement.get_message(), + ) + .unwrap(); // Attempt to verify the proof against the new statement, which should fail - assert!(!proof.verify(&evil_statement, Some(message))); + assert!(!proof.verify(&evil_statement)); } #[test] @@ -651,18 +655,18 @@ mod test { let (witness, statement) = generate_data(n, m, &mut rng); // Generate a proof - let message = "Proof messsage".as_bytes(); - let proof = Proof::prove_vartime(&witness, &statement, Some(message), &mut rng).unwrap(); + let proof = Proof::prove_vartime(&witness, &statement, &mut rng).unwrap(); // Generate a statement with a modified linking tag let evil_statement = Statement::new( statement.get_params(), statement.get_input_set(), &RistrettoPoint::random(&mut rng), + statement.get_message(), ) .unwrap(); // Attempt to verify the proof against the new statement, which should fail - assert!(!proof.verify(&evil_statement, Some(message))); + assert!(!proof.verify(&evil_statement)); } } diff --git a/src/statement.rs b/src/statement.rs index b914d1e..74dae20 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -50,7 +50,8 @@ impl InputSet { /// A Triptych proof statement. /// -/// The statement consists of an input set of verification keys and a linking tag. +/// The statement consists of an input set of verification keys, a linking tag, and an optional message. +/// If provided, the message is bound to any proof generated using the statement. /// It also contains parameters that, among other things, enforce the size of the input set. #[allow(non_snake_case)] #[derive(Clone, Eq, PartialEq)] @@ -58,6 +59,7 @@ pub struct Statement { params: Arc, input_set: Arc, J: RistrettoPoint, + message: Option>, } /// Errors that can arise relating to `Statement`. @@ -75,12 +77,14 @@ impl Statement { /// parameters `params`, and which does not contain the identity group element. /// If either of these conditions is not met, returns an error. /// + /// If provided, the optional `message` will be bound to any proof generated using the resulting statement. /// The linking tag `J` is assumed to have been computed from witness data or otherwise provided externally. #[allow(non_snake_case)] pub fn new( params: &Arc, input_set: &Arc, J: &RistrettoPoint, + message: Option<&[u8]>, ) -> Result { // Check that the input vector is valid against the parameters if input_set.get_keys().len() != params.get_N() as usize { @@ -94,6 +98,7 @@ impl Statement { params: params.clone(), input_set: input_set.clone(), J: *J, + message: message.map(|m| m.to_vec()), }) } @@ -112,4 +117,12 @@ impl Statement { pub fn get_J(&self) -> &RistrettoPoint { &self.J } + + /// Get the message for this statement + pub fn get_message(&self) -> Option<&[u8]> { + match &self.message { + Some(message) => Some(message.as_slice()), + None => None, + } + } }