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}