signature/
signer.rs

1//! Traits for generating digital signatures
2
3use crate::error::Error;
4
5#[cfg(feature = "digest")]
6use crate::digest::Update;
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: Update, S> {
79    /// Sign a message by updating the received `Digest` with it,
80    /// returning a signature.
81    ///
82    /// The given function can be invoked multiple times. It is expected that
83    /// in each invocation the `Digest` is updated with the entire equal message.
84    ///
85    /// Panics in the event of a signing error.
86    fn sign_digest<F: Fn(&mut D)>(&self, f: F) -> S {
87        self.try_sign_digest(|digest| {
88            f(digest);
89            Ok(())
90        })
91        .expect("signature operation failed")
92    }
93
94    /// Attempt to sign a message by updating the received `Digest` with it,
95    /// returning a digital signature on success, or an error if something went wrong.
96    ///
97    /// The given function can be invoked multiple times. It is expected that
98    /// in each invocation the `Digest` is updated with the entire equal message.
99    fn try_sign_digest<F: Fn(&mut D) -> Result<(), Error>>(&self, f: F) -> Result<S, Error>;
100}
101
102/// Sign the given message using the provided external randomness source.
103#[cfg(feature = "rand_core")]
104pub trait RandomizedSigner<S> {
105    /// Sign the given message and return a digital signature
106    fn sign_with_rng<R: CryptoRng + ?Sized>(&self, rng: &mut R, msg: &[u8]) -> S {
107        self.try_sign_with_rng(rng, msg)
108            .expect("signature operation failed")
109    }
110
111    /// Attempt to sign the given message, returning a digital signature on
112    /// success, or an error if something went wrong.
113    ///
114    /// The main intended use case for signing errors is when communicating
115    /// with external signers, e.g. cloud KMS, HSMs, or other hardware tokens.
116    fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
117        &self,
118        rng: &mut R,
119        msg: &[u8],
120    ) -> Result<S, Error>;
121}
122
123/// Equivalent of [`RandomizedSigner`] but the message is provided in non-contiguous byte slices.
124#[cfg(feature = "rand_core")]
125pub trait RandomizedMultipartSigner<S> {
126    /// Equivalent of [`RandomizedSigner::sign_with_rng()`] but
127    /// the message is provided in non-contiguous byte slices.
128    fn multipart_sign_with_rng<R: CryptoRng + ?Sized>(&self, rng: &mut R, msg: &[&[u8]]) -> S {
129        self.try_multipart_sign_with_rng(rng, msg)
130            .expect("signature operation failed")
131    }
132
133    /// Equivalent of [`RandomizedSigner::try_sign_with_rng()`] but
134    /// the message is provided in non-contiguous byte slices.
135    fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
136        &self,
137        rng: &mut R,
138        msg: &[&[u8]],
139    ) -> Result<S, Error>;
140}
141
142/// Combination of [`DigestSigner`] and [`RandomizedSigner`] with support for
143/// computing a signature over a digest which requires entropy from an RNG.
144#[cfg(all(feature = "digest", feature = "rand_core"))]
145pub trait RandomizedDigestSigner<D: Update, S> {
146    /// Sign a message by updating the received `Digest` with it,
147    /// returning a signature.
148    ///
149    /// The given function can be invoked multiple times. It is expected that
150    /// in each invocation the `Digest` is updated with the entire equal message.
151    ///
152    /// Panics in the event of a signing error.
153    fn sign_digest_with_rng<R: CryptoRng + ?Sized, F: Fn(&mut D)>(&self, rng: &mut R, f: F) -> S {
154        self.try_sign_digest_with_rng(rng, |digest| {
155            f(digest);
156            Ok(())
157        })
158        .expect("signature operation failed")
159    }
160
161    /// Attempt to sign a message by updating the received `Digest` with it,
162    /// returning a digital signature on success, or an error if something went wrong.
163    ///
164    /// The given function can be invoked multiple times. It is expected that
165    /// in each invocation the `Digest` is updated with the entire equal message.
166    fn try_sign_digest_with_rng<R: TryCryptoRng + ?Sized, F: Fn(&mut D) -> Result<(), Error>>(
167        &self,
168        rng: &mut R,
169        f: F,
170    ) -> Result<S, Error>;
171}
172
173/// Sign the provided message bytestring using `&mut Self` (e.g. an evolving
174/// cryptographic key such as a stateful hash-based signature), and a per-signature
175/// randomizer, returning a digital signature.
176#[cfg(feature = "rand_core")]
177pub trait RandomizedSignerMut<S> {
178    /// Sign the given message, update the state, and return a digital signature.
179    fn sign_with_rng<R: CryptoRng + ?Sized>(&mut self, rng: &mut R, msg: &[u8]) -> S {
180        self.try_sign_with_rng(rng, msg)
181            .expect("signature operation failed")
182    }
183
184    /// Attempt to sign the given message, updating the state, and returning a
185    /// digital signature on success, or an error if something went wrong.
186    ///
187    /// Signing can fail, e.g., if the number of time periods allowed by the
188    /// current key is exceeded.
189    fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
190        &mut self,
191        rng: &mut R,
192        msg: &[u8],
193    ) -> Result<S, Error>;
194}
195
196/// Equivalent of [`RandomizedSignerMut`] but the message is provided in non-contiguous byte slices.
197#[cfg(feature = "rand_core")]
198pub trait RandomizedMultipartSignerMut<S> {
199    /// Equivalent of [`RandomizedSignerMut::sign_with_rng()`] but
200    /// the message is provided in non-contiguous byte slices.
201    fn multipart_sign_with_rng<R: CryptoRng + ?Sized>(&mut self, rng: &mut R, msg: &[&[u8]]) -> S {
202        self.try_multipart_sign_with_rng(rng, msg)
203            .expect("signature operation failed")
204    }
205
206    /// Equivalent of [`RandomizedSignerMut::try_sign_with_rng()`]
207    /// but the message is provided in non-contiguous byte slices.
208    fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
209        &mut self,
210        rng: &mut R,
211        msg: &[&[u8]],
212    ) -> Result<S, Error>;
213}
214
215/// Blanket impl of [`RandomizedSignerMut`] for all [`RandomizedSigner`] types.
216#[cfg(feature = "rand_core")]
217impl<S, T: RandomizedSigner<S>> RandomizedSignerMut<S> for T {
218    fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
219        &mut self,
220        rng: &mut R,
221        msg: &[u8],
222    ) -> Result<S, Error> {
223        T::try_sign_with_rng(self, rng, msg)
224    }
225}
226
227/// Asynchronously sign the provided message bytestring using `Self`
228/// (e.g. client for a Cloud KMS or HSM), returning a digital signature.
229///
230/// This trait is an async equivalent of the [`Signer`] trait.
231pub trait AsyncSigner<S> {
232    /// Attempt to sign the given message, returning a digital signature on
233    /// success, or an error if something went wrong.
234    ///
235    /// The main intended use case for signing errors is when communicating
236    /// with external signers, e.g. cloud KMS, HSMs, or other hardware tokens.
237    async fn sign_async(&self, msg: &[u8]) -> Result<S, Error>;
238}
239
240impl<S, T> AsyncSigner<S> for T
241where
242    T: Signer<S>,
243{
244    async fn sign_async(&self, msg: &[u8]) -> Result<S, Error> {
245        self.try_sign(msg)
246    }
247}
248
249/// Asynchronously sign the given prehashed message `Digest` using `Self`.
250///
251/// This trait is an async equivalent of the [`DigestSigner`] trait.
252#[cfg(feature = "digest")]
253pub trait AsyncDigestSigner<D, S>
254where
255    D: Update,
256{
257    /// Attempt to sign a message by updating the received `Digest` with it,
258    /// returning a digital signature on success, or an error if something went wrong.
259    ///
260    /// The given function can be invoked multiple times. It is expected that
261    /// in each invocation the `Digest` is updated with the entire equal message.
262    async fn sign_digest_async<F: AsyncFn(&mut D) -> Result<(), Error>>(
263        &self,
264        f: F,
265    ) -> Result<S, Error>;
266}
267
268/// Sign the given message using the provided external randomness source.
269#[cfg(feature = "rand_core")]
270pub trait AsyncRandomizedSigner<S> {
271    /// Sign the given message and return a digital signature
272    async fn sign_with_rng_async<R: CryptoRng + ?Sized>(&self, rng: &mut R, msg: &[u8]) -> S {
273        self.try_sign_with_rng_async(rng, msg)
274            .await
275            .expect("signature operation failed")
276    }
277
278    /// Attempt to sign the given message, returning a digital signature on
279    /// success, or an error if something went wrong.
280    ///
281    /// The main intended use case for signing errors is when communicating
282    /// with external signers, e.g. cloud KMS, HSMs, or other hardware tokens.
283    async fn try_sign_with_rng_async<R: TryCryptoRng + ?Sized>(
284        &self,
285        rng: &mut R,
286        msg: &[u8],
287    ) -> Result<S, Error>;
288}
289
290#[cfg(feature = "rand_core")]
291impl<S, T> AsyncRandomizedSigner<S> for T
292where
293    T: RandomizedSigner<S>,
294{
295    async fn try_sign_with_rng_async<R: TryCryptoRng + ?Sized>(
296        &self,
297        rng: &mut R,
298        msg: &[u8],
299    ) -> Result<S, Error> {
300        self.try_sign_with_rng(rng, msg)
301    }
302}