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 DEFAULT_PROVIDER.get().map(|arc| arc.as_ref())
187 }
188}
189
190#[cfg(any(feature = "secp256k1", feature = "k256"))]
192pub mod secp256k1 {
193 pub use imp::{public_key_to_address, sign_message};
194
195 use super::*;
196 use alloy_primitives::{Address, B256};
197
198 #[cfg(not(feature = "secp256k1"))]
199 use super::impl_k256 as imp;
200 #[cfg(feature = "secp256k1")]
201 use super::impl_secp256k1 as imp;
202
203 pub fn recover_signer_unchecked(
210 signature: &Signature,
211 hash: B256,
212 ) -> Result<Address, RecoveryError> {
213 let mut sig: [u8; 65] = [0; 65];
214
215 sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
216 sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
217 sig[64] = signature.v() as u8;
218
219 #[cfg(feature = "crypto-backend")]
221 if let Some(provider) = super::backend::try_get_provider() {
222 return provider.recover_signer_unchecked(&sig, &hash.0);
223 }
224
225 imp::recover_signer_unchecked(&sig, &hash.0).map_err(|_| RecoveryError::new())
229 }
230
231 pub fn recover_signer(signature: &Signature, hash: B256) -> Result<Address, RecoveryError> {
237 if signature.s() > SECP256K1N_HALF {
238 return Err(RecoveryError::from_source(InvalidSignatureS));
239 }
240 recover_signer_unchecked(signature, hash)
241 }
242}
243
244#[cfg(feature = "secp256k1")]
245mod impl_secp256k1 {
246 pub(crate) use ::secp256k1::Error;
247 use ::secp256k1::{
248 ecdsa::{RecoverableSignature, RecoveryId},
249 Message, PublicKey, SecretKey, SECP256K1,
250 };
251 use alloy_primitives::{keccak256, Address, Signature, B256, U256};
252
253 pub(crate) fn recover_signer_unchecked(
260 sig: &[u8; 65],
261 msg: &[u8; 32],
262 ) -> Result<Address, Error> {
263 let sig =
264 RecoverableSignature::from_compact(&sig[0..64], RecoveryId::try_from(sig[64] as i32)?)?;
265
266 let public = SECP256K1.recover_ecdsa(&Message::from_digest(*msg), &sig)?;
267 Ok(public_key_to_address(public))
268 }
269
270 pub fn sign_message(secret: B256, message: B256) -> Result<Signature, Error> {
273 let sec = SecretKey::from_slice(secret.as_ref())?;
274 let s = SECP256K1.sign_ecdsa_recoverable(&Message::from_digest(message.0), &sec);
275 let (rec_id, data) = s.serialize_compact();
276
277 let signature = Signature::new(
278 U256::try_from_be_slice(&data[..32]).expect("The slice has at most 32 bytes"),
279 U256::try_from_be_slice(&data[32..64]).expect("The slice has at most 32 bytes"),
280 i32::from(rec_id) != 0,
281 );
282 Ok(signature)
283 }
284
285 pub fn public_key_to_address(public: PublicKey) -> Address {
288 let hash = keccak256(&public.serialize_uncompressed()[1..]);
291 Address::from_slice(&hash[12..])
292 }
293}
294
295#[cfg(feature = "k256")]
296#[cfg_attr(feature = "secp256k1", allow(unused, unreachable_pub))]
297mod impl_k256 {
298 pub(crate) use k256::ecdsa::Error;
299
300 use super::*;
301 use alloy_primitives::{keccak256, Address, B256};
302 use k256::ecdsa::{RecoveryId, SigningKey, VerifyingKey};
303
304 pub(crate) fn recover_signer_unchecked(
311 sig: &[u8; 65],
312 msg: &[u8; 32],
313 ) -> Result<Address, Error> {
314 let mut signature = k256::ecdsa::Signature::from_slice(&sig[0..64])?;
315 let mut recid = sig[64];
316
317 if let Some(sig_normalized) = signature.normalize_s() {
319 signature = sig_normalized;
320 recid ^= 1;
321 }
322 let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid");
323
324 let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &signature, recid)?;
326 Ok(public_key_to_address(recovered_key))
327 }
328
329 pub fn sign_message(secret: B256, message: B256) -> Result<Signature, Error> {
332 let sec = SigningKey::from_slice(secret.as_ref())?;
333 sec.sign_prehash_recoverable(&message.0).map(Into::into)
334 }
335
336 pub fn public_key_to_address(public: VerifyingKey) -> Address {
339 let hash = keccak256(&public.to_encoded_point(false).as_bytes()[1..]);
340 Address::from_slice(&hash[12..])
341 }
342}
343
344#[cfg(test)]
345mod tests {
346
347 #[cfg(feature = "secp256k1")]
348 #[test]
349 fn sanity_ecrecover_call_secp256k1() {
350 use super::impl_secp256k1::*;
351 use alloy_primitives::B256;
352
353 let (secret, public) = secp256k1::generate_keypair(&mut rand::thread_rng());
354 let signer = public_key_to_address(public);
355
356 let message = b"hello world";
357 let hash = alloy_primitives::keccak256(message);
358 let signature =
359 sign_message(B256::from_slice(&secret.secret_bytes()[..]), hash).expect("sign message");
360
361 let mut sig: [u8; 65] = [0; 65];
362 sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
363 sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
364 sig[64] = signature.v() as u8;
365
366 assert_eq!(recover_signer_unchecked(&sig, &hash), Ok(signer));
367 }
368
369 #[cfg(feature = "k256")]
370 #[test]
371 fn sanity_ecrecover_call_k256() {
372 use super::impl_k256::*;
373 use alloy_primitives::B256;
374
375 let secret = k256::ecdsa::SigningKey::random(&mut rand::thread_rng());
376 let public = *secret.verifying_key();
377 let signer = public_key_to_address(public);
378
379 let message = b"hello world";
380 let hash = alloy_primitives::keccak256(message);
381 let signature =
382 sign_message(B256::from_slice(&secret.to_bytes()[..]), hash).expect("sign message");
383
384 let mut sig: [u8; 65] = [0; 65];
385 sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
386 sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
387 sig[64] = signature.v() as u8;
388
389 assert_eq!(recover_signer_unchecked(&sig, &hash).ok(), Some(signer));
390 }
391
392 #[test]
393 #[cfg(all(feature = "secp256k1", feature = "k256"))]
394 fn sanity_secp256k1_k256_compat() {
395 use super::{impl_k256, impl_secp256k1};
396 use alloy_primitives::B256;
397
398 let (secp256k1_secret, secp256k1_public) =
399 secp256k1::generate_keypair(&mut rand::thread_rng());
400 let k256_secret = k256::ecdsa::SigningKey::from_slice(&secp256k1_secret.secret_bytes())
401 .expect("k256 secret");
402 let k256_public = *k256_secret.verifying_key();
403
404 let secp256k1_signer = impl_secp256k1::public_key_to_address(secp256k1_public);
405 let k256_signer = impl_k256::public_key_to_address(k256_public);
406 assert_eq!(secp256k1_signer, k256_signer);
407
408 let message = b"hello world";
409 let hash = alloy_primitives::keccak256(message);
410
411 let secp256k1_signature = impl_secp256k1::sign_message(
412 B256::from_slice(&secp256k1_secret.secret_bytes()[..]),
413 hash,
414 )
415 .expect("secp256k1 sign");
416 let k256_signature =
417 impl_k256::sign_message(B256::from_slice(&k256_secret.to_bytes()[..]), hash)
418 .expect("k256 sign");
419 assert_eq!(secp256k1_signature, k256_signature);
420
421 let mut sig: [u8; 65] = [0; 65];
422
423 sig[0..32].copy_from_slice(&secp256k1_signature.r().to_be_bytes::<32>());
424 sig[32..64].copy_from_slice(&secp256k1_signature.s().to_be_bytes::<32>());
425 sig[64] = secp256k1_signature.v() as u8;
426 let secp256k1_recovered =
427 impl_secp256k1::recover_signer_unchecked(&sig, &hash).expect("secp256k1 recover");
428 assert_eq!(secp256k1_recovered, secp256k1_signer);
429
430 sig[0..32].copy_from_slice(&k256_signature.r().to_be_bytes::<32>());
431 sig[32..64].copy_from_slice(&k256_signature.s().to_be_bytes::<32>());
432 sig[64] = k256_signature.v() as u8;
433 let k256_recovered =
434 impl_k256::recover_signer_unchecked(&sig, &hash).expect("k256 recover");
435 assert_eq!(k256_recovered, k256_signer);
436
437 assert_eq!(secp256k1_recovered, k256_recovered);
438 }
439
440 #[cfg(feature = "crypto-backend")]
441 mod backend_tests {
442 use crate::crypto::{backend::CryptoProvider, RecoveryError};
443 use alloc::sync::Arc;
444 use alloy_primitives::{Address, Signature, B256};
445
446 struct MockCryptoProvider {
448 should_fail: bool,
449 return_address: Address,
450 }
451
452 impl CryptoProvider for MockCryptoProvider {
453 fn recover_signer_unchecked(
454 &self,
455 _sig: &[u8; 65],
456 _msg: &[u8; 32],
457 ) -> Result<Address, RecoveryError> {
458 if self.should_fail {
459 Err(RecoveryError::new())
460 } else {
461 Ok(self.return_address)
462 }
463 }
464 }
465
466 #[test]
467 fn test_crypto_backend_basic_functionality() {
468 let custom_address = Address::from([0x99; 20]); let provider =
471 Arc::new(MockCryptoProvider { should_fail: false, return_address: custom_address });
472
473 let install_result = crate::crypto::backend::install_default_provider(provider);
475
476 let signature = Signature::new(
478 alloy_primitives::U256::from(123u64),
479 alloy_primitives::U256::from(456u64),
480 false,
481 );
482 let hash = B256::from([0xAB; 32]);
483
484 let result = crate::crypto::secp256k1::recover_signer_unchecked(&signature, hash);
486
487 if install_result.is_ok() {
489 assert!(result.is_ok());
490 assert_eq!(result.unwrap(), custom_address);
491 }
492 else {
494 assert!(result.is_ok()); }
496 }
497
498 #[test]
499 fn test_provider_already_set_error() {
500 let provider1 = Arc::new(MockCryptoProvider {
503 should_fail: false,
504 return_address: Address::from([0x11; 20]),
505 });
506 let _result1 = crate::crypto::backend::install_default_provider(provider1);
507
508 let provider2 = Arc::new(MockCryptoProvider {
510 should_fail: true,
511 return_address: Address::from([0x22; 20]),
512 });
513 let result2 = crate::crypto::backend::install_default_provider(provider2);
514
515 assert!(result2.is_err());
517
518 if let Err(err) = result2 {
520 let _provider_ref = err.provider.as_ref();
524 }
525 }
526 }
527}