Skip to main content

ecdsa/
der.rs

1//! Support for ASN.1 DER-encoded ECDSA signatures as specified in
2//! [RFC5912 Section 6].
3//!
4//! [RFC5912 Section 6]: https://www.rfc-editor.org/rfc/rfc5912#section-6
5
6use crate::{EcdsaCurve, Error, Result};
7use core::{
8    fmt::{self, Debug},
9    ops::{Add, Range},
10};
11use der::{
12    Decode, DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, Sequence, Tag,
13    Writer, asn1::UintRef,
14};
15use elliptic_curve::{
16    FieldBytesSize,
17    array::{Array, ArraySize, typenum::Unsigned},
18    consts::U9,
19};
20
21#[cfg(feature = "alloc")]
22use {
23    alloc::{boxed::Box, vec::Vec},
24    signature::SignatureEncoding,
25    spki::{SignatureBitStringEncoding, der::asn1::BitString},
26};
27
28#[cfg(feature = "serde")]
29use serdect::serde::{Deserialize, Serialize, de, ser};
30
31/// Maximum overhead of an ASN.1 DER-encoded ECDSA signature for a given curve:
32/// 9-bytes.
33///
34/// Includes 3-byte ASN.1 DER header:
35///
36/// - 1-byte: ASN.1 `SEQUENCE` tag (0x30)
37/// - 2-byte: length
38///
39/// ...followed by two ASN.1 `INTEGER` values, which each have a header whose
40/// maximum length is the following:
41///
42/// - 1-byte: ASN.1 `INTEGER` tag (0x02)
43/// - 1-byte: length
44/// - 1-byte: zero to indicate value is positive (`INTEGER` is signed)
45pub type MaxOverhead = U9;
46
47/// Maximum size of an ASN.1 DER encoded signature for the given elliptic curve.
48pub type MaxSize<C> = <<FieldBytesSize<C> as Add>::Output as Add<MaxOverhead>>::Output;
49
50/// Byte array containing a serialized ASN.1 signature
51type SignatureBytes<C> = Array<u8, MaxSize<C>>;
52
53/// ASN.1 DER-encoded signature as specified in [RFC5912 Section 6]:
54///
55/// ```text
56/// ECDSA-Sig-Value ::= SEQUENCE {
57///   r  INTEGER,
58///   s  INTEGER
59/// }
60/// ```
61///
62/// [RFC5912 Section 6]: https://www.rfc-editor.org/rfc/rfc5912#section-6
63pub struct Signature<C>
64where
65    C: EcdsaCurve,
66    MaxSize<C>: ArraySize,
67    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
68{
69    /// ASN.1 DER-encoded signature data
70    bytes: SignatureBytes<C>,
71
72    /// Range of the `r` value within the signature
73    r_range: Range<usize>,
74
75    /// Range of the `s` value within the signature
76    s_range: Range<usize>,
77}
78
79#[allow(clippy::len_without_is_empty)]
80impl<C> Signature<C>
81where
82    C: EcdsaCurve,
83    MaxSize<C>: ArraySize,
84    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
85{
86    /// Parse signature from DER-encoded bytes.
87    pub fn from_bytes(input: &[u8]) -> Result<Self> {
88        let SignatureRef { r, s } = SignatureRef::from_der(input).map_err(|_| Error::new())?;
89
90        if r.as_bytes().len() > C::FieldBytesSize::USIZE
91            || s.as_bytes().len() > C::FieldBytesSize::USIZE
92        {
93            return Err(Error::new());
94        }
95
96        let r_range = find_scalar_range(input, r.as_bytes())?;
97        let s_range = find_scalar_range(input, s.as_bytes())?;
98
99        if s_range.end != input.len() {
100            return Err(Error::new());
101        }
102
103        let mut bytes = SignatureBytes::<C>::default();
104        bytes[..s_range.end].copy_from_slice(input);
105
106        Ok(Signature {
107            bytes,
108            r_range,
109            s_range,
110        })
111    }
112
113    /// Create an ASN.1 DER encoded signature from big endian `r` and `s` scalar
114    /// components.
115    pub(crate) fn from_components(r: &[u8], s: &[u8]) -> der::Result<Self> {
116        let sig = SignatureRef {
117            r: UintRef::new(r)?,
118            s: UintRef::new(s)?,
119        };
120        let mut bytes = SignatureBytes::<C>::default();
121
122        sig.encode_to_slice(&mut bytes)?
123            .try_into()
124            .map_err(|_| Tag::Sequence.value_error().into())
125    }
126
127    /// Borrow this signature as a byte slice
128    pub fn as_bytes(&self) -> &[u8] {
129        &self.bytes.as_slice()[..self.len()]
130    }
131
132    /// Serialize this signature as a boxed byte slice
133    #[cfg(feature = "alloc")]
134    pub fn to_bytes(&self) -> Box<[u8]> {
135        self.as_bytes().to_vec().into_boxed_slice()
136    }
137
138    /// Get the length of the signature in bytes
139    pub fn len(&self) -> usize {
140        self.s_range.end
141    }
142
143    /// Get the `r` component of the signature (leading zeros removed)
144    pub(crate) fn r(&self) -> &[u8] {
145        &self.bytes[self.r_range.clone()]
146    }
147
148    /// Get the `s` component of the signature (leading zeros removed)
149    pub(crate) fn s(&self) -> &[u8] {
150        &self.bytes[self.s_range.clone()]
151    }
152}
153
154impl<C> core::hash::Hash for Signature<C>
155where
156    C: EcdsaCurve,
157    MaxSize<C>: ArraySize,
158    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
159{
160    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
161        self.as_bytes().hash(state);
162    }
163}
164
165impl<C> AsRef<[u8]> for Signature<C>
166where
167    C: EcdsaCurve,
168    MaxSize<C>: ArraySize,
169    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
170{
171    fn as_ref(&self) -> &[u8] {
172        self.as_bytes()
173    }
174}
175
176impl<C> Clone for Signature<C>
177where
178    C: EcdsaCurve,
179    MaxSize<C>: ArraySize,
180    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
181{
182    fn clone(&self) -> Self {
183        Self {
184            bytes: self.bytes.clone(),
185            r_range: self.r_range.clone(),
186            s_range: self.s_range.clone(),
187        }
188    }
189}
190
191impl<C> Debug for Signature<C>
192where
193    C: EcdsaCurve,
194    MaxSize<C>: ArraySize,
195    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
196{
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        write!(f, "ecdsa::der::Signature<{:?}>(", C::default())?;
199
200        for &byte in self.as_ref() {
201            write!(f, "{byte:02X}")?;
202        }
203
204        write!(f, ")")
205    }
206}
207
208impl<'a, C> Decode<'a> for Signature<C>
209where
210    C: EcdsaCurve,
211    MaxSize<C>: ArraySize,
212    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
213{
214    type Error = der::Error;
215
216    fn decode<R: Reader<'a>>(reader: &mut R) -> der::Result<Self> {
217        let header = Header::peek(reader)?;
218        header.tag().assert_eq(Tag::Sequence)?;
219
220        let mut buf = SignatureBytes::<C>::default();
221        let len = (header.encoded_len()? + header.length())?;
222        let slice = buf
223            .get_mut(..usize::try_from(len)?)
224            .ok_or_else(|| reader.error(Tag::Sequence.length_error()))?;
225
226        reader.read_into(slice)?;
227        Self::from_bytes(slice).map_err(|_| reader.error(Tag::Integer.value_error()))
228    }
229}
230
231impl<C> Encode for Signature<C>
232where
233    C: EcdsaCurve,
234    MaxSize<C>: ArraySize,
235    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
236{
237    fn encoded_len(&self) -> der::Result<Length> {
238        Length::try_from(self.len())
239    }
240
241    fn encode(&self, writer: &mut impl Writer) -> der::Result<()> {
242        writer.write(self.as_bytes())
243    }
244}
245
246impl<C> FixedTag for Signature<C>
247where
248    C: EcdsaCurve,
249    MaxSize<C>: ArraySize,
250    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
251{
252    const TAG: Tag = Tag::Sequence;
253}
254
255impl<C> From<crate::Signature<C>> for Signature<C>
256where
257    C: EcdsaCurve,
258    MaxSize<C>: ArraySize,
259    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
260{
261    fn from(sig: crate::Signature<C>) -> Signature<C> {
262        sig.to_der()
263    }
264}
265
266impl<C> TryFrom<&[u8]> for Signature<C>
267where
268    C: EcdsaCurve,
269    MaxSize<C>: ArraySize,
270    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
271{
272    type Error = Error;
273
274    fn try_from(input: &[u8]) -> Result<Self> {
275        Self::from_bytes(input)
276    }
277}
278
279impl<C> TryFrom<Signature<C>> for crate::Signature<C>
280where
281    C: EcdsaCurve,
282    MaxSize<C>: ArraySize,
283    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
284{
285    type Error = Error;
286
287    fn try_from(sig: Signature<C>) -> Result<super::Signature<C>> {
288        let mut bytes = super::SignatureBytes::<C>::default();
289        let r_begin = C::FieldBytesSize::USIZE.saturating_sub(sig.r().len());
290        let s_begin = bytes.len().saturating_sub(sig.s().len());
291        bytes[r_begin..C::FieldBytesSize::USIZE].copy_from_slice(sig.r());
292        bytes[s_begin..].copy_from_slice(sig.s());
293        Self::try_from(bytes.as_slice())
294    }
295}
296
297#[cfg(feature = "alloc")]
298impl<C> From<Signature<C>> for Box<[u8]>
299where
300    C: EcdsaCurve,
301    MaxSize<C>: ArraySize,
302    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
303{
304    fn from(signature: Signature<C>) -> Box<[u8]> {
305        signature.to_vec().into_boxed_slice()
306    }
307}
308
309#[cfg(feature = "alloc")]
310impl<C> SignatureEncoding for Signature<C>
311where
312    C: EcdsaCurve,
313    MaxSize<C>: ArraySize,
314    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
315{
316    type Repr = Box<[u8]>;
317
318    fn to_vec(&self) -> Vec<u8> {
319        self.as_bytes().into()
320    }
321}
322
323#[cfg(feature = "alloc")]
324impl<C> SignatureBitStringEncoding for Signature<C>
325where
326    C: EcdsaCurve,
327    MaxSize<C>: ArraySize,
328    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
329{
330    fn to_bitstring(&self) -> der::Result<BitString> {
331        BitString::new(0, self.to_vec())
332    }
333}
334
335#[cfg(feature = "serde")]
336impl<C> Serialize for Signature<C>
337where
338    C: EcdsaCurve,
339    MaxSize<C>: ArraySize,
340    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
341{
342    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
343    where
344        S: ser::Serializer,
345    {
346        serdect::slice::serialize_hex_upper_or_bin(&self.as_bytes(), serializer)
347    }
348}
349
350#[cfg(feature = "serde")]
351impl<'de, C> Deserialize<'de> for Signature<C>
352where
353    C: EcdsaCurve,
354    MaxSize<C>: ArraySize,
355    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArraySize,
356{
357    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
358    where
359        D: de::Deserializer<'de>,
360    {
361        let mut buf = SignatureBytes::<C>::default();
362        let slice = serdect::slice::deserialize_hex_or_bin(&mut buf, deserializer)?;
363        Self::try_from(slice).map_err(de::Error::custom)
364    }
365}
366
367struct SignatureRef<'a> {
368    pub r: UintRef<'a>,
369    pub s: UintRef<'a>,
370}
371
372impl EncodeValue for SignatureRef<'_> {
373    fn value_len(&self) -> der::Result<Length> {
374        self.r.encoded_len()? + self.s.encoded_len()?
375    }
376
377    fn encode_value(&self, encoder: &mut impl Writer) -> der::Result<()> {
378        self.r.encode(encoder)?;
379        self.s.encode(encoder)?;
380        Ok(())
381    }
382}
383
384impl<'a> DecodeValue<'a> for SignatureRef<'a> {
385    type Error = der::Error;
386
387    fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> der::Result<Self> {
388        Ok(Self {
389            r: UintRef::decode(reader)?,
390            s: UintRef::decode(reader)?,
391        })
392    }
393}
394impl<'a> Sequence<'a> for SignatureRef<'a> {}
395
396/// Locate the range within a slice at which a particular subslice is located
397fn find_scalar_range(outer: &[u8], inner: &[u8]) -> Result<Range<usize>> {
398    let outer_start = outer.as_ptr() as usize;
399    let inner_start = inner.as_ptr() as usize;
400    let start = inner_start
401        .checked_sub(outer_start)
402        .ok_or_else(Error::new)?;
403    let end = start.checked_add(inner.len()).ok_or_else(Error::new)?;
404    Ok(Range { start, end })
405}
406
407#[cfg(all(test, feature = "algorithm"))]
408mod tests {
409    use elliptic_curve::dev::MockCurve;
410
411    type Signature = crate::Signature<MockCurve>;
412
413    const EXAMPLE_SIGNATURE: [u8; 64] = [
414        0xf3, 0xac, 0x80, 0x61, 0xb5, 0x14, 0x79, 0x5b, 0x88, 0x43, 0xe3, 0xd6, 0x62, 0x95, 0x27,
415        0xed, 0x2a, 0xfd, 0x6b, 0x1f, 0x6a, 0x55, 0x5a, 0x7a, 0xca, 0xbb, 0x5e, 0x6f, 0x79, 0xc8,
416        0xc2, 0xac, 0x8b, 0xf7, 0x78, 0x19, 0xca, 0x5, 0xa6, 0xb2, 0x78, 0x6c, 0x76, 0x26, 0x2b,
417        0xf7, 0x37, 0x1c, 0xef, 0x97, 0xb2, 0x18, 0xe9, 0x6f, 0x17, 0x5a, 0x3c, 0xcd, 0xda, 0x2a,
418        0xcc, 0x5, 0x89, 0x3,
419    ];
420
421    #[test]
422    fn test_fixed_to_asn1_signature_roundtrip() {
423        let signature1 =
424            Signature::try_from(EXAMPLE_SIGNATURE.as_ref()).expect("decoded Signature");
425
426        // Convert to ASN.1 DER and back
427        let asn1_signature = signature1.to_der();
428        let signature2 = Signature::from_der(asn1_signature.as_ref()).expect("decoded Signature");
429
430        assert_eq!(signature1, signature2);
431    }
432
433    #[test]
434    fn test_asn1_too_short_signature() {
435        assert!(Signature::from_der(&[]).is_err());
436        assert!(Signature::from_der(&[0x30]).is_err());
437        assert!(Signature::from_der(&[0x30, 0x00]).is_err());
438        assert!(Signature::from_der(&[0x30, 0x03, 0x02, 0x01, 0x01]).is_err());
439    }
440
441    #[test]
442    fn test_asn1_non_der_signature() {
443        // A minimal 8-byte ASN.1 signature parses OK.
444        assert!(
445            Signature::from_der(&[
446                0x30, // Tag::Sequence,
447                0x06, // length of below
448                0x02, // Tag::Integer,
449                0x01, // length of value
450                0x01, // value=1
451                0x02, // Tag::Integer,
452                0x01, // length of value
453                0x01, // value=1
454            ])
455            .is_ok()
456        );
457
458        // But length fields that are not minimally encoded should be rejected, as they are not
459        // valid DER, cf.
460        // https://github.com/google/wycheproof/blob/2196000605e4/testvectors/ecdsa_secp256k1_sha256_test.json#L57-L66
461        assert!(
462            Signature::from_der(&[
463                0x30, // Tag::Sequence
464                0x81, // extended length: 1 length byte to come
465                0x06, // length of below
466                0x02, // Tag::Integer
467                0x01, // length of value
468                0x01, // value=1
469                0x02, // Tag::Integer
470                0x01, // length of value
471                0x01, // value=1
472            ])
473            .is_err()
474        );
475    }
476}