defuse_crypto/signer.rs
1use std::{
2 fmt::{Debug, Display},
3 sync::Arc,
4};
5
6use impl_tools::autoimpl;
7
8use crate::{Curve, RecoverableCurve};
9
10/// A signer capable of producing signatures for a specific [`Curve`].
11#[trait_variant::make(Send)]
12#[autoimpl(for<T: trait + ?Sized> &T, &mut T, Box<T>, Arc<T>)]
13pub trait Signer<C: Curve>: Sync {
14 /// An error that can occur during [signing](Self::sign).
15 type Error: Debug + Display;
16
17 /// Get the public key of the signer.
18 fn public_key(&self) -> C::PublicKey;
19
20 /// Sign a given message and return a signature.
21 ///
22 /// NOTE: implementations MAY require `msg` to be prehash (i.e. output
23 /// of cryptographic hash function) of a fixed length and return
24 /// an error otherwise. Check corresponding docs before using.
25 async fn sign(&self, msg: &[u8]) -> Result<C::Signature, Self::Error>;
26}
27
28/// A [`Signer`] that can produce [recoverable](RecoverableCurve::recover)
29/// signatures.
30#[trait_variant::make(Send)]
31#[autoimpl(for<T: trait + ?Sized> &T, &mut T, Box<T>, Arc<T>)]
32pub trait RecoverableSigner<C: RecoverableCurve>: Signer<C> {
33 /// Sign a given message and return a signature along with recovery id.
34 ///
35 /// NOTE: implementations MAY require `msg` to be prehash (i.e. output
36 /// of cryptographic hash function) of a fixed length and return
37 /// an error otherwise. Check corresponding docs before using.
38 async fn sign_recoverable(
39 &self,
40 msg: &[u8],
41 ) -> Result<(C::Signature, C::RecoveryId), Self::Error>;
42}
43
44/// An extention trait for [`Signer`].
45pub trait SignerExt {
46 /// Get the public key of the signer on a curve `C`.
47 ///
48 /// This method forwards to [`Signer::public_key()`].
49 ///
50 /// # Examples
51 ///
52 /// ```rust
53 /// use defuse_crypto::{SignerExt, ed25519::Ed25519};
54 /// # use defuse_crypto::ed25519::ed25519_dalek;
55 /// # use hex_literal::hex;
56 /// # let signer = ed25519_dalek::SigningKey::from_bytes(
57 /// # &hex!("5e20282e090a2be1487b29a38750d746ffcdb0b47ebff09441a2167b7523260f"),
58 /// # );
59 ///
60 /// let public_key = signer.public_key::<Ed25519>();
61 /// # assert_eq!(
62 /// # *public_key.as_bytes(),
63 /// # hex!("a0453cc99514600fd5f65a7c2e1e8c147fe1fdb3df7f7fe8d4bf18d619865110"),
64 /// # );
65 /// ```
66 #[inline]
67 fn public_key<C: Curve>(&self) -> C::PublicKey
68 where
69 Self: Signer<C>,
70 {
71 Signer::<C>::public_key(self)
72 }
73
74 /// Sign a given message and return a signature.
75 ///
76 /// **NOTE**: implementations MAY require `msg` to be prehash (i.e. output
77 /// of cryptographic hash function) of a fixed length and return an error
78 /// otherwise. Check corresponding docs before using.
79 ///
80 /// This method forwards to [`Signer::sign()`].
81 ///
82 /// # Examples
83 ///
84 /// ```rust
85 /// use defuse_crypto::{Curve, SignerExt, ed25519::Ed25519};
86 /// # use defuse_crypto::ed25519::ed25519_dalek;
87 ///
88 /// # #[tokio::main(flavor = "local")]
89 /// # async fn main() -> Result<(), Box<dyn core::error::Error>> {
90 /// # let signer = ed25519_dalek::SigningKey::from_bytes(&[0u8; 32]);
91 /// let msg = b"message";
92 /// let signature = signer.sign::<Ed25519>(msg).await?;
93 ///
94 /// assert!(Ed25519::verify(
95 /// &signer.public_key::<Ed25519>(),
96 /// msg,
97 /// &signature,
98 /// ));
99 /// # Ok(()) }
100 #[inline]
101 fn sign<C: Curve>(
102 &self,
103 msg: &[u8],
104 ) -> impl Future<Output = Result<C::Signature, Self::Error>> + Send
105 where
106 Self: Signer<C>,
107 {
108 Signer::<C>::sign(self, msg)
109 }
110
111 /// Sign a given message and return a signature along with recovery id.
112 ///
113 /// **NOTE**: implementations MAY require `msg` to be prehash (i.e. output
114 /// of cryptographic hash function) of a fixed length and return an error
115 /// otherwise. Check corresponding docs before using.
116 ///
117 /// This method forwards to [`RecoverableSigner::sign_recoverable()`].
118 ///
119 /// # Examples
120 ///
121 /// ```rust
122 /// use defuse_crypto::{RecoverableCurve, SignerExt, secp256k1::Secp256k1};
123 /// # use defuse_crypto::secp256k1::k256;
124 /// # use hex_literal::hex;
125 ///
126 /// # #[tokio::main(flavor = "local")]
127 /// # async fn main() -> Result<(), Box<dyn core::error::Error>> {
128 /// # fn make_signer() -> impl defuse_crypto::RecoverableSigner<Secp256k1, Error: core::error::Error> {
129 /// # k256::ecdsa::SigningKey::from_bytes(
130 /// # &hex!("f9151587531d664b5e266a1943c56530d8c6cebeedf631b15736176f1f9d8a90").into(),
131 /// # )
132 /// # .unwrap()
133 /// # }
134 /// # let signer = make_signer();
135 /// // secp256k1 signs over 32-byte prehash
136 /// let prehash = hex!("f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2");
137 /// let (sig, recovery_id) = signer.sign_recoverable::<Secp256k1>(&prehash).await?;
138 ///
139 /// assert_eq!(
140 /// Secp256k1::recover(&prehash, &sig, recovery_id),
141 /// Some(signer.public_key::<Secp256k1>()),
142 /// );
143 /// # Ok(()) }
144 #[inline]
145 fn sign_recoverable<C: RecoverableCurve>(
146 &self,
147 msg: &[u8],
148 ) -> impl Future<Output = Result<(C::Signature, C::RecoveryId), Self::Error>> + Send
149 where
150 Self: RecoverableSigner<C>,
151 {
152 RecoverableSigner::<C>::sign_recoverable(self, msg)
153 }
154
155 /// Calculate [`.public_key()`](Signer::public_key) and cache its value for
156 /// further invocations.
157 ///
158 /// This might be useful if a signer performs some expensive key derivation
159 /// underneath.
160 #[inline]
161 fn cache_public_key<C: Curve>(self) -> CachePublicKey<C, Self>
162 where
163 Self: Signer<C> + Sized,
164 {
165 CachePublicKey::new(self)
166 }
167}
168impl<S> SignerExt for S {}
169
170/// An adaptor for [`Signer`] that caches the [`.public_key`](Signer::public_key).
171///
172/// See [`.cache_public_key()`](SignerExt::cache_public_key) for usages.
173#[autoimpl(Deref using self.signer)]
174#[autoimpl(Debug, Clone, PartialEq, Eq where C::PublicKey: trait, S: trait)]
175pub struct CachePublicKey<C: Curve, S: Signer<C>> {
176 public_key: C::PublicKey,
177 signer: S,
178}
179
180impl<C, S> CachePublicKey<C, S>
181where
182 C: Curve,
183 S: Signer<C>,
184{
185 #[inline]
186 fn new(signer: S) -> Self {
187 Self {
188 public_key: signer.public_key(),
189 signer,
190 }
191 }
192}
193
194impl<C, S> Signer<C> for CachePublicKey<C, S>
195where
196 C: Curve<PublicKey: Clone + Send + Sync>,
197 S: Signer<C>,
198{
199 type Error = S::Error;
200
201 #[inline]
202 fn public_key(&self) -> C::PublicKey {
203 self.public_key.clone()
204 }
205
206 #[inline]
207 fn sign(&self, msg: &[u8]) -> impl Future<Output = Result<C::Signature, Self::Error>> + Send {
208 self.signer.sign(msg)
209 }
210}
211
212impl<C, S> RecoverableSigner<C> for CachePublicKey<C, S>
213where
214 C: RecoverableCurve<PublicKey: Clone + Send + Sync>,
215 S: RecoverableSigner<C>,
216{
217 #[inline]
218 fn sign_recoverable(
219 &self,
220 msg: &[u8],
221 ) -> impl Future<
222 Output = Result<(<C>::Signature, <C as RecoverableCurve>::RecoveryId), Self::Error>,
223 > + Send {
224 self.signer.sign_recoverable(msg)
225 }
226}
227
228/// Test helpers
229#[cfg(test)]
230#[allow(dead_code, clippy::redundant_pub_crate)]
231pub(crate) mod tests {
232 use std::fmt::Debug;
233
234 use super::*;
235
236 pub async fn test_sign_verify<C: Curve, S: Signer<C>>(signer: S, msg: impl AsRef<[u8]>) {
237 let msg = msg.as_ref();
238 let signature = signer.sign(msg).await.unwrap();
239 assert!(
240 C::verify(&signer.public_key(), msg, &signature),
241 "signer produced invalid signature"
242 );
243 }
244
245 pub async fn test_sign_recover<C, S>(signer: S, msg: impl AsRef<[u8]>)
246 where
247 C: RecoverableCurve<PublicKey: PartialEq + Debug>,
248 S: RecoverableSigner<C>,
249 {
250 let msg = msg.as_ref();
251 let (signature, recovery_id) = signer.sign_recoverable(msg).await.unwrap();
252
253 assert_eq!(
254 C::recover(msg, &signature, recovery_id),
255 Some(signer.public_key()),
256 "can't recover signer's public key"
257 );
258 }
259}