//! ECDSA verifying: checking signatures are authentic using a [`VerifyingKey`]. use crate::{ hazmat::{bits2field, DigestPrimitive, VerifyPrimitive}, Error, Result, Signature, SignatureSize, }; use core::{cmp::Ordering, fmt::Debug}; use elliptic_curve::{ generic_array::ArrayLength, point::PointCompression, sec1::{self, CompressedPoint, EncodedPoint, FromEncodedPoint, ToEncodedPoint}, AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, PublicKey, }; use signature::{ digest::{Digest, FixedOutput}, hazmat::PrehashVerifier, DigestVerifier, Verifier, }; #[cfg(feature = "alloc")] use alloc::boxed::Box; #[cfg(feature = "der")] use {crate::der, core::ops::Add}; #[cfg(feature = "pem")] use { core::str::FromStr, elliptic_curve::pkcs8::{DecodePublicKey, EncodePublicKey}, }; #[cfg(feature = "pkcs8")] use elliptic_curve::pkcs8::{ self, der::AnyRef, spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier}, AssociatedOid, ObjectIdentifier, }; #[cfg(feature = "sha2")] use { crate::{ SignatureWithOid, ECDSA_SHA224_OID, ECDSA_SHA256_OID, ECDSA_SHA384_OID, ECDSA_SHA512_OID, }, sha2::{Sha224, Sha256, Sha384, Sha512}, }; #[cfg(all(feature = "pem", feature = "serde"))] use serdect::serde::{de, ser, Deserialize, Serialize}; /// ECDSA public key used for verifying signatures. Generic over prime order /// elliptic curves (e.g. NIST P-curves) /// /// Requires an [`elliptic_curve::CurveArithmetic`] impl on the curve, and a /// [`VerifyPrimitive`] impl on its associated `AffinePoint` type. /// /// ## Usage /// /// The [`signature`] crate defines the following traits which are the /// primary API for verifying: /// /// - [`Verifier`]: verify a message against a provided key and signature /// - [`DigestVerifier`]: verify a message [`Digest`] against a provided key and signature /// - [`PrehashVerifier`]: verify the low-level raw output bytes of a message digest /// /// See the [`p256` crate](https://docs.rs/p256/latest/p256/ecdsa/index.html) /// for examples of using this type with a concrete elliptic curve. /// /// # `serde` support /// /// When the `serde` feature of this crate is enabled, it provides support for /// serializing and deserializing ECDSA signatures using the `Serialize` and /// `Deserialize` traits. /// /// The serialization leverages the encoding used by the [`PublicKey`] type, /// which is a binary-oriented ASN.1 DER encoding. #[derive(Clone, Debug)] pub struct VerifyingKey where C: PrimeCurve + CurveArithmetic, { pub(crate) inner: PublicKey, } impl VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { /// Initialize [`VerifyingKey`] from a SEC1-encoded public key. pub fn from_sec1_bytes(bytes: &[u8]) -> Result { PublicKey::from_sec1_bytes(bytes) .map(|pk| Self { inner: pk }) .map_err(|_| Error::new()) } /// Initialize [`VerifyingKey`] from an affine point. /// /// Returns an [`Error`] if the given affine point is the additive identity /// (a.k.a. point at infinity). pub fn from_affine(affine: AffinePoint) -> Result { Ok(Self { inner: PublicKey::from_affine(affine).map_err(|_| Error::new())?, }) } /// Initialize [`VerifyingKey`] from an [`EncodedPoint`]. pub fn from_encoded_point(public_key: &EncodedPoint) -> Result { Option::from(PublicKey::::from_encoded_point(public_key)) .map(|public_key| Self { inner: public_key }) .ok_or_else(Error::new) } /// Serialize this [`VerifyingKey`] as a SEC1 [`EncodedPoint`], optionally /// applying point compression. pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint { self.inner.to_encoded_point(compress) } /// Convert this [`VerifyingKey`] into the /// `Elliptic-Curve-Point-to-Octet-String` encoding described in /// SEC 1: Elliptic Curve Cryptography (Version 2.0) section 2.3.3 /// (page 10). /// /// #[cfg(feature = "alloc")] pub fn to_sec1_bytes(&self) -> Box<[u8]> where C: PointCompression, { self.inner.to_sec1_bytes() } /// Borrow the inner [`AffinePoint`] for this public key. pub fn as_affine(&self) -> &AffinePoint { self.inner.as_affine() } } // // `*Verifier` trait impls // impl DigestVerifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic, D: Digest + FixedOutput>, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, { fn verify_digest(&self, msg_digest: D, signature: &Signature) -> Result<()> { self.inner.as_affine().verify_digest(msg_digest, signature) } } impl PrehashVerifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, { fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> Result<()> { let field = bits2field::(prehash)?; self.inner.as_affine().verify_prehashed(&field, signature) } } impl Verifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, { fn verify(&self, msg: &[u8], signature: &Signature) -> Result<()> { self.verify_digest(C::Digest::new_with_prefix(msg), signature) } } #[cfg(feature = "sha2")] impl Verifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, { fn verify(&self, msg: &[u8], sig: &SignatureWithOid) -> Result<()> { match sig.oid() { ECDSA_SHA224_OID => self.verify_prehash(&Sha224::digest(msg), sig.signature()), ECDSA_SHA256_OID => self.verify_prehash(&Sha256::digest(msg), sig.signature()), ECDSA_SHA384_OID => self.verify_prehash(&Sha384::digest(msg), sig.signature()), ECDSA_SHA512_OID => self.verify_prehash(&Sha512::digest(msg), sig.signature()), _ => Err(Error::new()), } } } #[cfg(feature = "der")] impl DigestVerifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic, D: Digest + FixedOutput>, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn verify_digest(&self, msg_digest: D, signature: &der::Signature) -> Result<()> { let signature = Signature::::try_from(signature.clone())?; DigestVerifier::>::verify_digest(self, msg_digest, &signature) } } #[cfg(feature = "der")] impl PrehashVerifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn verify_prehash(&self, prehash: &[u8], signature: &der::Signature) -> Result<()> { let signature = Signature::::try_from(signature.clone())?; PrehashVerifier::>::verify_prehash(self, prehash, &signature) } } #[cfg(feature = "der")] impl Verifier> for VerifyingKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, AffinePoint: VerifyPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn verify(&self, msg: &[u8], signature: &der::Signature) -> Result<()> { let signature = Signature::::try_from(signature.clone())?; Verifier::>::verify(self, msg, &signature) } } // // Other trait impls // impl AsRef> for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn as_ref(&self) -> &AffinePoint { self.as_affine() } } impl Copy for VerifyingKey where C: PrimeCurve + CurveArithmetic {} impl From> for CompressedPoint where C: PrimeCurve + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn from(verifying_key: VerifyingKey) -> CompressedPoint { verifying_key.inner.into() } } impl From<&VerifyingKey> for CompressedPoint where C: PrimeCurve + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn from(verifying_key: &VerifyingKey) -> CompressedPoint { verifying_key.inner.into() } } impl From> for EncodedPoint where C: PrimeCurve + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn from(verifying_key: VerifyingKey) -> EncodedPoint { verifying_key.inner.into() } } impl From<&VerifyingKey> for EncodedPoint where C: PrimeCurve + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn from(verifying_key: &VerifyingKey) -> EncodedPoint { verifying_key.inner.into() } } impl Eq for VerifyingKey where C: PrimeCurve + CurveArithmetic {} impl PartialEq for VerifyingKey where C: PrimeCurve + CurveArithmetic, { fn eq(&self, other: &Self) -> bool { self.inner.eq(&other.inner) } } impl From> for VerifyingKey where C: PrimeCurve + CurveArithmetic, { fn from(public_key: PublicKey) -> VerifyingKey { VerifyingKey { inner: public_key } } } impl From<&PublicKey> for VerifyingKey where C: PrimeCurve + CurveArithmetic, { fn from(public_key: &PublicKey) -> VerifyingKey { (*public_key).into() } } impl From> for PublicKey where C: PrimeCurve + CurveArithmetic, { fn from(verifying_key: VerifyingKey) -> PublicKey { verifying_key.inner } } impl From<&VerifyingKey> for PublicKey where C: PrimeCurve + CurveArithmetic, { fn from(verifying_key: &VerifyingKey) -> PublicKey { (*verifying_key).into() } } impl PartialOrd for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn partial_cmp(&self, other: &Self) -> Option { self.inner.partial_cmp(&other.inner) } } impl Ord for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn cmp(&self, other: &Self) -> Ordering { self.inner.cmp(&other.inner) } } impl TryFrom<&[u8]> for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { type Error = Error; fn try_from(bytes: &[u8]) -> Result { Self::from_sec1_bytes(bytes) } } #[cfg(feature = "pkcs8")] impl AssociatedAlgorithmIdentifier for VerifyingKey where C: AssociatedOid + CurveArithmetic + PrimeCurve, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { type Params = ObjectIdentifier; const ALGORITHM_IDENTIFIER: AlgorithmIdentifier = PublicKey::::ALGORITHM_IDENTIFIER; } #[cfg(feature = "pkcs8")] impl SignatureAlgorithmIdentifier for VerifyingKey where C: PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, Signature: AssociatedAlgorithmIdentifier>, { type Params = AnyRef<'static>; const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier = Signature::::ALGORITHM_IDENTIFIER; } #[cfg(feature = "pkcs8")] impl TryFrom> for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { type Error = pkcs8::spki::Error; fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result { PublicKey::try_from(spki).map(|inner| Self { inner }) } } #[cfg(feature = "pem")] impl EncodePublicKey for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn to_public_key_der(&self) -> pkcs8::spki::Result { self.inner.to_public_key_der() } } #[cfg(feature = "pem")] impl FromStr for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { type Err = Error; fn from_str(s: &str) -> Result { Self::from_public_key_pem(s).map_err(|_| Error::new()) } } #[cfg(all(feature = "pem", feature = "serde"))] impl Serialize for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn serialize(&self, serializer: S) -> core::result::Result where S: ser::Serializer, { self.inner.serialize(serializer) } } #[cfg(all(feature = "pem", feature = "serde"))] impl<'de, C> Deserialize<'de> for VerifyingKey where C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, { fn deserialize(deserializer: D) -> core::result::Result where D: de::Deserializer<'de>, { PublicKey::::deserialize(deserializer).map(Into::into) } }