ecdsa/
lib.rs

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