ecdsa/
verify.rs

1//! ECDSA verification key.
2
3use crate::{
4    hazmat::{DigestPrimitive, VerifyPrimitive},
5    Error, Result, Signature, SignatureSize,
6};
7use core::{cmp::Ordering, fmt::Debug};
8use elliptic_curve::{
9    generic_array::ArrayLength,
10    ops::Reduce,
11    sec1::{self, EncodedPoint, FromEncodedPoint, ToEncodedPoint},
12    AffinePoint, FieldSize, PointCompression, PrimeCurve, ProjectiveArithmetic, PublicKey, Scalar,
13};
14use signature::{
15    digest::{Digest, FixedOutput},
16    hazmat::PrehashVerifier,
17    DigestVerifier, Verifier,
18};
19
20#[cfg(feature = "pkcs8")]
21use elliptic_curve::pkcs8::{self, AssociatedOid, DecodePublicKey};
22
23#[cfg(feature = "pem")]
24use elliptic_curve::pkcs8::EncodePublicKey;
25
26#[cfg(feature = "pem")]
27use core::str::FromStr;
28
29#[cfg(all(feature = "pem", feature = "serde"))]
30#[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "serde"))))]
31use serdect::serde::{de, ser, Deserialize, Serialize};
32
33/// ECDSA verification key (i.e. public key). Generic over elliptic curves.
34///
35/// Requires an [`elliptic_curve::ProjectiveArithmetic`] impl on the curve, and a
36/// [`VerifyPrimitive`] impl on its associated `AffinePoint` type.
37///
38/// # `serde` support
39///
40/// When the `serde` feature of this crate is enabled, it provides support for
41/// serializing and deserializing ECDSA signatures using the `Serialize` and
42/// `Deserialize` traits.
43///
44/// The serialization leverages the encoding used by the [`PublicKey`] type,
45/// which is a binary-oriented ASN.1 DER encoding.
46#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
47#[derive(Clone, Debug)]
48pub struct VerifyingKey<C>
49where
50    C: PrimeCurve + ProjectiveArithmetic,
51{
52    pub(crate) inner: PublicKey<C>,
53}
54
55impl<C> VerifyingKey<C>
56where
57    C: PrimeCurve + ProjectiveArithmetic,
58    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
59    FieldSize<C>: sec1::ModulusSize,
60{
61    /// Initialize [`VerifyingKey`] from a SEC1-encoded public key.
62    pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
63        PublicKey::from_sec1_bytes(bytes)
64            .map(|pk| Self { inner: pk })
65            .map_err(|_| Error::new())
66    }
67
68    /// Initialize [`VerifyingKey`] from an affine point.
69    ///
70    /// Returns an [`Error`] if the given affine point is the additive identity
71    /// (a.k.a. point at infinity).
72    pub fn from_affine(affine: AffinePoint<C>) -> Result<Self> {
73        Ok(Self {
74            inner: PublicKey::from_affine(affine).map_err(|_| Error::new())?,
75        })
76    }
77
78    /// Initialize [`VerifyingKey`] from an [`EncodedPoint`].
79    pub fn from_encoded_point(public_key: &EncodedPoint<C>) -> Result<Self> {
80        Option::from(PublicKey::<C>::from_encoded_point(public_key))
81            .map(|public_key| Self { inner: public_key })
82            .ok_or_else(Error::new)
83    }
84
85    /// Serialize this [`VerifyingKey`] as a SEC1 [`EncodedPoint`], optionally
86    /// applying point compression.
87    pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
88        self.inner.to_encoded_point(compress)
89    }
90
91    /// Borrow the inner [`AffinePoint`] for this public key.
92    pub fn as_affine(&self) -> &AffinePoint<C> {
93        self.inner.as_affine()
94    }
95}
96
97impl<C> AsRef<AffinePoint<C>> for VerifyingKey<C>
98where
99    C: PrimeCurve + ProjectiveArithmetic,
100    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
101    FieldSize<C>: sec1::ModulusSize,
102{
103    fn as_ref(&self) -> &AffinePoint<C> {
104        self.as_affine()
105    }
106}
107
108impl<C> Copy for VerifyingKey<C> where C: PrimeCurve + ProjectiveArithmetic {}
109
110impl<C, D> DigestVerifier<D, Signature<C>> for VerifyingKey<C>
111where
112    C: PrimeCurve + ProjectiveArithmetic,
113    D: Digest + FixedOutput<OutputSize = FieldSize<C>>,
114    AffinePoint<C>: VerifyPrimitive<C>,
115    Scalar<C>: Reduce<C::UInt>,
116    SignatureSize<C>: ArrayLength<u8>,
117{
118    fn verify_digest(&self, msg_digest: D, signature: &Signature<C>) -> Result<()> {
119        self.inner.as_affine().verify_digest(msg_digest, signature)
120    }
121}
122
123impl<C> PrehashVerifier<Signature<C>> for VerifyingKey<C>
124where
125    C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
126    AffinePoint<C>: VerifyPrimitive<C>,
127    Scalar<C>: Reduce<C::UInt>,
128    SignatureSize<C>: ArrayLength<u8>,
129{
130    fn verify_prehash(&self, prehash: &[u8], signature: &Signature<C>) -> Result<()> {
131        let prehash = C::prehash_to_field_bytes(prehash)?;
132        self.inner.as_affine().verify_prehashed(prehash, signature)
133    }
134}
135
136impl<C> Verifier<Signature<C>> for VerifyingKey<C>
137where
138    C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
139    C::Digest: FixedOutput<OutputSize = FieldSize<C>>,
140    AffinePoint<C>: VerifyPrimitive<C>,
141    Scalar<C>: Reduce<C::UInt>,
142    SignatureSize<C>: ArrayLength<u8>,
143{
144    fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<()> {
145        self.verify_digest(C::Digest::new_with_prefix(msg), signature)
146    }
147}
148
149impl<C> From<&VerifyingKey<C>> for EncodedPoint<C>
150where
151    C: PrimeCurve + ProjectiveArithmetic + PointCompression,
152    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
153    FieldSize<C>: sec1::ModulusSize,
154{
155    fn from(verifying_key: &VerifyingKey<C>) -> EncodedPoint<C> {
156        verifying_key.to_encoded_point(C::COMPRESS_POINTS)
157    }
158}
159
160impl<C> From<PublicKey<C>> for VerifyingKey<C>
161where
162    C: PrimeCurve + ProjectiveArithmetic,
163{
164    fn from(public_key: PublicKey<C>) -> VerifyingKey<C> {
165        VerifyingKey { inner: public_key }
166    }
167}
168
169impl<C> From<&PublicKey<C>> for VerifyingKey<C>
170where
171    C: PrimeCurve + ProjectiveArithmetic,
172{
173    fn from(public_key: &PublicKey<C>) -> VerifyingKey<C> {
174        (*public_key).into()
175    }
176}
177
178impl<C> From<VerifyingKey<C>> for PublicKey<C>
179where
180    C: PrimeCurve + ProjectiveArithmetic,
181{
182    fn from(verifying_key: VerifyingKey<C>) -> PublicKey<C> {
183        verifying_key.inner
184    }
185}
186
187impl<C> From<&VerifyingKey<C>> for PublicKey<C>
188where
189    C: PrimeCurve + ProjectiveArithmetic,
190{
191    fn from(verifying_key: &VerifyingKey<C>) -> PublicKey<C> {
192        (*verifying_key).into()
193    }
194}
195
196impl<C> Eq for VerifyingKey<C> where C: PrimeCurve + ProjectiveArithmetic {}
197
198impl<C> PartialEq for VerifyingKey<C>
199where
200    C: PrimeCurve + ProjectiveArithmetic,
201{
202    fn eq(&self, other: &Self) -> bool {
203        self.inner.eq(&other.inner)
204    }
205}
206
207impl<C> PartialOrd for VerifyingKey<C>
208where
209    C: PrimeCurve + ProjectiveArithmetic,
210    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
211    FieldSize<C>: sec1::ModulusSize,
212{
213    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
214        self.inner.partial_cmp(&other.inner)
215    }
216}
217
218impl<C> Ord for VerifyingKey<C>
219where
220    C: PrimeCurve + ProjectiveArithmetic,
221    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
222    FieldSize<C>: sec1::ModulusSize,
223{
224    fn cmp(&self, other: &Self) -> Ordering {
225        self.inner.cmp(&other.inner)
226    }
227}
228
229impl<C> TryFrom<&[u8]> for VerifyingKey<C>
230where
231    C: PrimeCurve + ProjectiveArithmetic,
232    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
233    FieldSize<C>: sec1::ModulusSize,
234{
235    type Error = Error;
236
237    fn try_from(bytes: &[u8]) -> Result<Self> {
238        Self::from_sec1_bytes(bytes)
239    }
240}
241
242#[cfg(feature = "pkcs8")]
243#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
244impl<C> TryFrom<pkcs8::SubjectPublicKeyInfo<'_>> for VerifyingKey<C>
245where
246    C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression,
247    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
248    FieldSize<C>: sec1::ModulusSize,
249{
250    type Error = pkcs8::spki::Error;
251
252    fn try_from(spki: pkcs8::SubjectPublicKeyInfo<'_>) -> pkcs8::spki::Result<Self> {
253        PublicKey::try_from(spki).map(|inner| Self { inner })
254    }
255}
256
257#[cfg(feature = "pkcs8")]
258#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
259impl<C> DecodePublicKey for VerifyingKey<C>
260where
261    C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression,
262    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
263    FieldSize<C>: sec1::ModulusSize,
264{
265}
266
267#[cfg(feature = "pem")]
268#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
269impl<C> EncodePublicKey for VerifyingKey<C>
270where
271    C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression,
272    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
273    FieldSize<C>: sec1::ModulusSize,
274{
275    fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
276        self.inner.to_public_key_der()
277    }
278}
279
280#[cfg(feature = "pem")]
281#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
282impl<C> FromStr for VerifyingKey<C>
283where
284    C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression,
285    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
286    FieldSize<C>: sec1::ModulusSize,
287{
288    type Err = Error;
289
290    fn from_str(s: &str) -> Result<Self> {
291        Self::from_public_key_pem(s).map_err(|_| Error::new())
292    }
293}
294
295#[cfg(all(feature = "pem", feature = "serde"))]
296#[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "serde"))))]
297impl<C> Serialize for VerifyingKey<C>
298where
299    C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression,
300    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
301    FieldSize<C>: sec1::ModulusSize,
302{
303    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
304    where
305        S: ser::Serializer,
306    {
307        self.inner.serialize(serializer)
308    }
309}
310
311#[cfg(all(feature = "pem", feature = "serde"))]
312#[cfg_attr(docsrs, doc(cfg(all(feature = "pem", feature = "serde"))))]
313impl<'de, C> Deserialize<'de> for VerifyingKey<C>
314where
315    C: PrimeCurve + AssociatedOid + ProjectiveArithmetic + PointCompression,
316    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
317    FieldSize<C>: sec1::ModulusSize,
318{
319    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
320    where
321        D: de::Deserializer<'de>,
322    {
323        PublicKey::<C>::deserialize(deserializer).map(Into::into)
324    }
325}