defuse_crypto/curve/
p256.rs

1use crate::{CryptoHash, Curve, CurveType, TypedCurve, VerifiableCurve};
2use generic_array::GenericArray;
3use p256::{
4    EncodedPoint,
5    ecdsa::{Signature, VerifyingKey, signature::hazmat::PrehashVerifier},
6    elliptic_curve::scalar::IsHigh,
7};
8
9pub struct P256;
10
11impl Curve for P256 {
12    /// Compressed SEC1 encoded coordinates.
13    type PublicKey = [u8; 33];
14
15    /// Concatenated `r || s` coordinates
16    type Signature = [u8; 64];
17
18    // Output of cryptographic hash function
19    type Message = CryptoHash;
20
21    type VerifyingKey = Self::PublicKey;
22}
23
24impl VerifiableCurve for P256 {
25    fn verify(
26        signature: &Self::Signature,
27        prehashed: &Self::Message,
28        public_key: &Self::VerifyingKey,
29    ) -> Option<Self::PublicKey> {
30        // convert signature
31        let signature =
32            Signature::from_bytes(GenericArray::from_slice(signature).as_0_14()).ok()?;
33
34        if signature.s().is_high().into() {
35            // guard against signature malleability
36            return None;
37        }
38
39        // convert verifying key
40        let verifying_key = VerifyingKey::from_sec1_bytes(public_key).ok()?;
41
42        // verify signature over prehashed
43        verifying_key
44            .verify_prehash(prehashed, &signature)
45            .is_ok()
46            .then_some(public_key)
47            .copied()
48    }
49}
50
51impl TypedCurve for P256 {
52    const CURVE_TYPE: CurveType = CurveType::P256;
53}
54
55/// Compressed public key, i.e. `x` coordinate with leading SEC1 tag byte
56#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
57#[cfg_attr(
58    feature = "serde",
59    derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
60    cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
61)]
62#[cfg_attr(
63    feature = "borsh",
64    derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
65    cfg_attr(feature = "abi", derive(::borsh::BorshSchema))
66)]
67#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
68#[repr(transparent)]
69pub struct P256CompressedPublicKey(
70    // schemars ignores `with` at struct level for newtypes; must be on the field
71    #[cfg_attr(all(feature = "abi", feature = "serde"), schemars(with = "String"))]
72    pub  <P256 as Curve>::PublicKey,
73);
74
75#[cfg(feature = "parse")]
76const _: () = {
77    use crate::ParseCurveError;
78    use core::fmt::{self, Debug, Display};
79    use std::str::FromStr;
80
81    impl Debug for P256CompressedPublicKey {
82        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83            Display::fmt(self, f)
84        }
85    }
86
87    impl Display for P256CompressedPublicKey {
88        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89            f.write_str(&<P256 as TypedCurve>::to_base58(self.0))
90        }
91    }
92
93    impl FromStr for P256CompressedPublicKey {
94        type Err = ParseCurveError;
95
96        fn from_str(s: &str) -> Result<Self, Self::Err> {
97            P256::parse_base58(s).map(Self)
98        }
99    }
100};
101
102/// Concatenated `x || y` coordinates with no leading SEC1 tag byte
103#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
104#[cfg_attr(
105    feature = "borsh",
106    derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
107    cfg_attr(feature = "abi", derive(::borsh::BorshSchema))
108)]
109#[cfg_attr(
110    feature = "serde",
111    derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
112    cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
113)]
114#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
115#[repr(transparent)]
116pub struct P256UncompressedPublicKey(
117    // schemars ignores `with` at struct level for newtypes; must be on the field
118    #[cfg_attr(all(feature = "abi", feature = "serde"), schemars(with = "String"))] pub [u8; 64],
119);
120
121#[cfg(feature = "parse")]
122const _: () = {
123    use crate::ParseCurveError;
124    use core::fmt::{self, Debug, Display};
125    use std::str::FromStr;
126
127    impl Debug for P256UncompressedPublicKey {
128        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129            Display::fmt(self, f)
130        }
131    }
132
133    impl Display for P256UncompressedPublicKey {
134        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135            f.write_str(&<P256 as TypedCurve>::to_base58(self.0))
136        }
137    }
138
139    impl FromStr for P256UncompressedPublicKey {
140        type Err = ParseCurveError;
141
142        fn from_str(s: &str) -> Result<Self, Self::Err> {
143            P256::parse_base58(s).map(Self)
144        }
145    }
146};
147
148#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
149#[cfg_attr(
150    feature = "borsh",
151    derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
152    cfg_attr(feature = "abi", derive(::borsh::BorshSchema))
153)]
154#[cfg_attr(
155    feature = "serde",
156    derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
157    cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
158)]
159#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
160#[repr(transparent)]
161pub struct P256Signature(
162    // schemars ignores `with` at struct level for newtypes; must be on the field
163    #[cfg_attr(all(feature = "abi", feature = "serde"), schemars(with = "String"))]
164    pub  <P256 as Curve>::Signature,
165);
166
167#[cfg(feature = "parse")]
168const _: () = {
169    use crate::ParseCurveError;
170    use core::fmt::{self, Debug, Display};
171    use std::str::FromStr;
172
173    impl Debug for P256Signature {
174        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175            Display::fmt(self, f)
176        }
177    }
178
179    impl Display for P256Signature {
180        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181            f.write_str(&<P256 as TypedCurve>::to_base58(self.0))
182        }
183    }
184
185    impl FromStr for P256Signature {
186        type Err = ParseCurveError;
187
188        fn from_str(s: &str) -> Result<Self, Self::Err> {
189            P256::parse_base58(s).map(Self)
190        }
191    }
192};
193
194/// Converts from untagged uncompressed form (i.e. concatenated `x || y`
195/// coordinates with no leading SEC1 tag byte) into compressed form
196/// (i.e. `x` coordinate with leading SEC1 tag byte)
197pub fn compress_public_key(public_key: P256UncompressedPublicKey) -> P256CompressedPublicKey {
198    EncodedPoint::from_untagged_bytes(GenericArray::from_array(public_key.0).as_0_14())
199        .compress()
200        .as_bytes()
201        .try_into()
202        .map_or_else(|_| unreachable!(), P256CompressedPublicKey)
203}