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

Add StreamVerifier #542

Closed
Closed
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
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 @@ -42,7 +42,7 @@ use crate::{
errors::{InternalError, SignatureError},
hazmat::ExpandedSecretKey,
signature::InternalSignature,
verifying::VerifyingKey,
verifying::{StreamVerifier, VerifyingKey},
Signature,
};

Expand Down Expand Up @@ -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<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 @@ -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() {
Expand Down