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