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
55pub type UncompressedPublicKey = [u8; 65];
57
58#[cfg(feature = "crypto-backend")]
60pub mod backend {
61 use super::*;
62 use alloc::sync::Arc;
63 use alloy_primitives::Address;
64
65 #[cfg(feature = "std")]
66 use std::sync::OnceLock;
67
68 #[cfg(not(feature = "std"))]
69 use once_cell::race::OnceBox;
70
71 pub trait CryptoProvider: Send + Sync + 'static {
117 fn recover_signer_unchecked(
119 &self,
120 sig: &[u8; 65],
121 msg: &[u8; 32],
122 ) -> Result<Address, RecoveryError>;
123
124 fn verify_and_compute_signer_unchecked(
126 &self,
127 pubkey: &[u8; 65],
128 sig: &[u8; 64],
129 msg: &[u8; 32],
130 ) -> Result<Address, RecoveryError>;
131 }
132
133 #[cfg(feature = "std")]
135 static DEFAULT_PROVIDER: OnceLock<Arc<dyn CryptoProvider>> = OnceLock::new();
136
137 #[cfg(not(feature = "std"))]
138 static DEFAULT_PROVIDER: OnceBox<Arc<dyn CryptoProvider>> = OnceBox::new();
139
140 pub struct CryptoProviderAlreadySetError {
143 pub provider: Arc<dyn CryptoProvider>,
145 }
146
147 impl core::fmt::Debug for CryptoProviderAlreadySetError {
148 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
149 f.debug_struct("CryptoProviderAlreadySetError")
150 .field("provider", &"<crypto provider>")
151 .finish()
152 }
153 }
154
155 impl core::fmt::Display for CryptoProviderAlreadySetError {
156 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
157 write!(f, "crypto provider already installed")
158 }
159 }
160
161 impl core::error::Error for CryptoProviderAlreadySetError {}
162
163 pub fn install_default_provider(
169 provider: Arc<dyn CryptoProvider>,
170 ) -> Result<(), CryptoProviderAlreadySetError> {
171 #[cfg(feature = "std")]
172 {
173 DEFAULT_PROVIDER.set(provider).map_err(|provider| {
174 CryptoProviderAlreadySetError { provider }
176 })
177 }
178 #[cfg(not(feature = "std"))]
179 {
180 DEFAULT_PROVIDER.set(Box::new(provider)).map_err(|provider| {
181 CryptoProviderAlreadySetError { provider: *provider }
183 })
184 }
185 }
186
187 pub fn get_default_provider() -> &'static dyn CryptoProvider {
189 try_get_provider().unwrap_or_else(|| {
190 panic!("No crypto backend installed. Call install_default_provider() first.")
191 })
192 }
193
194 pub(super) fn try_get_provider() -> Option<&'static dyn CryptoProvider> {
196 DEFAULT_PROVIDER.get().map(|arc| arc.as_ref())
197 }
198}
199
200#[cfg(any(feature = "secp256k1", feature = "k256"))]
202pub mod secp256k1 {
203 pub use imp::{public_key_to_address, sign_message};
204
205 use super::*;
206 use alloy_primitives::{Address, B256};
207
208 #[cfg(not(feature = "secp256k1"))]
209 use super::impl_k256 as imp;
210 #[cfg(feature = "secp256k1")]
211 use super::impl_secp256k1 as imp;
212
213 pub fn recover_signer_unchecked(
220 signature: &Signature,
221 hash: B256,
222 ) -> Result<Address, RecoveryError> {
223 let mut sig: [u8; 65] = [0; 65];
224
225 sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
226 sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
227 sig[64] = signature.v() as u8;
228
229 #[cfg(feature = "crypto-backend")]
231 if let Some(provider) = super::backend::try_get_provider() {
232 return provider.recover_signer_unchecked(&sig, &hash.0);
233 }
234
235 imp::recover_signer_unchecked(&sig, &hash.0).map_err(|_| RecoveryError::new())
239 }
240
241 pub fn recover_signer(signature: &Signature, hash: B256) -> Result<Address, RecoveryError> {
247 if signature.s() > SECP256K1N_HALF {
248 return Err(RecoveryError::from_source(InvalidSignatureS));
249 }
250 recover_signer_unchecked(signature, hash)
251 }
252
253 pub fn verify_and_compute_signer_unchecked(
256 pubkey: &UncompressedPublicKey,
257 signature: &Signature,
258 hash: B256,
259 ) -> Result<Address, RecoveryError> {
260 let mut sig: [u8; 64] = [0; 64];
261
262 sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
263 sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
264
265 #[cfg(feature = "crypto-backend")]
267 if let Some(provider) = super::backend::try_get_provider() {
268 return provider.verify_and_compute_signer_unchecked(pubkey, &sig, &hash.0);
269 }
270
271 imp::verify_and_compute_signer_unchecked(pubkey, &sig, &hash.0)
273 .map_err(|_| RecoveryError::new())
274 }
275
276 pub fn verify_and_compute_signer(
282 pubkey: &UncompressedPublicKey,
283 signature: &Signature,
284 hash: B256,
285 ) -> Result<Address, RecoveryError> {
286 if signature.s() > SECP256K1N_HALF {
287 return Err(RecoveryError::from_source(InvalidSignatureS));
288 }
289 verify_and_compute_signer_unchecked(pubkey, signature, hash)
290 }
291}
292
293#[cfg(feature = "secp256k1")]
294mod impl_secp256k1 {
295 pub(crate) use ::secp256k1::Error;
296 use ::secp256k1::{
297 ecdsa::{RecoverableSignature, RecoveryId, Signature as SecpSignature},
298 Message, PublicKey, SecretKey, SECP256K1,
299 };
300 use alloy_primitives::{keccak256, Address, Signature, B256, U256};
301
302 pub(crate) fn recover_signer_unchecked(
309 sig: &[u8; 65],
310 msg: &[u8; 32],
311 ) -> Result<Address, Error> {
312 let sig =
313 RecoverableSignature::from_compact(&sig[0..64], RecoveryId::try_from(sig[64] as i32)?)?;
314
315 let public = SECP256K1.recover_ecdsa(&Message::from_digest(*msg), &sig)?;
316 Ok(public_key_to_address(public))
317 }
318
319 pub(crate) fn verify_and_compute_signer_unchecked(
321 pubkey: &[u8; 65],
322 sig: &[u8; 64],
323 msg: &[u8; 32],
324 ) -> Result<Address, Error> {
325 let public_key = PublicKey::from_slice(pubkey)?;
326 let signature = SecpSignature::from_compact(&sig[0..64])?;
327 let message = Message::from_digest(*msg);
328
329 SECP256K1.verify_ecdsa(&message, &signature, &public_key)?;
330
331 Ok(public_key_to_address(public_key))
332 }
333
334 pub fn sign_message(secret: B256, message: B256) -> Result<Signature, Error> {
337 let sec = SecretKey::from_slice(secret.as_ref())?;
338 let s = SECP256K1.sign_ecdsa_recoverable(&Message::from_digest(message.0), &sec);
339 let (rec_id, data) = s.serialize_compact();
340
341 let signature = Signature::new(
342 U256::try_from_be_slice(&data[..32]).expect("The slice has at most 32 bytes"),
343 U256::try_from_be_slice(&data[32..64]).expect("The slice has at most 32 bytes"),
344 i32::from(rec_id) != 0,
345 );
346 Ok(signature)
347 }
348
349 pub fn public_key_to_address(public: PublicKey) -> Address {
352 let hash = keccak256(&public.serialize_uncompressed()[1..]);
355 Address::from_slice(&hash[12..])
356 }
357}
358
359#[cfg(feature = "k256")]
360#[cfg_attr(feature = "secp256k1", allow(unused, unreachable_pub))]
361mod impl_k256 {
362 pub(crate) use k256::ecdsa::Error;
363
364 use super::*;
365 use alloy_primitives::{keccak256, Address, B256};
366 use k256::ecdsa::{RecoveryId, SigningKey, VerifyingKey};
367
368 pub(crate) fn recover_signer_unchecked(
375 sig: &[u8; 65],
376 msg: &[u8; 32],
377 ) -> Result<Address, Error> {
378 let mut signature = k256::ecdsa::Signature::from_slice(&sig[0..64])?;
379 let mut recid = sig[64];
380
381 if let Some(sig_normalized) = signature.normalize_s() {
383 signature = sig_normalized;
384 recid ^= 1;
385 }
386 let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid");
387
388 let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &signature, recid)?;
390 Ok(public_key_to_address(recovered_key))
391 }
392
393 pub(crate) fn verify_and_compute_signer_unchecked(
395 pubkey: &[u8; 65],
396 sig: &[u8; 64],
397 msg: &[u8; 32],
398 ) -> Result<Address, Error> {
399 use k256::ecdsa::signature::hazmat::PrehashVerifier;
400
401 let vk = VerifyingKey::from_sec1_bytes(pubkey)?;
402
403 let mut signature = k256::ecdsa::Signature::from_slice(&sig[0..64])?;
404
405 if let Some(sig_normalized) = signature.normalize_s() {
407 signature = sig_normalized;
408 }
409
410 vk.verify_prehash(&msg[..], &signature)?;
411
412 Ok(public_key_to_address(vk))
413 }
414
415 pub fn sign_message(secret: B256, message: B256) -> Result<Signature, Error> {
418 let sec = SigningKey::from_slice(secret.as_ref())?;
419 sec.sign_prehash_recoverable(&message.0).map(Into::into)
420 }
421
422 pub fn public_key_to_address(public: VerifyingKey) -> Address {
425 let hash = keccak256(&public.to_encoded_point(false).as_bytes()[1..]);
426 Address::from_slice(&hash[12..])
427 }
428}
429
430#[cfg(test)]
431mod tests {
432
433 #[cfg(feature = "secp256k1")]
434 #[test]
435 fn sanity_ecrecover_call_secp256k1() {
436 use super::impl_secp256k1::*;
437 use alloy_primitives::B256;
438
439 let (secret, public) = secp256k1::generate_keypair(&mut rand::thread_rng());
440 let signer = public_key_to_address(public);
441
442 let message = b"hello world";
443 let hash = alloy_primitives::keccak256(message);
444 let signature =
445 sign_message(B256::from_slice(&secret.secret_bytes()[..]), hash).expect("sign message");
446
447 let mut sig: [u8; 65] = [0; 65];
448 sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
449 sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
450 sig[64] = signature.v() as u8;
451
452 assert_eq!(recover_signer_unchecked(&sig, &hash), Ok(signer));
453 }
454
455 #[cfg(feature = "k256")]
456 #[test]
457 fn sanity_ecrecover_call_k256() {
458 use super::impl_k256::*;
459 use alloy_primitives::B256;
460
461 let secret = k256::ecdsa::SigningKey::random(&mut rand::thread_rng());
462 let public = *secret.verifying_key();
463 let signer = public_key_to_address(public);
464
465 let message = b"hello world";
466 let hash = alloy_primitives::keccak256(message);
467 let signature =
468 sign_message(B256::from_slice(&secret.to_bytes()[..]), hash).expect("sign message");
469
470 let mut sig: [u8; 65] = [0; 65];
471 sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>());
472 sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>());
473 sig[64] = signature.v() as u8;
474
475 assert_eq!(recover_signer_unchecked(&sig, &hash).ok(), Some(signer));
476 }
477
478 #[test]
479 #[cfg(all(feature = "secp256k1", feature = "k256"))]
480 fn sanity_secp256k1_k256_compat() {
481 use super::{impl_k256, impl_secp256k1};
482 use alloy_primitives::B256;
483
484 let (secp256k1_secret, secp256k1_public) =
485 secp256k1::generate_keypair(&mut rand::thread_rng());
486 let k256_secret = k256::ecdsa::SigningKey::from_slice(&secp256k1_secret.secret_bytes())
487 .expect("k256 secret");
488 let k256_public = *k256_secret.verifying_key();
489
490 let secp256k1_signer = impl_secp256k1::public_key_to_address(secp256k1_public);
491 let k256_signer = impl_k256::public_key_to_address(k256_public);
492 assert_eq!(secp256k1_signer, k256_signer);
493
494 let message = b"hello world";
495 let hash = alloy_primitives::keccak256(message);
496
497 let secp256k1_signature = impl_secp256k1::sign_message(
498 B256::from_slice(&secp256k1_secret.secret_bytes()[..]),
499 hash,
500 )
501 .expect("secp256k1 sign");
502 let k256_signature =
503 impl_k256::sign_message(B256::from_slice(&k256_secret.to_bytes()[..]), hash)
504 .expect("k256 sign");
505 assert_eq!(secp256k1_signature, k256_signature);
506
507 let mut sig: [u8; 65] = [0; 65];
508
509 sig[0..32].copy_from_slice(&secp256k1_signature.r().to_be_bytes::<32>());
510 sig[32..64].copy_from_slice(&secp256k1_signature.s().to_be_bytes::<32>());
511 sig[64] = secp256k1_signature.v() as u8;
512 let secp256k1_recovered =
513 impl_secp256k1::recover_signer_unchecked(&sig, &hash).expect("secp256k1 recover");
514 assert_eq!(secp256k1_recovered, secp256k1_signer);
515
516 sig[0..32].copy_from_slice(&k256_signature.r().to_be_bytes::<32>());
517 sig[32..64].copy_from_slice(&k256_signature.s().to_be_bytes::<32>());
518 sig[64] = k256_signature.v() as u8;
519 let k256_recovered =
520 impl_k256::recover_signer_unchecked(&sig, &hash).expect("k256 recover");
521 assert_eq!(k256_recovered, k256_signer);
522
523 assert_eq!(secp256k1_recovered, k256_recovered);
524 }
525
526 #[cfg(feature = "crypto-backend")]
527 mod backend_tests {
528 use crate::crypto::{backend::CryptoProvider, RecoveryError};
529 use alloc::sync::Arc;
530 use alloy_primitives::{Address, Signature, B256};
531
532 struct MockCryptoProvider {
534 should_fail: bool,
535 return_address: Address,
536 }
537
538 impl CryptoProvider for MockCryptoProvider {
539 fn recover_signer_unchecked(
540 &self,
541 _sig: &[u8; 65],
542 _msg: &[u8; 32],
543 ) -> Result<Address, RecoveryError> {
544 if self.should_fail {
545 Err(RecoveryError::new())
546 } else {
547 Ok(self.return_address)
548 }
549 }
550
551 fn verify_and_compute_signer_unchecked(
552 &self,
553 _pubkey: &[u8; 65],
554 _sig: &[u8; 64],
555 _msg: &[u8; 32],
556 ) -> Result<Address, RecoveryError> {
557 if self.should_fail {
558 Err(RecoveryError::new())
559 } else {
560 Ok(self.return_address)
561 }
562 }
563 }
564
565 #[test]
566 fn test_crypto_backend_basic_functionality() {
567 let custom_address = Address::from([0x99; 20]); let provider =
570 Arc::new(MockCryptoProvider { should_fail: false, return_address: custom_address });
571
572 let install_result = crate::crypto::backend::install_default_provider(provider);
574
575 let signature = Signature::new(
577 alloy_primitives::U256::from(123u64),
578 alloy_primitives::U256::from(456u64),
579 false,
580 );
581 let hash = B256::from([0xAB; 32]);
582
583 let result = crate::crypto::secp256k1::recover_signer_unchecked(&signature, hash);
585
586 if install_result.is_ok() {
588 assert!(result.is_ok());
589 assert_eq!(result.unwrap(), custom_address);
590 }
591 else {
593 assert!(result.is_ok()); }
595 }
596
597 #[test]
598 fn test_provider_already_set_error() {
599 let provider1 = Arc::new(MockCryptoProvider {
602 should_fail: false,
603 return_address: Address::from([0x11; 20]),
604 });
605 let _result1 = crate::crypto::backend::install_default_provider(provider1);
606
607 let provider2 = Arc::new(MockCryptoProvider {
609 should_fail: true,
610 return_address: Address::from([0x22; 20]),
611 });
612 let result2 = crate::crypto::backend::install_default_provider(provider2);
613
614 assert!(result2.is_err());
616
617 if let Err(err) = result2 {
619 let _provider_ref = err.provider.as_ref();
623 }
624 }
625 }
626}