Skip to main content

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}