Skip to main content

defuse_crypto/curve/
p256.rs

1use crate::{CryptoHash, Curve, 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
51/// Compressed public key, i.e. `x` coordinate with leading SEC1 tag byte
52#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
53#[cfg_attr(
54    feature = "serde",
55    derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
56    cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
57)]
58#[cfg_attr(
59    feature = "borsh",
60    derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
61    cfg_attr(feature = "abi", derive(::borsh::BorshSchema))
62)]
63#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
64#[repr(transparent)]
65pub struct P256CompressedPublicKey(
66    // schemars ignores `with` at struct level for newtypes; must be on the field
67    #[cfg_attr(all(feature = "abi", feature = "serde"), schemars(with = "String"))]
68    pub  <P256 as Curve>::PublicKey,
69);
70
71/// Concatenated `x || y` coordinates with no leading SEC1 tag byte
72#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
73#[cfg_attr(
74    feature = "borsh",
75    derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
76    cfg_attr(feature = "abi", derive(::borsh::BorshSchema))
77)]
78#[cfg_attr(
79    feature = "serde",
80    derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
81    cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
82)]
83#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
84#[repr(transparent)]
85pub struct P256UncompressedPublicKey(
86    // schemars ignores `with` at struct level for newtypes; must be on the field
87    #[cfg_attr(all(feature = "abi", feature = "serde"), schemars(with = "String"))] pub [u8; 64],
88);
89
90#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
91#[cfg_attr(
92    feature = "borsh",
93    derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
94    cfg_attr(feature = "abi", derive(::borsh::BorshSchema))
95)]
96#[cfg_attr(
97    feature = "serde",
98    derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr),
99    cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
100)]
101#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
102#[repr(transparent)]
103pub struct P256Signature(
104    // schemars ignores `with` at struct level for newtypes; must be on the field
105    #[cfg_attr(all(feature = "abi", feature = "serde"), schemars(with = "String"))]
106    pub  <P256 as Curve>::Signature,
107);
108
109#[cfg(feature = "parse")]
110const _: () = {
111    use crate::{CurveType, ParseCurveError, TypedCurve};
112    use core::fmt::{self, Debug, Display};
113    use std::str::FromStr;
114
115    impl TypedCurve for P256 {
116        const CURVE_TYPE: CurveType = CurveType::P256;
117    }
118
119    impl Debug for P256CompressedPublicKey {
120        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121            Display::fmt(self, f)
122        }
123    }
124
125    impl Display for P256CompressedPublicKey {
126        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127            f.write_str(&<P256 as TypedCurve>::to_base58(self.0))
128        }
129    }
130
131    impl FromStr for P256CompressedPublicKey {
132        type Err = ParseCurveError;
133
134        fn from_str(s: &str) -> Result<Self, Self::Err> {
135            P256::parse_base58(s).map(Self)
136        }
137    }
138
139    impl Debug for P256UncompressedPublicKey {
140        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141            Display::fmt(self, f)
142        }
143    }
144
145    impl Display for P256UncompressedPublicKey {
146        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147            f.write_str(&<P256 as TypedCurve>::to_base58(self.0))
148        }
149    }
150
151    impl FromStr for P256UncompressedPublicKey {
152        type Err = ParseCurveError;
153
154        fn from_str(s: &str) -> Result<Self, Self::Err> {
155            P256::parse_base58(s).map(Self)
156        }
157    }
158
159    impl Debug for P256Signature {
160        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161            Display::fmt(self, f)
162        }
163    }
164
165    impl Display for P256Signature {
166        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167            f.write_str(&<P256 as TypedCurve>::to_base58(self.0))
168        }
169    }
170
171    impl FromStr for P256Signature {
172        type Err = ParseCurveError;
173
174        fn from_str(s: &str) -> Result<Self, Self::Err> {
175            P256::parse_base58(s).map(Self)
176        }
177    }
178};
179
180/// Converts from untagged uncompressed form (i.e. concatenated `x || y`
181/// coordinates with no leading SEC1 tag byte) into compressed form
182/// (i.e. `x` coordinate with leading SEC1 tag byte)
183pub fn compress_public_key(public_key: P256UncompressedPublicKey) -> P256CompressedPublicKey {
184    EncodedPoint::from_untagged_bytes(GenericArray::from_array(public_key.0).as_0_14())
185        .compress()
186        .as_bytes()
187        .try_into()
188        .map_or_else(|_| unreachable!(), P256CompressedPublicKey)
189}