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 #196

Closed
wants to merge 1 commit into from
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
12 changes: 11 additions & 1 deletion 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 Curve25519 scalar.
///
/// This is useful for e.g. performing X25519 Diffie-Hellman using
Expand Down
18 changes: 17 additions & 1 deletion 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 @@ -436,8 +439,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 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);
Comment on lines +47 to +50
Copy link
Contributor

@tarcieri tarcieri May 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if VerifyingKey::recover_R could be further decomposed into VerifyingKey::recover_R_from_k or thereabouts which captures this functionality.

cc @rozbb

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made that change along with some tangential work in #304


if R.compress() == self.signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
}
39 changes: 39 additions & 0 deletions 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