#![no_std] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![doc = include_str!("../README.md")] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" )] #![forbid(unsafe_code)] #![warn( clippy::cast_lossless, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_precision_loss, clippy::cast_sign_loss, clippy::checked_conversions, clippy::implicit_saturating_sub, clippy::panic, clippy::panic_in_result_fn, clippy::unwrap_used, missing_docs, rust_2018_idioms, unused_lifetimes, unused_qualifications )] //! ## `serde` support //! //! When the `serde` feature of this crate is enabled, `Serialize` and //! `Deserialize` impls are provided for the [`Signature`] and [`VerifyingKey`] //! types. //! //! Please see type-specific documentation for more information. //! //! ## Interop //! //! Any crates which provide an implementation of ECDSA for a particular //! elliptic curve can leverage the types from this crate, along with the //! [`k256`], [`p256`], and/or [`p384`] crates to expose ECDSA functionality in //! a generic, interoperable way by leveraging the [`Signature`] type with in //! conjunction with the [`signature::Signer`] and [`signature::Verifier`] //! traits. //! //! For example, the [`ring-compat`] crate implements the [`signature::Signer`] //! and [`signature::Verifier`] traits in conjunction with the //! [`p256::ecdsa::Signature`] and [`p384::ecdsa::Signature`] types to //! wrap the ECDSA implementations from [*ring*] in a generic, interoperable //! API. //! //! [`k256`]: https://docs.rs/k256 //! [`p256`]: https://docs.rs/p256 //! [`p256::ecdsa::Signature`]: https://docs.rs/p256/latest/p256/ecdsa/type.Signature.html //! [`p384`]: https://docs.rs/p384 //! [`p384::ecdsa::Signature`]: https://docs.rs/p384/latest/p384/ecdsa/type.Signature.html //! [`ring-compat`]: https://docs.rs/ring-compat //! [*ring*]: https://docs.rs/ring #[cfg(feature = "alloc")] extern crate alloc; mod recovery; #[cfg(feature = "der")] pub mod der; #[cfg(feature = "dev")] pub mod dev; #[cfg(feature = "hazmat")] pub mod hazmat; #[cfg(feature = "signing")] mod signing; #[cfg(feature = "verifying")] mod verifying; pub use crate::recovery::RecoveryId; // Re-export the `elliptic-curve` crate (and select types) pub use elliptic_curve::{self, sec1::EncodedPoint, PrimeCurve}; // Re-export the `signature` crate (and select types) pub use signature::{self, Error, Result, SignatureEncoding}; #[cfg(feature = "signing")] pub use crate::signing::SigningKey; #[cfg(feature = "verifying")] pub use crate::verifying::VerifyingKey; use core::{fmt, ops::Add}; use elliptic_curve::{ generic_array::{sequence::Concat, typenum::Unsigned, ArrayLength, GenericArray}, FieldBytes, FieldBytesSize, ScalarPrimitive, }; #[cfg(feature = "alloc")] use alloc::vec::Vec; #[cfg(feature = "arithmetic")] use { core::str, elliptic_curve::{scalar::IsHigh, CurveArithmetic, NonZeroScalar}, }; #[cfg(feature = "digest")] use digest::{ const_oid::{AssociatedOid, ObjectIdentifier}, Digest, }; #[cfg(feature = "pkcs8")] use elliptic_curve::pkcs8::spki::{ der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, }; #[cfg(feature = "serde")] use serdect::serde::{de, ser, Deserialize, Serialize}; #[cfg(all(feature = "alloc", feature = "pkcs8"))] use elliptic_curve::pkcs8::spki::{ self, AlgorithmIdentifierOwned, DynAssociatedAlgorithmIdentifier, }; /// OID for ECDSA with SHA-224 digests. /// /// ```text /// ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2) /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 1 } /// ``` // TODO(tarcieri): use `ObjectIdentifier::push_arc` when const unwrap is stable #[cfg(feature = "digest")] pub const ECDSA_SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.1"); /// OID for ECDSA with SHA-256 digests. /// /// ```text /// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } /// ``` #[cfg(feature = "digest")] pub const ECDSA_SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2"); /// OID for ECDSA with SHA-384 digests. /// /// ```text /// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 } /// ``` #[cfg(feature = "digest")] pub const ECDSA_SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.3"); /// OID for ECDSA with SHA-512 digests. /// /// ```text /// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) /// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } /// ``` #[cfg(feature = "digest")] pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4"); #[cfg(feature = "digest")] const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4"); #[cfg(feature = "digest")] const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1"); #[cfg(feature = "digest")] const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2"); #[cfg(feature = "digest")] const SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3"); /// Size of a fixed sized signature for the given elliptic curve. pub type SignatureSize = as Add>::Output; /// Fixed-size byte array containing an ECDSA signature pub type SignatureBytes = GenericArray>; /// ECDSA signature (fixed-size). Generic over elliptic curve types. /// /// Serialized as fixed-sized big endian scalar values with no added framing: /// /// - `r`: field element size for the given curve, big-endian /// - `s`: field element size for the given curve, big-endian /// /// For example, in a curve with a 256-bit modulus like NIST P-256 or /// secp256k1, `r` and `s` will both be 32-bytes, resulting in a signature /// with a total of 64-bytes. /// /// ASN.1 DER-encoded signatures also supported via the /// [`Signature::from_der`] and [`Signature::to_der`] methods. /// /// # `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 uses a hexadecimal encoding when used with /// "human readable" text formats, and a binary encoding otherwise. #[derive(Clone, Eq, PartialEq)] pub struct Signature { r: ScalarPrimitive, s: ScalarPrimitive, } impl Signature where C: PrimeCurve, SignatureSize: ArrayLength, { /// Parse a signature from fixed-with bytes. pub fn from_bytes(bytes: &SignatureBytes) -> Result { let (r_bytes, s_bytes) = bytes.split_at(C::FieldBytesSize::USIZE); let r = ScalarPrimitive::from_slice(r_bytes).map_err(|_| Error::new())?; let s = ScalarPrimitive::from_slice(s_bytes).map_err(|_| Error::new())?; if r.is_zero().into() || s.is_zero().into() { return Err(Error::new()); } Ok(Self { r, s }) } /// Parse a signature from a byte slice. pub fn from_slice(slice: &[u8]) -> Result { if slice.len() == SignatureSize::::USIZE { Self::from_bytes(SignatureBytes::::from_slice(slice)) } else { Err(Error::new()) } } /// Parse a signature from ASN.1 DER. #[cfg(feature = "der")] pub fn from_der(bytes: &[u8]) -> Result where der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { der::Signature::::try_from(bytes).and_then(Self::try_from) } /// Create a [`Signature`] from the serialized `r` and `s` scalar values /// which comprise the signature. pub fn from_scalars(r: impl Into>, s: impl Into>) -> Result { Self::try_from(r.into().concat(s.into()).as_slice()) } /// Split the signature into its `r` and `s` components, represented as bytes. pub fn split_bytes(&self) -> (FieldBytes, FieldBytes) { (self.r.to_bytes(), self.s.to_bytes()) } /// Serialize this signature as bytes. pub fn to_bytes(&self) -> SignatureBytes { let mut bytes = SignatureBytes::::default(); let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE); r_bytes.copy_from_slice(&self.r.to_bytes()); s_bytes.copy_from_slice(&self.s.to_bytes()); bytes } /// Serialize this signature as ASN.1 DER. #[cfg(feature = "der")] pub fn to_der(&self) -> der::Signature where der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { let (r, s) = self.split_bytes(); der::Signature::from_components(&r, &s).expect("DER encoding error") } /// Convert this signature into a byte vector. #[cfg(feature = "alloc")] pub fn to_vec(&self) -> Vec { self.to_bytes().to_vec() } } #[cfg(feature = "arithmetic")] impl Signature where C: PrimeCurve + CurveArithmetic, SignatureSize: ArrayLength, { /// Get the `r` component of this signature pub fn r(&self) -> NonZeroScalar { NonZeroScalar::new(self.r.into()).unwrap() } /// Get the `s` component of this signature pub fn s(&self) -> NonZeroScalar { NonZeroScalar::new(self.s.into()).unwrap() } /// Split the signature into its `r` and `s` scalars. pub fn split_scalars(&self) -> (NonZeroScalar, NonZeroScalar) { (self.r(), self.s()) } /// Normalize signature into "low S" form as described in /// [BIP 0062: Dealing with Malleability][1]. /// /// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki pub fn normalize_s(&self) -> Option { let s = self.s(); if s.is_high().into() { let mut result = self.clone(); result.s = ScalarPrimitive::from(-s); Some(result) } else { None } } } impl Copy for Signature where C: PrimeCurve, SignatureSize: ArrayLength, as ArrayLength>::ArrayType: Copy, { } impl From> for SignatureBytes where C: PrimeCurve, SignatureSize: ArrayLength, { fn from(signature: Signature) -> SignatureBytes { signature.to_bytes() } } impl SignatureEncoding for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { type Repr = SignatureBytes; } impl TryFrom<&[u8]> for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { type Error = Error; fn try_from(slice: &[u8]) -> Result { Self::from_slice(slice) } } impl fmt::Debug for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ecdsa::Signature<{:?}>(", C::default())?; for byte in self.to_bytes() { write!(f, "{:02X}", byte)?; } write!(f, ")") } } impl fmt::Display for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:X}", self) } } impl fmt::LowerHex for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for byte in self.to_bytes() { write!(f, "{:02x}", byte)?; } Ok(()) } } impl fmt::UpperHex for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for byte in self.to_bytes() { write!(f, "{:02X}", byte)?; } Ok(()) } } #[cfg(feature = "arithmetic")] impl str::FromStr for Signature where C: PrimeCurve + CurveArithmetic, SignatureSize: ArrayLength, { type Err = Error; fn from_str(hex: &str) -> Result { if hex.as_bytes().len() != C::FieldBytesSize::USIZE * 4 { return Err(Error::new()); } // This check is mainly to ensure `hex.split_at` below won't panic if !hex .as_bytes() .iter() .all(|&byte| matches!(byte, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z')) { return Err(Error::new()); } let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2); let r = r_hex .parse::>() .map_err(|_| Error::new())?; let s = s_hex .parse::>() .map_err(|_| Error::new())?; Self::from_scalars(r, s) } } /// ECDSA [`ObjectIdentifier`] which identifies the digest used by default /// with the `Signer` and `Verifier` traits. /// /// To support non-default digest algorithms, use the [`SignatureWithOid`] /// type instead. #[cfg(all(feature = "digest", feature = "hazmat"))] impl AssociatedOid for Signature where C: hazmat::DigestPrimitive, C::Digest: AssociatedOid, { const OID: ObjectIdentifier = match ecdsa_oid_for_digest(C::Digest::OID) { Some(oid) => oid, None => panic!("no RFC5758 ECDSA OID defined for DigestPrimitive::Digest"), }; } /// ECDSA `AlgorithmIdentifier` which identifies the digest used by default /// with the `Signer` and `Verifier` traits. #[cfg(feature = "pkcs8")] impl AssociatedAlgorithmIdentifier for Signature where C: PrimeCurve, Self: AssociatedOid, { type Params = AnyRef<'static>; const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef { oid: Self::OID, parameters: None, }; } #[cfg(feature = "serde")] impl Serialize for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn serialize(&self, serializer: S) -> core::result::Result where S: ser::Serializer, { serdect::array::serialize_hex_upper_or_bin(&self.to_bytes(), serializer) } } #[cfg(feature = "serde")] impl<'de, C> Deserialize<'de> for Signature where C: PrimeCurve, SignatureSize: ArrayLength, { fn deserialize(deserializer: D) -> core::result::Result where D: de::Deserializer<'de>, { let mut bytes = SignatureBytes::::default(); serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?; Self::try_from(bytes.as_slice()).map_err(de::Error::custom) } } /// An extended [`Signature`] type which is parameterized by an /// `ObjectIdentifier` which identifies the ECDSA variant used by a /// particular signature. /// /// Valid `ObjectIdentifiers` are defined in [RFC5758 § 3.2]: /// /// - SHA-224: [`ECDSA_SHA224_OID`] (1.2.840.10045.4.3.1) /// - SHA-256: [`ECDSA_SHA256_OID`] (1.2.840.10045.4.3.2) /// - SHA-384: [`ECDSA_SHA384_OID`] (1.2.840.10045.4.3.3) /// - SHA-512: [`ECDSA_SHA512_OID`] (1.2.840.10045.4.3.4) /// /// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 #[cfg(feature = "digest")] #[derive(Clone, Eq, PartialEq)] pub struct SignatureWithOid { /// Inner signature type. signature: Signature, /// OID which identifies the ECDSA variant used. /// /// MUST be one of the ECDSA algorithm variants as defined in RFC5758. /// /// These OIDs begin with `1.2.840.10045.4`. oid: ObjectIdentifier, } #[cfg(feature = "digest")] impl SignatureWithOid where C: PrimeCurve, { /// Create a new signature with an explicitly provided OID. /// /// OID must begin with `1.2.840.10045.4`, the [RFC5758] OID prefix for /// ECDSA variants. /// /// [RFC5758]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 pub fn new(signature: Signature, oid: ObjectIdentifier) -> Result { // TODO(tarcieri): use `ObjectIdentifier::starts_with` for (arc1, arc2) in ObjectIdentifier::new_unwrap("1.2.840.10045.4.3") .arcs() .zip(oid.arcs()) { if arc1 != arc2 { return Err(Error::new()); } } Ok(Self { signature, oid }) } /// Create a new signature, determining the OID from the given digest. /// /// Supports SHA-2 family digests as enumerated in [RFC5758 § 3.2], i.e. /// SHA-224, SHA-256, SHA-384, or SHA-512. /// /// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2 pub fn new_with_digest(signature: Signature) -> Result where D: AssociatedOid + Digest, { let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?; Ok(Self { signature, oid }) } /// Parse a signature from fixed-with bytes. pub fn from_bytes_with_digest(bytes: &SignatureBytes) -> Result where D: AssociatedOid + Digest, SignatureSize: ArrayLength, { Self::new_with_digest::(Signature::::from_bytes(bytes)?) } /// Parse a signature from a byte slice. pub fn from_slice_with_digest(slice: &[u8]) -> Result where D: AssociatedOid + Digest, SignatureSize: ArrayLength, { Self::new_with_digest::(Signature::::from_slice(slice)?) } /// Get the fixed-width ECDSA signature. pub fn signature(&self) -> &Signature { &self.signature } /// Get the ECDSA OID for this signature. pub fn oid(&self) -> ObjectIdentifier { self.oid } /// Serialize this signature as bytes. pub fn to_bytes(&self) -> SignatureBytes where SignatureSize: ArrayLength, { self.signature.to_bytes() } } #[cfg(feature = "digest")] impl Copy for SignatureWithOid where C: PrimeCurve, SignatureSize: ArrayLength, as ArrayLength>::ArrayType: Copy, { } #[cfg(feature = "digest")] impl From> for Signature where C: PrimeCurve, { fn from(sig: SignatureWithOid) -> Signature { sig.signature } } #[cfg(feature = "digest")] impl From> for SignatureBytes where C: PrimeCurve, SignatureSize: ArrayLength, { fn from(signature: SignatureWithOid) -> SignatureBytes { signature.to_bytes() } } /// NOTE: this implementation assumes the default digest for the given elliptic /// curve as defined by [`hazmat::DigestPrimitive`]. /// /// When working with alternative digests, you will need to use e.g. /// [`SignatureWithOid::new_with_digest`]. #[cfg(all(feature = "digest", feature = "hazmat"))] impl SignatureEncoding for SignatureWithOid where C: hazmat::DigestPrimitive, C::Digest: AssociatedOid, SignatureSize: ArrayLength, { type Repr = SignatureBytes; } /// NOTE: this implementation assumes the default digest for the given elliptic /// curve as defined by [`hazmat::DigestPrimitive`]. /// /// When working with alternative digests, you will need to use e.g. /// [`SignatureWithOid::new_with_digest`]. #[cfg(all(feature = "digest", feature = "hazmat"))] impl TryFrom<&[u8]> for SignatureWithOid where C: hazmat::DigestPrimitive, C::Digest: AssociatedOid, SignatureSize: ArrayLength, { type Error = Error; fn try_from(slice: &[u8]) -> Result { Self::new(Signature::::from_slice(slice)?, C::Digest::OID) } } #[cfg(all(feature = "alloc", feature = "pkcs8"))] impl DynAssociatedAlgorithmIdentifier for SignatureWithOid where C: PrimeCurve, { fn algorithm_identifier(&self) -> spki::Result { Ok(AlgorithmIdentifierOwned { oid: self.oid, parameters: None, }) } } /// Get the ECDSA OID for a given digest OID. #[cfg(feature = "digest")] const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option { match digest_oid { SHA224_OID => Some(ECDSA_SHA224_OID), SHA256_OID => Some(ECDSA_SHA256_OID), SHA384_OID => Some(ECDSA_SHA384_OID), SHA512_OID => Some(ECDSA_SHA512_OID), _ => None, } }