Coverage Report

Created: 2026-11-12 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ecdsa-0.17.0/src/lib.rs
Line
Count
Source
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")]
37
extern crate alloc;
38
39
mod recovery;
40
41
#[cfg(feature = "der")]
42
pub mod der;
43
#[cfg(feature = "dev")]
44
pub mod dev;
45
#[cfg(feature = "algorithm")]
46
pub mod hazmat;
47
#[cfg(feature = "algorithm")]
48
mod signing;
49
#[cfg(feature = "algorithm")]
50
mod verifying;
51
52
pub use crate::recovery::RecoveryId;
53
54
// Re-export the `elliptic-curve` crate (and select types)
55
pub use elliptic_curve::{self, PrimeCurve, sec1::Sec1Point};
56
57
// Re-export the `signature` crate (and select types)
58
pub use signature::{self, Error, Result, SignatureEncoding};
59
use zeroize::Zeroize;
60
61
#[cfg(feature = "algorithm")]
62
pub use crate::signing::SigningKey;
63
#[cfg(feature = "algorithm")]
64
pub use crate::verifying::VerifyingKey;
65
66
use core::{fmt, ops::Add};
67
use elliptic_curve::{
68
    Curve, FieldBytes, FieldBytesSize, ScalarValue,
69
    array::{Array, ArraySize, typenum::Unsigned},
70
};
71
72
#[cfg(feature = "alloc")]
73
use alloc::vec::Vec;
74
#[cfg(feature = "digest")]
75
use digest::{
76
    Digest, FixedOutput,
77
    common::BlockSizeUser,
78
    const_oid::{AssociatedOid, ObjectIdentifier},
79
};
80
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
81
use elliptic_curve::pkcs8::spki::{
82
    self, AlgorithmIdentifierOwned, DynAssociatedAlgorithmIdentifier,
83
};
84
#[cfg(feature = "pkcs8")]
85
use elliptic_curve::pkcs8::spki::{
86
    AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, der::AnyRef,
87
};
88
#[cfg(feature = "serde")]
89
use serdect::serde::{Deserialize, Serialize, de, ser};
90
#[cfg(feature = "algorithm")]
91
use {
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")]
106
pub 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")]
115
pub 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")]
124
pub 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")]
133
pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4");
134
135
#[cfg(feature = "digest")]
136
const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4");
137
#[cfg(feature = "digest")]
138
const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
139
#[cfg(feature = "digest")]
140
const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
141
#[cfg(feature = "digest")]
142
const 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.
145
pub 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.
155
pub type SignatureSize<C> = <FieldBytesSize<C> as Add>::Output;
156
157
/// Fixed-size byte array containing an ECDSA signature
158
pub 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)]
185
pub struct Signature<C: EcdsaCurve> {
186
    r: ScalarValue<C>,
187
    s: ScalarValue<C>,
188
}
189
190
impl<C> Signature<C>
191
where
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
0
    pub fn from_bytes(bytes: &SignatureBytes<C>) -> Result<Self> {
201
0
        let chunks = FieldBytes::<C>::slice_as_chunks(bytes).0;
202
0
        let r = chunks[0];
203
0
        let s = chunks[1];
204
0
        Self::from_scalars(r, s)
205
0
    }
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
0
    pub fn from_slice(slice: &[u8]) -> Result<Self> {
213
0
        <&SignatureBytes<C>>::try_from(slice)
214
0
            .map_err(|_| Error::new())
215
0
            .and_then(Self::from_bytes)
216
0
    }
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
0
    pub fn from_der(bytes: &[u8]) -> Result<Self>
224
0
    where
225
0
        der::MaxSize<C>: ArraySize,
226
0
        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
227
    {
228
0
        der::Signature::<C>::try_from(bytes).and_then(Self::try_from)
229
0
    }
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
2.16k
    pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
238
2.16k
        let r = ScalarValue::from_slice(&r.into()).map_err(|_| Error::new())?;
Unexecuted instantiation: <ecdsa::Signature<p256::NistP256>>::from_scalars::<p256::arithmetic::scalar::Scalar, p256::arithmetic::scalar::Scalar>::{closure#0}
Unexecuted instantiation: <ecdsa::Signature<_>>::from_scalars::<_, _>::{closure#0}
239
2.16k
        let s = ScalarValue::from_slice(&s.into()).map_err(|_| Error::new())?;
Unexecuted instantiation: <ecdsa::Signature<p256::NistP256>>::from_scalars::<p256::arithmetic::scalar::Scalar, p256::arithmetic::scalar::Scalar>::{closure#1}
Unexecuted instantiation: <ecdsa::Signature<_>>::from_scalars::<_, _>::{closure#1}
240
241
2.16k
        if r.is_zero().into() || s.is_zero().into() {
242
0
            return Err(Error::new());
243
2.16k
        }
244
245
2.16k
        Ok(Self { r, s })
246
2.16k
    }
<ecdsa::Signature<p256::NistP256>>::from_scalars::<p256::arithmetic::scalar::Scalar, p256::arithmetic::scalar::Scalar>
Line
Count
Source
237
2.16k
    pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
238
2.16k
        let r = ScalarValue::from_slice(&r.into()).map_err(|_| Error::new())?;
239
2.16k
        let s = ScalarValue::from_slice(&s.into()).map_err(|_| Error::new())?;
240
241
2.16k
        if r.is_zero().into() || s.is_zero().into() {
242
0
            return Err(Error::new());
243
2.16k
        }
244
245
2.16k
        Ok(Self { r, s })
246
2.16k
    }
Unexecuted instantiation: <ecdsa::Signature<_>>::from_scalars::<_, _>
247
248
    /// Split the signature into its `r` and `s` components, represented as bytes.
249
2.16k
    pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
250
2.16k
        (self.r.to_bytes(), self.s.to_bytes())
251
2.16k
    }
<ecdsa::Signature<p256::NistP256>>::split_bytes
Line
Count
Source
249
2.16k
    pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
250
2.16k
        (self.r.to_bytes(), self.s.to_bytes())
251
2.16k
    }
Unexecuted instantiation: <ecdsa::Signature<_>>::split_bytes
252
253
    /// Serialize this signature as bytes.
254
0
    pub fn to_bytes(&self) -> SignatureBytes<C> {
255
0
        let mut bytes = SignatureBytes::<C>::default();
256
0
        let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE);
257
0
        r_bytes.copy_from_slice(&self.r.to_bytes());
258
0
        s_bytes.copy_from_slice(&self.s.to_bytes());
259
0
        bytes
260
0
    }
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
2.16k
    pub fn to_der(&self) -> der::Signature<C>
266
2.16k
    where
267
2.16k
        der::MaxSize<C>: ArraySize,
268
2.16k
        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
269
    {
270
2.16k
        let (r, s) = self.split_bytes();
271
2.16k
        der::Signature::from_components(&r, &s).expect("DER encoding error")
272
2.16k
    }
<ecdsa::Signature<p256::NistP256>>::to_der
Line
Count
Source
265
2.16k
    pub fn to_der(&self) -> der::Signature<C>
266
2.16k
    where
267
2.16k
        der::MaxSize<C>: ArraySize,
268
2.16k
        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
269
    {
270
2.16k
        let (r, s) = self.split_bytes();
271
2.16k
        der::Signature::from_components(&r, &s).expect("DER encoding error")
272
2.16k
    }
Unexecuted instantiation: <ecdsa::Signature<_>>::to_der
273
274
    /// Convert this signature into a byte vector.
275
    #[cfg(feature = "alloc")]
276
0
    pub fn to_vec(&self) -> Vec<u8> {
277
0
        self.to_bytes().to_vec()
278
0
    }
279
}
280
281
#[cfg(feature = "algorithm")]
282
impl<C> Signature<C>
283
where
284
    C: EcdsaCurve + CurveArithmetic,
285
{
286
    /// Get the `r` component of this signature
287
0
    pub fn r(&self) -> NonZeroScalar<C> {
288
0
        NonZeroScalar::new(self.r.into()).unwrap()
289
0
    }
290
291
    /// Get the `s` component of this signature
292
0
    pub fn s(&self) -> NonZeroScalar<C> {
293
0
        NonZeroScalar::new(self.s.into()).unwrap()
294
0
    }
295
296
    /// Split the signature into its `r` and `s` scalars.
297
0
    pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
298
0
        (self.r(), self.s())
299
0
    }
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
0
    pub fn normalize_s(&self) -> Self {
306
0
        let mut result = *self;
307
0
        let s_inv = ScalarValue::from(-self.s());
308
0
        result.s.conditional_assign(&s_inv, self.s.is_high());
309
0
        result
310
0
    }
311
}
312
313
impl<C> From<Signature<C>> for SignatureBytes<C>
314
where
315
    C: EcdsaCurve,
316
{
317
0
    fn from(signature: Signature<C>) -> SignatureBytes<C> {
318
0
        signature.to_bytes()
319
0
    }
320
}
321
322
impl<C> SignatureEncoding for Signature<C>
323
where
324
    C: EcdsaCurve,
325
{
326
    type Repr = SignatureBytes<C>;
327
}
328
329
impl<C> TryFrom<&[u8]> for Signature<C>
330
where
331
    C: EcdsaCurve,
332
{
333
    type Error = Error;
334
335
0
    fn try_from(slice: &[u8]) -> Result<Self> {
336
0
        Self::from_slice(slice)
337
0
    }
338
}
339
340
impl<C> fmt::Debug for Signature<C>
341
where
342
    C: EcdsaCurve,
343
{
344
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345
0
        write!(f, "ecdsa::Signature<{:?}>(", C::default())?;
346
347
0
        for byte in self.to_bytes() {
348
0
            write!(f, "{byte:02X}")?;
349
        }
350
351
0
        write!(f, ")")
352
0
    }
353
}
354
355
impl<C> fmt::Display for Signature<C>
356
where
357
    C: EcdsaCurve,
358
{
359
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360
0
        write!(f, "{self:X}")
361
0
    }
362
}
363
364
impl<C> core::hash::Hash for Signature<C>
365
where
366
    C: EcdsaCurve,
367
{
368
0
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
369
0
        self.to_bytes().hash(state);
370
0
    }
371
}
372
373
impl<C> fmt::LowerHex for Signature<C>
374
where
375
    C: EcdsaCurve,
376
{
377
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378
0
        for byte in self.to_bytes() {
379
0
            write!(f, "{byte:02x}")?;
380
        }
381
0
        Ok(())
382
0
    }
383
}
384
385
impl<C> fmt::UpperHex for Signature<C>
386
where
387
    C: EcdsaCurve,
388
{
389
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390
0
        for byte in self.to_bytes() {
391
0
            write!(f, "{byte:02X}")?;
392
        }
393
0
        Ok(())
394
0
    }
395
}
396
397
#[cfg(feature = "algorithm")]
398
impl<C> str::FromStr for Signature<C>
399
where
400
    C: EcdsaCurve + CurveArithmetic,
401
{
402
    type Err = Error;
403
404
0
    fn from_str(hex: &str) -> Result<Self> {
405
0
        if hex.len() != C::FieldBytesSize::USIZE * 4 {
406
0
            return Err(Error::new());
407
0
        }
408
409
0
        let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2);
410
411
0
        let r = r_hex
412
0
            .parse::<NonZeroScalar<C>>()
413
0
            .map_err(|_| Error::new())?;
414
415
0
        let s = s_hex
416
0
            .parse::<NonZeroScalar<C>>()
417
0
            .map_err(|_| Error::new())?;
418
419
0
        Self::from_scalars(r, s)
420
0
    }
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")]
428
impl<C> AssociatedOid for Signature<C>
429
where
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")]
442
impl<C> AssociatedAlgorithmIdentifier for Signature<C>
443
where
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")]
456
impl<C> Serialize for Signature<C>
457
where
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")]
469
impl<'de, C> Deserialize<'de> for Signature<C>
470
where
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
483
impl<C: EcdsaCurve> Zeroize for Signature<C> {
484
0
    fn zeroize(&mut self) {
485
0
        self.r = ScalarValue::ONE;
486
0
        self.s = ScalarValue::ONE;
487
0
    }
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)]
503
pub 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")]
516
impl<C> SignatureWithOid<C>
517
where
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
0
    pub fn new(signature: Signature<C>, oid: ObjectIdentifier) -> Result<Self> {
529
0
        if !oid.starts_with(ObjectIdentifier::new_unwrap("1.2.840.10045.4")) {
530
0
            return Err(Error::new());
531
0
        }
532
533
0
        Ok(Self { signature, oid })
534
0
    }
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
0
    pub fn new_with_digest<D>(signature: Signature<C>) -> Result<Self>
546
0
    where
547
0
        D: AssociatedOid + Digest,
548
    {
549
0
        let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
550
0
        Ok(Self { signature, oid })
551
0
    }
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
0
    pub fn from_bytes_with_digest<D>(bytes: &SignatureBytes<C>) -> Result<Self>
559
0
    where
560
0
        D: AssociatedOid + Digest,
561
    {
562
0
        Self::new_with_digest::<D>(Signature::<C>::from_bytes(bytes)?)
563
0
    }
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
0
    pub fn from_slice_with_digest<D>(slice: &[u8]) -> Result<Self>
571
0
    where
572
0
        D: AssociatedOid + Digest,
573
    {
574
0
        Self::new_with_digest::<D>(Signature::<C>::from_slice(slice)?)
575
0
    }
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
0
    pub fn from_der_with_digest<D>(der_bytes: &[u8]) -> Result<Self>
584
0
    where
585
0
        D: AssociatedOid + Digest,
586
0
        der::MaxSize<C>: ArraySize,
587
0
        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
588
    {
589
0
        Self::new_with_digest::<D>(Signature::<C>::from_der(der_bytes)?)
590
0
    }
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
0
    pub fn from_der_with_oid(der_bytes: &[u8], oid: ObjectIdentifier) -> Result<Self>
599
0
    where
600
0
        der::MaxSize<C>: ArraySize,
601
0
        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
602
    {
603
0
        Self::new(Signature::<C>::from_der(der_bytes)?, oid)
604
0
    }
605
606
    /// Get the fixed-width ECDSA signature.
607
0
    pub fn signature(&self) -> &Signature<C> {
608
0
        &self.signature
609
0
    }
610
611
    /// Get the ECDSA OID for this signature.
612
0
    pub fn oid(&self) -> ObjectIdentifier {
613
0
        self.oid
614
0
    }
615
616
    /// Serialize this signature as fixed-width bytes.
617
0
    pub fn to_bytes(&self) -> SignatureBytes<C>
618
0
where {
619
0
        self.signature.to_bytes()
620
0
    }
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
0
    pub fn to_der(&self) -> der::Signature<C>
629
0
    where
630
0
        der::MaxSize<C>: ArraySize,
631
0
        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
632
    {
633
0
        self.signature.into()
634
0
    }
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")]
642
pub 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")]
649
impl<C> core::hash::Hash for SignatureWithOid<C>
650
where
651
    C: EcdsaCurve,
652
{
653
0
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
654
0
        self.signature.hash(state);
655
0
        self.oid.hash(state);
656
0
    }
657
}
658
659
#[cfg(feature = "digest")]
660
impl<C> From<SignatureWithOid<C>> for Signature<C>
661
where
662
    C: EcdsaCurve,
663
{
664
0
    fn from(sig: SignatureWithOid<C>) -> Signature<C> {
665
0
        sig.signature
666
0
    }
667
}
668
669
#[cfg(feature = "digest")]
670
impl<C> From<SignatureWithOid<C>> for SignatureBytes<C>
671
where
672
    C: EcdsaCurve,
673
{
674
0
    fn from(signature: SignatureWithOid<C>) -> SignatureBytes<C> {
675
0
        signature.to_bytes()
676
0
    }
677
}
678
679
#[cfg(all(feature = "der", feature = "digest"))]
680
impl<C> From<SignatureWithOid<C>> for der::Signature<C>
681
where
682
    C: EcdsaCurve,
683
    der::MaxSize<C>: ArraySize,
684
    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
685
{
686
0
    fn from(sig: SignatureWithOid<C>) -> der::Signature<C> {
687
0
        sig.to_der()
688
0
    }
689
}
690
691
#[cfg(all(feature = "der", feature = "digest"))]
692
impl<C> From<&SignatureWithOid<C>> for der::Signature<C>
693
where
694
    C: EcdsaCurve,
695
    der::MaxSize<C>: ArraySize,
696
    <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArraySize,
697
{
698
0
    fn from(sig: &SignatureWithOid<C>) -> der::Signature<C> {
699
0
        sig.to_der()
700
0
    }
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")]
709
impl<C> SignatureEncoding for SignatureWithOid<C>
710
where
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")]
723
impl<C> TryFrom<&[u8]> for SignatureWithOid<C>
724
where
725
    C: DigestAlgorithm,
726
    C::Digest: AssociatedOid,
727
{
728
    type Error = Error;
729
730
0
    fn try_from(slice: &[u8]) -> Result<Self> {
731
0
        Self::new(Signature::<C>::from_slice(slice)?, C::Digest::OID)
732
0
    }
733
}
734
735
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
736
impl<C> DynAssociatedAlgorithmIdentifier for SignatureWithOid<C>
737
where
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")]
750
0
const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option<ObjectIdentifier> {
751
0
    match digest_oid {
752
0
        SHA224_OID => Some(ECDSA_SHA224_OID),
753
0
        SHA256_OID => Some(ECDSA_SHA256_OID),
754
0
        SHA384_OID => Some(ECDSA_SHA384_OID),
755
0
        SHA512_OID => Some(ECDSA_SHA512_OID),
756
0
        _ => None,
757
    }
758
0
}