From ddefde34b99faa8d1d5abffe646b4e27f94b8e83 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 2 Jul 2023 18:42:02 +0100 Subject: [PATCH] Add StreamVerifier --- ed25519-dalek/CHANGELOG.md | 1 + ed25519-dalek/src/signing.rs | 12 +++++- ed25519-dalek/src/verifying.rs | 18 ++++++++- ed25519-dalek/src/verifying/stream.rs | 58 +++++++++++++++++++++++++++ ed25519-dalek/tests/ed25519.rs | 39 ++++++++++++++++++ 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 ed25519-dalek/src/verifying/stream.rs diff --git a/ed25519-dalek/CHANGELOG.md b/ed25519-dalek/CHANGELOG.md index c3fc94a5c..ee94ea0b9 100644 --- a/ed25519-dalek/CHANGELOG.md +++ b/ed25519-dalek/CHANGELOG.md @@ -34,6 +34,7 @@ Entries are listed in reverse chronological order per undeprecated major series. * Add `pkcs` feature to support PKCS #8 (de)serialization of `SigningKey` and `VerifyingKey` * Add `fast` feature to include basepoint tables * Add tests for validation criteria +* Add `SigningKey::verify_stream()`, and `VerifyingKey::verify_stream()` * Impl `DigestSigner`/`DigestVerifier` for `SigningKey`/`VerifyingKey`, respectively * Impl `Hash` for `VerifyingKey` * Impl `Clone`, `Drop`, and `ZeroizeOnDrop` for `SigningKey` diff --git a/ed25519-dalek/src/signing.rs b/ed25519-dalek/src/signing.rs index c141e6d71..d18a48115 100644 --- a/ed25519-dalek/src/signing.rs +++ b/ed25519-dalek/src/signing.rs @@ -42,7 +42,7 @@ use crate::{ errors::{InternalError, SignatureError}, hazmat::ExpandedSecretKey, signature::InternalSignature, - verifying::VerifyingKey, + verifying::{StreamVerifier, VerifyingKey}, Signature, }; @@ -480,6 +480,16 @@ impl SigningKey { self.verifying_key.verify_strict(message, signature) } + /// Constructs stream verifier with candidate `signature`. + /// + /// See [`VerifyingKey::verify_stream()`] for more details. + pub fn verify_stream( + &self, + signature: &ed25519::Signature, + ) -> Result { + self.verifying_key.verify_stream(signature) + } + /// Convert this signing key into a byte representation of a(n) (unreduced) Curve25519 scalar. /// /// This can be used for performing X25519 Diffie-Hellman using Ed25519 keys. The bytes output diff --git a/ed25519-dalek/src/verifying.rs b/ed25519-dalek/src/verifying.rs index 29f8a4d14..6ae57e729 100644 --- a/ed25519-dalek/src/verifying.rs +++ b/ed25519-dalek/src/verifying.rs @@ -43,6 +43,9 @@ use crate::{ signing::SigningKey, }; +mod stream; +pub use self::stream::StreamVerifier; + /// An ed25519 public key. /// /// # Note @@ -424,8 +427,21 @@ impl VerifyingKey { } } + /// Constructs stream verifier with candidate `signature`. + /// + /// Useful for cases where the whole message is not available all at once, allowing the + /// internal signature state to be updated incrementally and verified at the end. In some cases, + /// this will reduce the need for additional allocations. + pub fn verify_stream( + &self, + signature: &ed25519::Signature, + ) -> Result { + let signature = InternalSignature::try_from(signature)?; + Ok(StreamVerifier::new(*self, signature)) + } + /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm, - /// using strict signture checking as defined by [`Self::verify_strict`]. + /// using strict signature checking as defined by [`Self::verify_strict`]. /// /// # Inputs /// diff --git a/ed25519-dalek/src/verifying/stream.rs b/ed25519-dalek/src/verifying/stream.rs new file mode 100644 index 000000000..76f7d3ffe --- /dev/null +++ b/ed25519-dalek/src/verifying/stream.rs @@ -0,0 +1,58 @@ +use curve25519_dalek::{edwards::EdwardsPoint, scalar::Scalar}; +use sha2::{Digest, Sha512}; + +use crate::{signature::InternalSignature, InternalError, SignatureError, VerifyingKey}; + +/// An IUF verifier for ed25519. +/// +/// Created with [`VerifyingKey::verify_stream()`] or [`SigningKey::verify_stream()`]. +/// +/// [`SigningKey::verify_stream()`]: super::SigningKey::verify_stream() +#[derive(Debug)] +pub struct StreamVerifier { + /// Public key to verify with. + pub(crate) public_key: VerifyingKey, + + /// Candidate signature to verify against. + pub(crate) signature: InternalSignature, + + /// Hash state. + pub(crate) hasher: Sha512, +} + +impl StreamVerifier { + /// Constructs new stream verifier. + /// + /// Seeds hash state with public key and signature components. + pub(crate) fn new(public_key: VerifyingKey, signature: InternalSignature) -> Self { + let mut hasher = Sha512::new(); + hasher.update(signature.R.as_bytes()); + hasher.update(public_key.as_bytes()); + + Self { + public_key, + hasher, + signature, + } + } + + /// Digest message chunk. + pub fn update(&mut self, chunk: impl AsRef<[u8]>) { + self.hasher.update(&chunk); + } + + /// Finalize verifier and check against candidate signature. + #[allow(non_snake_case)] + pub fn finalize_and_verify(self) -> Result<(), SignatureError> { + let minus_A: EdwardsPoint = -self.public_key.point; + let k = Scalar::from_hash(self.hasher); + let R = + EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &self.signature.s); + + if R.compress() == self.signature.R { + Ok(()) + } else { + Err(InternalError::Verify.into()) + } + } +} diff --git a/ed25519-dalek/tests/ed25519.rs b/ed25519-dalek/tests/ed25519.rs index c05efa3c3..745211d1d 100644 --- a/ed25519-dalek/tests/ed25519.rs +++ b/ed25519-dalek/tests/ed25519.rs @@ -335,6 +335,45 @@ mod integrations { ); } + #[cfg(feature = "digest")] + #[test] + fn sign_verify_digest_equivalence() { + // TestSignVerify + let keypair: SigningKey; + let good_sig: Signature; + let bad_sig: Signature; + + let good: &[u8] = "test message".as_bytes(); + let bad: &[u8] = "wrong message".as_bytes(); + + let mut csprng = OsRng {}; + + keypair = SigningKey::generate(&mut csprng); + good_sig = keypair.sign(&good); + bad_sig = keypair.sign(&bad); + + let mut verifier = keypair.verify_stream(&good_sig).unwrap(); + verifier.update(&good); + assert!( + verifier.finalize_and_verify().is_ok(), + "Verification of a valid signature failed!" + ); + + let mut verifier = keypair.verify_stream(&bad_sig).unwrap(); + verifier.update(&good); + assert!( + verifier.finalize_and_verify().is_err(), + "Verification of a signature on a different message passed!" + ); + + let mut verifier = keypair.verify_stream(&good_sig).unwrap(); + verifier.update(&bad); + assert!( + verifier.finalize_and_verify().is_err(), + "Verification of a signature on a different message passed!" + ); + } + #[cfg(feature = "digest")] #[test] fn ed25519ph_sign_verify() {