Skip to content

Commit

Permalink
signature_derive: DigestSigner/DigestVerifier support (#1103)
Browse files Browse the repository at this point in the history
Adds support for deriving `DigestSigner`/`DigestVerifier` for types
which impl `hazmat::{PrehashSigner, PrehashVerifier}`.

Custom derive is used instead of a blanket impl in case implementations
wish to specialize the `Digest*` impls. For example, they may want to
use the `Digest` parameter elsewhere, for example as the hash function
HMAC-DRBG is instantiated with for RFC6979.
  • Loading branch information
tarcieri committed Sep 9, 2022
1 parent db853f1 commit 2711ff5
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 28 deletions.
173 changes: 156 additions & 17 deletions signature/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
use syn::{parse_macro_input, DeriveInput, Ident, TypeParam};

/// Derive the [`Signer`] trait for a type which impls [`DigestSigner`].
///
Expand All @@ -37,20 +37,18 @@ pub fn derive_signer(input: TokenStream) -> TokenStream {
}

fn emit_signer_impl(input: DeriveInput) -> TokenStream2 {
let ident = input.ident;
let type_params = input.generics.type_params().collect::<Vec<_>>();
let type_idents = type_params
.iter()
.map(|bound| bound.ident.clone())
.collect::<Vec<_>>();
let params = DeriveParams::new(input);
let ident = &params.ident;
let type_params = &params.type_params;
let type_idents = params.type_idents();

quote! {
impl<S, #(#type_params),*> ::signature::Signer<S> for #ident<#(#type_idents),*>
where
S: ::signature::PrehashSignature,
Self: ::signature::DigestSigner<S::Digest, S>
{
fn try_sign(&self, msg: &[u8]) -> Result<S, ::signature::Error> {
fn try_sign(&self, msg: &[u8]) -> ::signature::Result<S> {
self.try_sign_digest(S::Digest::new_with_prefix(msg))
}
}
Expand Down Expand Up @@ -80,26 +78,111 @@ pub fn derive_verifier(input: TokenStream) -> TokenStream {
}

fn emit_verifier_impl(input: DeriveInput) -> TokenStream2 {
let ident = input.ident;
let type_params = input.generics.type_params().collect::<Vec<_>>();
let type_idents = type_params
.iter()
.map(|bound| bound.ident.clone())
.collect::<Vec<_>>();
let params = DeriveParams::new(input);
let ident = &params.ident;
let type_params = &params.type_params;
let type_idents = params.type_idents();

quote! {
impl<S, #(#type_params),*> ::signature::Verifier<S> for #ident<#(#type_idents),*>
where
S: ::signature::PrehashSignature,
Self: ::signature::DigestVerifier<S::Digest, S>
{
fn verify(&self, msg: &[u8], signature: &S) -> Result<(), ::signature::Error> {
fn verify(&self, msg: &[u8], signature: &S) -> ::signature::Result<()> {
self.verify_digest(S::Digest::new_with_prefix(msg), signature)
}
}
}
}

/// Derive the [`DigestSigner`] trait for a type which impls [`PrehashSigner`].
///
/// [`DigestSigner`]: https://docs.rs/signature/latest/signature/trait.DigestSigner.html
/// [`PrehashSigner`]: https://docs.rs/signature/latest/signature/hazmat/trait.PrehashSigner.html
#[proc_macro_derive(DigestSigner)]
pub fn derive_digest_signer(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
emit_digest_signer_impl(input).into()
}

fn emit_digest_signer_impl(input: DeriveInput) -> TokenStream2 {
let params = DeriveParams::new(input);
let ident = &params.ident;
let type_params = &params.type_params;
let type_idents = params.type_idents();

quote! {
impl<D, S, #(#type_params),*> ::signature::DigestSigner<D, S> for #ident<#(#type_idents),*>
where
D: ::signature::digest::Digest,
S: ::signature::Signature,
Self: ::signature::hazmat::PrehashSigner<S>
{
fn try_sign_digest(&self, digest: D) -> ::signature::Result<S> {
self.sign_prehash(&digest.finalize())
}
}
}
}

/// Derive the [`DigestVerifier`] trait for a type which impls [`PrehashVerifier`].
///
/// [`DigestVerifier`]: https://docs.rs/signature/latest/signature/trait.DigestVerifier.html
/// [`PrehashVerifier`]: https://docs.rs/signature/latest/signature/hazmat/trait.PrehashVerifier.html
#[proc_macro_derive(DigestVerifier)]
pub fn derive_digest_verifier(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
emit_digest_verifier_impl(input).into()
}

fn emit_digest_verifier_impl(input: DeriveInput) -> TokenStream2 {
let params = DeriveParams::new(input);
let ident = &params.ident;
let type_params = &params.type_params;
let type_idents = params.type_idents();

quote! {
impl<D, S, #(#type_params),*> ::signature::DigestVerifier<D, S> for #ident<#(#type_idents),*>
where
D: ::signature::digest::Digest,
S: ::signature::Signature,
Self: ::signature::hazmat::PrehashVerifier<S>
{
fn verify_digest(&self, digest: D, signature: &S) -> ::signature::Result<()> {
self.verify_prehash(&digest.finalize(), signature)
}
}
}
}

/// Derivation parameters parsed from `DeriveInput`.
struct DeriveParams {
/// `Ident` for the struct the trait impls are being added to.
ident: Ident,

/// Generic type parameters.
type_params: Vec<TypeParam>,
}

impl DeriveParams {
/// Parse parameters from `DeriveInput`.
fn new(input: DeriveInput) -> Self {
Self {
ident: input.ident,
type_params: input.generics.type_params().cloned().collect(),
}
}

/// Get the `Ident`s which correspond to each of the `type_params`.
fn type_idents(&self) -> Vec<Ident> {
self.type_params
.iter()
.map(|bound| bound.ident.clone())
.collect()
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -124,7 +207,7 @@ mod tests {
S: ::signature::PrehashSignature,
Self: ::signature::DigestSigner<S::Digest, S>
{
fn try_sign(&self, msg: &[u8]) -> Result <S, ::signature::Error> {
fn try_sign(&self, msg: &[u8]) -> ::signature::Result<S> {
self.try_sign_digest(S::Digest::new_with_prefix(msg))
}
}
Expand Down Expand Up @@ -152,12 +235,68 @@ mod tests {
S: ::signature::PrehashSignature,
Self: ::signature::DigestVerifier<S::Digest, S>
{
fn verify(&self, msg: &[u8], signature: &S) -> Result<(), ::signature::Error> {
fn verify(&self, msg: &[u8], signature: &S) -> ::signature::Result<()> {
self.verify_digest(S::Digest::new_with_prefix(msg), signature)
}
}
}
.to_string()
);
}

#[test]
fn digest_signer() {
let input = parse_quote! {
#[derive(DigestSigner)]
struct MySigner<C: EllipticCurve> {
scalar: Scalar<C::ScalarSize>
}
};

let output = emit_digest_signer_impl(input);

assert_eq!(
output.to_string(),
quote! {
impl<D, S, C: EllipticCurve> ::signature::DigestSigner<D, S> for MySigner<C>
where
S: ::signature::Signature,
Self: ::signature::hazmat::PrehashSigner<S>
{
fn try_sign_digest(&self, digest: D) -> ::signature::Result<S> {
self.sign_prehash(&digest.finalize())
}
}
}
.to_string()
);
}

#[test]
fn digest_verifier() {
let input = parse_quote! {
#[derive(DigestVerifier)]
struct MyVerifier<C: EllipticCurve> {
point: UncompressedPoint<C>
}
};

let output = emit_digest_verifier_impl(input);

assert_eq!(
output.to_string(),
quote! {
impl<D, S, C: EllipticCurve> ::signature::DigestVerifier<D, S> for MyVerifier<C>
where
S: ::signature::Signature,
Self: ::signature::hazmat::PrehashVerifier<S>
{
fn verify_digest(&self, digest: D) -> ::signature::Result<S> {
self.verify_prehash(&digest.finalize())
}
}
}
.to_string()
);
}
}
2 changes: 1 addition & 1 deletion signature/src/hazmat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub trait PrehashSigner<S: Signature> {
///
/// Allowed lengths are algorithm-dependent and up to a particular
/// implementation to decide.
fn try_sign_prehash(&self, prehash: &[u8]) -> Result<S, Error>;
fn sign_prehash(&self, prehash: &[u8]) -> Result<S, Error>;
}

/// Verify the provided message prehash using `Self` (e.g. a public key)
Expand Down
7 changes: 7 additions & 0 deletions signature/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ mod verifier;
#[cfg_attr(docsrs, doc(cfg(feature = "derive-preview")))]
pub use signature_derive::{Signer, Verifier};

#[cfg(all(feature = "derive-preview", feature = "digest-preview"))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "derive-preview", feature = "digest-preview")))
)]
pub use signature_derive::{DigestSigner, DigestVerifier};

#[cfg(feature = "digest-preview")]
pub use digest;

Expand Down
20 changes: 10 additions & 10 deletions signature/tests/signature_derive.rs → signature/tests/derive.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! Tests for code generated by `signature_derive`

#![cfg(all(test, feature = "derive-preview"))]
#![cfg(all(feature = "derive-preview", feature = "hazmat-preview"))]

use digest::{generic_array::GenericArray, Digest, OutputSizeUser};
use hex_literal::hex;
use sha2::Sha256;
use signature::{
hazmat::{PrehashSigner, PrehashVerifier},
DigestSigner, DigestVerifier, Error, PrehashSignature, Signature, Signer, Verifier,
};

Expand Down Expand Up @@ -39,26 +40,25 @@ impl PrehashSignature for DummySignature {
}

/// Dummy signer which just returns the message digest as a `DummySignature`
#[derive(Signer, Default)]
#[derive(Signer, DigestSigner, Default)]
struct DummySigner {}

impl DigestSigner<Sha256, DummySignature> for DummySigner {
fn try_sign_digest(&self, digest: Sha256) -> Result<DummySignature, Error> {
DummySignature::from_bytes(&digest.finalize())
impl PrehashSigner<DummySignature> for DummySigner {
fn sign_prehash(&self, prehash: &[u8]) -> signature::Result<DummySignature> {
DummySignature::from_bytes(prehash)
}
}

/// Dummy verifier which ensures the `DummySignature` digest matches the
/// expected value.
///
/// Panics (via `assert_eq!`) if the value is not what is expected.
#[derive(Verifier, Default)]
#[derive(Verifier, DigestVerifier, Default)]
struct DummyVerifier {}

impl DigestVerifier<Sha256, DummySignature> for DummyVerifier {
fn verify_digest(&self, digest: Sha256, signature: &DummySignature) -> Result<(), Error> {
let actual_digest = digest.finalize();
assert_eq!(signature.as_ref(), actual_digest.as_slice());
impl PrehashVerifier<DummySignature> for DummyVerifier {
fn verify_prehash(&self, prehash: &[u8], signature: &DummySignature) -> signature::Result<()> {
assert_eq!(signature.as_ref(), prehash);
Ok(())
}
}
Expand Down

0 comments on commit 2711ff5

Please sign in to comment.