Skip to content

Commit

Permalink
Adds batch_verify to the nextgen_crypto API
Browse files Browse the repository at this point in the history
Previsible follow-up to D16157589 inserting a `batch_verify_signature` function in the `VerifyingKey` API.
Provides the same implementation on ed25519.
  • Loading branch information
huitseeker authored and calibra-opensource committed Jul 19, 2019
1 parent 71da992 commit 23e8bb6
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 1 deletion.
23 changes: 23 additions & 0 deletions crypto/nextgen_crypto/src/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,29 @@ impl Signature for Ed25519Signature {
.and(Ok(()))
}

/// Batch signature verification as described in the original EdDSA article
/// by Bernstein et al. "High-speed high-security signatures". Current implementation works for
/// signatures on the same message and it checks for malleability.
fn batch_verify_signatures(
message: &HashValue,
keys_and_signatures: Vec<(Self::VerifyingKeyMaterial, Self)>,
) -> Result<()> {
for (_, sig) in keys_and_signatures.iter() {
Ed25519Signature::check_malleability(&sig.to_bytes())?
}
let batch_argument = keys_and_signatures
.into_iter()
.map(|(key, signature)| (key.0, signature.0));
let (dalek_public_keys, dalek_signatures): (Vec<_>, Vec<_>) = batch_argument.unzip();
let message_ref = &message.as_ref()[..];
// The original batching algorithm works for different messages and it expects as many
// messages as the number of signatures. In our case, we just populate the same
// message to meet dalek's api requirements.
let messages = vec![message_ref; dalek_signatures.len()];
ed25519_dalek::verify_batch(&messages[..], &dalek_signatures[..], &dalek_public_keys[..])?;
Ok(())
}

fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes().to_vec()
}
Expand Down
21 changes: 21 additions & 0 deletions crypto/nextgen_crypto/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ pub trait VerifyingKey:
) -> Result<()> {
signature.verify(message, self)
}

/// We provide the implementation which dispatches to the signature.
fn batch_verify_signatures(
message: &HashValue,
keys_and_signatures: Vec<(Self, Self::SignatureMaterial)>,
) -> Result<()> {
Self::SignatureMaterial::batch_verify_signatures(message, keys_and_signatures)
}
}

/// A type family for signature material that knows which public key type
Expand Down Expand Up @@ -203,6 +211,19 @@ pub trait Signature:

/// Convert the signature into a byte representation.
fn to_bytes(&self) -> Vec<u8>;

/// The implementer can override a batch verification implementation
/// that by default iterates over each signature. More efficient
/// implementations exist and should be implemented for many schemes.
fn batch_verify_signatures(
message: &HashValue,
keys_and_signatures: Vec<(Self::VerifyingKeyMaterial, Self)>,
) -> Result<()> {
for (key, signature) in keys_and_signatures {
signature.verify(message, &key)?
}
Ok(())
}
}

/// An alias for the RNG used in the [`Uniform`] trait.
Expand Down
18 changes: 17 additions & 1 deletion crypto/nextgen_crypto/src/unit_tests/ed25519_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ proptest! {
}
}

#[test]
fn test_batch_verify(
hash in any::<HashValue>(),
keypairs in proptest::array::uniform10(uniform_keypair_strategy::<Ed25519PrivateKey, Ed25519PublicKey>())) {
let mut signatures: Vec<(Ed25519PublicKey, Ed25519Signature)> = keypairs.iter().map(|keypair| {
(keypair.public_key.clone(), keypair.private_key.sign_message(&hash))
}).collect();
prop_assert!(Ed25519Signature::batch_verify_signatures(&hash, signatures.clone()).is_ok());
// We swap message and signature for the last element,
// resulting in an incorrect signature
let (key, _sig) = signatures.pop().unwrap();
let other_sig = signatures.last().unwrap().clone().1;
signatures.push((key, other_sig));
prop_assert!(Ed25519Signature::batch_verify_signatures(&hash, signatures).is_err());
}

#[test]
fn test_keys_custom_serialisation(
keypair in uniform_keypair_strategy::<Ed25519PrivateKey, Ed25519PublicKey>()
Expand Down Expand Up @@ -62,7 +78,7 @@ proptest! {
let serialized: &[u8] = &(signature.to_bytes());
prop_assert_eq!(ed25519_dalek::SIGNATURE_LENGTH, serialized.len());
let deserialized = Ed25519Signature::try_from(serialized).unwrap();
assert!(keypair.public_key.verify_signature(&hash, &deserialized).is_ok());
prop_assert!(keypair.public_key.verify_signature(&hash, &deserialized).is_ok());
}

// Check for canonical s.
Expand Down

0 comments on commit 23e8bb6

Please sign in to comment.