1pub use k256;
2
3use k256::{
4 Sec1Point,
5 ecdsa::{RecoveryId, Signature, VerifyingKey},
6};
7
8use crate::{Curve, RecoverableCurve};
9
10pub struct Secp256k1;
12
13impl Curve for Secp256k1 {
14 type PublicKey = VerifyingKey;
15 type Signature = Signature;
16
17 #[inline]
20 fn verify(public_key: &VerifyingKey, prehash: &[u8], signature: &Self::Signature) -> bool {
21 let Ok(prehash) = <&[u8; 32]>::try_from(prehash) else {
23 return false;
24 };
25
26 cfg_select! {
27 near => {
28 for id in 0..=RecoveryId::MAX {
31 let recovery_id = RecoveryId::from_byte(id).unwrap_or_else(|| unreachable!());
32
33 if let Some(recovered) = Self::recover(prehash, signature, recovery_id)
34 && recovered == *public_key
35 {
36 return true;
37 }
38 }
39 false
41 }
42 _ => {{
43 use k256::{
44 ecdsa::signature::hazmat::PrehashVerifier,
45 elliptic_curve::scalar::IsHigh,
46 };
47
48 if signature.s().is_high().into() {
49 return false;
51 }
52
53 public_key.verify_prehash(prehash, signature).is_ok()
54 }}
55 }
56 }
57}
58
59impl RecoverableCurve for Secp256k1 {
60 type RecoveryId = RecoveryId;
61
62 #[inline]
63 fn recover(
64 prehash: &[u8],
65 signature: &Self::Signature,
66 recovery_id: Self::RecoveryId,
67 ) -> Option<Self::PublicKey> {
68 let prehash: &[u8; 32] = prehash.try_into().ok()?;
70
71 let public_key = {
72 cfg_select! {
73 near => {
74 let pk: [u8; 64] = ::near_sdk::env::ecrecover(
75 prehash,
76 &signature.to_bytes(),
77 recovery_id.to_byte(),
78 true,
81 )?;
82
83 Secp256k1UncompressedPublicKey(pk).try_into().ok()
84 }
85 _ => {
86 use k256::elliptic_curve::scalar::IsHigh;
87
88 if signature.s().is_high().into() {
89 return None;
91 }
92
93 VerifyingKey::recover_from_prehash(prehash, signature, recovery_id).ok()
94 }
95 }
96 }?;
97
98 Some(public_key)
99 }
100}
101
102#[cfg(feature = "signing")]
103const _: () = {
104 use k256::ecdsa::{Error, SigningKey};
105
106 use crate::{RecoverableSigner, Signer};
107
108 impl Signer<Secp256k1> for SigningKey {
109 type Error = Error;
110
111 #[inline]
112 fn public_key(&self) -> <Secp256k1 as Curve>::PublicKey {
113 *self.verifying_key()
114 }
115
116 async fn sign(
121 &self,
122 prehash: &[u8],
123 ) -> Result<<Secp256k1 as Curve>::Signature, Self::Error> {
124 RecoverableSigner::sign_recoverable(self, prehash)
125 .await
126 .map(|s| s.0)
127 }
128 }
129
130 impl RecoverableSigner<Secp256k1> for SigningKey {
131 async fn sign_recoverable(
136 &self,
137 prehash: &[u8],
138 ) -> Result<
139 (
140 <Secp256k1 as Curve>::Signature,
141 <Secp256k1 as RecoverableCurve>::RecoveryId,
142 ),
143 Self::Error,
144 > {
145 let prehash: &[u8; 32] = prehash
146 .try_into()
147 .map_err(|_| Error::from_source("prehash must be 32 bytes long"))?;
148
149 Ok(self.sign_prehash_recoverable(prehash))
152 }
153 }
154};
155
156#[cfg_attr(
158 feature = "serde",
159 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
160 cfg_attr(
161 feature = "schemars-v0_8",
162 derive(::schemars::JsonSchema),
163 schemars(example = "Self::example"),
164 )
165)]
166#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
167#[cfg_attr(
168 feature = "borsh",
169 derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
170 cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
171)]
172#[derive(
173 Debug,
174 Clone,
175 Copy,
176 PartialEq,
177 Eq,
178 PartialOrd,
179 Ord,
180 Hash,
181 derive_more::AsRef,
182 derive_more::From,
183 derive_more::Into,
184)]
185#[as_ref([u8], [u8; 64])]
186#[into(owned, ref)]
187#[repr(transparent)]
188pub struct Secp256k1UncompressedPublicKey(
189 #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 64],
191);
192
193impl Secp256k1UncompressedPublicKey {
194 #[cfg(feature = "schemars-v0_8")]
195 const fn example() -> Self {
196 Self(hex_literal::hex!(
197 "85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"
198 ))
199 }
200}
201
202impl From<VerifyingKey> for Secp256k1UncompressedPublicKey {
203 #[inline]
204 fn from(value: VerifyingKey) -> Self {
205 (&value).into()
206 }
207}
208
209impl From<&VerifyingKey> for Secp256k1UncompressedPublicKey {
210 #[inline]
211 fn from(value: &VerifyingKey) -> Self {
212 Self(
213 value
214 .to_sec1_point(false) .as_bytes()[1..] .try_into()
217 .unwrap_or_else(|_| unreachable!()),
218 )
219 }
220}
221
222impl TryFrom<Secp256k1UncompressedPublicKey> for VerifyingKey {
223 type Error = k256::ecdsa::Error;
224
225 #[inline]
226 fn try_from(value: Secp256k1UncompressedPublicKey) -> Result<Self, Self::Error> {
227 (&value).try_into()
228 }
229}
230
231impl TryFrom<&Secp256k1UncompressedPublicKey> for VerifyingKey {
232 type Error = k256::ecdsa::Error;
233
234 #[inline]
235 fn try_from(value: &Secp256k1UncompressedPublicKey) -> Result<Self, Self::Error> {
236 Self::from_sec1_point(&Sec1Point::from_untagged_bytes((&value.0).into()))
237 }
238}
239
240#[cfg_attr(
243 feature = "serde",
244 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
245 cfg_attr(
246 feature = "schemars-v0_8",
247 derive(::schemars::JsonSchema),
248 schemars(example = "Self::example"),
249 )
250)]
251#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
252#[cfg_attr(
253 feature = "borsh",
254 derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
255 cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
256)]
257#[derive(
258 Debug,
259 Clone,
260 Copy,
261 PartialEq,
262 Eq,
263 PartialOrd,
264 Ord,
265 Hash,
266 derive_more::AsRef,
267 derive_more::From,
268 derive_more::Into,
269)]
270#[as_ref([u8], [u8; 65])]
271#[into(owned, ref)]
272#[repr(transparent)]
273pub struct Secp256k1RecoverableSignature(
274 #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 65],
276);
277
278impl Secp256k1RecoverableSignature {
279 #[cfg(feature = "schemars-v0_8")]
280 const fn example() -> Self {
281 Self(hex_literal::hex!(
282 "7800a70d05cde2c49ed546a6ce887ce6027c2c268c0285f6efef0cdfc4366b23643790f67a86468ee8301ed12cfffcb07c6530f90a9327ec057800fabd332e4701"
283 ))
284 }
285}
286
287impl From<(Signature, RecoveryId)> for Secp256k1RecoverableSignature {
288 #[inline]
289 fn from((signature, recovery_id): (Signature, RecoveryId)) -> Self {
290 (&signature, recovery_id).into()
291 }
292}
293
294impl From<(&Signature, RecoveryId)> for Secp256k1RecoverableSignature {
295 #[inline]
296 fn from((signature, recovery_id): (&Signature, RecoveryId)) -> Self {
297 let mut buf = [0u8; 65];
298 buf[..64].copy_from_slice(&signature.to_bytes());
299 buf[64] = recovery_id.to_byte();
300 Self(buf)
301 }
302}
303
304impl TryFrom<Secp256k1RecoverableSignature> for (Signature, RecoveryId) {
305 type Error = k256::ecdsa::Error;
306
307 #[inline]
308 fn try_from(value: Secp256k1RecoverableSignature) -> Result<Self, Self::Error> {
309 (&value).try_into()
310 }
311}
312
313impl TryFrom<Secp256k1RecoverableSignature> for Signature {
314 type Error = k256::ecdsa::Error;
315
316 #[inline]
317 fn try_from(value: Secp256k1RecoverableSignature) -> Result<Self, Self::Error> {
318 (&value).try_into()
319 }
320}
321
322impl TryFrom<&Secp256k1RecoverableSignature> for (Signature, RecoveryId) {
323 type Error = k256::ecdsa::Error;
324
325 #[inline]
326 fn try_from(value: &Secp256k1RecoverableSignature) -> Result<Self, Self::Error> {
327 let [ref signature @ .., recovery_id] = value.0;
328 Ok((
329 Signature::from_bytes(signature.into())?,
330 RecoveryId::from_byte(recovery_id).ok_or_else(k256::ecdsa::Error::new)?,
331 ))
332 }
333}
334
335impl TryFrom<&Secp256k1RecoverableSignature> for Signature {
336 type Error = k256::ecdsa::Error;
337
338 #[inline]
339 fn try_from(value: &Secp256k1RecoverableSignature) -> Result<Self, Self::Error> {
340 <(Self, RecoveryId)>::try_from(value).map(|t| t.0)
341 }
342}
343
344#[cfg(feature = "fmt")]
345const _: () = {
346 use core::{
347 fmt::{self, Display},
348 str::FromStr,
349 };
350
351 use crate::fmt::{ParseCurveError, TypedCurve};
352
353 impl TypedCurve for Secp256k1 {
354 const CURVE_TYPE: &str = "secp256k1";
355 }
356
357 impl Display for Secp256k1UncompressedPublicKey {
358 #[inline]
359 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360 f.write_str(&Secp256k1::to_base58(self.0))
361 }
362 }
363
364 impl FromStr for Secp256k1UncompressedPublicKey {
365 type Err = ParseCurveError;
366
367 #[inline]
368 fn from_str(s: &str) -> Result<Self, Self::Err> {
369 Secp256k1::parse_base58(s).map(Self)
370 }
371 }
372
373 impl Display for Secp256k1RecoverableSignature {
374 #[inline]
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 f.write_str(&Secp256k1::to_base58(self.0))
377 }
378 }
379
380 impl FromStr for Secp256k1RecoverableSignature {
381 type Err = ParseCurveError;
382
383 #[inline]
384 fn from_str(s: &str) -> Result<Self, Self::Err> {
385 Secp256k1::parse_base58(s).map(Self)
386 }
387 }
388};
389
390#[cfg(test)]
391mod tests {
392 use hex_literal::hex;
393 use k256::{ecdsa::SigningKey, elliptic_curve::Generate};
394 use rstest::rstest;
395
396 use crate::tests::test_sign_recover;
397
398 use super::*;
399
400 #[rstest]
401 #[case(
402 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
403 hex!("aa05af77f274774b8bdc7b61d98bc40da523dc2821fdea555f4d6aa413199bcc"),
404 hex!("7800a70d05cde2c49ed546a6ce887ce6027c2c268c0285f6efef0cdfc4366b23643790f67a86468ee8301ed12cfffcb07c6530f90a9327ec057800fabd332e4701"),
405 )]
406 #[case(
407 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
408 hex!("1632c0ebba467e157675403ba3ba280b836e1801b5678d878dfc90bfc403d6e1"),
409 hex!("eea1651a60600ec4d9c45e8ae81da1a78377f789f0ac2019de66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c01"),
410 )]
411 fn verify_ok(
412 #[case] public_key: impl Into<Secp256k1UncompressedPublicKey>,
413 #[case] prehash: [u8; 32],
414 #[case] signature: impl Into<Secp256k1RecoverableSignature>,
415 ) {
416 assert!(
417 Secp256k1::verify(
418 &public_key.into().try_into().unwrap(),
419 &prehash,
420 &signature.into().try_into().unwrap(),
421 ),
422 "signature is invalid",
423 );
424 }
425
426 #[rstest]
427 #[case(
428 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
429 hex!("1632c0ebba467e157675403ba3ba280b836e1801b5678d878dfc90bfc403d6e1"),
430 hex!("7800a70d05cde2c49ed546a6ce887ce6027c2c268c0285f6efef0cdfc4366b23643790f67a86468ee8301ed12cfffcb07c6530f90a9327ec057800fabd332e4701"),
431 )]
432 #[case(
433 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
434 hex!("aa05af77f274774b8bdc7b61d98bc40da523dc2821fdea555f4d6aa413199bcc"),
435 hex!("eea1651a60600ec4d9c45e8ae81da1a78377f789f0ac2019de66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c01"),
436 )]
437 fn verify_fail(
438 #[case] public_key: impl Into<Secp256k1UncompressedPublicKey>,
439 #[case] prehash: [u8; 32],
440 #[case] signature: impl Into<Secp256k1RecoverableSignature>,
441 ) {
442 assert!(
443 !Secp256k1::verify(
444 &public_key.into().try_into().unwrap(),
445 &prehash,
446 &signature.into().try_into().unwrap(),
447 ),
448 "invalid signature passed verification",
449 );
450 }
451
452 #[rstest]
453 #[case(
454 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
455 hex!("aa05af77f274774b8bdc7b61d98bc40da523dc2821fdea555f4d6aa413199bcc"),
456 hex!("7800a70d05cde2c49ed546a6ce887ce6027c2c268c0285f6efef0cdfc4366b23643790f67a86468ee8301ed12cfffcb07c6530f90a9327ec057800fabd332e4701"),
457 )]
458 #[case(
459 hex!("85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"),
460 hex!("1632c0ebba467e157675403ba3ba280b836e1801b5678d878dfc90bfc403d6e1"),
461 hex!("eea1651a60600ec4d9c45e8ae81da1a78377f789f0ac2019de66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c01"),
462 )]
463 fn recover_ok(
464 #[case] public_key: impl Into<Secp256k1UncompressedPublicKey>,
465 #[case] prehash: [u8; 32],
466 #[case] signature: impl Into<Secp256k1RecoverableSignature>,
467 ) {
468 let (signature, recovery_id) = signature.into().try_into().unwrap();
469
470 assert_eq!(
471 Secp256k1::recover(&prehash, &signature, recovery_id),
472 Some(public_key.into().try_into().unwrap()),
473 "invalid recovered public key",
474 );
475 }
476
477 #[rstest]
478 #[case(
479 hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
480 )]
481 #[case(
482 hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"),
483 )]
484 #[tokio::test]
485 async fn sign_recover(#[case] prehash: [u8; 32]) {
486 test_sign_recover(SigningKey::generate(), prehash).await;
487 }
488}