signature/
signer.rs

1//! Traits for generating digital signatures
2
3use crate::error::Error;
4
5#[cfg(feature = "digest")]
6use crate::digest::Digest;
7
8#[cfg(feature = "rand_core")]
9use crate::rand_core::{CryptoRng, TryCryptoRng};
10
11/// Sign the provided message bytestring using `Self` (e.g. a cryptographic key
12/// or connection to an HSM), returning a digital signature.
13pub trait Signer<S> {
14    /// Sign the given message and return a digital signature
15    fn sign(&self, msg: &[u8]) -> S {
16        self.try_sign(msg).expect("signature operation failed")
17    }
18
19    /// Attempt to sign the given message, returning a digital signature on
20    /// success, or an error if something went wrong.
21    ///
22    /// The main intended use case for signing errors is when communicating
23    /// with external signers, e.g. cloud KMS, HSMs, or other hardware tokens.
24    fn try_sign(&self, msg: &[u8]) -> Result<S, Error>;
25}
26
27/// Equivalent of [`Signer`] but the message is provided in non-contiguous byte slices.
28pub trait MultipartSigner<S> {
29    /// Equivalent of [`Signer::sign()`] but the message
30    /// is provided in non-contiguous byte slices.
31    fn multipart_sign(&self, msg: &[&[u8]]) -> S {
32        self.try_multipart_sign(msg)
33            .expect("signature operation failed")
34    }
35
36    /// Equivalent of [`Signer::try_sign()`] but the
37    /// message is provided in non-contiguous byte slices.
38    fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<S, Error>;
39}
40
41/// Sign the provided message bytestring using `&mut Self` (e.g. an evolving
42/// cryptographic key such as a stateful hash-based signature), returning a
43/// digital signature.
44pub trait SignerMut<S> {
45    /// Sign the given message, update the state, and return a digital signature.
46    fn sign(&mut self, msg: &[u8]) -> S {
47        self.try_sign(msg).expect("signature operation failed")
48    }
49
50    /// Attempt to sign the given message, updating the state, and returning a
51    /// digital signature on success, or an error if something went wrong.
52    ///
53    /// Signing can fail, e.g., if the number of time periods allowed by the
54    /// current key is exceeded.
55    fn try_sign(&mut self, msg: &[u8]) -> Result<S, Error>;
56}
57
58/// Sign the given prehashed message [`Digest`] using `Self`.
59///
60/// ## Notes
61///
62/// This trait is primarily intended for signature algorithms based on the
63/// [Fiat-Shamir heuristic], a method for converting an interactive
64/// challenge/response-based proof-of-knowledge protocol into an offline
65/// digital signature through the use of a random oracle, i.e. a digest
66/// function.
67///
68/// The security of such protocols critically rests upon the inability of
69/// an attacker to solve for the output of the random oracle, as generally
70/// otherwise such signature algorithms are a system of linear equations and
71/// therefore doing so would allow the attacker to trivially forge signatures.
72///
73/// To prevent misuse which would potentially allow this to be possible, this
74/// API accepts a [`Digest`] instance, rather than a raw digest value.
75///
76/// [Fiat-Shamir heuristic]: https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic
77#[cfg(feature = "digest")]
78pub trait DigestSigner<D: Digest, S> {
79    /// Sign the given prehashed message [`Digest`], returning a signature.
80    ///
81    /// Panics in the event of a signing error.
82    fn sign_digest(&self, digest: D) -> S {
83        self.try_sign_digest(digest)
84            .expect("signature operation failed")
85    }
86
87    /// Attempt to sign the given prehashed message [`Digest`], returning a
88    /// digital signature on success, or an error if something went wrong.
89    fn try_sign_digest(&self, digest: D) -> Result<S, Error>;
90}
91
92/// Sign the given message using the provided external randomness source.
93#[cfg(feature = "rand_core")]
94pub trait RandomizedSigner<S> {
95    /// Sign the given message and return a digital signature
96    fn sign_with_rng<R: CryptoRng + ?Sized>(&self, rng: &mut R, msg: &[u8]) -> S {
97        self.try_sign_with_rng(rng, msg)
98            .expect("signature operation failed")
99    }
100
101    /// Attempt to sign the given message, returning a digital signature on
102    /// success, or an error if something went wrong.
103    ///
104    /// The main intended use case for signing errors is when communicating
105    /// with external signers, e.g. cloud KMS, HSMs, or other hardware tokens.
106    fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
107        &self,
108        rng: &mut R,
109        msg: &[u8],
110    ) -> Result<S, Error>;
111}
112
113/// Equivalent of [`RandomizedSigner`] but the message is provided in non-contiguous byte slices.
114#[cfg(feature = "rand_core")]
115pub trait RandomizedMultipartSigner<S> {
116    /// Equivalent of [`RandomizedSigner::sign_with_rng()`] but
117    /// the message is provided in non-contiguous byte slices.
118    fn multipart_sign_with_rng<R: CryptoRng + ?Sized>(&self, rng: &mut R, msg: &[&[u8]]) -> S {
119        self.try_multipart_sign_with_rng(rng, msg)
120            .expect("signature operation failed")
121    }
122
123    /// Equivalent of [`RandomizedSigner::try_sign_with_rng()`] but
124    /// the message is provided in non-contiguous byte slices.
125    fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
126        &self,
127        rng: &mut R,
128        msg: &[&[u8]],
129    ) -> Result<S, Error>;
130}
131
132/// Combination of [`DigestSigner`] and [`RandomizedSigner`] with support for
133/// computing a signature over a digest which requires entropy from an RNG.
134#[cfg(all(feature = "digest", feature = "rand_core"))]
135pub trait RandomizedDigestSigner<D: Digest, S> {
136    /// Sign the given prehashed message `Digest`, returning a signature.
137    ///
138    /// Panics in the event of a signing error.
139    fn sign_digest_with_rng<R: CryptoRng + ?Sized>(&self, rng: &mut R, digest: D) -> S {
140        self.try_sign_digest_with_rng(rng, digest)
141            .expect("signature operation failed")
142    }
143
144    /// Attempt to sign the given prehashed message `Digest`, returning a
145    /// digital signature on success, or an error if something went wrong.
146    fn try_sign_digest_with_rng<R: TryCryptoRng + ?Sized>(
147        &self,
148        rng: &mut R,
149        digest: D,
150    ) -> Result<S, Error>;
151}
152
153/// Sign the provided message bytestring using `&mut Self` (e.g. an evolving
154/// cryptographic key such as a stateful hash-based signature), and a per-signature
155/// randomizer, returning a digital signature.
156#[cfg(feature = "rand_core")]
157pub trait RandomizedSignerMut<S> {
158    /// Sign the given message, update the state, and return a digital signature.
159    fn sign_with_rng<R: CryptoRng + ?Sized>(&mut self, rng: &mut R, msg: &[u8]) -> S {
160        self.try_sign_with_rng(rng, msg)
161            .expect("signature operation failed")
162    }
163
164    /// Attempt to sign the given message, updating the state, and returning a
165    /// digital signature on success, or an error if something went wrong.
166    ///
167    /// Signing can fail, e.g., if the number of time periods allowed by the
168    /// current key is exceeded.
169    fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
170        &mut self,
171        rng: &mut R,
172        msg: &[u8],
173    ) -> Result<S, Error>;
174}
175
176/// Equivalent of [`RandomizedSignerMut`] but the message is provided in non-contiguous byte slices.
177#[cfg(feature = "rand_core")]
178pub trait RandomizedMultipartSignerMut<S> {
179    /// Equivalent of [`RandomizedSignerMut::sign_with_rng()`] but
180    /// the message is provided in non-contiguous byte slices.
181    fn multipart_sign_with_rng<R: CryptoRng + ?Sized>(&mut self, rng: &mut R, msg: &[&[u8]]) -> S {
182        self.try_multipart_sign_with_rng(rng, msg)
183            .expect("signature operation failed")
184    }
185
186    /// Equivalent of [`RandomizedSignerMut::try_sign_with_rng()`]
187    /// but the message is provided in non-contiguous byte slices.
188    fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
189        &mut self,
190        rng: &mut R,
191        msg: &[&[u8]],
192    ) -> Result<S, Error>;
193}
194
195/// Blanket impl of [`RandomizedSignerMut`] for all [`RandomizedSigner`] types.
196#[cfg(feature = "rand_core")]
197impl<S, T: RandomizedSigner<S>> RandomizedSignerMut<S> for T {
198    fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
199        &mut self,
200        rng: &mut R,
201        msg: &[u8],
202    ) -> Result<S, Error> {
203        T::try_sign_with_rng(self, rng, msg)
204    }
205}
206
207/// Asynchronously sign the provided message bytestring using `Self`
208/// (e.g. client for a Cloud KMS or HSM), returning a digital signature.
209///
210/// This trait is an async equivalent of the [`Signer`] trait.
211pub trait AsyncSigner<S> {
212    /// Attempt to sign the given message, returning a digital signature on
213    /// success, or an error if something went wrong.
214    ///
215    /// The main intended use case for signing errors is when communicating
216    /// with external signers, e.g. cloud KMS, HSMs, or other hardware tokens.
217    async fn sign_async(&self, msg: &[u8]) -> Result<S, Error>;
218}
219
220impl<S, T> AsyncSigner<S> for T
221where
222    T: Signer<S>,
223{
224    async fn sign_async(&self, msg: &[u8]) -> Result<S, Error> {
225        self.try_sign(msg)
226    }
227}
228
229/// Asynchronously sign the given prehashed message [`Digest`] using `Self`.
230///
231/// This trait is an async equivalent of the [`DigestSigner`] trait.
232#[cfg(feature = "digest")]
233pub trait AsyncDigestSigner<D, S>
234where
235    D: Digest,
236{
237    /// Attempt to sign the given prehashed message [`Digest`], returning a
238    /// digital signature on success, or an error if something went wrong.
239    async fn sign_digest_async(&self, digest: D) -> Result<S, Error>;
240}
241
242/// Sign the given message using the provided external randomness source.
243#[cfg(feature = "rand_core")]
244pub trait AsyncRandomizedSigner<S> {
245    /// Sign the given message and return a digital signature
246    async fn sign_with_rng_async<R: CryptoRng + ?Sized>(&self, rng: &mut R, msg: &[u8]) -> S {
247        self.try_sign_with_rng_async(rng, msg)
248            .await
249            .expect("signature operation failed")
250    }
251
252    /// Attempt to sign the given message, returning a digital signature on
253    /// success, or an error if something went wrong.
254    ///
255    /// The main intended use case for signing errors is when communicating
256    /// with external signers, e.g. cloud KMS, HSMs, or other hardware tokens.
257    async fn try_sign_with_rng_async<R: TryCryptoRng + ?Sized>(
258        &self,
259        rng: &mut R,
260        msg: &[u8],
261    ) -> Result<S, Error>;
262}
263
264#[cfg(feature = "rand_core")]
265impl<S, T> AsyncRandomizedSigner<S> for T
266where
267    T: RandomizedSigner<S>,
268{
269    async fn try_sign_with_rng_async<R: TryCryptoRng + ?Sized>(
270        &self,
271        rng: &mut R,
272        msg: &[u8],
273    ) -> Result<S, Error> {
274        self.try_sign_with_rng(rng, msg)
275    }
276}