Skip to main content

ecdsa/
verifying.rs

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