1pub use k256;
4
5use k256::{
6 Sec1Point,
7 ecdsa::{RecoveryId, Signature, VerifyingKey},
8};
9
10use crate::{Curve, RecoverableCurve};
11
12pub struct Secp256k1;
14
15impl Curve for Secp256k1 {
16 type PublicKey = VerifyingKey;
17 type Signature = Signature;
18
19 #[inline]
22 fn verify(public_key: &VerifyingKey, prehash: &[u8], signature: &Self::Signature) -> bool {
23 let Ok(prehash) = <&[u8; 32]>::try_from(prehash) else {
25 return false;
26 };
27
28 cfg_select! {
29 near => {
30 for id in 0..=RecoveryId::MAX {
33 let recovery_id = RecoveryId::from_byte(id).unwrap_or_else(|| unreachable!());
34
35 if let Some(recovered) = Self::recover(prehash, signature, recovery_id)
36 && recovered == *public_key
37 {
38 return true;
39 }
40 }
41 false
43 }
44 _ => {{
45 use k256::{
46 ecdsa::signature::hazmat::PrehashVerifier,
47 elliptic_curve::scalar::IsHigh,
48 };
49
50 if signature.s().is_high().into() {
51 return false;
53 }
54
55 public_key.verify_prehash(prehash, signature).is_ok()
56 }}
57 }
58 }
59}
60
61impl RecoverableCurve for Secp256k1 {
62 type RecoveryId = RecoveryId;
63
64 #[inline]
65 fn recover(
66 prehash: &[u8],
67 signature: &Self::Signature,
68 recovery_id: Self::RecoveryId,
69 ) -> Option<Self::PublicKey> {
70 let prehash: &[u8; 32] = prehash.try_into().ok()?;
72
73 let public_key = {
74 cfg_select! {
75 near => {
76 let pk: [u8; 64] = ::near_sdk::env::ecrecover(
77 prehash,
78 &signature.to_bytes(),
79 recovery_id.to_byte(),
80 true,
83 )?;
84
85 Secp256k1UncompressedPublicKey(pk).try_into().ok()
86 }
87 _ => {
88 use k256::elliptic_curve::scalar::IsHigh;
89
90 if signature.s().is_high().into() {
91 return None;
93 }
94
95 VerifyingKey::recover_from_prehash(prehash, signature, recovery_id).ok()
96 }
97 }
98 }?;
99
100 Some(public_key)
101 }
102}
103
104#[cfg(feature = "signing")]
105const _: () = {
106 use futures_util::{TryFutureExt, future};
107 use k256::ecdsa::{Error, SigningKey};
108
109 use crate::{RecoverableSigner, Signer};
110
111 impl Signer<Secp256k1> for SigningKey {
112 type Error = Error;
113
114 #[inline]
115 fn public_key(&self) -> <Secp256k1 as Curve>::PublicKey {
116 *self.verifying_key()
117 }
118
119 #[inline]
124 fn sign(
125 &self,
126 prehash: &[u8],
127 ) -> impl Future<Output = Result<<Secp256k1 as Curve>::Signature, Self::Error>> + Send
128 {
129 RecoverableSigner::sign_recoverable(self, prehash).map_ok(|s| s.0)
130 }
131 }
132
133 impl RecoverableSigner<Secp256k1> for SigningKey {
134 #[inline]
139 fn sign_recoverable(
140 &self,
141 prehash: &[u8],
142 ) -> impl Future<
143 Output = Result<
144 (
145 <Secp256k1 as Curve>::Signature,
146 <Secp256k1 as RecoverableCurve>::RecoveryId,
147 ),
148 Self::Error,
149 >,
150 > + Send {
151 let Ok(prehash) = <&[u8; 32]>::try_from(prehash) else {
152 return future::err(Error::from_source("prehash must be 32 bytes long"));
153 };
154
155 future::ok(self.sign_prehash_recoverable(prehash))
158 }
159 }
160};
161
162#[cfg_attr(
164 feature = "serde",
165 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
166 cfg_attr(
167 feature = "schemars-v0_8",
168 derive(::schemars::JsonSchema),
169 schemars(example = "Self::example"),
170 )
171)]
172#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
173#[cfg_attr(
174 feature = "borsh",
175 derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
176 cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
177)]
178#[derive(
179 Debug,
180 Clone,
181 Copy,
182 PartialEq,
183 Eq,
184 PartialOrd,
185 Ord,
186 Hash,
187 derive_more::AsRef,
188 derive_more::From,
189 derive_more::Into,
190)]
191#[as_ref([u8], [u8; 64])]
192#[into(owned, ref)]
193#[repr(transparent)]
194pub struct Secp256k1UncompressedPublicKey(
195 #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 64],
197);
198
199impl Secp256k1UncompressedPublicKey {
200 #[inline]
219 pub fn compress(&self) -> Result<Secp256k1CompressedPublicKey, k256::ecdsa::Error> {
220 VerifyingKey::try_from(self).map(Into::into)
221 }
222
223 #[cfg(feature = "schemars-v0_8")]
224 const fn example() -> Self {
225 Self(hex_literal::hex!(
226 "85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"
227 ))
228 }
229}
230
231impl From<VerifyingKey> for Secp256k1UncompressedPublicKey {
232 #[inline]
233 fn from(value: VerifyingKey) -> Self {
234 (&value).into()
235 }
236}
237
238impl From<&VerifyingKey> for Secp256k1UncompressedPublicKey {
239 #[inline]
240 fn from(value: &VerifyingKey) -> Self {
241 Self(
242 value
243 .to_sec1_point(false) .as_bytes()[1..] .try_into()
246 .unwrap_or_else(|_| unreachable!()),
247 )
248 }
249}
250
251impl TryFrom<Secp256k1UncompressedPublicKey> for VerifyingKey {
252 type Error = k256::ecdsa::Error;
253
254 #[inline]
255 fn try_from(value: Secp256k1UncompressedPublicKey) -> Result<Self, Self::Error> {
256 (&value).try_into()
257 }
258}
259
260impl TryFrom<&Secp256k1UncompressedPublicKey> for VerifyingKey {
261 type Error = k256::ecdsa::Error;
262
263 #[inline]
264 fn try_from(value: &Secp256k1UncompressedPublicKey) -> Result<Self, Self::Error> {
265 Self::from_sec1_point(&Sec1Point::from_untagged_bytes((&value.0).into()))
266 }
267}
268
269impl TryFrom<Secp256k1UncompressedPublicKey> for Secp256k1CompressedPublicKey {
270 type Error = k256::ecdsa::Error;
271
272 #[inline]
273 fn try_from(pk: Secp256k1UncompressedPublicKey) -> Result<Self, Self::Error> {
274 (&pk).try_into()
275 }
276}
277
278impl TryFrom<&Secp256k1UncompressedPublicKey> for Secp256k1CompressedPublicKey {
279 type Error = k256::ecdsa::Error;
280
281 #[inline]
282 fn try_from(pk: &Secp256k1UncompressedPublicKey) -> Result<Self, Self::Error> {
283 pk.compress()
284 }
285}
286
287#[cfg_attr(
289 feature = "serde",
290 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
291 cfg_attr(
292 feature = "schemars-v0_8",
293 derive(::schemars::JsonSchema),
294 schemars(example = "Self::example"),
295 )
296)]
297#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
298#[cfg_attr(
299 feature = "borsh",
300 derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
301 cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
302)]
303#[derive(
304 Debug,
305 Clone,
306 Copy,
307 PartialEq,
308 Eq,
309 PartialOrd,
310 Ord,
311 Hash,
312 derive_more::AsRef,
313 derive_more::From,
314 derive_more::Into,
315)]
316#[as_ref([u8], [u8; 33])]
317#[into(owned, ref)]
318#[repr(transparent)]
319pub struct Secp256k1CompressedPublicKey(
320 #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 33],
322);
323
324impl Secp256k1CompressedPublicKey {
325 #[cfg(feature = "schemars-v0_8")]
326 const fn example() -> Self {
327 Self(hex_literal::hex!(
328 "0285a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b8"
329 ))
330 }
331}
332
333impl From<VerifyingKey> for Secp256k1CompressedPublicKey {
334 #[inline]
335 fn from(value: VerifyingKey) -> Self {
336 (&value).into()
337 }
338}
339
340impl From<&VerifyingKey> for Secp256k1CompressedPublicKey {
341 #[inline]
342 fn from(value: &VerifyingKey) -> Self {
343 Self(
344 value
345 .to_sec1_point(true) .as_bytes()
347 .try_into()
348 .unwrap_or_else(|_| unreachable!()),
350 )
351 }
352}
353
354impl TryFrom<Secp256k1CompressedPublicKey> for VerifyingKey {
355 type Error = k256::ecdsa::Error;
356
357 #[inline]
358 fn try_from(value: Secp256k1CompressedPublicKey) -> Result<Self, Self::Error> {
359 (&value).try_into()
360 }
361}
362
363impl TryFrom<&Secp256k1CompressedPublicKey> for VerifyingKey {
364 type Error = k256::ecdsa::Error;
365
366 #[inline]
367 fn try_from(value: &Secp256k1CompressedPublicKey) -> Result<Self, Self::Error> {
368 Self::from_sec1_bytes(&value.0)
369 }
370}
371
372impl TryFrom<Secp256k1CompressedPublicKey> for Secp256k1UncompressedPublicKey {
373 type Error = k256::ecdsa::Error;
374
375 #[inline]
376 fn try_from(pk: Secp256k1CompressedPublicKey) -> Result<Self, Self::Error> {
377 (&pk).try_into()
378 }
379}
380
381impl TryFrom<&Secp256k1CompressedPublicKey> for Secp256k1UncompressedPublicKey {
382 type Error = k256::ecdsa::Error;
383
384 #[inline]
385 fn try_from(pk: &Secp256k1CompressedPublicKey) -> Result<Self, Self::Error> {
386 VerifyingKey::try_from(pk).map(Into::into)
387 }
388}
389
390#[cfg_attr(
392 feature = "serde",
393 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
394 cfg_attr(
395 feature = "schemars-v0_8",
396 derive(::schemars::JsonSchema),
397 schemars(example = "Self::example"),
398 )
399)]
400#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
401#[cfg_attr(
402 feature = "borsh",
403 derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
404 cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
405)]
406#[derive(
407 Debug,
408 Clone,
409 Copy,
410 PartialEq,
411 Eq,
412 PartialOrd,
413 Ord,
414 Hash,
415 derive_more::AsRef,
416 derive_more::From,
417 derive_more::Into,
418)]
419#[as_ref([u8], [u8; 64])]
420#[into(owned, ref)]
421#[repr(transparent)]
422pub struct Secp256k1Signature(
423 #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 64],
425);
426
427impl Secp256k1Signature {
428 #[cfg(feature = "schemars-v0_8")]
429 const fn example() -> Self {
430 Self(hex_literal::hex!(
431 "7800a70d05cde2c49ed546a6ce887ce6027c2c268c0285f6efef0cdfc4366b23643790f67a86468ee8301ed12cfffcb07c6530f90a9327ec057800fabd332e47"
432 ))
433 }
434}
435
436impl From<Signature> for Secp256k1Signature {
437 #[inline]
438 fn from(value: Signature) -> Self {
439 (&value).into()
440 }
441}
442
443impl From<&Signature> for Secp256k1Signature {
444 #[inline]
445 fn from(value: &Signature) -> Self {
446 Self(value.to_bytes().into())
447 }
448}
449
450impl TryFrom<Secp256k1Signature> for Signature {
451 type Error = k256::ecdsa::Error;
452
453 #[inline]
454 fn try_from(value: Secp256k1Signature) -> Result<Self, Self::Error> {
455 (&value).try_into()
456 }
457}
458
459impl TryFrom<&Secp256k1Signature> for Signature {
460 type Error = k256::ecdsa::Error;
461
462 #[inline]
463 fn try_from(value: &Secp256k1Signature) -> Result<Self, Self::Error> {
464 Self::from_bytes((&value.0).into())
465 }
466}
467
468#[cfg_attr(
471 feature = "serde",
472 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
473 cfg_attr(
474 feature = "schemars-v0_8",
475 derive(::schemars::JsonSchema),
476 schemars(example = "Self::example"),
477 )
478)]
479#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
480#[cfg_attr(
481 feature = "borsh",
482 derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
483 cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
484)]
485#[derive(
486 Debug,
487 Clone,
488 Copy,
489 PartialEq,
490 Eq,
491 PartialOrd,
492 Ord,
493 Hash,
494 derive_more::AsRef,
495 derive_more::From,
496 derive_more::Into,
497)]
498#[as_ref([u8], [u8; 65])]
499#[into(owned, ref)]
500#[repr(transparent)]
501pub struct Secp256k1RecoverableSignature(
502 #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 65],
504);
505
506impl Secp256k1RecoverableSignature {
507 #[cfg(feature = "schemars-v0_8")]
508 const fn example() -> Self {
509 Self(hex_literal::hex!(
510 "7800a70d05cde2c49ed546a6ce887ce6027c2c268c0285f6efef0cdfc4366b23643790f67a86468ee8301ed12cfffcb07c6530f90a9327ec057800fabd332e4701"
511 ))
512 }
513}
514
515impl From<Secp256k1RecoverableSignature> for Secp256k1Signature {
516 #[inline]
517 fn from(sig: Secp256k1RecoverableSignature) -> Self {
518 let [sig @ .., _v] = sig.0;
519 Self(sig)
520 }
521}
522
523impl From<(Signature, RecoveryId)> for Secp256k1RecoverableSignature {
524 #[inline]
525 fn from((signature, recovery_id): (Signature, RecoveryId)) -> Self {
526 (&signature, recovery_id).into()
527 }
528}
529
530impl From<(&Signature, RecoveryId)> for Secp256k1RecoverableSignature {
531 #[inline]
532 fn from((signature, recovery_id): (&Signature, RecoveryId)) -> Self {
533 let mut buf = [0u8; 65];
534 buf[..64].copy_from_slice(&signature.to_bytes());
535 buf[64] = recovery_id.to_byte();
536 Self(buf)
537 }
538}
539
540impl TryFrom<Secp256k1RecoverableSignature> for (Signature, RecoveryId) {
541 type Error = k256::ecdsa::Error;
542
543 #[inline]
544 fn try_from(value: Secp256k1RecoverableSignature) -> Result<Self, Self::Error> {
545 (&value).try_into()
546 }
547}
548
549impl TryFrom<Secp256k1RecoverableSignature> for Signature {
550 type Error = k256::ecdsa::Error;
551
552 #[inline]
553 fn try_from(value: Secp256k1RecoverableSignature) -> Result<Self, Self::Error> {
554 (&value).try_into()
555 }
556}
557
558impl TryFrom<&Secp256k1RecoverableSignature> for (Signature, RecoveryId) {
559 type Error = k256::ecdsa::Error;
560
561 #[inline]
562 fn try_from(value: &Secp256k1RecoverableSignature) -> Result<Self, Self::Error> {
563 let [ref signature @ .., recovery_id] = value.0;
564 Ok((
565 Signature::from_bytes(signature.into())?,
566 RecoveryId::from_byte(recovery_id).ok_or_else(k256::ecdsa::Error::new)?,
567 ))
568 }
569}
570
571impl TryFrom<&Secp256k1RecoverableSignature> for Signature {
572 type Error = k256::ecdsa::Error;
573
574 #[inline]
575 fn try_from(value: &Secp256k1RecoverableSignature) -> Result<Self, Self::Error> {
576 <(Self, RecoveryId)>::try_from(value).map(|t| t.0)
577 }
578}
579
580#[cfg(feature = "fmt")]
581const _: () = {
582 use core::{
583 fmt::{self, Display},
584 str::FromStr,
585 };
586
587 use crate::fmt::{ParseCurveError, TypedCurve};
588
589 impl TypedCurve for Secp256k1 {
590 const CURVE_TYPE: &str = "secp256k1";
591 }
592
593 impl Display for Secp256k1CompressedPublicKey {
594 #[inline]
595 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596 f.write_str(&Secp256k1::to_base58(self.0))
597 }
598 }
599
600 impl FromStr for Secp256k1CompressedPublicKey {
601 type Err = ParseCurveError;
602
603 #[inline]
604 fn from_str(s: &str) -> Result<Self, Self::Err> {
605 Secp256k1::parse_base58(s).map(Self)
606 }
607 }
608
609 impl Display for Secp256k1UncompressedPublicKey {
610 #[inline]
611 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612 f.write_str(&Secp256k1::to_base58(self.0))
613 }
614 }
615
616 impl FromStr for Secp256k1UncompressedPublicKey {
617 type Err = ParseCurveError;
618
619 #[inline]
620 fn from_str(s: &str) -> Result<Self, Self::Err> {
621 Secp256k1::parse_base58(s).map(Self)
622 }
623 }
624
625 impl Display for Secp256k1Signature {
626 #[inline]
627 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
628 f.write_str(&Secp256k1::to_base58(self.0))
629 }
630 }
631
632 impl FromStr for Secp256k1Signature {
633 type Err = ParseCurveError;
634
635 #[inline]
636 fn from_str(s: &str) -> Result<Self, Self::Err> {
637 Secp256k1::parse_base58(s).map(Self)
638 }
639 }
640
641 impl Display for Secp256k1RecoverableSignature {
642 #[inline]
643 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644 f.write_str(&Secp256k1::to_base58(self.0))
645 }
646 }
647
648 impl FromStr for Secp256k1RecoverableSignature {
649 type Err = ParseCurveError;
650
651 #[inline]
652 fn from_str(s: &str) -> Result<Self, Self::Err> {
653 Secp256k1::parse_base58(s).map(Self)
654 }
655 }
656};
657
658#[cfg(test)]
659mod tests {
660 use hex_literal::hex;
661 use k256::{ecdsa::SigningKey, elliptic_curve::Generate};
662 use rstest::rstest;
663
664 use crate::tests::test_sign_recover;
665
666 use super::*;
667
668 #[rstest]
669 #[case(
670 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
671 hex!("aa05af77f274774b8bdc7b61d98bc40da523dc2821fdea555f4d6aa413199bcc"),
672 hex!("7800a70d05cde2c49ed546a6ce887ce6027c2c268c0285f6efef0cdfc4366b23643790f67a86468ee8301ed12cfffcb07c6530f90a9327ec057800fabd332e4701"),
673 )]
674 #[case(
675 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
676 hex!("1632c0ebba467e157675403ba3ba280b836e1801b5678d878dfc90bfc403d6e1"),
677 hex!("eea1651a60600ec4d9c45e8ae81da1a78377f789f0ac2019de66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c01"),
678 )]
679 fn verify_ok(
680 #[case] public_key: impl Into<Secp256k1UncompressedPublicKey>,
681 #[case] prehash: [u8; 32],
682 #[case] signature: impl Into<Secp256k1RecoverableSignature>,
683 ) {
684 assert!(
685 Secp256k1::verify(
686 &public_key.into().try_into().unwrap(),
687 &prehash,
688 &signature.into().try_into().unwrap(),
689 ),
690 "signature is invalid",
691 );
692 }
693
694 #[rstest]
695 #[case(
696 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
697 hex!("1632c0ebba467e157675403ba3ba280b836e1801b5678d878dfc90bfc403d6e1"),
698 hex!("7800a70d05cde2c49ed546a6ce887ce6027c2c268c0285f6efef0cdfc4366b23643790f67a86468ee8301ed12cfffcb07c6530f90a9327ec057800fabd332e4701"),
699 )]
700 #[case(
701 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
702 hex!("aa05af77f274774b8bdc7b61d98bc40da523dc2821fdea555f4d6aa413199bcc"),
703 hex!("eea1651a60600ec4d9c45e8ae81da1a78377f789f0ac2019de66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c01"),
704 )]
705 fn verify_fail(
706 #[case] public_key: impl Into<Secp256k1UncompressedPublicKey>,
707 #[case] prehash: [u8; 32],
708 #[case] signature: impl Into<Secp256k1RecoverableSignature>,
709 ) {
710 assert!(
711 !Secp256k1::verify(
712 &public_key.into().try_into().unwrap(),
713 &prehash,
714 &signature.into().try_into().unwrap(),
715 ),
716 "invalid signature passed verification",
717 );
718 }
719
720 #[rstest]
721 #[case(
722 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
723 hex!("aa05af77f274774b8bdc7b61d98bc40da523dc2821fdea555f4d6aa413199bcc"),
724 hex!("7800a70d05cde2c49ed546a6ce887ce6027c2c268c0285f6efef0cdfc4366b23643790f67a86468ee8301ed12cfffcb07c6530f90a9327ec057800fabd332e4701"),
725 )]
726 #[case(
727 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
728 hex!("1632c0ebba467e157675403ba3ba280b836e1801b5678d878dfc90bfc403d6e1"),
729 hex!("eea1651a60600ec4d9c45e8ae81da1a78377f789f0ac2019de66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c01"),
730 )]
731 fn recover_ok(
732 #[case] public_key: impl Into<Secp256k1UncompressedPublicKey>,
733 #[case] prehash: [u8; 32],
734 #[case] signature: impl Into<Secp256k1RecoverableSignature>,
735 ) {
736 let (signature, recovery_id) = signature.into().try_into().unwrap();
737
738 assert_eq!(
739 Secp256k1::recover(&prehash, &signature, recovery_id),
740 Some(public_key.into().try_into().unwrap()),
741 "invalid recovered public key",
742 );
743 }
744
745 #[rstest]
746 #[case(
747 hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
748 )]
749 #[case(
750 hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"),
751 )]
752 #[tokio::test]
753 async fn sign_recover(#[case] prehash: [u8; 32]) {
754 test_sign_recover(SigningKey::generate(), prehash).await;
755 }
756}