1pub use p256;
4use p256::{
5 Sec1Point,
6 ecdsa::{Signature, VerifyingKey},
7 elliptic_curve::scalar::IsHigh,
8};
9
10use crate::Curve;
11
12pub struct P256;
14
15impl Curve for P256 {
16 type PublicKey = VerifyingKey;
17
18 type Signature = Signature;
19
20 #[inline]
23 fn verify(public_key: &Self::PublicKey, prehash: &[u8], signature: &Self::Signature) -> bool {
24 let Ok(prehash) = <&[u8; 32]>::try_from(prehash) else {
26 return false;
27 };
28
29 if signature.s().is_high().into() {
30 return false;
32 }
33
34 cfg_select! {
35 near => {
36 ::near_sdk::env::p256_verify(
37 signature.to_bytes().as_ref(),
38 prehash,
39 P256CompressedPublicKey::from(public_key).as_ref(),
40 )
41 }
42 _ => {{
43 use p256::ecdsa::signature::hazmat::PrehashVerifier;
44
45 public_key.verify_prehash(prehash, signature).is_ok()
46 }}
47 }
48 }
49}
50
51#[cfg(feature = "signing")]
52const _: () = {
53 use futures_util::future;
54 use p256::ecdsa::{self, SigningKey};
55
56 use crate::Signer;
57
58 impl Signer<P256> for SigningKey {
59 type Error = ecdsa::Error;
60
61 #[inline]
62 fn public_key(&self) -> <P256 as Curve>::PublicKey {
63 *self.verifying_key()
64 }
65
66 #[inline]
71 fn sign(
72 &self,
73 prehash: &[u8],
74 ) -> impl Future<Output = Result<<P256 as Curve>::Signature, Self::Error>> + Send {
75 let Ok(prehash) = <&[u8; 32]>::try_from(prehash) else {
76 return future::err(ecdsa::Error::from_source("prehash must be 32 bytes long"));
77 };
78
79 let signature = self.sign_prehash_recoverable(prehash).0;
80 future::ok(signature.normalize_s())
84 }
85 }
86};
87
88#[cfg_attr(
90 feature = "serde",
91 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
92 cfg_attr(
93 feature = "schemars-v0_8",
94 derive(::schemars::JsonSchema),
95 schemars(example = "Self::example"),
96 )
97)]
98#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
99#[cfg_attr(
100 feature = "borsh",
101 derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
102 cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
103)]
104#[derive(
105 Debug,
106 Clone,
107 Copy,
108 PartialEq,
109 Eq,
110 PartialOrd,
111 Ord,
112 Hash,
113 derive_more::AsRef,
114 derive_more::From,
115 derive_more::Into,
116)]
117#[as_ref([u8], [u8; 64])]
118#[into(owned, ref)]
119#[repr(transparent)]
120pub struct P256UncompressedPublicKey(
121 #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 64],
123);
124
125impl P256UncompressedPublicKey {
126 #[inline]
145 pub fn compress(&self) -> Result<P256CompressedPublicKey, p256::ecdsa::Error> {
146 VerifyingKey::try_from(self).map(Into::into)
147 }
148
149 #[cfg(feature = "schemars-v0_8")]
150 const fn example() -> Self {
151 Self(hex_literal::hex!(
152 "b87da10683d04e6ec4e2f1775556a63cbb01be843058eb737fe02f9e22663093cf61b416dfb33ba2e32e9c6f3a09d84fa90d0ab9709f4a298d52e0799c8217f5"
153 ))
154 }
155}
156
157impl From<VerifyingKey> for P256UncompressedPublicKey {
158 #[inline]
159 fn from(value: VerifyingKey) -> Self {
160 (&value).into()
161 }
162}
163
164impl From<&VerifyingKey> for P256UncompressedPublicKey {
165 #[inline]
166 fn from(value: &VerifyingKey) -> Self {
167 Self(
168 value
169 .to_sec1_point(false) .as_bytes()[1..] .try_into()
172 .unwrap_or_else(|_| unreachable!()),
173 )
174 }
175}
176
177impl TryFrom<P256UncompressedPublicKey> for VerifyingKey {
178 type Error = p256::ecdsa::Error;
179
180 #[inline]
181 fn try_from(value: P256UncompressedPublicKey) -> Result<Self, Self::Error> {
182 (&value).try_into()
183 }
184}
185
186impl TryFrom<&P256UncompressedPublicKey> for VerifyingKey {
187 type Error = p256::ecdsa::Error;
188
189 #[inline]
190 fn try_from(value: &P256UncompressedPublicKey) -> Result<Self, Self::Error> {
191 Self::from_sec1_point(&Sec1Point::from_untagged_bytes((&value.0).into()))
192 }
193}
194
195#[cfg_attr(
197 feature = "serde",
198 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
199 cfg_attr(
200 feature = "schemars-v0_8",
201 derive(::schemars::JsonSchema),
202 schemars(example = "Self::example"),
203 )
204)]
205#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
206#[cfg_attr(
207 feature = "borsh",
208 derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
209 cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
210)]
211#[derive(
212 Debug,
213 Clone,
214 Copy,
215 PartialEq,
216 Eq,
217 PartialOrd,
218 Ord,
219 Hash,
220 derive_more::AsRef,
221 derive_more::From,
222 derive_more::Into,
223)]
224#[as_ref([u8], [u8; 33])]
225#[into(owned, ref)]
226#[repr(transparent)]
227pub struct P256CompressedPublicKey(
228 #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 33],
230);
231
232impl P256CompressedPublicKey {
233 #[cfg(feature = "schemars-v0_8")]
234 const fn example() -> Self {
235 Self(hex_literal::hex!(
236 "03b87da10683d04e6ec4e2f1775556a63cbb01be843058eb737fe02f9e22663093"
237 ))
238 }
239}
240
241impl From<VerifyingKey> for P256CompressedPublicKey {
242 #[inline]
243 fn from(value: VerifyingKey) -> Self {
244 (&value).into()
245 }
246}
247
248impl From<&VerifyingKey> for P256CompressedPublicKey {
249 #[inline]
250 fn from(value: &VerifyingKey) -> Self {
251 Self(
252 value
253 .to_sec1_point(true) .as_bytes()
255 .try_into()
256 .unwrap_or_else(|_| unreachable!()),
258 )
259 }
260}
261
262impl TryFrom<P256CompressedPublicKey> for VerifyingKey {
263 type Error = p256::ecdsa::Error;
264
265 #[inline]
266 fn try_from(value: P256CompressedPublicKey) -> Result<Self, Self::Error> {
267 (&value).try_into()
268 }
269}
270
271impl TryFrom<&P256CompressedPublicKey> for VerifyingKey {
272 type Error = p256::ecdsa::Error;
273
274 #[inline]
275 fn try_from(value: &P256CompressedPublicKey) -> Result<Self, Self::Error> {
276 Self::from_sec1_bytes(&value.0)
277 }
278}
279
280impl TryFrom<P256UncompressedPublicKey> for P256CompressedPublicKey {
281 type Error = p256::ecdsa::Error;
282
283 #[inline]
285 fn try_from(value: P256UncompressedPublicKey) -> Result<Self, Self::Error> {
286 (&value).try_into()
287 }
288}
289
290impl TryFrom<&P256UncompressedPublicKey> for P256CompressedPublicKey {
291 type Error = p256::ecdsa::Error;
292
293 #[inline]
295 fn try_from(value: &P256UncompressedPublicKey) -> Result<Self, Self::Error> {
296 value.compress()
297 }
298}
299
300#[cfg_attr(
302 feature = "serde",
303 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
304 cfg_attr(
305 feature = "schemars-v0_8",
306 derive(::schemars::JsonSchema),
307 schemars(example = "Self::example"),
308 )
309)]
310#[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))]
311#[cfg_attr(
312 feature = "borsh",
313 derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
314 cfg_attr(feature = "borsh-schema", derive(::borsh::BorshSchema))
315)]
316#[derive(
317 Debug,
318 Clone,
319 Copy,
320 PartialEq,
321 Eq,
322 PartialOrd,
323 Ord,
324 Hash,
325 derive_more::AsRef,
326 derive_more::From,
327 derive_more::Into,
328)]
329#[as_ref([u8], [u8; 64])]
330#[into(owned, ref)]
331#[repr(transparent)]
332pub struct P256Signature(
333 #[cfg_attr(feature = "schemars-v0_8", schemars(with = "String"))] pub [u8; 64],
335);
336
337impl P256Signature {
338 #[cfg(feature = "schemars-v0_8")]
339 const fn example() -> Self {
340 Self(hex_literal::hex!(
341 "002527c17a17d709ab62ffab033e0a26d901f5bee6686a0d605032828e490a7c47a9f4161e6a17688ae42a66adb67c9c22c86153afb08103b1e9eccd5314c271"
342 ))
343 }
344}
345
346impl From<Signature> for P256Signature {
347 #[inline]
348 fn from(value: Signature) -> Self {
349 (&value).into()
350 }
351}
352
353impl From<&Signature> for P256Signature {
354 #[inline]
355 fn from(value: &Signature) -> Self {
356 Self(value.to_bytes().into())
357 }
358}
359
360impl TryFrom<P256Signature> for Signature {
361 type Error = p256::ecdsa::Error;
362
363 #[inline]
364 fn try_from(value: P256Signature) -> Result<Self, Self::Error> {
365 (&value).try_into()
366 }
367}
368
369impl TryFrom<&P256Signature> for Signature {
370 type Error = p256::ecdsa::Error;
371
372 #[inline]
373 fn try_from(value: &P256Signature) -> Result<Self, Self::Error> {
374 Self::from_bytes((&value.0).into())
375 }
376}
377
378#[cfg(feature = "fmt")]
379const _: () = {
380 use core::{
381 fmt::{self, Display},
382 str::FromStr,
383 };
384
385 use crate::fmt::{ParseCurveError, TypedCurve};
386
387 impl TypedCurve for P256 {
388 const CURVE_TYPE: &str = "p256";
389 }
390
391 impl Display for P256UncompressedPublicKey {
392 #[inline]
393 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394 f.write_str(&P256::to_base58(self.0))
395 }
396 }
397
398 impl FromStr for P256UncompressedPublicKey {
399 type Err = ParseCurveError;
400
401 #[inline]
402 fn from_str(s: &str) -> Result<Self, Self::Err> {
403 P256::parse_base58(s).map(Self)
404 }
405 }
406
407 impl Display for P256CompressedPublicKey {
408 #[inline]
409 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
410 f.write_str(&P256::to_base58(self.0))
411 }
412 }
413
414 impl FromStr for P256CompressedPublicKey {
415 type Err = ParseCurveError;
416
417 #[inline]
418 fn from_str(s: &str) -> Result<Self, Self::Err> {
419 P256::parse_base58(s).map(Self)
420 }
421 }
422
423 impl Display for P256Signature {
424 #[inline]
425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426 f.write_str(&P256::to_base58(self.0))
427 }
428 }
429
430 impl FromStr for P256Signature {
431 type Err = ParseCurveError;
432
433 #[inline]
434 fn from_str(s: &str) -> Result<Self, Self::Err> {
435 P256::parse_base58(s).map(Self)
436 }
437 }
438};
439
440#[cfg(test)]
441mod tests {
442 use hex_literal::hex;
443 use p256::{ecdsa::SigningKey, elliptic_curve::Generate};
444 use rstest::rstest;
445
446 use crate::tests::test_sign_verify;
447
448 use super::*;
449
450 #[rstest]
451 #[case(
452 hex!("03b87da10683d04e6ec4e2f1775556a63cbb01be843058eb737fe02f9e22663093"),
453 hex!("0fc6357f688865a5a5ce1ac484f8ade578325ccda012091a533924404890e794"),
454 hex!("002527c17a17d709ab62ffab033e0a26d901f5bee6686a0d605032828e490a7c47a9f4161e6a17688ae42a66adb67c9c22c86153afb08103b1e9eccd5314c271"),
455 )]
456 #[case(
457 hex!("03f616f10f0841ff81b0caa52859ee168d03b657c6adef468761289fe75b8e291c"),
458 hex!("b64fd2202ddf8ef361dcbdf224fe6c2a06bd1fc67f40a89cb49a92d2071ffecc"),
459 hex!("6bb2de380eefd199ee44364902f4b89679b8df04024e24047dc0ec1c61c11b2d527495fed4a6903ebe086e831bd000bf683c2a36f0454eaccaecf109678fd33e"),
460 )]
461 fn verify_ok(
462 #[case] public_key: impl Into<P256CompressedPublicKey>,
463 #[case] prehash: [u8; 32],
464 #[case] signature: impl Into<P256Signature>,
465 ) {
466 assert!(
467 P256::verify(
468 &public_key.into().try_into().unwrap(),
469 &prehash,
470 &signature.into().try_into().unwrap(),
471 ),
472 "signature is invalid",
473 );
474 }
475
476 #[rstest]
477 #[case(
478 hex!("03b87da10683d04e6ec4e2f1775556a63cbb01be843058eb737fe02f9e22663093"),
479 hex!("0fc6357f688865a5a5ce1ac484f8ade578325ccda012091a533924404890e794"),
480 hex!("6bb2de380eefd199ee44364902f4b89679b8df04024e24047dc0ec1c61c11b2d527495fed4a6903ebe086e831bd000bf683c2a36f0454eaccaecf109678fd33e"),
481 )]
482 #[case(
483 hex!("03f616f10f0841ff81b0caa52859ee168d03b657c6adef468761289fe75b8e291c"),
484 hex!("b64fd2202ddf8ef361dcbdf224fe6c2a06bd1fc67f40a89cb49a92d2071ffecc"),
485 hex!("002527c17a17d709ab62ffab033e0a26d901f5bee6686a0d605032828e490a7c47a9f4161e6a17688ae42a66adb67c9c22c86153afb08103b1e9eccd5314c271"),
486 )]
487 #[case(
488 hex!("03b87da10683d04e6ec4e2f1775556a63cbb01be843058eb737fe02f9e22663093"),
489 hex!("b64fd2202ddf8ef361dcbdf224fe6c2a06bd1fc67f40a89cb49a92d2071ffecc"),
490 hex!("6bb2de380eefd199ee44364902f4b89679b8df04024e24047dc0ec1c61c11b2d527495fed4a6903ebe086e831bd000bf683c2a36f0454eaccaecf109678fd33e"),
491 )]
492 fn verify_fail(
493 #[case] public_key: impl Into<P256CompressedPublicKey>,
494 #[case] prehash: [u8; 32],
495 #[case] signature: impl Into<P256Signature>,
496 ) {
497 assert!(
498 !P256::verify(
499 &public_key.into().try_into().unwrap(),
500 &prehash,
501 &signature.into().try_into().unwrap(),
502 ),
503 "invalid signature passed verification",
504 );
505 }
506
507 #[rstest]
508 #[case(
509 hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
510 )]
511 #[case(
512 hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"),
513 )]
514 #[tokio::test]
515 async fn sign_verify(#[case] prehash: [u8; 32]) {
516 test_sign_verify(SigningKey::generate(), prehash).await;
517 }
518}