diff --git a/signature-crate/signature_derive/src/lib.rs b/signature-crate/signature_derive/src/lib.rs index aa0953f9..d49d37b5 100644 --- a/signature-crate/signature_derive/src/lib.rs +++ b/signature-crate/signature_derive/src/lib.rs @@ -12,96 +12,102 @@ extern crate proc_macro; use proc_macro2::TokenStream; use quote::quote; +use syn::{Attribute, Meta, NestedMeta}; use synstructure::{decl_derive, AddBounds}; +/// Name of the digest attribute +const DIGEST_ATTRIBUTE_NAME: &str = "digest"; + /// Derive the `Signer` trait for `DigestSigner` types fn derive_signer(mut s: synstructure::Structure) -> TokenStream { + let digest_path = DigestAttribute::parse(&s).into_meta("Signer"); + s.add_bounds(AddBounds::None); s.gen_impl(quote! { gen impl signature::Signer for @Self where - S: signature::DigestSignature, - Self: signature::DigestSigner + S: Signature, + Self: signature::DigestSigner<#digest_path, S> { fn try_sign(&self, msg: &[u8]) -> Result { - self.try_sign_digest(S::Digest::new().chain(msg)) + self.try_sign_digest(#digest_path::new().chain(msg)) } } }) } -decl_derive!([Signer] => derive_signer); +decl_derive!([Signer, attributes(digest)] => derive_signer); /// Derive the `Verifier` trait for `DigestVerifier` types fn derive_verifier(mut s: synstructure::Structure) -> TokenStream { + let digest_path = DigestAttribute::parse(&s).into_meta("Verifier"); + s.add_bounds(AddBounds::None); s.gen_impl(quote! { gen impl signature::Verifier for @Self where - S: signature::DigestSignature, - Self: signature::DigestVerifier + S: Signature, + Self: signature::DigestVerifier<#digest_path, S> { fn verify(&self, msg: &[u8], signature: &S) -> Result<(), signature::Error> { - self.verify_digest(S::Digest::new().chain(msg), signature) + self.verify_digest(#digest_path::new().chain(msg), signature) } } }) } -decl_derive!([Verifier] => derive_verifier); - -#[cfg(test)] -mod tests { - use super::*; - use synstructure::test_derive; - - #[test] - fn signer() { - test_derive! { - derive_signer { - struct MySigner { - scalar: Scalar - } - } - expands to { - #[allow(non_upper_case_globals)] - const _DERIVE_signature_Signer_S_FOR_MySigner: () = { - impl signature::Signer for MySigner - where - S: signature::DigestSignature, - Self: signature::DigestSigner - { - fn try_sign(&self, msg: &[u8]) -> Result { - self.try_sign_digest(S::Digest::new().chain(msg)) - } - } - }; +decl_derive!([Verifier, attributes(digest)] => derive_verifier); + +/// The `#[digest(...)]` attribute passed to the proc macro +#[derive(Default)] +struct DigestAttribute { + digest: Option, +} + +impl DigestAttribute { + /// Parse attributes from the incoming AST + fn parse(s: &synstructure::Structure<'_>) -> Self { + let mut result = Self::default(); + + for v in s.variants().iter() { + for attr in v.ast().attrs.iter() { + result.parse_attr(attr); } - no_build // tests in `signature-crate/tests` } + + result } - #[test] - fn verifier() { - test_derive! { - derive_verifier { - struct MyVerifier { - point: UncompressedPoint - } + /// Parse attribute and handle `#[digest(...)]` attribute + fn parse_attr(&mut self, attr: &Attribute) { + let meta = attr + .parse_meta() + .unwrap_or_else(|e| panic!("error parsing digest attribute: {:?} ({})", attr, e)); + + if let Meta::List(list) = meta { + if !list.path.is_ident(DIGEST_ATTRIBUTE_NAME) { + return; } - expands to { - #[allow(non_upper_case_globals)] - const _DERIVE_signature_Verifier_S_FOR_MyVerifier: () = { - impl signature::Verifier for MyVerifier - where - S: signature::DigestSignature, - Self: signature::DigestVerifier - { - fn verify(&self, msg: &[u8], signature: &S) -> Result<(), signature::Error> { - self.verify_digest(S::Digest::new().chain(msg), signature) - } + + for nested_meta in &list.nested { + if let NestedMeta::Meta(meta) = nested_meta { + if self.digest.is_none() { + self.digest = Some(meta.to_owned()); + } else { + panic!("multiple digest attributes in custom derive"); } - }; + } else { + panic!("malformed digest attribute: {:?}", nested_meta); + } } - no_build // tests in `signature-crate/tests` } } + + /// Convert parsed attributes into the recovered `Meta` + fn into_meta(self, trait_name: &str) -> Meta { + self.digest.unwrap_or_else(|| { + panic!( + "#[digest(...)] attribute is mandatory when deriving {}", + trait_name + ) + }) + } } diff --git a/signature-crate/src/signature.rs b/signature-crate/src/signature.rs index 8d021467..8376a5a4 100644 --- a/signature-crate/src/signature.rs +++ b/signature-crate/src/signature.rs @@ -22,19 +22,3 @@ pub trait Signature: AsRef<[u8]> + Debug + Sized { self.as_slice().into() } } - -/// Marker trait for `Signature` types computable as `S(H(m))` -/// -/// - `S`: signature algorithm -/// - `H`: hash (a.k.a. digest) function -/// - `m`: message -/// -/// For signature types that implement this trait, a blanket impl of -/// `Signer` will be provided for all types that `impl DigestSigner` -/// along with a corresponding impl of `Verifier` for all types that -/// `impl DigestVerifier`. -#[cfg(feature = "digest")] -pub trait DigestSignature: Signature { - /// Preferred `Digest` algorithm to use when computing this signature type. - type Digest: digest::Digest; -} diff --git a/signature-crate/tests/signature_derive.rs b/signature-crate/tests/signature_derive.rs index 1adffaa6..7b92c8d8 100644 --- a/signature-crate/tests/signature_derive.rs +++ b/signature-crate/tests/signature_derive.rs @@ -4,9 +4,7 @@ mod tests { use digest::{generic_array::GenericArray, Digest}; use hex_literal::hex; use sha2::Sha256; - use signature::{ - DigestSignature, DigestSigner, DigestVerifier, Error, Signature, Signer, Verifier, - }; + use signature::{DigestSigner, DigestVerifier, Error, Signature, Signer, Verifier}; /// Test vector to compute SHA-256 digest of const INPUT_STRING: &[u8] = b"abc"; @@ -33,12 +31,9 @@ mod tests { } } - impl DigestSignature for DummySignature { - type Digest = Sha256; - } - /// Dummy signer which just returns the message digest as a `DummySignature` #[derive(Signer, Default)] + #[digest(Sha256)] struct DummySigner {} impl DigestSigner for DummySigner { @@ -52,6 +47,7 @@ mod tests { /// /// Panics (via `assert_eq!`) if the value is not what is expected. #[derive(Verifier, Default)] + #[digest(Sha256)] struct DummyVerifier {} impl DigestVerifier for DummyVerifier {