Skip to main content

ecdsa/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
6    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
7)]
8
9//! ## `serde` support
10//!
11//! When the `serde` feature of this crate is enabled, `Serialize` and `Deserialize` impls are
12//! provided for the [`Signature`] and [`VerifyingKey`] types.
13//!
14//! Please see type-specific documentation for more information.
15//!
16//! ## Interop
17//!
18//! Any crates which provide an implementation of ECDSA for a particular elliptic curve can leverage
19//! the types from this crate, along with the [`k256`], [`p256`], and/or [`p384`] crates to expose
20//! ECDSA functionality in a generic, interoperable way by leveraging the [`Signature`] type with in
21//! conjunction with the [`signature::Signer`] and [`signature::Verifier`] traits.
22//!
23//! For example, the [`ring-compat`] crate implements the [`signature::Signer`] and
24//! [`signature::Verifier`] traits in conjunction with the [`p256::ecdsa::Signature`] and
25//! [`p384::ecdsa::Signature`] types to wrap the ECDSA implementations from [*ring*] in a generic,
26//! interoperable API.
27//!
28//! [`k256`]: https://docs.rs/k256
29//! [`p256`]: https://docs.rs/p256
30//! [`p256::ecdsa::Signature`]: https://docs.rs/p256/latest/p256/ecdsa/type.Signature.html
31//! [`p384`]: https://docs.rs/p384
32//! [`p384::ecdsa::Signature`]: https://docs.rs/p384/latest/p384/ecdsa/type.Signature.html
33//! [`ring-compat`]: https://docs.rs/ring-compat
34//! [*ring*]: https://docs.rs/ring
35
36#[cfg(feature = "alloc")]
37extern crate alloc;
38
39mod recovery;
40
41#[cfg(feature = "der")]
42pub mod der;
43#[cfg(feature = "dev")]
44pub mod dev;
45#[cfg(feature = "algorithm")]
46pub mod hazmat;
47#[cfg(feature = "algorithm")]
48mod signing;
49#[cfg(feature = "algorithm")]
50mod verifying;
51
52pub use crate::recovery::RecoveryId;
53
54// Re-export the `elliptic-curve` crate (and select types)
55pub use elliptic_curve::{self, PrimeCurve, sec1::Sec1Point};
56
57// Re-export the `signature` crate (and select types)
58pub use signature::{self, Error, Result, SignatureEncoding};
59use zeroize::Zeroize;
60
61#[cfg(feature = "algorithm")]
62pub use crate::signing::SigningKey;
63#[cfg(feature = "algorithm")]
64pub use crate::verifying::VerifyingKey;
65
66use core::{fmt, ops::Add};
67use elliptic_curve::{
68    Curve, FieldBytes, FieldBytesSize, ScalarValue,
69    array::{Array, ArraySize, typenum::Unsigned},
70};
71
72#[cfg(feature = "alloc")]
73use alloc::vec::Vec;
74#[cfg(feature = "digest")]
75use digest::{
76    Digest, FixedOutput,
77    common::BlockSizeUser,
78    const_oid::{AssociatedOid, ObjectIdentifier},
79};
80#[cfg(all(feature = "alloc", feature = "pkcs8"))]
81use elliptic_curve::pkcs8::spki::{
82    self, AlgorithmIdentifierOwned, DynAssociatedAlgorithmIdentifier,
83};
84#[cfg(feature = "pkcs8")]
85use elliptic_curve::pkcs8::spki::{
86    AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, der::AnyRef,
87};
88#[cfg(feature = "serde")]
89use serdect::serde::{Deserialize, Serialize, de, ser};
90#[cfg(feature = "algorithm")]
91use {
92    core::str,
93    elliptic_curve::{
94        CurveArithmetic, NonZeroScalar, scalar::IsHigh, subtle::ConditionallySelectable,
95    },
96};
97
98/// OID for ECDSA with SHA-224 digests.
99///
100/// ```text
101/// ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
102///      us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 1 }
103/// ```
104// TODO(tarcieri): use `ObjectIdentifier::push_arc` when const unwrap is stable
105#[cfg(feature = "digest")]
106pub const ECDSA_SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.1");
107
108/// OID for ECDSA with SHA-256 digests.
109///
110/// ```text
111/// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
112///      us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
113/// ```
114#[cfg(feature = "digest")]
115pub const ECDSA_SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
116
117/// OID for ECDSA with SHA-384 digests.
118///
119/// ```text
120/// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
121///      us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 }
122/// ```
123#[cfg(feature = "digest")]
124pub const ECDSA_SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.3");
125
126/// OID for ECDSA with SHA-512 digests.
127///
128/// ```text
129/// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
130///      us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 }
131/// ```
132#[cfg(feature = "digest")]
133pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4");
134
135#[cfg(feature = "digest")]
136const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4");
137#[cfg(feature = "digest")]
138const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
139#[cfg(feature = "digest")]
140const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
141#[cfg(feature = "digest")]
142const SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
143
144/// Marker trait for elliptic curves intended for use with ECDSA.
145pub trait EcdsaCurve:
146    Curve<FieldBytesSize: Add<Output: ArraySize<ArrayType<u8>: Copy>>> + PrimeCurve
147{
148    /// Does this curve use low-S normalized signatures?
149    ///
150    /// This is typically `false`. See [`Signature::normalize_s`] for more information.
151    const NORMALIZE_S: bool;
152}
153
154/// Size of a fixed sized signature for the given elliptic curve.
155pub type SignatureSize<C> = <FieldBytesSize<C> as Add>::Output;
156
157/// Fixed-size byte array containing an ECDSA signature
158pub type SignatureBytes<C> = Array<u8, SignatureSize<C>>;
159
160/// ECDSA signature (fixed-size, a.k.a. [IEEE P1363]). Generic over elliptic curve types.
161///
162/// Serialized as fixed-sized big endian scalar values with no added framing:
163///
164/// - `r`: field element size for the given curve, big-endian
165/// - `s`: field element size for the given curve, big-endian
166///
167/// Both `r` and `s` MUST be non-zero.
168///
169/// For example, in a curve with a 256-bit modulus like NIST P-256 or secp256k1, `r` and `s` are
170/// both 32-bytes and serialized as big endian, resulting in a signature with a total of 64-bytes.
171///
172/// ASN.1 DER-encoded signatures also supported via the [`Signature::from_der`] and
173/// [`Signature::to_der`] methods.
174///
175/// # `serde` support
176///
177/// When the `serde` feature of this crate is enabled, it provides support for serializing and
178/// deserializing ECDSA signatures using the `Serialize` and `Deserialize` traits.
179///
180/// The serialization uses a hexadecimal encoding when used with "human readable" text formats, and
181/// a binary encoding otherwise.
182///
183/// [IEEE P1363]: https://en.wikipedia.org/wiki/IEEE_P1363
184#[derive(Clone, Copy, Eq, PartialEq)]
185pub struct Signature<C: EcdsaCurve> {
186    r: ScalarValue<C>,
187    s: ScalarValue<C>,
188}
189
190impl<C> Signature<C>
191where
192    C: EcdsaCurve,
193{
194    /// Parse a signature from fixed-width bytes, i.e. 2 * the size of [`FieldBytes`] for a
195    /// particular curve.
196    ///
197    /// # Errors
198    /// If the `r` and/or `s` component of the signature is out-of-range when interpreted as a big
199    /// endian integer.
200    pub fn from_bytes(bytes: &SignatureBytes<C>) -> Result<Self> {
201        let chunks = FieldBytes::<C>::slice_as_chunks(bytes).0;
202        let r = chunks[0];
203        let s = chunks[1];
204        Self::from_scalars(r, s)
205    }
206
207    /// Parse a signature from a byte slice.
208    ///
209    /// # Errors
210    /// Returns [`Error`] in the event the signature is not the expected size, i.e. 2 * the size of
211    /// [`FieldBytes`] for a particular curve.
212    pub fn from_slice(slice: &[u8]) -> Result<Self> {
213        <&SignatureBytes<C>>::try_from(slice)
214            .map_err(|_| Error::new())
215            .and_then(Self::from_bytes)
216    }
217
218    /// Parse a signature from ASN.1 DER.
219    ///
220    /// # Errors
221    /// Returns [`Error`] if `input` failed to parse as an ASN.1 DER-encoded ECDSA signature.
222    #[cfg(feature = "der")]
223    pub fn from_der(bytes: &[u8]) -> Result<Self>
224    where
225        der::MaxSize<C>: ArraySize,
226        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
227    {
228        der::Signature::<C>::try_from(bytes).and_then(Self::try_from)
229    }
230
231    /// Create a [`Signature`] from the serialized `r` and `s` scalar values
232    /// which comprise the signature.
233    ///
234    /// # Errors
235    /// If the `r` and/or `s` component of the signature is out-of-range when interpreted as a big
236    /// endian integer.
237    pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
238        let r = ScalarValue::from_slice(&r.into()).map_err(|_| Error::new())?;
239        let s = ScalarValue::from_slice(&s.into()).map_err(|_| Error::new())?;
240
241        if r.is_zero().into() || s.is_zero().into() {
242            return Err(Error::new());
243        }
244
245        Ok(Self { r, s })
246    }
247
248    /// Split the signature into its `r` and `s` components, represented as bytes.
249    pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
250        (self.r.to_bytes(), self.s.to_bytes())
251    }
252
253    /// Serialize this signature as bytes.
254    pub fn to_bytes(&self) -> SignatureBytes<C> {
255        let mut bytes = SignatureBytes::<C>::default();
256        let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE);
257        r_bytes.copy_from_slice(&self.r.to_bytes());
258        s_bytes.copy_from_slice(&self.s.to_bytes());
259        bytes
260    }
261
262    /// Serialize this signature as ASN.1 DER.
263    #[cfg(feature = "der")]
264    #[allow(clippy::missing_panics_doc, reason = "should not panic in practice")]
265    pub fn to_der(&self) -> der::Signature<C>
266    where
267        der::MaxSize<C>: ArraySize,
268        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
269    {
270        let (r, s) = self.split_bytes();
271        der::Signature::from_components(&r, &s).expect("DER encoding error")
272    }
273
274    /// Convert this signature into a byte vector.
275    #[cfg(feature = "alloc")]
276    pub fn to_vec(&self) -> Vec<u8> {
277        self.to_bytes().to_vec()
278    }
279}
280
281#[cfg(feature = "algorithm")]
282impl<C> Signature<C>
283where
284    C: EcdsaCurve + CurveArithmetic,
285{
286    /// Get the `r` component of this signature
287    pub fn r(&self) -> NonZeroScalar<C> {
288        NonZeroScalar::new(self.r.into()).unwrap()
289    }
290
291    /// Get the `s` component of this signature
292    pub fn s(&self) -> NonZeroScalar<C> {
293        NonZeroScalar::new(self.s.into()).unwrap()
294    }
295
296    /// Split the signature into its `r` and `s` scalars.
297    pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
298        (self.r(), self.s())
299    }
300
301    /// Normalize signature into "low S" form described in [BIP 0062: Dealing with Malleability][1].
302    ///
303    /// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki
304    #[must_use]
305    pub fn normalize_s(&self) -> Self {
306        let mut result = *self;
307        let s_inv = ScalarValue::from(-self.s());
308        result.s.conditional_assign(&s_inv, self.s.is_high());
309        result
310    }
311}
312
313impl<C> From<Signature<C>> for SignatureBytes<C>
314where
315    C: EcdsaCurve,
316{
317    fn from(signature: Signature<C>) -> SignatureBytes<C> {
318        signature.to_bytes()
319    }
320}
321
322impl<C> SignatureEncoding for Signature<C>
323where
324    C: EcdsaCurve,
325{
326    type Repr = SignatureBytes<C>;
327}
328
329impl<C> TryFrom<&[u8]> for Signature<C>
330where
331    C: EcdsaCurve,
332{
333    type Error = Error;
334
335    fn try_from(slice: &[u8]) -> Result<Self> {
336        Self::from_slice(slice)
337    }
338}
339
340impl<C> fmt::Debug for Signature<C>
341where
342    C: EcdsaCurve,
343{
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        write!(f, "ecdsa::Signature<{:?}>(", C::default())?;
346
347        for byte in self.to_bytes() {
348            write!(f, "{byte:02X}")?;
349        }
350
351        write!(f, ")")
352    }
353}
354
355impl<C> fmt::Display for Signature<C>
356where
357    C: EcdsaCurve,
358{
359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        write!(f, "{self:X}")
361    }
362}
363
364impl<C> core::hash::Hash for Signature<C>
365where
366    C: EcdsaCurve,
367{
368    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
369        self.to_bytes().hash(state);
370    }
371}
372
373impl<C> fmt::LowerHex for Signature<C>
374where
375    C: EcdsaCurve,
376{
377    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378        for byte in self.to_bytes() {
379            write!(f, "{byte:02x}")?;
380        }
381        Ok(())
382    }
383}
384
385impl<C> fmt::UpperHex for Signature<C>
386where
387    C: EcdsaCurve,
388{
389    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390        for byte in self.to_bytes() {
391            write!(f, "{byte:02X}")?;
392        }
393        Ok(())
394    }
395}
396
397#[cfg(feature = "algorithm")]
398impl<C> str::FromStr for Signature<C>
399where
400    C: EcdsaCurve + CurveArithmetic,
401{
402    type Err = Error;
403
404    fn from_str(hex: &str) -> Result<Self> {
405        if hex.len() != C::FieldBytesSize::USIZE * 4 {
406            return Err(Error::new());
407        }
408
409        let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2);
410
411        let r = r_hex
412            .parse::<NonZeroScalar<C>>()
413            .map_err(|_| Error::new())?;
414
415        let s = s_hex
416            .parse::<NonZeroScalar<C>>()
417            .map_err(|_| Error::new())?;
418
419        Self::from_scalars(r, s)
420    }
421}
422
423/// ECDSA [`ObjectIdentifier`] which identifies the digest used by default with the `Signer` and
424/// `Verifier` traits.
425///
426/// To support non-default digest algorithms, use the [`SignatureWithOid`] type instead.
427#[cfg(feature = "digest")]
428impl<C> AssociatedOid for Signature<C>
429where
430    C: DigestAlgorithm,
431    C::Digest: AssociatedOid,
432{
433    const OID: ObjectIdentifier = match ecdsa_oid_for_digest(C::Digest::OID) {
434        Some(oid) => oid,
435        None => panic!("no RFC5758 ECDSA OID defined for DigestAlgorithm::Digest"),
436    };
437}
438
439/// ECDSA `AlgorithmIdentifier` which identifies the digest used by default
440/// with the `Signer` and `Verifier` traits.
441#[cfg(feature = "pkcs8")]
442impl<C> AssociatedAlgorithmIdentifier for Signature<C>
443where
444    C: EcdsaCurve,
445    Self: AssociatedOid,
446{
447    type Params = AnyRef<'static>;
448
449    const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
450        oid: Self::OID,
451        parameters: None,
452    };
453}
454
455#[cfg(feature = "serde")]
456impl<C> Serialize for Signature<C>
457where
458    C: EcdsaCurve,
459{
460    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
461    where
462        S: ser::Serializer,
463    {
464        serdect::array::serialize_hex_upper_or_bin(&self.to_bytes(), serializer)
465    }
466}
467
468#[cfg(feature = "serde")]
469impl<'de, C> Deserialize<'de> for Signature<C>
470where
471    C: EcdsaCurve,
472{
473    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
474    where
475        D: de::Deserializer<'de>,
476    {
477        let mut bytes = SignatureBytes::<C>::default();
478        serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?;
479        Self::try_from(bytes.as_slice()).map_err(de::Error::custom)
480    }
481}
482
483impl<C: EcdsaCurve> Zeroize for Signature<C> {
484    fn zeroize(&mut self) {
485        self.r = ScalarValue::ONE;
486        self.s = ScalarValue::ONE;
487    }
488}
489
490/// An extended [`Signature`] type which is parameterized by an `ObjectIdentifier` which identifies
491/// the ECDSA variant used by a particular signature.
492///
493/// Valid `ObjectIdentifiers` are defined in [RFC5758 § 3.2]:
494///
495/// - SHA-224: [`ECDSA_SHA224_OID`] (1.2.840.10045.4.3.1)
496/// - SHA-256: [`ECDSA_SHA256_OID`] (1.2.840.10045.4.3.2)
497/// - SHA-384: [`ECDSA_SHA384_OID`] (1.2.840.10045.4.3.3)
498/// - SHA-512: [`ECDSA_SHA512_OID`] (1.2.840.10045.4.3.4)
499///
500/// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2
501#[cfg(feature = "digest")]
502#[derive(Clone, Copy, Debug, Eq, PartialEq)]
503pub struct SignatureWithOid<C: EcdsaCurve> {
504    /// Inner signature type.
505    signature: Signature<C>,
506
507    /// OID which identifies the ECDSA variant used.
508    ///
509    /// MUST be one of the ECDSA algorithm variants as defined in RFC5758.
510    ///
511    /// These OIDs begin with `1.2.840.10045.4`.
512    oid: ObjectIdentifier,
513}
514
515#[cfg(feature = "digest")]
516impl<C> SignatureWithOid<C>
517where
518    C: EcdsaCurve,
519{
520    /// Create a new signature with an explicitly provided OID.
521    ///
522    /// OID must begin with `1.2.840.10045.4`, the [RFC5758] OID prefix for ECDSA variants.
523    ///
524    /// [RFC5758]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2
525    ///
526    /// # Errors
527    /// Returns [`Error`] if `oid` does not start with `1.2.840.10045.4`.
528    pub fn new(signature: Signature<C>, oid: ObjectIdentifier) -> Result<Self> {
529        if !oid.starts_with(ObjectIdentifier::new_unwrap("1.2.840.10045.4")) {
530            return Err(Error::new());
531        }
532
533        Ok(Self { signature, oid })
534    }
535
536    /// Create a new signature, determining the OID from the given digest.
537    ///
538    /// Supports SHA-2 family digests as enumerated in [RFC5758 § 3.2], i.e. SHA-224, SHA-256,
539    /// SHA-384, or SHA-512.
540    ///
541    /// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2
542    ///
543    /// # Errors
544    /// Returns [`Error`] if the [`AssociatedOid`] for `D` is not one from the SHA2 family.
545    pub fn new_with_digest<D>(signature: Signature<C>) -> Result<Self>
546    where
547        D: AssociatedOid + Digest,
548    {
549        let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
550        Ok(Self { signature, oid })
551    }
552
553    /// Parse a signature from fixed-with bytes.
554    ///
555    /// # Errors
556    /// Returns [`Error`] if [`Signature`] fails to parse, or if `D` is not a valid digest.
557    /// See [`SignatureWithOid::new_with_digest`] documentation.
558    pub fn from_bytes_with_digest<D>(bytes: &SignatureBytes<C>) -> Result<Self>
559    where
560        D: AssociatedOid + Digest,
561    {
562        Self::new_with_digest::<D>(Signature::<C>::from_bytes(bytes)?)
563    }
564
565    /// Parse a signature from a byte slice.
566    ///
567    /// # Errors
568    /// Returns [`Error`] if [`Signature`] fails to parse, or if `D` is not a valid digest.
569    /// See [`SignatureWithOid::new_with_digest`] documentation.
570    pub fn from_slice_with_digest<D>(slice: &[u8]) -> Result<Self>
571    where
572        D: AssociatedOid + Digest,
573    {
574        Self::new_with_digest::<D>(Signature::<C>::from_slice(slice)?)
575    }
576
577    /// Parse a signature from ASN.1 DER and associate the given digest's OID with it.
578    ///
579    /// # Errors
580    /// Returns [`Error`] if `input` failed to parse as an ASN.1 DER-encoded ECDSA signature,
581    /// or if `D` is not a valid digest.
582    #[cfg(feature = "der")]
583    pub fn from_der_with_digest<D>(der_bytes: &[u8]) -> Result<Self>
584    where
585        D: AssociatedOid + Digest,
586        der::MaxSize<C>: ArraySize,
587        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
588    {
589        Self::new_with_digest::<D>(Signature::<C>::from_der(der_bytes)?)
590    }
591
592    /// Parse a signature from ASN.1 DER and associate the given OID with it.
593    ///
594    /// # Errors
595    /// Returns [`Error`] if `input` failed to parse as an ASN.1 DER-encoded ECDSA signature,
596    /// or if `D` is not a valid digest.
597    #[cfg(feature = "der")]
598    pub fn from_der_with_oid(der_bytes: &[u8], oid: ObjectIdentifier) -> Result<Self>
599    where
600        der::MaxSize<C>: ArraySize,
601        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
602    {
603        Self::new(Signature::<C>::from_der(der_bytes)?, oid)
604    }
605
606    /// Get the fixed-width ECDSA signature.
607    pub fn signature(&self) -> &Signature<C> {
608        &self.signature
609    }
610
611    /// Get the ECDSA OID for this signature.
612    pub fn oid(&self) -> ObjectIdentifier {
613        self.oid
614    }
615
616    /// Serialize this signature as fixed-width bytes.
617    pub fn to_bytes(&self) -> SignatureBytes<C>
618where {
619        self.signature.to_bytes()
620    }
621
622    /// Serialize this signature as ASN.1 DER.
623    ///
624    /// Note that this includes only the `r` and `s` signature components, and not the OID.
625    ///
626    /// See [`der::Signature`] documentation for more information.
627    #[cfg(feature = "der")]
628    pub fn to_der(&self) -> der::Signature<C>
629    where
630        der::MaxSize<C>: ArraySize,
631        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
632    {
633        self.signature.into()
634    }
635}
636
637/// Bind a preferred [`Digest`] algorithm to an elliptic curve type.
638///
639/// Generally there is a preferred variety of the SHA-2 family used with ECDSA
640/// for a particular elliptic curve.
641#[cfg(feature = "digest")]
642pub trait DigestAlgorithm: EcdsaCurve {
643    /// Preferred digest to use when computing ECDSA signatures for this
644    /// elliptic curve. This is typically a member of the SHA-2 family.
645    type Digest: BlockSizeUser + Digest + FixedOutput;
646}
647
648#[cfg(feature = "digest")]
649impl<C> core::hash::Hash for SignatureWithOid<C>
650where
651    C: EcdsaCurve,
652{
653    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
654        self.signature.hash(state);
655        self.oid.hash(state);
656    }
657}
658
659#[cfg(feature = "digest")]
660impl<C> From<SignatureWithOid<C>> for Signature<C>
661where
662    C: EcdsaCurve,
663{
664    fn from(sig: SignatureWithOid<C>) -> Signature<C> {
665        sig.signature
666    }
667}
668
669#[cfg(feature = "digest")]
670impl<C> From<SignatureWithOid<C>> for SignatureBytes<C>
671where
672    C: EcdsaCurve,
673{
674    fn from(signature: SignatureWithOid<C>) -> SignatureBytes<C> {
675        signature.to_bytes()
676    }
677}
678
679#[cfg(all(feature = "der", feature = "digest"))]
680impl<C> From<SignatureWithOid<C>> for der::Signature<C>
681where
682    C: EcdsaCurve,
683    der::MaxSize<C>: ArraySize,
684    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
685{
686    fn from(sig: SignatureWithOid<C>) -> der::Signature<C> {
687        sig.to_der()
688    }
689}
690
691#[cfg(all(feature = "der", feature = "digest"))]
692impl<C> From<&SignatureWithOid<C>> for der::Signature<C>
693where
694    C: EcdsaCurve,
695    der::MaxSize<C>: ArraySize,
696    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
697{
698    fn from(sig: &SignatureWithOid<C>) -> der::Signature<C> {
699        sig.to_der()
700    }
701}
702
703/// NOTE: this implementation assumes the default digest for the given elliptic
704/// curve as defined by [`DigestAlgorithm`].
705///
706/// When working with alternative digests, you will need to use e.g.
707/// [`SignatureWithOid::new_with_digest`].
708#[cfg(feature = "digest")]
709impl<C> SignatureEncoding for SignatureWithOid<C>
710where
711    C: DigestAlgorithm,
712    C::Digest: AssociatedOid,
713{
714    type Repr = SignatureBytes<C>;
715}
716
717/// NOTE: this implementation assumes the default digest for the given elliptic
718/// curve as defined by [`DigestAlgorithm`].
719///
720/// When working with alternative digests, you will need to use e.g.
721/// [`SignatureWithOid::new_with_digest`].
722#[cfg(feature = "digest")]
723impl<C> TryFrom<&[u8]> for SignatureWithOid<C>
724where
725    C: DigestAlgorithm,
726    C::Digest: AssociatedOid,
727{
728    type Error = Error;
729
730    fn try_from(slice: &[u8]) -> Result<Self> {
731        Self::new(Signature::<C>::from_slice(slice)?, C::Digest::OID)
732    }
733}
734
735#[cfg(all(feature = "alloc", feature = "pkcs8"))]
736impl<C> DynAssociatedAlgorithmIdentifier for SignatureWithOid<C>
737where
738    C: EcdsaCurve,
739{
740    fn algorithm_identifier(&self) -> spki::Result<AlgorithmIdentifierOwned> {
741        Ok(AlgorithmIdentifierOwned {
742            oid: self.oid,
743            parameters: None,
744        })
745    }
746}
747
748/// Get the ECDSA OID for a given digest OID.
749#[cfg(feature = "digest")]
750const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option<ObjectIdentifier> {
751    match digest_oid {
752        SHA224_OID => Some(ECDSA_SHA224_OID),
753        SHA256_OID => Some(ECDSA_SHA256_OID),
754        SHA384_OID => Some(ECDSA_SHA384_OID),
755        SHA512_OID => Some(ECDSA_SHA512_OID),
756        _ => None,
757    }
758}