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