Skip to content

Commit

Permalink
Add StreamVerifier
Browse files Browse the repository at this point in the history
  • Loading branch information
robjtede committed Jul 22, 2023
1 parent e44d4b5 commit 11a97dc
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 2 deletions.
1 change: 1 addition & 0 deletions ed25519-dalek/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
12 changes: 11 additions & 1 deletion ed25519-dalek/src/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use crate::{
errors::{InternalError, SignatureError},
hazmat::ExpandedSecretKey,
signature::InternalSignature,
verifying::VerifyingKey,
verifying::{StreamVerifier, VerifyingKey},
Signature,
};

Expand Down Expand Up @@ -473,6 +473,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<StreamVerifier, SignatureError> {
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
Expand Down
18 changes: 17 additions & 1 deletion ed25519-dalek/src/verifying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ use crate::{
signing::SigningKey,
};

mod stream;
pub use self::stream::StreamVerifier;

/// An ed25519 public key.
///
/// # Note
Expand Down Expand Up @@ -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<StreamVerifier, SignatureError> {
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
///
Expand Down
58 changes: 58 additions & 0 deletions ed25519-dalek/src/verifying/stream.rs
Original file line number Diff line number Diff line change
@@ -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())
}
}
}
39 changes: 39 additions & 0 deletions ed25519-dalek/tests/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,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() {
Expand Down

0 comments on commit 11a97dc

Please sign in to comment.