From 2c1b3bd0e12a308c5fdf945a2951013961dec989 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 23 Oct 2022 21:23:21 -0600 Subject: [PATCH 1/3] [WIP] signature v2.0.0-pre This commit contains the first breaking changes to the `signature` crate made since its initial 1.0 stabilization. Planning for these changes occurred in #237. The following changes have been made: - The `Signature` trait has been renamed to `SignatureEncoding`. The `Signature` bound on `*Signer` and `*Verifier` traits has been removed, meaning there are no longer any mandatory trait bounds on the signature paramters at all. - The `AsRef<[u8]>` bound formerly found on the `Signature` crate has been replaced with an associated `Repr` type, inspired by the `group::GroupEncoding` trait. This means signature types no longer need to retain a serialized form, and can parse the bag-of-bytes representation to something more convenient, which is useful for e.g. batch verification. - The `std` feature is no longer enabled by default, which matches the other RustCrypto/traits crates. - The `derive-preview` and `hazmat-preview` features have been stabilized. - The former `Keypair` trait has been renamed to `KeypairRef`, and the signature generic parameter removed. A new `Keypair` trait has been added which returns an owned instance of the associated `VerifyingKey`, and a blanket impl of `Keypair` for `KeypairRef` has been added. This addresses the issues described in #1124. --- .github/workflows/signature.yml | 6 +-- Cargo.lock | 8 ++-- crypto/Cargo.toml | 4 +- signature/Cargo.toml | 7 ++- signature/README.md | 25 +++++------ signature/async/Cargo.toml | 2 +- signature/async/src/lib.rs | 37 +++------------- signature/derive/src/lib.rs | 27 ++++++------ signature/src/encoding.rs | 21 +++++++++ signature/src/error.rs | 6 +-- signature/src/hazmat.rs | 8 ++-- signature/src/keypair.rs | 24 ++++++++--- signature/src/lib.rs | 53 +++++++++-------------- signature/src/prehash_signature.rs | 32 ++++++++++++++ signature/src/signature.rs | 68 ------------------------------ signature/src/signer.rs | 28 ++++-------- signature/src/verifier.rs | 10 ++--- signature/tests/derive.rs | 38 ++++++++++------- 18 files changed, 174 insertions(+), 230 deletions(-) create mode 100644 signature/src/encoding.rs create mode 100644 signature/src/prehash_signature.rs delete mode 100644 signature/src/signature.rs diff --git a/.github/workflows/signature.yml b/.github/workflows/signature.yml index 1e87d18c8..592915ecf 100644 --- a/.github/workflows/signature.yml +++ b/.github/workflows/signature.yml @@ -37,10 +37,10 @@ jobs: override: true profile: minimal - run: cargo build --target ${{ matrix.target }} --release --no-default-features - - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features derive-preview + - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features derive - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features digest-preview - - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features hazmat-preview - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features rand-preview + - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features derive,digest-preview,rand-preview minimal-versions: uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master @@ -83,4 +83,4 @@ jobs: override: true profile: minimal - run: cargo test --release - working-directory: signature/derive \ No newline at end of file + working-directory: signature/derive diff --git a/Cargo.lock b/Cargo.lock index 46cc60015..10fdcfc57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,7 @@ name = "async-signature" version = "0.2.1" dependencies = [ "async-trait", - "signature 1.6.4", + "signature 2.0.0-pre", ] [[package]] @@ -303,7 +303,7 @@ dependencies = [ [[package]] name = "crypto" -version = "0.4.0" +version = "0.5.0-pre" dependencies = [ "aead 0.5.1", "cipher 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -311,7 +311,7 @@ dependencies = [ "digest 0.10.5", "elliptic-curve 0.12.3", "password-hash", - "signature 1.6.4", + "signature 2.0.0-pre", "universal-hash 0.5.0", ] @@ -1189,7 +1189,7 @@ dependencies = [ [[package]] name = "signature" -version = "1.6.4" +version = "2.0.0-pre" dependencies = [ "digest 0.10.5", "hex-literal", diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 3f26148de..1f73bd7aa 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crypto" -version = "0.4.0" # Also update html_root_url in lib.rs when bumping this +version = "0.5.0-pre" # Also update html_root_url in lib.rs when bumping this description = """ Resources for building cryptosystems in Rust using the RustCrypto project's ecosystem. """ @@ -23,7 +23,7 @@ cipher = { version = "0.4", optional = true } digest = { version = "0.10", optional = true, features = ["mac"] } elliptic-curve = { version = "0.12", optional = true, path = "../elliptic-curve" } password-hash = { version = "0.4", optional = true, path = "../password-hash" } -signature = { version = "1.5", optional = true, default-features = false, path = "../signature" } +signature = { version = "=2.0.0-pre", optional = true, default-features = false, path = "../signature" } universal-hash = { version = "0.5", optional = true, path = "../universal-hash" } [features] diff --git a/signature/Cargo.toml b/signature/Cargo.toml index 9235fac31..c2195a337 100644 --- a/signature/Cargo.toml +++ b/signature/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "signature" description = "Traits for cryptographic signature algorithms (e.g. ECDSA, Ed25519)" -version = "1.6.4" +version = "2.0.0-pre" authors = ["RustCrypto Developers"] license = "Apache-2.0 OR MIT" documentation = "https://docs.rs/signature" @@ -22,14 +22,13 @@ hex-literal = "0.3" sha2 = { version = "0.10", default-features = false } [features] -default = ["std"] std = [] +derive = ["signature_derive"] + # Preview features are unstable and exempt from semver. # See https://docs.rs/signature/latest/signature/#unstable-features for more information. -derive-preview = ["digest-preview", "signature_derive"] digest-preview = ["digest"] -hazmat-preview = [] rand-preview = ["rand_core"] [package.metadata.docs.rs] diff --git a/signature/README.md b/signature/README.md index a1a6fa4ae..52ae899de 100644 --- a/signature/README.md +++ b/signature/README.md @@ -1,4 +1,4 @@ -# RustCrypto: Digital Signature Algorithms +# [RustCrypto]: Digital Signature Algorithms [![crate][crate-image]][crate-link] [![Docs][docs-image]][docs-link] @@ -8,14 +8,10 @@ [![Project Chat][chat-image]][chat-link] This crate contains traits which provide generic, object-safe APIs for -generating and verifying [digital signatures][1]. +generating and verifying [digital signatures]. -Used by the [`ecdsa`][2] and [`ed25519`][3] crates, with forthcoming support -in the [`rsa`][4] crate. - -See also the [Signatory][5] crate for trait wrappers for using these traits -with many popular Rust cryptography crates, including `ed25519-dalek`, *ring*, -`secp256k1-rs`, and `sodiumoxide`. +Used by the [`dsa`], [`ecdsa`], [`ed25519`], and [`rsa`] crates maintained by +the [RustCrypto] organization, as well as [`ed25519-dalek`]. [Documentation][docs-link] @@ -63,10 +59,11 @@ dual licensed as above, without any additional terms or conditions. [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260048-signatures -[//]: # (general links) +[//]: # (links) -[1]: https://en.wikipedia.org/wiki/Digital_signature -[2]: https://github.com/RustCrypto/signatures/tree/master/ecdsa -[3]: https://github.com/RustCrypto/signatures/tree/master/ed25519 -[4]: https://github.com/RustCrypto/RSA -[5]: https://docs.rs/signatory +[digital signatures]: https://en.wikipedia.org/wiki/Digital_signature +[`dsa`]: https://github.com/RustCrypto/signatures/tree/master/dsa +[`ecdsa`]: https://github.com/RustCrypto/signatures/tree/master/ecdsa +[`ed25519`]: https://github.com/RustCrypto/signatures/tree/master/ed25519 +[`ed25519-dalek`]: https://github.com/dalek-cryptography/ed25519-dalek +[`rsa`]: https://github.com/RustCrypto/RSA diff --git a/signature/async/Cargo.toml b/signature/async/Cargo.toml index c81957044..13c964df8 100644 --- a/signature/async/Cargo.toml +++ b/signature/async/Cargo.toml @@ -14,7 +14,7 @@ rust-version = "1.56" [dependencies] async-trait = "0.1.9" -signature = { version = "1.6", path = ".." } +signature = { version = "=2.0.0-pre", path = ".." } [features] digest = ["signature/digest-preview"] diff --git a/signature/async/src/lib.rs b/signature/async/src/lib.rs index fe12a3ca4..2e8bb09f0 100644 --- a/signature/async/src/lib.rs +++ b/signature/async/src/lib.rs @@ -7,7 +7,7 @@ #![forbid(unsafe_code)] #![warn(missing_docs, rust_2018_idioms, unused_qualifications)] -pub use signature::{self, Error, Signature}; +pub use signature::{self, Error}; #[cfg(feature = "digest")] pub use signature::digest::{self, Digest}; @@ -22,7 +22,7 @@ use async_trait::async_trait; pub trait AsyncSigner where Self: Send + Sync, - S: Signature + Send + 'static, + S: Send + 'static, { /// Attempt to sign the given message, returning a digital signature on /// success, or an error if something went wrong. @@ -35,7 +35,7 @@ where #[async_trait] impl AsyncSigner for T where - S: Signature + Send + 'static, + S: Send + 'static, T: signature::Signer + Send + Sync, { async fn sign_async(&self, msg: &[u8]) -> Result { @@ -53,7 +53,7 @@ pub trait AsyncDigestSigner where Self: Send + Sync, D: Digest + Send + 'static, - S: Signature + 'static, + S: 'static, { /// Attempt to sign the given prehashed message [`Digest`], returning a /// digital signature on success, or an error if something went wrong. @@ -65,37 +65,10 @@ where impl AsyncDigestSigner for T where D: Digest + Send + 'static, - S: Signature + Send + 'static, + S: Send + 'static, T: signature::DigestSigner + Send + Sync, { async fn sign_digest_async(&self, digest: D) -> Result { self.try_sign_digest(digest) } } - -/// Keypair with async signer component and an associated verifying key. -/// -/// This represents a type which holds both an async signing key and a verifying key. -#[deprecated(since = "0.2.1", note = "use signature::Keypair instead")] -pub trait AsyncKeypair: AsRef -where - S: Signature + Send + 'static, -{ - /// Verifying key type for this keypair. - type VerifyingKey; - - /// Get the verifying key which can verify signatures produced by the - /// signing key portion of this keypair. - fn verifying_key(&self) -> &Self::VerifyingKey { - self.as_ref() - } -} - -#[allow(deprecated)] -impl AsyncKeypair for T -where - S: Signature + Send + 'static, - T: signature::Keypair + Send + Sync, -{ - type VerifyingKey = >::VerifyingKey; -} diff --git a/signature/derive/src/lib.rs b/signature/derive/src/lib.rs index 8de761813..1afb0aaa2 100644 --- a/signature/derive/src/lib.rs +++ b/signature/derive/src/lib.rs @@ -129,7 +129,7 @@ fn emit_digest_signer_impl(input: DeriveInput) -> TokenStream2 { let mut params = DeriveParams::new(input); params.add_bound(&d_ident, parse_quote!(::signature::digest::Digest)); - params.add_bound(&s_ident, parse_quote!(::signature::Signature)); + params.add_param(&s_ident); params.add_bound( &Ident::new("Self", Span::call_site()), parse_quote!(::signature::hazmat::PrehashSigner<#s_ident>), @@ -167,7 +167,7 @@ fn emit_digest_verifier_impl(input: DeriveInput) -> TokenStream2 { let mut params = DeriveParams::new(input); params.add_bound(&d_ident, parse_quote!(::signature::digest::Digest)); - params.add_bound(&s_ident, parse_quote!(::signature::Signature)); + params.add_param(&s_ident); params.add_bound( &Ident::new("Self", Span::call_site()), parse_quote!(::signature::hazmat::PrehashVerifier<#s_ident>), @@ -235,14 +235,7 @@ impl DeriveParams { /// Add a generic parameter with the given bound. fn add_bound(&mut self, name: &Ident, bound: TraitBound) { if name != "Self" { - self.impl_generics.push(TypeParam { - attrs: vec![], - ident: name.clone(), - colon_token: None, - bounds: Default::default(), - eq_token: None, - default: None, - }); + self.add_param(name); } let type_path = parse_quote!(#name); @@ -261,6 +254,18 @@ impl DeriveParams { .predicates .push(WherePredicate::Type(predicate_type)) } + + /// Add a generic parameter without a bound. + fn add_param(&mut self, name: &Ident) { + self.impl_generics.push(TypeParam { + attrs: vec![], + ident: name.clone(), + colon_token: None, + bounds: Default::default(), + eq_token: None, + default: None, + }); + } } #[cfg(test)] @@ -345,7 +350,6 @@ mod tests { impl ::signature::DigestSigner<__D, __S> for MySigner where __D: ::signature::digest::Digest, - __S: ::signature::Signature, Self: ::signature::hazmat::PrehashSigner<__S> { fn try_sign_digest(&self, digest: __D) -> ::signature::Result<__S> { @@ -374,7 +378,6 @@ mod tests { impl ::signature::DigestVerifier<__D, __S> for MyVerifier where __D: ::signature::digest::Digest, - __S: ::signature::Signature, Self: ::signature::hazmat::PrehashVerifier<__S> { fn verify_digest(&self, digest: __D, signature: &__S) -> ::signature::Result<()> { diff --git a/signature/src/encoding.rs b/signature/src/encoding.rs new file mode 100644 index 000000000..7c5fc29d6 --- /dev/null +++ b/signature/src/encoding.rs @@ -0,0 +1,21 @@ +//! Encoding support. + +use crate::{Error, Result}; + +/// Support for decoding/encoding signatures as bytes. +pub trait SignatureEncoding: + Clone + Sized + for<'a> TryFrom<&'a [u8], Error = Error> + Into +{ + /// Byte representation of a signature. + type Repr: 'static + AsRef<[u8]> + AsMut<[u8]> + Clone + Default + Send + Sync; + + /// Decode signature from its byte representation. + fn from_bytes(bytes: &Self::Repr) -> Result { + Self::try_from(bytes.as_ref()) + } + + /// Encode signature as its byte representation. + fn to_bytes(&self) -> Self::Repr { + self.clone().into() + } +} diff --git a/signature/src/error.rs b/signature/src/error.rs index 06e22d527..831dbc89d 100644 --- a/signature/src/error.rs +++ b/signature/src/error.rs @@ -22,11 +22,8 @@ pub type Result = core::result::Result; /// /// [BB'06]: https://en.wikipedia.org/wiki/Daniel_Bleichenbacher #[derive(Default)] +#[non_exhaustive] pub struct Error { - /// Prevent from being instantiated as `Error {}` when the `std` feature - /// is disabled - _private: (), - /// Source of the error (if applicable). #[cfg(feature = "std")] source: Option>, @@ -50,7 +47,6 @@ impl Error { source: impl Into>, ) -> Self { Self { - _private: (), source: Some(source.into()), } } diff --git a/signature/src/hazmat.rs b/signature/src/hazmat.rs index 8119225c6..1b2e702ae 100644 --- a/signature/src/hazmat.rs +++ b/signature/src/hazmat.rs @@ -10,13 +10,13 @@ //! feature is semi-unstable and not subject to regular 1.x SemVer guarantees. //! However, any breaking changes will be accompanied with a minor version bump. -use crate::{Error, Signature}; +use crate::Error; #[cfg(feature = "rand-preview")] use crate::rand_core::{CryptoRng, RngCore}; /// Sign the provided message prehash, returning a digital signature. -pub trait PrehashSigner { +pub trait PrehashSigner { /// Attempt to sign the given message digest, returning a digital signature /// on success, or an error if something went wrong. /// @@ -35,7 +35,7 @@ pub trait PrehashSigner { /// Sign the provided message prehash using the provided external randomness source, returning a digital signature. #[cfg(feature = "rand-preview")] #[cfg_attr(docsrs, doc(cfg(feature = "rand-preview")))] -pub trait RandomizedPrehashSigner { +pub trait RandomizedPrehashSigner { /// Attempt to sign the given message digest, returning a digital signature /// on success, or an error if something went wrong. /// @@ -56,7 +56,7 @@ pub trait RandomizedPrehashSigner { } /// Verify the provided message prehash using `Self` (e.g. a public key) -pub trait PrehashVerifier { +pub trait PrehashVerifier { /// Use `Self` to verify that the provided signature for a given message /// `prehash` is authentic. /// diff --git a/signature/src/keypair.rs b/signature/src/keypair.rs index 6d9f947c6..d4795f2f9 100644 --- a/signature/src/keypair.rs +++ b/signature/src/keypair.rs @@ -1,17 +1,29 @@ //! Signing keypairs. -use crate::Signature; - /// Signing keypair with an associated verifying key. /// /// This represents a type which holds both a signing key and a verifying key. -pub trait Keypair: AsRef { +pub trait Keypair { /// Verifying key type for this keypair. - type VerifyingKey; + type VerifyingKey: Clone; /// Get the verifying key which can verify signatures produced by the /// signing key portion of this keypair. - fn verifying_key(&self) -> &Self::VerifyingKey { - self.as_ref() + fn verifying_key(&self) -> Self::VerifyingKey; +} + +/// Signing keypair with an associated verifying key. +/// +/// This represents a type which holds both a signing key and a verifying key. +pub trait KeypairRef: AsRef { + /// Verifying key type for this keypair. + type VerifyingKey: Clone; +} + +impl Keypair for K { + type VerifyingKey = ::VerifyingKey; + + fn verifying_key(&self) -> Self::VerifyingKey { + self.as_ref().clone() } } diff --git a/signature/src/lib.rs b/signature/src/lib.rs index ab504c2ac..74c7846b5 100644 --- a/signature/src/lib.rs +++ b/signature/src/lib.rs @@ -43,24 +43,14 @@ //! ## Implementation //! //! To accomplish the above goals, the [`Signer`] and [`Verifier`] traits -//! provided by this are generic over a [`Signature`] return value, and use -//! generic parameters rather than associated types. Notably, they use such -//! a parameter for the return value, allowing it to be inferred by the type -//! checker based on the desired signature type. -//! -//! The [`Signature`] trait is bounded on `AsRef<[u8]>`, enforcing that -//! signature types are thin wrappers around a "bag-of-bytes" -//! serialization. Inspiration for this approach comes from the Ed25519 -//! signature system, which was based on the observation that past -//! systems were not prescriptive about how signatures should be represented -//! on-the-wire, and that lead to a proliferation of different wire formats -//! and confusion about which ones should be used. This crate aims to provide -//! similar simplicity by minimizing the number of steps involved to obtain -//! a serializable signature. +//! provided by this are generic over a signature value, and use generic +//! parameters rather than associated types. Notably, they use such a parameter +//! for the return value, allowing it to be inferred by the type checker based +//! on the desired signature type. //! //! ## Alternatives considered //! -//! This crate is based on over two years of exploration of how to encapsulate +//! This crate is based on many years of exploration of how to encapsulate //! digital signature systems in the most flexible, developer-friendly way. //! During that time many design alternatives were explored, tradeoffs //! compared, and ultimately the provided API was selected. @@ -73,10 +63,7 @@ //! - "Bag-of-bytes" serialization precludes signature providers from using //! their own internal representation of a signature, which can be helpful //! for many reasons (e.g. advanced signature system features like batch -//! verification). Alternatively each provider could define its own signature -//! type, using a marker trait to identify the particular signature algorithm, -//! have `From` impls for converting to/from `[u8; N]`, and a marker trait -//! for identifying a specific signature algorithm. +//! verification). //! - Associated types, rather than generic parameters of traits, could allow //! more customization of the types used by a particular signature system, //! e.g. using custom error types. @@ -121,7 +108,7 @@ //! [`DigestSigner`] and [`DigestVerifier`], the `derive-preview` feature //! can be used to derive [`Signer`] and [`Verifier`] traits which prehash //! the input message using the [`PrehashSignature::Digest`] algorithm for -//! a given [`Signature`] type. When the `derive-preview` feature is enabled +//! a given signature type. When the `derive-preview` feature is enabled //! import the proc macros with `use signature::{Signer, Verifier}` and then //! add a `derive(Signer)` or `derive(Verifier)` attribute to the given //! digest signer/verifier type. Enabling this feature also enables `digest` @@ -146,10 +133,10 @@ #[cfg(feature = "std")] extern crate std; -#[cfg(all(feature = "signature_derive", not(feature = "derive-preview")))] +#[cfg(all(feature = "signature_derive", not(feature = "derive")))] compile_error!( "The `signature_derive` feature should not be enabled directly. \ - Use the `derive-preview` feature instead." + Use the `derive` feature instead." ); #[cfg(all(feature = "digest", not(feature = "digest-preview")))] @@ -168,28 +155,28 @@ compile_error!( #[cfg_attr(docsrs, doc(cfg(feature = "hazmat-preview")))] pub mod hazmat; +mod encoding; mod error; mod keypair; -mod signature; mod signer; mod verifier; -#[cfg(feature = "derive-preview")] -#[cfg_attr(docsrs, doc(cfg(feature = "derive-preview")))] +#[cfg(feature = "digest-preview")] +mod prehash_signature; + +pub use crate::{encoding::*, error::*, keypair::*, signer::*, verifier::*}; + +#[cfg(feature = "derive")] +#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] 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"))) -)] +#[cfg(all(feature = "derive", feature = "digest-preview"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "derive", feature = "digest-preview"))))] pub use signature_derive::{DigestSigner, DigestVerifier}; #[cfg(feature = "digest-preview")] -pub use digest; +pub use {crate::prehash_signature::*, digest}; #[cfg(feature = "rand-preview")] #[cfg_attr(docsrs, doc(cfg(feature = "rand-preview")))] pub use rand_core; - -pub use crate::{error::*, keypair::*, signature::*, signer::*, verifier::*}; diff --git a/signature/src/prehash_signature.rs b/signature/src/prehash_signature.rs new file mode 100644 index 000000000..779b0dcdb --- /dev/null +++ b/signature/src/prehash_signature.rs @@ -0,0 +1,32 @@ +//! `PrehashSignature` trait. + +/// For intra-doc link resolution. +#[allow(unused_imports)] +use crate::{ + signer::{DigestSigner, Signer}, + verifier::{DigestVerifier, Verifier}, +}; + +/// Marker trait for `Signature` types computable as `𝐒(𝐇(𝒎))` +/// i.e. ones which prehash a message to be signed as `𝐇(𝒎)` +/// +/// Where: +/// +/// - `𝐒`: signature algorithm +/// - `𝐇`: hash (a.k.a. digest) function +/// - `𝒎`: message +/// +/// This approach is relatively common in signature schemes based on the +/// [Fiat-Shamir heuristic]. +/// +/// For signature types that implement this trait, when the `derive-preview` +/// Cargo feature is enabled a custom derive for [`Signer`] is available for any +/// types that impl [`DigestSigner`], and likewise for deriving [`Verifier`] for +/// types which impl [`DigestVerifier`]. +/// +/// [Fiat-Shamir heuristic]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic +#[cfg_attr(docsrs, doc(cfg(feature = "digest")))] +pub trait PrehashSignature { + /// Preferred `Digest` algorithm to use when computing this signature type. + type Digest: digest::Digest; +} diff --git a/signature/src/signature.rs b/signature/src/signature.rs deleted file mode 100644 index 29aa0b845..000000000 --- a/signature/src/signature.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! Signature traits - -use crate::error::Error; -use core::fmt::Debug; - -/// For intra-doc link resolution -#[cfg(feature = "digest-preview")] -#[allow(unused_imports)] -use crate::{ - signer::{DigestSigner, Signer}, - verifier::{DigestVerifier, Verifier}, -}; - -/// Trait impl'd by concrete types that represent digital signatures. -/// -/// Signature types *must* (as mandated by the `AsRef<[u8]>` bound) be a thin -/// wrapper around the "bag-of-bytes" serialized form of a signature which can -/// be directly parsed from or written to the "wire". -/// -/// Inspiration for this approach comes from the Ed25519 signature system, -/// which adopted it based on the observation that past signature systems -/// were not prescriptive about how signatures should be represented -/// on-the-wire, and that lead to a proliferation of different wire formats and -/// confusion about which ones should be used. -/// -/// The [`Signature`] trait aims to provide similar simplicity by minimizing -/// the number of steps involved to obtain a serializable signature and -/// ideally ensuring there is one signature type for any given signature system -/// shared by all "provider" crates. -/// -/// For signature systems which require a more advanced internal representation -/// (e.g. involving decoded scalars or decompressed elliptic curve points) it's -/// recommended that "provider" libraries maintain their own internal signature -/// type and use `From` bounds to provide automatic conversions. -pub trait Signature: AsRef<[u8]> + Debug + Sized { - /// Parse a signature from its byte representation - fn from_bytes(bytes: &[u8]) -> Result; - - /// Borrow a byte slice representing the serialized form of this signature - fn as_bytes(&self) -> &[u8] { - self.as_ref() - } -} - -/// Marker trait for `Signature` types computable as `𝐒(𝐇(𝒎))` -/// i.e. ones which prehash a message to be signed as `𝐇(𝒎)` -/// -/// Where: -/// -/// - `𝐒`: signature algorithm -/// - `𝐇`: hash (a.k.a. digest) function -/// - `𝒎`: message -/// -/// This approach is relatively common in signature schemes based on the -/// [Fiat-Shamir heuristic]. -/// -/// For signature types that implement this trait, when the `derive-preview` -/// Cargo feature is enabled a custom derive for [`Signer`] is available for any -/// types that impl [`DigestSigner`], and likewise for deriving [`Verifier`] for -/// types which impl [`DigestVerifier`]. -/// -/// [Fiat-Shamir heuristic]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic -#[cfg(feature = "digest-preview")] -#[cfg_attr(docsrs, doc(cfg(feature = "digest-preview")))] -pub trait PrehashSignature: Signature { - /// Preferred `Digest` algorithm to use when computing this signature type. - type Digest: digest::Digest; -} diff --git a/signature/src/signer.rs b/signature/src/signer.rs index c025711fe..25c6614a2 100644 --- a/signature/src/signer.rs +++ b/signature/src/signer.rs @@ -1,6 +1,6 @@ //! Traits for generating digital signatures -use crate::{error::Error, Signature}; +use crate::error::Error; #[cfg(feature = "digest-preview")] use crate::digest::Digest; @@ -10,7 +10,7 @@ use crate::rand_core::{CryptoRng, RngCore}; /// Sign the provided message bytestring using `Self` (e.g. a cryptographic key /// or connection to an HSM), returning a digital signature. -pub trait Signer { +pub trait Signer { /// Sign the given message and return a digital signature fn sign(&self, msg: &[u8]) -> S { self.try_sign(msg).expect("signature operation failed") @@ -26,7 +26,7 @@ pub trait Signer { /// Sign the provided message bytestring using `&mut Self` (e.g., an evolving /// cryptographic key), returning a digital signature. -pub trait SignerMut { +pub trait SignerMut { /// Sign the given message, update the state, and return a digital signature fn sign(&mut self, msg: &[u8]) -> S { self.try_sign(msg).expect("signature operation failed") @@ -40,12 +40,8 @@ pub trait SignerMut { fn try_sign(&mut self, msg: &[u8]) -> Result; } -// Blanket impl of SignerMut for all Signer types -impl SignerMut for T -where - T: Signer, - S: Signature, -{ +/// Blanket impl of [`SignerMut`] for all [`Signer`] types. +impl> SignerMut for T { fn try_sign(&mut self, msg: &[u8]) -> Result { T::try_sign(self, msg) } @@ -72,11 +68,7 @@ where /// [Fiat-Shamir heuristic]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic #[cfg(feature = "digest-preview")] #[cfg_attr(docsrs, doc(cfg(feature = "digest-preview")))] -pub trait DigestSigner -where - D: Digest, - S: Signature, -{ +pub trait DigestSigner { /// Sign the given prehashed message [`Digest`], returning a signature. /// /// Panics in the event of a signing error. @@ -93,7 +85,7 @@ where /// Sign the given message using the provided external randomness source. #[cfg(feature = "rand-preview")] #[cfg_attr(docsrs, doc(cfg(feature = "rand-preview")))] -pub trait RandomizedSigner { +pub trait RandomizedSigner { /// Sign the given message and return a digital signature fn sign_with_rng(&self, rng: impl CryptoRng + RngCore, msg: &[u8]) -> S { self.try_sign_with_rng(rng, msg) @@ -113,11 +105,7 @@ pub trait RandomizedSigner { #[cfg(all(feature = "digest-preview", feature = "rand-preview"))] #[cfg_attr(docsrs, doc(cfg(feature = "digest-preview")))] #[cfg_attr(docsrs, doc(cfg(feature = "rand-preview")))] -pub trait RandomizedDigestSigner -where - D: Digest, - S: Signature, -{ +pub trait RandomizedDigestSigner { /// Sign the given prehashed message `Digest`, returning a signature. /// /// Panics in the event of a signing error. diff --git a/signature/src/verifier.rs b/signature/src/verifier.rs index 4d6efbc2b..47bfef360 100644 --- a/signature/src/verifier.rs +++ b/signature/src/verifier.rs @@ -1,12 +1,12 @@ //! Trait for verifying digital signatures -use crate::{error::Error, Signature}; +use crate::error::Error; #[cfg(feature = "digest-preview")] use crate::digest::Digest; /// Verify the provided message bytestring using `Self` (e.g. a public key) -pub trait Verifier { +pub trait Verifier { /// Use `Self` to verify that the provided signature for a given message /// bytestring is authentic. /// @@ -36,11 +36,7 @@ pub trait Verifier { /// [Fiat-Shamir heuristic]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic #[cfg(feature = "digest-preview")] #[cfg_attr(docsrs, doc(cfg(feature = "digest-preview")))] -pub trait DigestVerifier -where - D: Digest, - S: Signature, -{ +pub trait DigestVerifier { /// Verify the signature against the given [`Digest`] output. fn verify_digest(&self, digest: D, signature: &S) -> Result<(), Error>; } diff --git a/signature/tests/derive.rs b/signature/tests/derive.rs index 5048dc682..684aca66f 100644 --- a/signature/tests/derive.rs +++ b/signature/tests/derive.rs @@ -7,7 +7,7 @@ use hex_literal::hex; use sha2::Sha256; use signature::{ hazmat::{PrehashSigner, PrehashVerifier}, - DigestSigner, DigestVerifier, Error, PrehashSignature, Signature, Signer, Verifier, + DigestSigner, DigestVerifier, Error, PrehashSignature, SignatureEncoding, Signer, Verifier, }; /// Test vector to compute SHA-256 digest of @@ -17,35 +17,43 @@ const INPUT_STRING: &[u8] = b"abc"; const INPUT_STRING_DIGEST: [u8; 32] = hex!("ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad"); +type Repr = GenericArray::OutputSize>; + /// Dummy signature which just contains a digest output -#[derive(Debug)] -struct DummySignature(GenericArray::OutputSize>); +#[derive(Clone, Debug)] +struct DummySignature(Repr); + +impl PrehashSignature for DummySignature { + type Digest = Sha256; +} + +impl SignatureEncoding for DummySignature { + type Repr = Repr; +} + +impl TryFrom<&[u8]> for DummySignature { + type Error = Error; -impl Signature for DummySignature { - fn from_bytes(bytes: &[u8]) -> Result { + fn try_from(bytes: &[u8]) -> Result { Ok(DummySignature(GenericArray::clone_from_slice( bytes.as_ref(), ))) } } -impl AsRef<[u8]> for DummySignature { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() +impl From for Repr { + fn from(sig: DummySignature) -> Repr { + sig.0 } } -impl PrehashSignature for DummySignature { - type Digest = Sha256; -} - /// Dummy signer which just returns the message digest as a `DummySignature` #[derive(Signer, DigestSigner, Default)] struct DummySigner {} impl PrehashSigner for DummySigner { fn sign_prehash(&self, prehash: &[u8]) -> signature::Result { - DummySignature::from_bytes(prehash) + DummySignature::try_from(prehash) } } @@ -58,7 +66,7 @@ struct DummyVerifier {} impl PrehashVerifier for DummyVerifier { fn verify_prehash(&self, prehash: &[u8], signature: &DummySignature) -> signature::Result<()> { - assert_eq!(signature.as_ref(), prehash); + assert_eq!(signature.to_bytes().as_slice(), prehash); Ok(()) } } @@ -66,7 +74,7 @@ impl PrehashVerifier for DummyVerifier { #[test] fn derived_signer_impl() { let sig: DummySignature = DummySigner::default().sign(INPUT_STRING); - assert_eq!(sig.as_ref(), INPUT_STRING_DIGEST.as_ref()) + assert_eq!(sig.to_bytes().as_slice(), INPUT_STRING_DIGEST.as_ref()) } #[test] From 217a416c084672b9f8fbe61da33208b3a3756b2e Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 24 Oct 2022 17:09:00 -0600 Subject: [PATCH 2/3] signature: remove intermediate `derive` feature Use `package` on the dependency import instead --- signature/Cargo.toml | 7 +++---- signature/src/lib.rs | 10 ++-------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/signature/Cargo.toml b/signature/Cargo.toml index c2195a337..c55628df7 100644 --- a/signature/Cargo.toml +++ b/signature/Cargo.toml @@ -15,16 +15,15 @@ categories = ["cryptography", "no-std"] [dependencies] digest = { version = "0.10.3", optional = true, default-features = false } rand_core = { version = "0.6", optional = true, default-features = false } -signature_derive = { version = "=1.0.0-pre.7", optional = true, path = "derive" } +derive = { package = "signature_derive", version = "=1.0.0-pre.7", optional = true, path = "derive" } [dev-dependencies] hex-literal = "0.3" sha2 = { version = "0.10", default-features = false } [features] -std = [] - -derive = ["signature_derive"] +alloc = [] +std = ["alloc"] # Preview features are unstable and exempt from semver. # See https://docs.rs/signature/latest/signature/#unstable-features for more information. diff --git a/signature/src/lib.rs b/signature/src/lib.rs index 74c7846b5..de168863c 100644 --- a/signature/src/lib.rs +++ b/signature/src/lib.rs @@ -133,12 +133,6 @@ #[cfg(feature = "std")] extern crate std; -#[cfg(all(feature = "signature_derive", not(feature = "derive")))] -compile_error!( - "The `signature_derive` feature should not be enabled directly. \ - Use the `derive` feature instead." -); - #[cfg(all(feature = "digest", not(feature = "digest-preview")))] compile_error!( "The `digest` feature should not be enabled directly. \ @@ -168,11 +162,11 @@ pub use crate::{encoding::*, error::*, keypair::*, signer::*, verifier::*}; #[cfg(feature = "derive")] #[cfg_attr(docsrs, doc(cfg(feature = "derive")))] -pub use signature_derive::{Signer, Verifier}; +pub use derive::{Signer, Verifier}; #[cfg(all(feature = "derive", feature = "digest-preview"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "derive", feature = "digest-preview"))))] -pub use signature_derive::{DigestSigner, DigestVerifier}; +pub use derive::{DigestSigner, DigestVerifier}; #[cfg(feature = "digest-preview")] pub use {crate::prehash_signature::*, digest}; From f6ffa5c06f15ecf8640a83c3ca02a7b0ed9899bf Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 24 Oct 2022 17:14:26 -0600 Subject: [PATCH 3/3] signature: `alloc` encoding conversions to Vec/Box Since signatures all encode to a "bag of bytes" of some form, choosing a common representation is useful when abstracting over a number of different signature systems. These helpers make it easy to use either a `Vec` or `Box<[u8]>` as that common type. --- signature/src/encoding.rs | 17 +++++++++++++++++ signature/src/lib.rs | 5 +++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/signature/src/encoding.rs b/signature/src/encoding.rs index 7c5fc29d6..f4d281c2a 100644 --- a/signature/src/encoding.rs +++ b/signature/src/encoding.rs @@ -2,6 +2,9 @@ use crate::{Error, Result}; +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, vec::Vec}; + /// Support for decoding/encoding signatures as bytes. pub trait SignatureEncoding: Clone + Sized + for<'a> TryFrom<&'a [u8], Error = Error> + Into @@ -18,4 +21,18 @@ pub trait SignatureEncoding: fn to_bytes(&self) -> Self::Repr { self.clone().into() } + + /// Encode signature as a byte vector. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn to_vec(&self) -> Vec { + self.to_bytes().as_ref().to_vec() + } + + /// Encode the signature as a boxed byte slice. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn to_boxed_slice(&self) -> Box<[u8]> { + self.to_vec().into_boxed_slice() + } } diff --git a/signature/src/lib.rs b/signature/src/lib.rs index de168863c..d35ec5f9a 100644 --- a/signature/src/lib.rs +++ b/signature/src/lib.rs @@ -130,6 +130,9 @@ //! [`Digest`]: https://docs.rs/digest/latest/digest/trait.Digest.html //! [Fiat-Shamir heuristic]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic +#[cfg(feature = "alloc")] +extern crate alloc; + #[cfg(feature = "std")] extern crate std; @@ -145,8 +148,6 @@ compile_error!( Use the `rand-preview` feature instead." ); -#[cfg(feature = "hazmat-preview")] -#[cfg_attr(docsrs, doc(cfg(feature = "hazmat-preview")))] pub mod hazmat; mod encoding;