1use alloc::boxed::Box;
4use alloy_primitives::U256;
5
6#[cfg(any(feature = "secp256k1", feature = "k256"))]
7use alloy_primitives::Signature;
8
9#[cfg(feature = "crypto-backend")]
10pub use backend::{install_default_provider, CryptoProvider, CryptoProviderAlreadySetError};
11
12#[derive(Debug, thiserror::Error)]
14#[error("signature S value is greater than `secp256k1n / 2`")]
15pub struct InvalidSignatureS;
16
17#[derive(Debug, Default, thiserror::Error)]
19#[error("Failed to recover the signer")]
20pub struct RecoveryError {
21    #[source]
22    source: Option<Box<dyn core::error::Error + Send + Sync + 'static>>,
23}
24
25impl RecoveryError {
26    pub fn new() -> Self {
28        Self::default()
29    }
30
31    pub fn from_source<E: core::error::Error + Send + Sync + 'static>(err: E) -> Self {
36        Self { source: Some(Box::new(err)) }
37    }
38}
39
40impl From<alloy_primitives::SignatureError> for RecoveryError {
41    fn from(err: alloy_primitives::SignatureError) -> Self {
42        Self::from_source(err)
43    }
44}
45
46pub const SECP256K1N_HALF: U256 = U256::from_be_bytes([
51    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
52    0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0,
53]);
54
55#[cfg(feature = "crypto-backend")]
57pub mod backend {
58    use super::*;
59    use alloc::sync::Arc;
60    use alloy_primitives::Address;
61
62    #[cfg(feature = "std")]
63    use std::sync::OnceLock;
64
65    #[cfg(not(feature = "std"))]
66    use once_cell::race::OnceBox;
67
68    pub trait CryptoProvider: Send + Sync + 'static {
114        fn recover_signer_unchecked(
116            &self,
117            sig: &[u8; 65],
118            msg: &[u8; 32],
119        ) -> Result<Address, RecoveryError>;
120    }
121
122    #[cfg(feature = "std")]
124    static DEFAULT_PROVIDER: OnceLock<Arc<dyn CryptoProvider>> = OnceLock::new();
125
126    #[cfg(not(feature = "std"))]
127    static DEFAULT_PROVIDER: OnceBox<Arc<dyn CryptoProvider>> = OnceBox::new();
128
129    pub struct CryptoProviderAlreadySetError {
132        pub provider: Arc<dyn CryptoProvider>,
134    }
135
136    impl core::fmt::Debug for CryptoProviderAlreadySetError {
137        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
138            f.debug_struct("CryptoProviderAlreadySetError")
139                .field("provider", &"<crypto provider>")
140                .finish()
141        }
142    }
143
144    impl core::fmt::Display for CryptoProviderAlreadySetError {
145        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146            write!(f, "crypto provider already installed")
147        }
148    }
149
150    impl core::error::Error for CryptoProviderAlreadySetError {}
151
152    pub fn install_default_provider(
158        provider: Arc<dyn CryptoProvider>,
159    ) -> Result<(), CryptoProviderAlreadySetError> {
160        #[cfg(feature = "std")]
161        {
162            DEFAULT_PROVIDER.set(provider).map_err(|provider| {
163                CryptoProviderAlreadySetError { provider }
165            })
166        }
167        #[cfg(not(feature = "std"))]
168        {
169            DEFAULT_PROVIDER.set(Box::new(provider)).map_err(|provider| {
170                CryptoProviderAlreadySetError { provider: *provider }
172            })
173        }
174    }
175
176    pub fn get_default_provider() -> &'static dyn CryptoProvider {
178        try_get_provider().map_or_else(
179            || panic!("No crypto backend installed. Call install_default_provider() first."),
180            |provider| provider,
181        )
182    }
183
184    pub(super) fn try_get_provider() -> Option<&'static dyn CryptoProvider> {
186        #[cfg(feature = "std")]
187        {
188            DEFAULT_PROVIDER.get().map(|arc| arc.as_ref())
189        }
190        #[cfg(not(feature = "std"))]
191        {
192            DEFAULT_PROVIDER.get().map(|arc| arc.as_ref())
193        }
194    }
195}
196
197#[cfg(any(feature = "secp256k1", feature = "k256"))]
199pub mod secp256k1 {
200    pub use imp::{public_key_to_address, sign_message};
201
202    use super::*;
203    use alloy_primitives::{Address, B256};
204
205    #[cfg(not(feature = "secp256k1"))]
206    use super::impl_k256 as imp;
207    #[cfg(feature = "secp256k1")]
208    use super::impl_secp256k1 as imp;
209
210    pub fn recover_signer_unchecked(
217        signature: &Signature,
218        hash: B256,
219    ) -> Result<Address, RecoveryError> {
220        let mut sig: [u8; 65] = [0; 65];
221
222        sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
223        sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
224        sig[64] = signature.v() as u8;
225
226        #[cfg(feature = "crypto-backend")]
228        if let Some(provider) = super::backend::try_get_provider() {
229            return provider.recover_signer_unchecked(&sig, &hash.0);
230        }
231
232        imp::recover_signer_unchecked(&sig, &hash.0).map_err(|_| RecoveryError::new())
236    }
237
238    pub fn recover_signer(signature: &Signature, hash: B256) -> Result<Address, RecoveryError> {
244        if signature.s() > SECP256K1N_HALF {
245            return Err(RecoveryError::from_source(InvalidSignatureS));
246        }
247        recover_signer_unchecked(signature, hash)
248    }
249}
250
251#[cfg(any(test, feature = "secp256k1"))]
252mod impl_secp256k1 {
253    pub(crate) use ::secp256k1::Error;
254    use ::secp256k1::{
255        ecdsa::{RecoverableSignature, RecoveryId},
256        Message, PublicKey, SecretKey, SECP256K1,
257    };
258    use alloy_primitives::{keccak256, Address, Signature, B256, U256};
259
260    pub(crate) fn recover_signer_unchecked(
267        sig: &[u8; 65],
268        msg: &[u8; 32],
269    ) -> Result<Address, Error> {
270        let sig =
271            RecoverableSignature::from_compact(&sig[0..64], RecoveryId::try_from(sig[64] as i32)?)?;
272
273        let public = SECP256K1.recover_ecdsa(&Message::from_digest(*msg), &sig)?;
274        Ok(public_key_to_address(public))
275    }
276
277    pub fn sign_message(secret: B256, message: B256) -> Result<Signature, Error> {
280        let sec = SecretKey::from_slice(secret.as_ref())?;
281        let s = SECP256K1.sign_ecdsa_recoverable(&Message::from_digest(message.0), &sec);
282        let (rec_id, data) = s.serialize_compact();
283
284        let signature = Signature::new(
285            U256::try_from_be_slice(&data[..32]).expect("The slice has at most 32 bytes"),
286            U256::try_from_be_slice(&data[32..64]).expect("The slice has at most 32 bytes"),
287            i32::from(rec_id) != 0,
288        );
289        Ok(signature)
290    }
291
292    pub fn public_key_to_address(public: PublicKey) -> Address {
295        let hash = keccak256(&public.serialize_uncompressed()[1..]);
298        Address::from_slice(&hash[12..])
299    }
300}
301
302#[cfg(feature = "k256")]
303#[cfg_attr(feature = "secp256k1", allow(unused, unreachable_pub))]
304mod impl_k256 {
305    pub(crate) use k256::ecdsa::Error;
306
307    use super::*;
308    use alloy_primitives::{keccak256, Address, B256};
309    use k256::ecdsa::{RecoveryId, SigningKey, VerifyingKey};
310
311    pub(crate) fn recover_signer_unchecked(
318        sig: &[u8; 65],
319        msg: &[u8; 32],
320    ) -> Result<Address, Error> {
321        let mut signature = k256::ecdsa::Signature::from_slice(&sig[0..64])?;
322        let mut recid = sig[64];
323
324        if let Some(sig_normalized) = signature.normalize_s() {
326            signature = sig_normalized;
327            recid ^= 1;
328        }
329        let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid");
330
331        let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &signature, recid)?;
333        Ok(public_key_to_address(recovered_key))
334    }
335
336    pub fn sign_message(secret: B256, message: B256) -> Result<Signature, Error> {
339        let sec = SigningKey::from_slice(secret.as_ref())?;
340        sec.sign_prehash_recoverable(&message.0).map(Into::into)
341    }
342
343    pub fn public_key_to_address(public: VerifyingKey) -> Address {
346        let hash = keccak256(&public.to_encoded_point(false).as_bytes()[1..]);
347        Address::from_slice(&hash[12..])
348    }
349}
350
351#[cfg(test)]
352mod tests {
353
354    #[cfg(feature = "secp256k1")]
355    #[test]
356    fn sanity_ecrecover_call_secp256k1() {
357        use super::impl_secp256k1::*;
358        use alloy_primitives::B256;
359
360        let (secret, public) = secp256k1::generate_keypair(&mut rand::thread_rng());
361        let signer = public_key_to_address(public);
362
363        let message = b"hello world";
364        let hash = alloy_primitives::keccak256(message);
365        let signature =
366            sign_message(B256::from_slice(&secret.secret_bytes()[..]), hash).expect("sign message");
367
368        let mut sig: [u8; 65] = [0; 65];
369        sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
370        sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
371        sig[64] = signature.v() as u8;
372
373        assert_eq!(recover_signer_unchecked(&sig, &hash), Ok(signer));
374    }
375
376    #[cfg(feature = "k256")]
377    #[test]
378    fn sanity_ecrecover_call_k256() {
379        use super::impl_k256::*;
380        use alloy_primitives::B256;
381
382        let secret = k256::ecdsa::SigningKey::random(&mut rand::thread_rng());
383        let public = *secret.verifying_key();
384        let signer = public_key_to_address(public);
385
386        let message = b"hello world";
387        let hash = alloy_primitives::keccak256(message);
388        let signature =
389            sign_message(B256::from_slice(&secret.to_bytes()[..]), hash).expect("sign message");
390
391        let mut sig: [u8; 65] = [0; 65];
392        sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
393        sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
394        sig[64] = signature.v() as u8;
395
396        assert_eq!(recover_signer_unchecked(&sig, &hash).ok(), Some(signer));
397    }
398
399    #[test]
400    #[cfg(all(feature = "secp256k1", feature = "k256"))]
401    fn sanity_secp256k1_k256_compat() {
402        use super::{impl_k256, impl_secp256k1};
403        use alloy_primitives::B256;
404
405        let (secp256k1_secret, secp256k1_public) =
406            secp256k1::generate_keypair(&mut rand::thread_rng());
407        let k256_secret = k256::ecdsa::SigningKey::from_slice(&secp256k1_secret.secret_bytes())
408            .expect("k256 secret");
409        let k256_public = *k256_secret.verifying_key();
410
411        let secp256k1_signer = impl_secp256k1::public_key_to_address(secp256k1_public);
412        let k256_signer = impl_k256::public_key_to_address(k256_public);
413        assert_eq!(secp256k1_signer, k256_signer);
414
415        let message = b"hello world";
416        let hash = alloy_primitives::keccak256(message);
417
418        let secp256k1_signature = impl_secp256k1::sign_message(
419            B256::from_slice(&secp256k1_secret.secret_bytes()[..]),
420            hash,
421        )
422        .expect("secp256k1 sign");
423        let k256_signature =
424            impl_k256::sign_message(B256::from_slice(&k256_secret.to_bytes()[..]), hash)
425                .expect("k256 sign");
426        assert_eq!(secp256k1_signature, k256_signature);
427
428        let mut sig: [u8; 65] = [0; 65];
429
430        sig[0..32].copy_from_slice(&secp256k1_signature.r().to_be_bytes::<32>());
431        sig[32..64].copy_from_slice(&secp256k1_signature.s().to_be_bytes::<32>());
432        sig[64] = secp256k1_signature.v() as u8;
433        let secp256k1_recovered =
434            impl_secp256k1::recover_signer_unchecked(&sig, &hash).expect("secp256k1 recover");
435        assert_eq!(secp256k1_recovered, secp256k1_signer);
436
437        sig[0..32].copy_from_slice(&k256_signature.r().to_be_bytes::<32>());
438        sig[32..64].copy_from_slice(&k256_signature.s().to_be_bytes::<32>());
439        sig[64] = k256_signature.v() as u8;
440        let k256_recovered =
441            impl_k256::recover_signer_unchecked(&sig, &hash).expect("k256 recover");
442        assert_eq!(k256_recovered, k256_signer);
443
444        assert_eq!(secp256k1_recovered, k256_recovered);
445    }
446
447    #[cfg(feature = "crypto-backend")]
448    mod backend_tests {
449        use crate::crypto::{backend::CryptoProvider, RecoveryError};
450        use alloc::sync::Arc;
451        use alloy_primitives::{Address, Signature, B256};
452
453        struct MockCryptoProvider {
455            should_fail: bool,
456            return_address: Address,
457        }
458
459        impl CryptoProvider for MockCryptoProvider {
460            fn recover_signer_unchecked(
461                &self,
462                _sig: &[u8; 65],
463                _msg: &[u8; 32],
464            ) -> Result<Address, RecoveryError> {
465                if self.should_fail {
466                    Err(RecoveryError::new())
467                } else {
468                    Ok(self.return_address)
469                }
470            }
471        }
472
473        #[test]
474        fn test_crypto_backend_basic_functionality() {
475            let custom_address = Address::from([0x99; 20]); let provider =
478                Arc::new(MockCryptoProvider { should_fail: false, return_address: custom_address });
479
480            let install_result = crate::crypto::backend::install_default_provider(provider);
482
483            let signature = Signature::new(
485                alloy_primitives::U256::from(123u64),
486                alloy_primitives::U256::from(456u64),
487                false,
488            );
489            let hash = B256::from([0xAB; 32]);
490
491            let result = crate::crypto::secp256k1::recover_signer_unchecked(&signature, hash);
493
494            if install_result.is_ok() {
496                assert!(result.is_ok());
497                assert_eq!(result.unwrap(), custom_address);
498            }
499            else {
501                assert!(result.is_ok()); }
503        }
504
505        #[test]
506        fn test_provider_already_set_error() {
507            let provider1 = Arc::new(MockCryptoProvider {
510                should_fail: false,
511                return_address: Address::from([0x11; 20]),
512            });
513            let _result1 = crate::crypto::backend::install_default_provider(provider1);
514
515            let provider2 = Arc::new(MockCryptoProvider {
517                should_fail: true,
518                return_address: Address::from([0x22; 20]),
519            });
520            let result2 = crate::crypto::backend::install_default_provider(provider2);
521
522            assert!(result2.is_err());
524
525            if let Err(err) = result2 {
527                let _provider_ref = err.provider.as_ref();
531            }
532        }
533    }
534}