1use std::{
2 fmt::{Debug, Display},
3 sync::{Arc, OnceLock},
4};
5
6use impl_tools::autoimpl;
7
8use crate::{Curve, RecoverableCurve};
9
10#[trait_variant::make(Send)]
12#[autoimpl(for<T: trait + ?Sized> &T, &mut T, Box<T>, Arc<T>)]
13pub trait Signer<C: Curve>: Sync {
14 type Error: Debug + Display;
16
17 fn public_key(&self) -> C::PublicKey;
19
20 async fn sign(&self, msg: &[u8]) -> Result<C::Signature, Self::Error>;
26
27 #[inline]
28 fn cache_public_key(self) -> CachePublicKey<C, Self>
29 where
30 Self: Sized,
31 {
32 CachePublicKey::new(self)
33 }
34}
35
36#[trait_variant::make(Send)]
39#[autoimpl(for<T: trait + ?Sized> &T, &mut T, Box<T>, Arc<T>)]
40pub trait RecoverableSigner<C: RecoverableCurve>: Signer<C> {
41 async fn sign_recoverable(
47 &self,
48 msg: &[u8],
49 ) -> Result<(C::Signature, C::RecoveryId), Self::Error>;
50}
51
52#[autoimpl(Deref using self.signer)]
54#[autoimpl(Debug, Clone, PartialEq, Eq where C::PublicKey: trait, S: trait)]
55pub struct CachePublicKey<C: Curve, S> {
56 public_key: OnceLock<C::PublicKey>,
57 signer: S,
58}
59
60impl<C: Curve, S> CachePublicKey<C, S> {
61 #[inline]
62 const fn new(signer: S) -> Self {
63 Self {
64 public_key: OnceLock::new(),
65 signer,
66 }
67 }
68}
69
70impl<C, S> Signer<C> for CachePublicKey<C, S>
71where
72 C: Curve,
73 C::PublicKey: Clone + Send + Sync,
74 S: Signer<C>,
75{
76 type Error = S::Error;
77
78 #[inline]
79 fn public_key(&self) -> C::PublicKey {
80 self.public_key
81 .get_or_init(|| self.signer.public_key())
82 .clone()
83 }
84
85 async fn sign(&self, msg: &[u8]) -> Result<C::Signature, Self::Error> {
86 self.signer.sign(msg).await
87 }
88}
89
90impl<C, S> RecoverableSigner<C> for CachePublicKey<C, S>
91where
92 C: RecoverableCurve,
93 C::PublicKey: Clone + Send + Sync,
94 S: RecoverableSigner<C>,
95{
96 async fn sign_recoverable(
97 &self,
98 msg: &[u8],
99 ) -> Result<(<C>::Signature, <C as RecoverableCurve>::RecoveryId), Self::Error> {
100 self.signer.sign_recoverable(msg).await
101 }
102}
103
104#[cfg(test)]
106#[allow(dead_code, clippy::redundant_pub_crate)]
107pub(crate) mod tests {
108 use std::fmt::Debug;
109
110 use super::*;
111
112 pub async fn test_sign_verify<C: Curve, S: Signer<C>>(signer: S, msg: impl AsRef<[u8]>) {
113 let msg = msg.as_ref();
114 let signature = signer.sign(msg).await.unwrap();
115 assert!(
116 C::verify(&signer.public_key(), msg, &signature),
117 "signer produced invalid signature"
118 );
119 }
120
121 pub async fn test_sign_recover<C, S>(signer: S, msg: impl AsRef<[u8]>)
122 where
123 C: RecoverableCurve<PublicKey: PartialEq + Debug>,
124 S: RecoverableSigner<C>,
125 {
126 let msg = msg.as_ref();
127 let (signature, recovery_id) = signer.sign_recoverable(msg).await.unwrap();
128
129 assert_eq!(
130 C::recover(msg, &signature, recovery_id),
131 Some(signer.public_key()),
132 "can't recover signer's public key"
133 );
134 }
135}