Coverage Report

Created: 2025-11-16 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ecdsa-0.16.9/src/lib.rs
Line
Count
Source
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")]
58
extern crate alloc;
59
60
mod normalized;
61
mod recovery;
62
63
#[cfg(feature = "der")]
64
pub mod der;
65
#[cfg(feature = "dev")]
66
pub mod dev;
67
#[cfg(feature = "hazmat")]
68
pub mod hazmat;
69
#[cfg(feature = "signing")]
70
mod signing;
71
#[cfg(feature = "verifying")]
72
mod verifying;
73
74
pub use crate::{normalized::NormalizedSignature, recovery::RecoveryId};
75
76
// Re-export the `elliptic-curve` crate (and select types)
77
pub use elliptic_curve::{self, sec1::EncodedPoint, PrimeCurve};
78
79
// Re-export the `signature` crate (and select types)
80
pub use signature::{self, Error, Result, SignatureEncoding};
81
82
#[cfg(feature = "signing")]
83
pub use crate::signing::SigningKey;
84
#[cfg(feature = "verifying")]
85
pub use crate::verifying::VerifyingKey;
86
87
use core::{fmt, ops::Add};
88
use elliptic_curve::{
89
    generic_array::{typenum::Unsigned, ArrayLength, GenericArray},
90
    FieldBytes, FieldBytesSize, ScalarPrimitive,
91
};
92
93
#[cfg(feature = "alloc")]
94
use alloc::vec::Vec;
95
96
#[cfg(feature = "arithmetic")]
97
use {
98
    core::str,
99
    elliptic_curve::{scalar::IsHigh, CurveArithmetic, NonZeroScalar},
100
};
101
102
#[cfg(feature = "digest")]
103
use digest::{
104
    const_oid::{AssociatedOid, ObjectIdentifier},
105
    Digest,
106
};
107
108
#[cfg(feature = "pkcs8")]
109
use elliptic_curve::pkcs8::spki::{
110
    der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
111
};
112
113
#[cfg(feature = "serde")]
114
use serdect::serde::{de, ser, Deserialize, Serialize};
115
116
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
117
use 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")]
129
pub 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")]
138
pub 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")]
147
pub 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")]
156
pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4");
157
158
#[cfg(feature = "digest")]
159
const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4");
160
#[cfg(feature = "digest")]
161
const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
162
#[cfg(feature = "digest")]
163
const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
164
#[cfg(feature = "digest")]
165
const 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.
168
pub type SignatureSize<C> = <FieldBytesSize<C> as Add>::Output;
169
170
/// Fixed-size byte array containing an ECDSA signature
171
pub 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)]
198
pub struct Signature<C: PrimeCurve> {
199
    r: ScalarPrimitive<C>,
200
    s: ScalarPrimitive<C>,
201
}
202
203
impl<C> Signature<C>
204
where
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
0
    pub fn from_bytes(bytes: &SignatureBytes<C>) -> Result<Self> {
217
0
        let (r_bytes, s_bytes) = bytes.split_at(C::FieldBytesSize::USIZE);
218
0
        let r = FieldBytes::<C>::clone_from_slice(r_bytes);
219
0
        let s = FieldBytes::<C>::clone_from_slice(s_bytes);
220
0
        Self::from_scalars(r, s)
221
0
    }
222
223
    /// Parse a signature from a byte slice.
224
0
    pub fn from_slice(slice: &[u8]) -> Result<Self> {
225
0
        if slice.len() == SignatureSize::<C>::USIZE {
226
0
            Self::from_bytes(SignatureBytes::<C>::from_slice(slice))
227
        } else {
228
0
            Err(Error::new())
229
        }
230
0
    }
231
232
    /// Parse a signature from ASN.1 DER.
233
    #[cfg(feature = "der")]
234
0
    pub fn from_der(bytes: &[u8]) -> Result<Self>
235
0
    where
236
0
        der::MaxSize<C>: ArrayLength<u8>,
237
0
        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
238
    {
239
0
        der::Signature::<C>::try_from(bytes).and_then(Self::try_from)
240
0
    }
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
2.89k
    pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
251
2.89k
        let r = ScalarPrimitive::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}
252
2.89k
        let s = ScalarPrimitive::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}
253
254
2.89k
        if r.is_zero().into() || s.is_zero().into() {
255
0
            return Err(Error::new());
256
2.89k
        }
257
258
2.89k
        Ok(Self { r, s })
259
2.89k
    }
<ecdsa::Signature<p256::NistP256>>::from_scalars::<p256::arithmetic::scalar::Scalar, p256::arithmetic::scalar::Scalar>
Line
Count
Source
250
2.89k
    pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
251
2.89k
        let r = ScalarPrimitive::from_slice(&r.into()).map_err(|_| Error::new())?;
252
2.89k
        let s = ScalarPrimitive::from_slice(&s.into()).map_err(|_| Error::new())?;
253
254
2.89k
        if r.is_zero().into() || s.is_zero().into() {
255
0
            return Err(Error::new());
256
2.89k
        }
257
258
2.89k
        Ok(Self { r, s })
259
2.89k
    }
Unexecuted instantiation: <ecdsa::Signature<_>>::from_scalars::<_, _>
260
261
    /// Split the signature into its `r` and `s` components, represented as bytes.
262
2.89k
    pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
263
2.89k
        (self.r.to_bytes(), self.s.to_bytes())
264
2.89k
    }
<ecdsa::Signature<p256::NistP256>>::split_bytes
Line
Count
Source
262
2.89k
    pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
263
2.89k
        (self.r.to_bytes(), self.s.to_bytes())
264
2.89k
    }
Unexecuted instantiation: <ecdsa::Signature<_>>::split_bytes
265
266
    /// Serialize this signature as bytes.
267
0
    pub fn to_bytes(&self) -> SignatureBytes<C> {
268
0
        let mut bytes = SignatureBytes::<C>::default();
269
0
        let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE);
270
0
        r_bytes.copy_from_slice(&self.r.to_bytes());
271
0
        s_bytes.copy_from_slice(&self.s.to_bytes());
272
0
        bytes
273
0
    }
274
275
    /// Serialize this signature as ASN.1 DER.
276
    #[cfg(feature = "der")]
277
2.89k
    pub fn to_der(&self) -> der::Signature<C>
278
2.89k
    where
279
2.89k
        der::MaxSize<C>: ArrayLength<u8>,
280
2.89k
        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
281
    {
282
2.89k
        let (r, s) = self.split_bytes();
283
2.89k
        der::Signature::from_components(&r, &s).expect("DER encoding error")
284
2.89k
    }
<ecdsa::Signature<p256::NistP256>>::to_der
Line
Count
Source
277
2.89k
    pub fn to_der(&self) -> der::Signature<C>
278
2.89k
    where
279
2.89k
        der::MaxSize<C>: ArrayLength<u8>,
280
2.89k
        <FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
281
    {
282
2.89k
        let (r, s) = self.split_bytes();
283
2.89k
        der::Signature::from_components(&r, &s).expect("DER encoding error")
284
2.89k
    }
Unexecuted instantiation: <ecdsa::Signature<_>>::to_der
285
286
    /// Convert this signature into a byte vector.
287
    #[cfg(feature = "alloc")]
288
0
    pub fn to_vec(&self) -> Vec<u8> {
289
0
        self.to_bytes().to_vec()
290
0
    }
291
}
292
293
#[cfg(feature = "arithmetic")]
294
impl<C> Signature<C>
295
where
296
    C: PrimeCurve + CurveArithmetic,
297
    SignatureSize<C>: ArrayLength<u8>,
298
{
299
    /// Get the `r` component of this signature
300
0
    pub fn r(&self) -> NonZeroScalar<C> {
301
0
        NonZeroScalar::new(self.r.into()).unwrap()
302
0
    }
303
304
    /// Get the `s` component of this signature
305
0
    pub fn s(&self) -> NonZeroScalar<C> {
306
0
        NonZeroScalar::new(self.s.into()).unwrap()
307
0
    }
308
309
    /// Split the signature into its `r` and `s` scalars.
310
0
    pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
311
0
        (self.r(), self.s())
312
0
    }
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
0
    pub fn normalize_s(&self) -> Option<Self> {
319
0
        let s = self.s();
320
321
0
        if s.is_high().into() {
322
0
            let mut result = self.clone();
323
0
            result.s = ScalarPrimitive::from(-s);
324
0
            Some(result)
325
        } else {
326
0
            None
327
        }
328
0
    }
329
}
330
331
impl<C> Copy for Signature<C>
332
where
333
    C: PrimeCurve,
334
    SignatureSize<C>: ArrayLength<u8>,
335
    <SignatureSize<C> as ArrayLength<u8>>::ArrayType: Copy,
336
{
337
}
338
339
impl<C> From<Signature<C>> for SignatureBytes<C>
340
where
341
    C: PrimeCurve,
342
    SignatureSize<C>: ArrayLength<u8>,
343
{
344
0
    fn from(signature: Signature<C>) -> SignatureBytes<C> {
345
0
        signature.to_bytes()
346
0
    }
347
}
348
349
impl<C> SignatureEncoding for Signature<C>
350
where
351
    C: PrimeCurve,
352
    SignatureSize<C>: ArrayLength<u8>,
353
{
354
    type Repr = SignatureBytes<C>;
355
}
356
357
impl<C> TryFrom<&[u8]> for Signature<C>
358
where
359
    C: PrimeCurve,
360
    SignatureSize<C>: ArrayLength<u8>,
361
{
362
    type Error = Error;
363
364
0
    fn try_from(slice: &[u8]) -> Result<Self> {
365
0
        Self::from_slice(slice)
366
0
    }
367
}
368
369
impl<C> fmt::Debug for Signature<C>
370
where
371
    C: PrimeCurve,
372
    SignatureSize<C>: ArrayLength<u8>,
373
{
374
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375
0
        write!(f, "ecdsa::Signature<{:?}>(", C::default())?;
376
377
0
        for byte in self.to_bytes() {
378
0
            write!(f, "{:02X}", byte)?;
379
        }
380
381
0
        write!(f, ")")
382
0
    }
383
}
384
385
impl<C> fmt::Display for Signature<C>
386
where
387
    C: PrimeCurve,
388
    SignatureSize<C>: ArrayLength<u8>,
389
{
390
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391
0
        write!(f, "{:X}", self)
392
0
    }
393
}
394
395
impl<C> fmt::LowerHex for Signature<C>
396
where
397
    C: PrimeCurve,
398
    SignatureSize<C>: ArrayLength<u8>,
399
{
400
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401
0
        for byte in self.to_bytes() {
402
0
            write!(f, "{:02x}", byte)?;
403
        }
404
0
        Ok(())
405
0
    }
406
}
407
408
impl<C> fmt::UpperHex for Signature<C>
409
where
410
    C: PrimeCurve,
411
    SignatureSize<C>: ArrayLength<u8>,
412
{
413
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414
0
        for byte in self.to_bytes() {
415
0
            write!(f, "{:02X}", byte)?;
416
        }
417
0
        Ok(())
418
0
    }
419
}
420
421
#[cfg(feature = "arithmetic")]
422
impl<C> str::FromStr for Signature<C>
423
where
424
    C: PrimeCurve + CurveArithmetic,
425
    SignatureSize<C>: ArrayLength<u8>,
426
{
427
    type Err = Error;
428
429
0
    fn from_str(hex: &str) -> Result<Self> {
430
0
        if hex.as_bytes().len() != C::FieldBytesSize::USIZE * 4 {
431
0
            return Err(Error::new());
432
0
        }
433
434
        // This check is mainly to ensure `hex.split_at` below won't panic
435
0
        if !hex
436
0
            .as_bytes()
437
0
            .iter()
438
0
            .all(|&byte| matches!(byte, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z'))
439
        {
440
0
            return Err(Error::new());
441
0
        }
442
443
0
        let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2);
444
445
0
        let r = r_hex
446
0
            .parse::<NonZeroScalar<C>>()
447
0
            .map_err(|_| Error::new())?;
448
449
0
        let s = s_hex
450
0
            .parse::<NonZeroScalar<C>>()
451
0
            .map_err(|_| Error::new())?;
452
453
0
        Self::from_scalars(r, s)
454
0
    }
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"))]
463
impl<C> AssociatedOid for Signature<C>
464
where
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")]
477
impl<C> AssociatedAlgorithmIdentifier for Signature<C>
478
where
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")]
491
impl<C> Serialize for Signature<C>
492
where
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")]
505
impl<'de, C> Deserialize<'de> for Signature<C>
506
where
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)]
534
pub 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")]
547
impl<C> SignatureWithOid<C>
548
where
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
0
    pub fn new(signature: Signature<C>, oid: ObjectIdentifier) -> Result<Self> {
558
        // TODO(tarcieri): use `ObjectIdentifier::starts_with`
559
0
        for (arc1, arc2) in ObjectIdentifier::new_unwrap("1.2.840.10045.4.3")
560
0
            .arcs()
561
0
            .zip(oid.arcs())
562
        {
563
0
            if arc1 != arc2 {
564
0
                return Err(Error::new());
565
0
            }
566
        }
567
568
0
        Ok(Self { signature, oid })
569
0
    }
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
0
    pub fn new_with_digest<D>(signature: Signature<C>) -> Result<Self>
578
0
    where
579
0
        D: AssociatedOid + Digest,
580
    {
581
0
        let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
582
0
        Ok(Self { signature, oid })
583
0
    }
584
585
    /// Parse a signature from fixed-with bytes.
586
0
    pub fn from_bytes_with_digest<D>(bytes: &SignatureBytes<C>) -> Result<Self>
587
0
    where
588
0
        D: AssociatedOid + Digest,
589
0
        SignatureSize<C>: ArrayLength<u8>,
590
    {
591
0
        Self::new_with_digest::<D>(Signature::<C>::from_bytes(bytes)?)
592
0
    }
593
594
    /// Parse a signature from a byte slice.
595
0
    pub fn from_slice_with_digest<D>(slice: &[u8]) -> Result<Self>
596
0
    where
597
0
        D: AssociatedOid + Digest,
598
0
        SignatureSize<C>: ArrayLength<u8>,
599
    {
600
0
        Self::new_with_digest::<D>(Signature::<C>::from_slice(slice)?)
601
0
    }
602
603
    /// Get the fixed-width ECDSA signature.
604
0
    pub fn signature(&self) -> &Signature<C> {
605
0
        &self.signature
606
0
    }
607
608
    /// Get the ECDSA OID for this signature.
609
0
    pub fn oid(&self) -> ObjectIdentifier {
610
0
        self.oid
611
0
    }
612
613
    /// Serialize this signature as bytes.
614
0
    pub fn to_bytes(&self) -> SignatureBytes<C>
615
0
    where
616
0
        SignatureSize<C>: ArrayLength<u8>,
617
    {
618
0
        self.signature.to_bytes()
619
0
    }
620
}
621
622
#[cfg(feature = "digest")]
623
impl<C> Copy for SignatureWithOid<C>
624
where
625
    C: PrimeCurve,
626
    SignatureSize<C>: ArrayLength<u8>,
627
    <SignatureSize<C> as ArrayLength<u8>>::ArrayType: Copy,
628
{
629
}
630
631
#[cfg(feature = "digest")]
632
impl<C> From<SignatureWithOid<C>> for Signature<C>
633
where
634
    C: PrimeCurve,
635
{
636
0
    fn from(sig: SignatureWithOid<C>) -> Signature<C> {
637
0
        sig.signature
638
0
    }
639
}
640
641
#[cfg(feature = "digest")]
642
impl<C> From<SignatureWithOid<C>> for SignatureBytes<C>
643
where
644
    C: PrimeCurve,
645
    SignatureSize<C>: ArrayLength<u8>,
646
{
647
0
    fn from(signature: SignatureWithOid<C>) -> SignatureBytes<C> {
648
0
        signature.to_bytes()
649
0
    }
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"))]
658
impl<C> SignatureEncoding for SignatureWithOid<C>
659
where
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"))]
673
impl<C> TryFrom<&[u8]> for SignatureWithOid<C>
674
where
675
    C: hazmat::DigestPrimitive,
676
    C::Digest: AssociatedOid,
677
    SignatureSize<C>: ArrayLength<u8>,
678
{
679
    type Error = Error;
680
681
0
    fn try_from(slice: &[u8]) -> Result<Self> {
682
0
        Self::new(Signature::<C>::from_slice(slice)?, C::Digest::OID)
683
0
    }
684
}
685
686
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
687
impl<C> DynAssociatedAlgorithmIdentifier for SignatureWithOid<C>
688
where
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")]
701
0
const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option<ObjectIdentifier> {
702
0
    match digest_oid {
703
0
        SHA224_OID => Some(ECDSA_SHA224_OID),
704
0
        SHA256_OID => Some(ECDSA_SHA256_OID),
705
0
        SHA384_OID => Some(ECDSA_SHA384_OID),
706
0
        SHA512_OID => Some(ECDSA_SHA512_OID),
707
0
        _ => None,
708
    }
709
0
}