ecdsa/
verifying.rs

1//! ECDSA verifying: checking signatures are authentic using a [`VerifyingKey`].
2
3use crate::{
4    hazmat::{bits2field, DigestPrimitive, VerifyPrimitive},
5    Error, Result, Signature, SignatureSize,
6};
7use core::{cmp::Ordering, fmt::Debug};
8use elliptic_curve::{
9    generic_array::ArrayLength,
10    point::PointCompression,
11    sec1::{self, CompressedPoint, EncodedPoint, FromEncodedPoint, ToEncodedPoint},
12    AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, PublicKey,
13};
14use signature::{
15    digest::{Digest, FixedOutput},
16    hazmat::PrehashVerifier,
17    DigestVerifier, Verifier,
18};
19
20#[cfg(feature = "alloc")]
21use alloc::boxed::Box;
22
23#[cfg(feature = "der")]
24use {crate::der, core::ops::Add};
25
26#[cfg(feature = "pem")]
27use {
28    core::str::FromStr,
29    elliptic_curve::pkcs8::{DecodePublicKey, EncodePublicKey},
30};
31
32#[cfg(feature = "pkcs8")]
33use elliptic_curve::pkcs8::{
34    self,
35    der::AnyRef,
36    spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
37    AssociatedOid, ObjectIdentifier,
38};
39
40#[cfg(feature = "sha2")]
41use {
42    crate::{
43        SignatureWithOid, ECDSA_SHA224_OID, ECDSA_SHA256_OID, ECDSA_SHA384_OID, ECDSA_SHA512_OID,
44    },
45    sha2::{Sha224, Sha256, Sha384, Sha512},
46};
47
48#[cfg(all(feature = "pem", feature = "serde"))]
49use serdect::serde::{de, ser, Deserialize, Serialize};
50
51/// ECDSA public key used for verifying signatures. Generic over prime order
52/// elliptic curves (e.g. NIST P-curves)
53///
54/// Requires an [`elliptic_curve::CurveArithmetic`] impl on the curve, and a
55/// [`VerifyPrimitive`] impl on its associated `AffinePoint` type.
56///
57/// ## Usage
58///
59/// The [`signature`] crate defines the following traits which are the
60/// primary API for verifying:
61///
62/// - [`Verifier`]: verify a message against a provided key and signature
63/// - [`DigestVerifier`]: verify a message [`Digest`] against a provided key and signature
64/// - [`PrehashVerifier`]: verify the low-level raw output bytes of a message digest
65///
66/// See the [`p256` crate](https://docs.rs/p256/latest/p256/ecdsa/index.html)
67/// for examples of using this type with a concrete elliptic curve.
68///
69/// # `serde` support
70///
71/// When the `serde` feature of this crate is enabled, it provides support for
72/// serializing and deserializing ECDSA signatures using the `Serialize` and
73/// `Deserialize` traits.
74///
75/// The serialization leverages the encoding used by the [`PublicKey`] type,
76/// which is a binary-oriented ASN.1 DER encoding.
77#[derive(Clone, Debug)]
78pub struct VerifyingKey<C>
79where
80    C: PrimeCurve + CurveArithmetic,
81{
82    pub(crate) inner: PublicKey<C>,
83}
84
85impl<C> VerifyingKey<C>
86where
87    C: PrimeCurve + CurveArithmetic,
88    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
89    FieldBytesSize<C>: sec1::ModulusSize,
90{
91    /// Initialize [`VerifyingKey`] from a SEC1-encoded public key.
92    pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
93        PublicKey::from_sec1_bytes(bytes)
94            .map(|pk| Self { inner: pk })
95            .map_err(|_| Error::new())
96    }
97
98    /// Initialize [`VerifyingKey`] from an affine point.
99    ///
100    /// Returns an [`Error`] if the given affine point is the additive identity
101    /// (a.k.a. point at infinity).
102    pub fn from_affine(affine: AffinePoint<C>) -> Result<Self> {
103        Ok(Self {
104            inner: PublicKey::from_affine(affine).map_err(|_| Error::new())?,
105        })
106    }
107
108    /// Initialize [`VerifyingKey`] from an [`EncodedPoint`].
109    pub fn from_encoded_point(public_key: &EncodedPoint<C>) -> Result<Self> {
110        Option::from(PublicKey::<C>::from_encoded_point(public_key))
111            .map(|public_key| Self { inner: public_key })
112            .ok_or_else(Error::new)
113    }
114
115    /// Serialize this [`VerifyingKey`] as a SEC1 [`EncodedPoint`], optionally
116    /// applying point compression.
117    pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
118        self.inner.to_encoded_point(compress)
119    }
120
121    /// Convert this [`VerifyingKey`] into the
122    /// `Elliptic-Curve-Point-to-Octet-String` encoding described in
123    /// SEC 1: Elliptic Curve Cryptography (Version 2.0) section 2.3.3
124    /// (page 10).
125    ///
126    /// <http://www.secg.org/sec1-v2.pdf>
127    #[cfg(feature = "alloc")]
128    pub fn to_sec1_bytes(&self) -> Box<[u8]>
129    where
130        C: PointCompression,
131    {
132        self.inner.to_sec1_bytes()
133    }
134
135    /// Borrow the inner [`AffinePoint`] for this public key.
136    pub fn as_affine(&self) -> &AffinePoint<C> {
137        self.inner.as_affine()
138    }
139}
140
141//
142// `*Verifier` trait impls
143//
144
145impl<C, D> DigestVerifier<D, Signature<C>> for VerifyingKey<C>
146where
147    C: PrimeCurve + CurveArithmetic,
148    D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
149    AffinePoint<C>: VerifyPrimitive<C>,
150    SignatureSize<C>: ArrayLength<u8>,
151{
152    fn verify_digest(&self, msg_digest: D, signature: &Signature<C>) -> Result<()> {
153        self.inner.as_affine().verify_digest(msg_digest, signature)
154    }
155}
156
157impl<C> PrehashVerifier<Signature<C>> for VerifyingKey<C>
158where
159    C: PrimeCurve + CurveArithmetic,
160    AffinePoint<C>: VerifyPrimitive<C>,
161    SignatureSize<C>: ArrayLength<u8>,
162{
163    fn verify_prehash(&self, prehash: &[u8], signature: &Signature<C>) -> Result<()> {
164        let field = bits2field::<C>(prehash)?;
165        self.inner.as_affine().verify_prehashed(&field, signature)
166    }
167}
168
169impl<C> Verifier<Signature<C>> for VerifyingKey<C>
170where
171    C: PrimeCurve + CurveArithmetic + DigestPrimitive,
172    AffinePoint<C>: VerifyPrimitive<C>,
173    SignatureSize<C>: ArrayLength<u8>,
174{
175    fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<()> {
176        self.verify_digest(C::Digest::new_with_prefix(msg), signature)
177    }
178}
179
180#[cfg(feature = "sha2")]
181impl<C> Verifier<SignatureWithOid<C>> for VerifyingKey<C>
182where
183    C: PrimeCurve + CurveArithmetic + DigestPrimitive,
184    AffinePoint<C>: VerifyPrimitive<C>,
185    SignatureSize<C>: ArrayLength<u8>,
186{
187    fn verify(&self, msg: &[u8], sig: &SignatureWithOid<C>) -> Result<()> {
188        match sig.oid() {
189            ECDSA_SHA224_OID => self.verify_prehash(&Sha224::digest(msg), sig.signature()),
190            ECDSA_SHA256_OID => self.verify_prehash(&Sha256::digest(msg), sig.signature()),
191            ECDSA_SHA384_OID => self.verify_prehash(&Sha384::digest(msg), sig.signature()),
192            ECDSA_SHA512_OID => self.verify_prehash(&Sha512::digest(msg), sig.signature()),
193            _ => Err(Error::new()),
194        }
195    }
196}
197
198#[cfg(feature = "der")]
199impl<C, D> DigestVerifier<D, der::Signature<C>> for VerifyingKey<C>
200where
201    C: PrimeCurve + CurveArithmetic,
202    D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
203    AffinePoint<C>: VerifyPrimitive<C>,
204    SignatureSize<C>: ArrayLength<u8>,
205    der::MaxSize<C>: ArrayLength<u8>,
206    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
207{
208    fn verify_digest(&self, msg_digest: D, signature: &der::Signature<C>) -> Result<()> {
209        let signature = Signature::<C>::try_from(signature.clone())?;
210        DigestVerifier::<D, Signature<C>>::verify_digest(self, msg_digest, &signature)
211    }
212}
213
214#[cfg(feature = "der")]
215impl<C> PrehashVerifier<der::Signature<C>> for VerifyingKey<C>
216where
217    C: PrimeCurve + CurveArithmetic + DigestPrimitive,
218    AffinePoint<C>: VerifyPrimitive<C>,
219    SignatureSize<C>: ArrayLength<u8>,
220    der::MaxSize<C>: ArrayLength<u8>,
221    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
222{
223    fn verify_prehash(&self, prehash: &[u8], signature: &der::Signature<C>) -> Result<()> {
224        let signature = Signature::<C>::try_from(signature.clone())?;
225        PrehashVerifier::<Signature<C>>::verify_prehash(self, prehash, &signature)
226    }
227}
228
229#[cfg(feature = "der")]
230impl<C> Verifier<der::Signature<C>> for VerifyingKey<C>
231where
232    C: PrimeCurve + CurveArithmetic + DigestPrimitive,
233    AffinePoint<C>: VerifyPrimitive<C>,
234    SignatureSize<C>: ArrayLength<u8>,
235    der::MaxSize<C>: ArrayLength<u8>,
236    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
237{
238    fn verify(&self, msg: &[u8], signature: &der::Signature<C>) -> Result<()> {
239        let signature = Signature::<C>::try_from(signature.clone())?;
240        Verifier::<Signature<C>>::verify(self, msg, &signature)
241    }
242}
243
244//
245// Other trait impls
246//
247
248impl<C> AsRef<AffinePoint<C>> for VerifyingKey<C>
249where
250    C: PrimeCurve + CurveArithmetic,
251    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
252    FieldBytesSize<C>: sec1::ModulusSize,
253{
254    fn as_ref(&self) -> &AffinePoint<C> {
255        self.as_affine()
256    }
257}
258
259impl<C> Copy for VerifyingKey<C> where C: PrimeCurve + CurveArithmetic {}
260
261impl<C> From<VerifyingKey<C>> for CompressedPoint<C>
262where
263    C: PrimeCurve + CurveArithmetic + PointCompression,
264    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
265    FieldBytesSize<C>: sec1::ModulusSize,
266{
267    fn from(verifying_key: VerifyingKey<C>) -> CompressedPoint<C> {
268        verifying_key.inner.into()
269    }
270}
271
272impl<C> From<&VerifyingKey<C>> for CompressedPoint<C>
273where
274    C: PrimeCurve + CurveArithmetic + PointCompression,
275    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
276    FieldBytesSize<C>: sec1::ModulusSize,
277{
278    fn from(verifying_key: &VerifyingKey<C>) -> CompressedPoint<C> {
279        verifying_key.inner.into()
280    }
281}
282
283impl<C> From<VerifyingKey<C>> for EncodedPoint<C>
284where
285    C: PrimeCurve + CurveArithmetic + PointCompression,
286    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
287    FieldBytesSize<C>: sec1::ModulusSize,
288{
289    fn from(verifying_key: VerifyingKey<C>) -> EncodedPoint<C> {
290        verifying_key.inner.into()
291    }
292}
293
294impl<C> From<&VerifyingKey<C>> for EncodedPoint<C>
295where
296    C: PrimeCurve + CurveArithmetic + PointCompression,
297    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
298    FieldBytesSize<C>: sec1::ModulusSize,
299{
300    fn from(verifying_key: &VerifyingKey<C>) -> EncodedPoint<C> {
301        verifying_key.inner.into()
302    }
303}
304
305impl<C> Eq for VerifyingKey<C> where C: PrimeCurve + CurveArithmetic {}
306
307impl<C> PartialEq for VerifyingKey<C>
308where
309    C: PrimeCurve + CurveArithmetic,
310{
311    fn eq(&self, other: &Self) -> bool {
312        self.inner.eq(&other.inner)
313    }
314}
315
316impl<C> From<PublicKey<C>> for VerifyingKey<C>
317where
318    C: PrimeCurve + CurveArithmetic,
319{
320    fn from(public_key: PublicKey<C>) -> VerifyingKey<C> {
321        VerifyingKey { inner: public_key }
322    }
323}
324
325impl<C> From<&PublicKey<C>> for VerifyingKey<C>
326where
327    C: PrimeCurve + CurveArithmetic,
328{
329    fn from(public_key: &PublicKey<C>) -> VerifyingKey<C> {
330        (*public_key).into()
331    }
332}
333
334impl<C> From<VerifyingKey<C>> for PublicKey<C>
335where
336    C: PrimeCurve + CurveArithmetic,
337{
338    fn from(verifying_key: VerifyingKey<C>) -> PublicKey<C> {
339        verifying_key.inner
340    }
341}
342
343impl<C> From<&VerifyingKey<C>> for PublicKey<C>
344where
345    C: PrimeCurve + CurveArithmetic,
346{
347    fn from(verifying_key: &VerifyingKey<C>) -> PublicKey<C> {
348        (*verifying_key).into()
349    }
350}
351
352impl<C> PartialOrd for VerifyingKey<C>
353where
354    C: PrimeCurve + CurveArithmetic,
355    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
356    FieldBytesSize<C>: sec1::ModulusSize,
357{
358    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
359        self.inner.partial_cmp(&other.inner)
360    }
361}
362
363impl<C> Ord for VerifyingKey<C>
364where
365    C: PrimeCurve + CurveArithmetic,
366    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
367    FieldBytesSize<C>: sec1::ModulusSize,
368{
369    fn cmp(&self, other: &Self) -> Ordering {
370        self.inner.cmp(&other.inner)
371    }
372}
373
374impl<C> TryFrom<&[u8]> for VerifyingKey<C>
375where
376    C: PrimeCurve + CurveArithmetic,
377    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
378    FieldBytesSize<C>: sec1::ModulusSize,
379{
380    type Error = Error;
381
382    fn try_from(bytes: &[u8]) -> Result<Self> {
383        Self::from_sec1_bytes(bytes)
384    }
385}
386
387#[cfg(feature = "pkcs8")]
388impl<C> AssociatedAlgorithmIdentifier for VerifyingKey<C>
389where
390    C: AssociatedOid + CurveArithmetic + PrimeCurve,
391    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
392    FieldBytesSize<C>: sec1::ModulusSize,
393{
394    type Params = ObjectIdentifier;
395
396    const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<ObjectIdentifier> =
397        PublicKey::<C>::ALGORITHM_IDENTIFIER;
398}
399
400#[cfg(feature = "pkcs8")]
401impl<C> SignatureAlgorithmIdentifier for VerifyingKey<C>
402where
403    C: PrimeCurve + CurveArithmetic,
404    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
405    FieldBytesSize<C>: sec1::ModulusSize,
406    Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
407{
408    type Params = AnyRef<'static>;
409
410    const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
411        Signature::<C>::ALGORITHM_IDENTIFIER;
412}
413
414#[cfg(feature = "pkcs8")]
415impl<C> TryFrom<pkcs8::SubjectPublicKeyInfoRef<'_>> for VerifyingKey<C>
416where
417    C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
418    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
419    FieldBytesSize<C>: sec1::ModulusSize,
420{
421    type Error = pkcs8::spki::Error;
422
423    fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
424        PublicKey::try_from(spki).map(|inner| Self { inner })
425    }
426}
427
428#[cfg(feature = "pem")]
429impl<C> EncodePublicKey for VerifyingKey<C>
430where
431    C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
432    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
433    FieldBytesSize<C>: sec1::ModulusSize,
434{
435    fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
436        self.inner.to_public_key_der()
437    }
438}
439
440#[cfg(feature = "pem")]
441impl<C> FromStr for VerifyingKey<C>
442where
443    C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
444    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
445    FieldBytesSize<C>: sec1::ModulusSize,
446{
447    type Err = Error;
448
449    fn from_str(s: &str) -> Result<Self> {
450        Self::from_public_key_pem(s).map_err(|_| Error::new())
451    }
452}
453
454#[cfg(all(feature = "pem", feature = "serde"))]
455impl<C> Serialize for VerifyingKey<C>
456where
457    C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
458    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
459    FieldBytesSize<C>: sec1::ModulusSize,
460{
461    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
462    where
463        S: ser::Serializer,
464    {
465        self.inner.serialize(serializer)
466    }
467}
468
469#[cfg(all(feature = "pem", feature = "serde"))]
470impl<'de, C> Deserialize<'de> for VerifyingKey<C>
471where
472    C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
473    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
474    FieldBytesSize<C>: sec1::ModulusSize,
475{
476    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
477    where
478        D: de::Deserializer<'de>,
479    {
480        PublicKey::<C>::deserialize(deserializer).map(Into::into)
481    }
482}