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}