diff --git a/benches/signatures.rs b/benches/signatures.rs index 224bd554..8ab2d9e9 100644 --- a/benches/signatures.rs +++ b/benches/signatures.rs @@ -46,7 +46,7 @@ fn sign_message(c: &mut Criterion) { b.iter_batched( gen_keypair, |d| { - let _sig = RistrettoSchnorr::sign_message(&d.k, d.m, &mut OsRng).unwrap(); + let _sig = RistrettoSchnorr::sign(&d.k, d.m, &mut OsRng).unwrap(); }, BatchSize::SmallInput, ); @@ -60,10 +60,10 @@ fn verify_message(c: &mut Criterion) { b.iter_batched( || { let d = gen_keypair(); - let s = RistrettoSchnorr::sign_message(&d.k, d.m, &mut OsRng).unwrap(); + let s = RistrettoSchnorr::sign(&d.k, d.m, &mut OsRng).unwrap(); (d, s) }, - |(d, s)| assert!(s.verify_message(&d.p, d.m)), + |(d, s)| assert!(s.verify(&d.p, d.m)), BatchSize::SmallInput, ); }); diff --git a/src/ristretto/ristretto_sig.rs b/src/ristretto/ristretto_sig.rs index 28a36b14..b41a18b2 100644 --- a/src/ristretto/ristretto_sig.rs +++ b/src/ristretto/ristretto_sig.rs @@ -58,7 +58,7 @@ use crate::{ /// let (k, P) = get_keypair(); /// let msg = "Small Gods"; /// let mut rng = thread_rng(); -/// let sig = RistrettoSchnorr::sign_message(&k, &msg, &mut rng); +/// let sig = RistrettoSchnorr::sign(&k, &msg, &mut rng); /// ``` /// /// # Verifying signatures @@ -84,8 +84,8 @@ use crate::{ /// let P = RistrettoPublicKey::from_secret_key(&k); /// let mut rng = thread_rng(); /// let sig: SchnorrSignature = -/// SchnorrSignature::sign_message(&k, msg, &mut rng).unwrap(); -/// assert!(sig.verify_message(&P, msg)); +/// SchnorrSignature::sign(&k, msg, &mut rng).unwrap(); +/// assert!(sig.verify(&P, msg)); /// ``` pub type RistrettoSchnorr = SchnorrSignature; @@ -116,8 +116,8 @@ pub type RistrettoSchnorr = SchnorrSignature = -/// SchnorrSignature::sign_message(&k, msg, &mut rng).unwrap(); -/// assert!(sig.verify_message(&P, msg)); +/// SchnorrSignature::sign(&k, msg, &mut rng).unwrap(); +/// assert!(sig.verify(&P, msg)); /// ``` pub type RistrettoSchnorrWithDomain = SchnorrSignature; @@ -164,16 +164,16 @@ mod test { .finalize(); let e_key = RistrettoSecretKey::from_bytes_wide(&e).unwrap(); let s = &r + &e_key * &k; - let sig = RistrettoSchnorr::sign_raw(&k, r, &e).unwrap(); + let sig = RistrettoSchnorr::sign_raw_wide(&k, r, &e).unwrap(); let R_calc = sig.get_public_nonce(); assert_eq!(R, *R_calc); assert_eq!(sig.get_signature(), &s); - assert!(sig.verify_challenge(&P, &e)); + assert!(sig.verify_raw_wide(&P, &e)); // Doesn't work for invalid credentials - assert!(!sig.verify_challenge(&R, &e)); + assert!(!sig.verify_raw_wide(&R, &e)); // Doesn't work for different challenge let wrong_challenge = Blake2b::::digest(b"Guards! Guards!"); - assert!(!sig.verify_challenge(&P, &wrong_challenge)); + assert!(!sig.verify_raw_wide(&P, &wrong_challenge)); } /// This test checks that the linearity of Schnorr signatures hold, i.e. that s = s1 + s2 is validated by R1 + R2 @@ -196,13 +196,13 @@ mod test { .chain_update(b"Moving Pictures") .finalize(); // Calculate Alice's signature - let s1 = RistrettoSchnorr::sign_raw(&k1, r1, &e).unwrap(); + let s1 = RistrettoSchnorr::sign_raw_wide(&k1, r1, &e).unwrap(); // Calculate Bob's signature - let s2 = RistrettoSchnorr::sign_raw(&k2, r2, &e).unwrap(); + let s2 = RistrettoSchnorr::sign_raw_wide(&k2, r2, &e).unwrap(); // Now add the two signatures together let s_agg = &s1 + &s2; // Check that the multi-sig verifies - assert!(s_agg.verify_challenge(&(P1 + P2), &e)); + assert!(s_agg.verify_raw_wide(&(P1 + P2), &e)); } #[test] @@ -247,8 +247,8 @@ mod test { // assert_ne!(sig1, sig2); // Prove that the nonces were reused. Again, NEVER do this assert_eq!(sig1.get_public_nonce(), sig2.get_public_nonce()); - assert!(sig1.verify_message(&P, msg)); - assert!(sig2.verify_message(&P, msg)); + assert!(sig1.verify(&P, msg)); + assert!(sig2.verify(&P, msg)); // But the signatures are different, for the same message, secret and nonce. assert_ne!(sig1.get_signature(), sig2.get_signature()); } @@ -258,10 +258,9 @@ mod test { fn sign_and_verify_message() { let mut rng = rand::thread_rng(); let (k, P) = RistrettoPublicKey::random_keypair(&mut rng); - let sig = - RistrettoSchnorr::sign_message(&k, "Queues are things that happen to other people", &mut rng).unwrap(); - assert!(sig.verify_message(&P, "Queues are things that happen to other people")); - assert!(!sig.verify_message(&P, "Qs are things that happen to other people")); - assert!(!sig.verify_message(&(&P + &P), "Queues are things that happen to other people")); + let sig = RistrettoSchnorr::sign(&k, "Queues are things that happen to other people", &mut rng).unwrap(); + assert!(sig.verify(&P, "Queues are things that happen to other people")); + assert!(!sig.verify(&P, "Qs are things that happen to other people")); + assert!(!sig.verify(&(&P + &P), "Queues are things that happen to other people")); } } diff --git a/src/signatures/schnorr.rs b/src/signatures/schnorr.rs index 80756a01..18ff8233 100644 --- a/src/signatures/schnorr.rs +++ b/src/signatures/schnorr.rs @@ -73,40 +73,43 @@ where P::from_secret_key(&self.signature) } - /// Sign a challenge with the given `secret` and private `nonce`. Returns an SchnorrSignatureError if `::from_bytes(challenge)` returns an error. + /// Generate a signature using a given secret key, nonce, and challenge byte slice. /// - /// WARNING: The public key and nonce are NOT bound to the challenge. This method assumes that the challenge has - /// been constructed such that all commitments are already included in the challenge. + /// WARNING: This is intended for use cases where the challenge byte slice was generated correctly. + /// In particlar, it _must_ be the result of securely applying a cryptographic hash function to the correct public + /// key, public nonce, and input message; further, it must be of a length suitable for scalar wide reduction. + /// This function only checks that the byte slice is of the correct length. + /// The nonce _must_ also have been sampled uniformly at random and not reused with the same secret key and a + /// different message. /// - /// Use [`sign_raw`] instead if this is what you want. (This method is a deprecated alias for `sign_raw`). - /// - /// If you want a simple API that binds the nonce and public key to the message, use [`sign_message`] instead. - #[deprecated( - since = "0.16.0", - note = "This method probably doesn't do what you think it does. Please use `sign_message` or `sign_raw` \ - instead, depending on your use case. This function will be removed in v1.0.0" - )] - #[allow(clippy::needless_pass_by_value)] - pub fn sign(secret: K, nonce: K, challenge: &[u8]) -> Result - where - K: Add, - for<'a> K: Mul<&'a K, Output = K>, - { - Self::sign_raw(&secret, nonce, challenge) + /// If you aren't sure that you can meet these requirements, and want a simple and safe API, use [`sign`]. + pub fn sign_raw_wide<'a>(secret: &'a K, nonce: K, challenge: &[u8]) -> Result + where K: Add + Mul<&'a K, Output = K> { + // s = r + e.k + let e = match K::from_bytes_wide(challenge) { + Ok(e) => e, + Err(_) => return Err(SchnorrSignatureError::InvalidChallenge), + }; + let public_nonce = P::from_secret_key(&nonce); + let ek = e * secret; + let s = ek + nonce; + Ok(Self::new(public_nonce, s)) } - /// Sign a challenge with the given `secret` and private `nonce`. Returns an SchnorrSignatureError if `::from_bytes(challenge)` returns an error. + /// Generate a signature using a given secret key, nonce, and challenge byte slice. /// - /// WARNING: The public key and nonce are NOT bound to the challenge. This method assumes that the challenge has - /// been constructed such that all commitments are already included in the challenge. + /// WARNING: This is intended for use cases where the challenge byte slice was generated correctly. + /// In particlar, it _must_ be the result of securely applying a cryptographic hash function to the correct public + /// key, public nonce, and input message; further, it must be the canonical representation of a scalar. + /// This function only checks that the byte slice is of the correct length. + /// The nonce _must_ also have been sampled uniformly at random and not reused with the same secret key and a + /// different message. /// - /// If you want a simple API that binds the nonce and public key to the message, use [`sign_message`] instead. - pub fn sign_raw<'a>(secret: &'a K, nonce: K, challenge: &[u8]) -> Result + /// If you aren't sure that you can meet these requirements, and want a simple and safe API, use [`sign`]. + pub fn sign_raw_canonical<'a>(secret: &'a K, nonce: K, challenge: &[u8]) -> Result where K: Add + Mul<&'a K, Output = K> { // s = r + e.k - let e = match K::from_bytes_wide(challenge) { + let e = match K::from_canonical_bytes(challenge) { Ok(e) => e, Err(_) => return Err(SchnorrSignatureError::InvalidChallenge), }; @@ -119,11 +122,8 @@ where /// Signs a message with the given secret key. /// /// This method correctly binds a nonce and the public key to the signature challenge, using domain-separated - /// hashing. The hasher is also opinionated in the sense that Blake2b 256-bit digest is always used. - /// - /// it is possible to customise the challenge by using [`construct_domain_separated_challenge`] and [`sign_raw`] - /// yourself, or even use [`sign_raw`] using a completely custom challenge. - pub fn sign_message<'a, B, R: RngCore + CryptoRng>( + /// hashing. The hasher is also opinionated in the sense that Blake2b 512-bit digest is always used. + pub fn sign<'a, B, R: RngCore + CryptoRng>( secret: &'a K, message: B, rng: &mut R, @@ -136,16 +136,13 @@ where Self::sign_with_nonce_and_message(secret, nonce, message) } - /// Signs a message with the given secret key and provided nonce. + /// Signs a message with the given secret key and nonce. /// /// This method correctly binds the nonce and the public key to the signature challenge, using domain-separated - /// hashing. The hasher is also opinionated in the sense that Blake2b 256-bit digest is always used. - /// - /// ** Important **: It is the caller's responsibility to ensure that the nonce is unique. This API tries to - /// prevent this by taking ownership of the nonce, which means that the caller has to explicitly clone the nonce - /// in order to re-use it, which is a small deterrent, but better than nothing. + /// hashing. The hasher is also opinionated in the sense that Blake2b 512-bit digest is always used. /// - /// To delegate nonce handling to the callee, use [`Self::sign_message`] instead. + /// WARNING: The nonce _must_ also have been sampled uniformly at random and not reused with the same secret key and + /// a different message. pub fn sign_with_nonce_and_message<'a, B>( secret: &'a K, nonce: K, @@ -159,7 +156,7 @@ where let public_key = P::from_secret_key(secret); let challenge = Self::construct_domain_separated_challenge::<_, Blake2b>(&public_nonce, &public_key, message); - Self::sign_raw(secret, nonce, challenge.as_ref()) + Self::sign_raw_wide(secret, nonce, challenge.as_ref()) } /// Constructs an opinionated challenge hash for the given public nonce, public key and message. @@ -168,8 +165,8 @@ where /// the challenge. In this implementation, the challenge is constructed by means of domain separated hashing /// using the provided digest. /// - /// This challenge is used in the [`sign_message`] and [`verify_message`] methods.If you wish to use a custom - /// challenge, you can use [`sign_raw`] instead. + /// This challenge is used in the [`sign_message`] and [`verify_message`] methods. If you wish to use a custom + /// challenge, you can use [`sign_raw_canonical`] or [`sign_raw_wide`] instead. pub fn construct_domain_separated_challenge( public_nonce: &P, public_key: &P, @@ -186,10 +183,10 @@ where .finalize() } - /// Verifies a signature created by the `sign_message` method. The function returns `true` if and only if the + /// Verifies a signature created by the `sign` method. The function returns `true` if and only if the /// message was signed by the secret key corresponding to the given public key, and that the challenge was /// constructed using the domain-separation method defined in [`construct_domain_separated_challenge`]. - pub fn verify_message<'a, B>(&self, public_key: &'a P, message: B) -> bool + pub fn verify<'a, B>(&self, public_key: &'a P, message: B) -> bool where for<'b> &'b K: Mul<&'a P, Output = P>, for<'b> &'b P: Add, @@ -197,12 +194,12 @@ where { let challenge = Self::construct_domain_separated_challenge::<_, Blake2b>(&self.public_nonce, public_key, message); - self.verify_challenge(public_key, challenge.as_ref()) + self.verify_raw_wide(public_key, challenge.as_ref()) } - /// Returns true if this signature is valid for a public key and challenge, otherwise false. This will always return - /// false if `::from_bytes(challenge)` returns an error. - pub fn verify_challenge<'a>(&self, public_key: &'a P, challenge: &[u8]) -> bool + /// Verifies a signature against a given public key and challenge byte slice. + /// The byte slice is converted to a scalar using wide reduction. + pub fn verify_raw_wide<'a>(&self, public_key: &'a P, challenge: &[u8]) -> bool where for<'b> &'b K: Mul<&'a P, Output = P>, for<'b> &'b P: Add, @@ -211,11 +208,25 @@ where Ok(e) => e, Err(_) => return false, }; - self.verify(public_key, &e) + self.verify_challenge_scalar(public_key, &e) + } + + /// Verifies a signature against a given public key and challenge byte slice. + /// The byte slice is converted to a scalar assuming a canonical representation. + pub fn verify_raw_canonical<'a>(&self, public_key: &'a P, challenge: &[u8]) -> bool + where + for<'b> &'b K: Mul<&'a P, Output = P>, + for<'b> &'b P: Add, + { + let e = match K::from_canonical_bytes(challenge) { + Ok(e) => e, + Err(_) => return false, + }; + self.verify_challenge_scalar(public_key, &e) } /// Returns true if this signature is valid for a public key and challenge scalar, otherwise false. - pub fn verify<'a>(&self, public_key: &'a P, challenge: &K) -> bool + pub fn verify_challenge_scalar<'a>(&self, public_key: &'a P, challenge: &K) -> bool where for<'b> &'b K: Mul<&'a P, Output = P>, for<'b> &'b P: Add,