From db853f17c7103698ca61cf8e3e3845eb4fce885b Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 9 Sep 2022 08:07:54 -0600 Subject: [PATCH] signature: add `hazmat-preview` feature (#1099) Adds a `hazmat` module gated under a newly added `hazmat-preview` feature which calls out the relevant functionality as subject to change with minor versions. It adds the following traits: - `PrehashSigner` - `PrehashVerifier` These APIs accept the digest to be signed/verified as a raw byte slice. This comes with potential misuses like failing to use a cryptographically secure hash function as the `prehash`, which could enable existential forgeries of signatures, hence gating it under a `hazmat-preview` feature and placing it in a `hazmat` module. Note that we previously explored APIs like this for `DigestSigner`. They were removed in RustCrypto/signatures#17 due to the afforementioned misuse potential. However, these APIs are occasionally needed for implementing protocols that use special rules for computing hashes (e.g. EIP-712 structured hashes), or for implementing things like network signing services which want to accept a prehash of a message to be signed rather than the full message (to cut down on network bandwidth). The traits accept a byte slice `prehash`, which permits multiple lengths and allows the implementation to decide which lengths are valid. This makes it possible for e.g. ECDSA implementations to automatically truncate message prehashes which are larger than the field size. --- .github/workflows/signature.yml | 6 +++- signature/Cargo.toml | 1 + signature/src/hazmat.rs | 49 +++++++++++++++++++++++++++++++++ signature/src/lib.rs | 16 +++++++---- 4 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 signature/src/hazmat.rs diff --git a/.github/workflows/signature.yml b/.github/workflows/signature.yml index f96dbea0c..922f032e9 100644 --- a/.github/workflows/signature.yml +++ b/.github/workflows/signature.yml @@ -36,7 +36,11 @@ jobs: target: ${{ matrix.target }} override: true profile: minimal - - run: cargo build --no-default-features --release --target ${{ matrix.target }} + - 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 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 minimal-versions: uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master diff --git a/signature/Cargo.toml b/signature/Cargo.toml index 6c593523a..0c24eda49 100644 --- a/signature/Cargo.toml +++ b/signature/Cargo.toml @@ -29,6 +29,7 @@ std = [] # 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/src/hazmat.rs b/signature/src/hazmat.rs new file mode 100644 index 000000000..348cb20c8 --- /dev/null +++ b/signature/src/hazmat.rs @@ -0,0 +1,49 @@ +//! Hazardous Materials: low-level APIs which can be insecure if misused. +//! +//! The traits in this module are not generally recommended, and should only be +//! used in special cases where they are specifically needed. +//! +//! Using them incorrectly can introduce security vulnerabilities. Please +//! carefully read the documentation before attempting to use them. +//! +//! To use them, enable the `hazmat-preview` crate feature. Note that this +//! 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}; + +/// Sign the provided message prehash, returning a digital signature. +pub trait PrehashSigner { + /// Attempt to sign the given message digest, returning a digital signature + /// on success, or an error if something went wrong. + /// + /// The `prehash` parameter should be the output of a secure cryptographic + /// hash function. + /// + /// This API takes a `prehash` byte slice as there can potentially be many + /// compatible lengths for the message digest for a given concrete signature + /// algorithm. + /// + /// Allowed lengths are algorithm-dependent and up to a particular + /// implementation to decide. + fn try_sign_prehash(&self, prehash: &[u8]) -> Result; +} + +/// Verify the provided message prehash using `Self` (e.g. a public key) +pub trait PrehashVerifier { + /// Use `Self` to verify that the provided signature for a given message + /// `prehash` is authentic. + /// + /// The `prehash` parameter should be the output of a secure cryptographic + /// hash function. + /// + /// Returns `Error` if it is inauthentic or some other error occurred, or + /// otherwise returns `Ok(())`. + /// + /// # ⚠️ Security Warning + /// + /// If `prehash` is something other than the output of a cryptographically + /// secure hash function, an attacker can potentially forge signatures by + /// solving a system of linear equations. + fn verify_prehash(&self, prehash: &[u8], signature: &S) -> Result<(), Error>; +} diff --git a/signature/src/lib.rs b/signature/src/lib.rs index 022f22188..6f7cc7899 100644 --- a/signature/src/lib.rs +++ b/signature/src/lib.rs @@ -164,6 +164,16 @@ compile_error!( Use the `rand-preview` feature instead." ); +#[cfg(feature = "hazmat-preview")] +#[cfg_attr(docsrs, doc(cfg(feature = "hazmat-preview")))] +pub mod hazmat; + +mod error; +mod keypair; +mod signature; +mod signer; +mod verifier; + #[cfg(feature = "derive-preview")] #[cfg_attr(docsrs, doc(cfg(feature = "derive-preview")))] pub use signature_derive::{Signer, Verifier}; @@ -175,10 +185,4 @@ pub use digest; #[cfg_attr(docsrs, doc(cfg(feature = "rand-preview")))] pub use rand_core; -mod error; -mod keypair; -mod signature; -mod signer; -mod verifier; - pub use crate::{error::*, keypair::*, signature::*, signer::*, verifier::*};