From e38f88f292ae689c0a6865c21ac92018a96fe767 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 26 Mar 2019 14:07:36 -0700 Subject: [PATCH] Add "raw digest" signature marker trait and sign/verify traits Introduces the notion of a "raw digest" signature algorithm, i.e. any algorithm where signatures are always computed as `S(H(m)))` where: - `S`: signature algorithm - `H`: hash (a.k.a. digest) function - `m`: message Notably this does not hold true for Ed25519, which hashes the input message twice in an effort to remain secure even in the event of collisions in the underlying hash function. However, it supports a separate IUF mode Ed25519ph, which is trivial for any Ed25519 implementation to implement (it hashes the message in advance, then performs a regular Ed25519 signature albeit with a domain separation tweak). For cases like this, where the IUF mode is deliberately domain separated from the normal mode, it would be nice to avoid a blanket impl which assumes all signature algorithms are composed as `S(H(m))`. To accomplish this, this commit adds a `RawDigestSignature` marker trait to be applied at the algorithm level to the corresponding signature trait. For signature types marked as `RawDigestSignature`, it's possible to `impl` a corresponding `SignRawDigest` trait for which a blanket `impl` of `Sign` exists which hashes the message with the corresponding `Digest` algorithm. Additionally, `SignDigest` is blanket impl'd for all `SignRawDigest` types. --- signature-crate/src/sign/digest.rs | 50 ++++++++++++++++++++++++-- signature-crate/src/signature.rs | 12 +++++++ signature-crate/src/verify/digest.rs | 53 ++++++++++++++++++++++++++-- 3 files changed, 111 insertions(+), 4 deletions(-) diff --git a/signature-crate/src/sign/digest.rs b/signature-crate/src/sign/digest.rs index e4f21c2b..504a852f 100644 --- a/signature-crate/src/sign/digest.rs +++ b/signature-crate/src/sign/digest.rs @@ -4,7 +4,11 @@ //! For use signature algorithms that support an Initialize-Update-Finalize //! (IUF) API, such as ECDSA or Ed25519ph. -use crate::{error::Error, Signature}; +use super::Sign; +use crate::{ + error::Error, + signature::{RawDigestSignature, Signature}, +}; use digest::Digest; /// Sign the given prehashed message `Digest` using `Self`. @@ -14,5 +18,47 @@ where S: Signature, { /// Sign the given prehashed message `Digest`, returning a signature. - fn sign(&self, digest: D) -> Result; + fn sign_digest(&self, digest: D) -> Result; +} + +/// Sign the given message using a "raw digest" signature algorithm, i.e. +/// any algorithm where signatures are always computed as `S(H(m)))` where: +/// +/// - `S`: signature algorithm +/// - `H`: hash (a.k.a. digest) function +/// - `m`: message +/// +/// This is the preferred trait to be `impl`'d for such algorithms, and when +/// used will take advantage of a blanket `impl` of `Sign` which will hash +/// the original message in advance using the relevant `Digest` algorithm +pub trait SignRawDigest: Send + Sync +where + S: Signature + RawDigestSignature, +{ + /// Digest algorithm to hash the input message with + type Digest: Digest; + + /// Sign the given prehashed message `Digest`, returning a signature. + fn sign_raw_digest(&self, digest: Self::Digest) -> Result; +} + +impl SignDigest for T +where + D: Digest, + S: Signature + RawDigestSignature, + T: SignRawDigest, +{ + fn sign_digest(&self, digest: D) -> Result { + self.sign_raw_digest(digest) + } +} + +impl Sign for T +where + S: Signature + RawDigestSignature, + T: SignRawDigest, +{ + fn sign(&self, msg: &[u8]) -> Result { + self.sign_digest(>::Digest::new().chain(msg)) + } } diff --git a/signature-crate/src/signature.rs b/signature-crate/src/signature.rs index 8376a5a4..db6acca1 100644 --- a/signature-crate/src/signature.rs +++ b/signature-crate/src/signature.rs @@ -22,3 +22,15 @@ pub trait Signature: AsRef<[u8]> + Debug + Sized { self.as_slice().into() } } + +/// Marker trait for "raw digest" signature algorithms, i.e. any algorithm +/// where signatures are exclusively computed as `S(H(m)))` where: +/// +/// - `S`: signature algorithm +/// - `H`: hash (a.k.a. digest) function +/// - `m`: message +/// +/// Notably this does not hold true for Ed25519, which hashes the input message +/// twice in an effort to remain secure even in the event of collisions in the +/// underlying hash function. +pub trait RawDigestSignature {} diff --git a/signature-crate/src/verify/digest.rs b/signature-crate/src/verify/digest.rs index c577e700..f2d12db6 100644 --- a/signature-crate/src/verify/digest.rs +++ b/signature-crate/src/verify/digest.rs @@ -4,7 +4,11 @@ //! For use signature algorithms that support an Initialize-Update-Finalize //! (IUF) API, such as ECDSA or Ed25519ph. -use crate::{error::Error, Signature}; +use super::Verify; +use crate::{ + error::Error, + signature::{RawDigestSignature, Signature}, +}; use digest::Digest; /// Verify the provided signature for the given prehashed message `Digest` @@ -15,5 +19,50 @@ where S: Signature, { /// Verify the signature against the given `Digest` - fn verify(&self, digest: D, signature: &S) -> Result<(), Error>; + fn verify_digest(&self, digest: D, signature: &S) -> Result<(), Error>; +} + +/// Verify the given message using a "raw digest" signature algorithm, i.e. +/// any algorithm where signatures are always computed as `S(H(m)))` where: +/// +/// - `S`: signature algorithm +/// - `H`: hash (a.k.a. digest) function +/// - `m`: message +/// +/// This is the preferred trait to be `impl`'d for such algorithms, and when +/// used will take advantage of a blanket `impl` of `Sign` which will hash +/// the original message in advance using the relevant `Digest` algorithm +pub trait VerifyRawDigest: Send + Sync +where + S: Signature + RawDigestSignature, +{ + /// Digest algorithm to hash the input message with + type Digest: Digest; + + /// Verify the signature against given prehashed message `Digest` + fn verify_raw_digest(&self, digest: Self::Digest, signature: &S) -> Result<(), Error>; +} + +impl VerifyDigest for T +where + D: Digest, + S: Signature + RawDigestSignature, + T: VerifyRawDigest, +{ + fn verify_digest(&self, digest: D, signature: &S) -> Result<(), Error> { + self.verify_raw_digest(digest, signature) + } +} + +impl Verify for T +where + S: Signature + RawDigestSignature, + T: VerifyRawDigest, +{ + fn verify(&self, msg: &[u8], signature: &S) -> Result<(), Error> { + self.verify_raw_digest( + >::Digest::new().chain(msg), + signature, + ) + } }