Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: move proof message into statement #23

Merged
merged 1 commit into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions benches/triptych.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ fn generate_proof(c: &mut Criterion) {

// Generate statement
let J = witness.compute_linking_tag();
let statement = Statement::new(&params, &input_set, &J).unwrap();
let message = "Proof message".as_bytes();
let statement = Statement::new(&params, &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();
})
});
}
Expand Down Expand Up @@ -103,13 +103,13 @@ fn generate_proof_vartime(c: &mut Criterion) {

// Generate statement
let J = witness.compute_linking_tag();
let statement = Statement::new(&params, &input_set, &J).unwrap();
let message = "Proof message".as_bytes();
let statement = Statement::new(&params, &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();
})
});
}
Expand Down Expand Up @@ -147,14 +147,14 @@ fn verify_proof(c: &mut Criterion) {

// Generate statement
let J = witness.compute_linking_tag();
let statement = Statement::new(&params, &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(&params, &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));
})
});
}
Expand Down
16 changes: 5 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,22 +88,16 @@
//! .collect::<Vec<RistrettoPoint>>();
//! 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(&params, &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(&params, &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]
Expand Down
78 changes: 41 additions & 37 deletions src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<R: CryptoRngCore>(
witness: &Witness,
statement: &Statement,
message: Option<&[u8]>,
rng: &mut R,
) -> Result<Self, ProofError> {
Self::prove_internal(witness, statement, message, rng, true)
Self::prove_internal(witness, statement, rng, true)
}

/// Generate a Triptych proof.
Expand All @@ -113,26 +112,20 @@ 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<R: CryptoRngCore>(
witness: &Witness,
statement: &Statement,
message: Option<&[u8]>,
rng: &mut R,
) -> Result<Self, ProofError> {
Self::prove_internal(witness, statement, message, rng, false)
pub fn prove<R: CryptoRngCore>(witness: &Witness, statement: &Statement, rng: &mut R) -> Result<Self, ProofError> {
Self::prove_internal(witness, statement, rng, false)
}

/// The actual prover functionality.
#[allow(clippy::too_many_lines, non_snake_case)]
fn prove_internal<R: CryptoRngCore>(
witness: &Witness,
statement: &Statement,
message: Option<&[u8]>,
rng: &mut R,
vartime: bool,
) -> Result<Self, ProofError> {
Expand All @@ -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());
Expand Down Expand Up @@ -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();
Expand All @@ -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());
Expand Down Expand Up @@ -564,7 +557,8 @@ mod test {

// Generate statement
let J = witness.compute_linking_tag();
let statement = Statement::new(&params, &input_set, &J).unwrap();
let message = "Proof message".as_bytes();
let statement = Statement::new(&params, &input_set, &J, Some(message)).unwrap();

(witness, statement)
}
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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));
}
}
15 changes: 14 additions & 1 deletion src/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,16 @@ 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)]
pub struct Statement {
params: Arc<Parameters>,
input_set: Arc<InputSet>,
J: RistrettoPoint,
message: Option<Vec<u8>>,
}

/// Errors that can arise relating to `Statement`.
Expand All @@ -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<Parameters>,
input_set: &Arc<InputSet>,
J: &RistrettoPoint,
message: Option<&[u8]>,
) -> Result<Self, StatementError> {
// Check that the input vector is valid against the parameters
if input_set.get_keys().len() != params.get_N() as usize {
Expand All @@ -94,6 +98,7 @@ impl Statement {
params: params.clone(),
input_set: input_set.clone(),
J: *J,
message: message.map(|m| m.to_vec()),
})
}

Expand All @@ -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,
}
}
}