Skip to main content

ecdsa/
verifying.rs

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