ecdsa/
der.rs

1//! Support for ECDSA signatures encoded as ASN.1 DER.
2
3use crate::{Error, Result};
4use core::{
5    fmt,
6    ops::{Add, Range},
7};
8use der::{asn1::UIntRef, Decode, Encode, Reader};
9use elliptic_curve::{
10    bigint::Encoding as _,
11    consts::U9,
12    generic_array::{ArrayLength, GenericArray},
13    FieldSize, PrimeCurve,
14};
15
16#[cfg(feature = "alloc")]
17use alloc::boxed::Box;
18
19#[cfg(feature = "serde")]
20use serdect::serde::{de, ser, Deserialize, Serialize};
21
22/// Maximum overhead of an ASN.1 DER-encoded ECDSA signature for a given curve:
23/// 9-bytes.
24///
25/// Includes 3-byte ASN.1 DER header:
26///
27/// - 1-byte: ASN.1 `SEQUENCE` tag (0x30)
28/// - 2-byte: length
29///
30/// ...followed by two ASN.1 `INTEGER` values, which each have a header whose
31/// maximum length is the following:
32///
33/// - 1-byte: ASN.1 `INTEGER` tag (0x02)
34/// - 1-byte: length
35/// - 1-byte: zero to indicate value is positive (`INTEGER` is signed)
36pub type MaxOverhead = U9;
37
38/// Maximum size of an ASN.1 DER encoded signature for the given elliptic curve.
39pub type MaxSize<C> = <<FieldSize<C> as Add>::Output as Add<MaxOverhead>>::Output;
40
41/// Byte array containing a serialized ASN.1 signature
42type SignatureBytes<C> = GenericArray<u8, MaxSize<C>>;
43
44/// ASN.1 DER-encoded signature.
45///
46/// Generic over the scalar size of the elliptic curve.
47pub struct Signature<C>
48where
49    C: PrimeCurve,
50    MaxSize<C>: ArrayLength<u8>,
51    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
52{
53    /// ASN.1 DER-encoded signature data
54    bytes: SignatureBytes<C>,
55
56    /// Range of the `r` value within the signature
57    r_range: Range<usize>,
58
59    /// Range of the `s` value within the signature
60    s_range: Range<usize>,
61}
62
63impl<C> signature::Signature for Signature<C>
64where
65    C: PrimeCurve,
66    MaxSize<C>: ArrayLength<u8>,
67    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
68{
69    /// Parse an ASN.1 DER-encoded ECDSA signature from a byte slice
70    fn from_bytes(bytes: &[u8]) -> Result<Self> {
71        bytes.try_into()
72    }
73}
74
75#[allow(clippy::len_without_is_empty)]
76impl<C> Signature<C>
77where
78    C: PrimeCurve,
79    MaxSize<C>: ArrayLength<u8>,
80    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
81{
82    /// Get the length of the signature in bytes
83    pub fn len(&self) -> usize {
84        self.s_range.end
85    }
86
87    /// Borrow this signature as a byte slice
88    pub fn as_bytes(&self) -> &[u8] {
89        &self.bytes.as_slice()[..self.len()]
90    }
91
92    /// Serialize this signature as a boxed byte slice
93    #[cfg(feature = "alloc")]
94    pub fn to_bytes(&self) -> Box<[u8]> {
95        self.as_bytes().to_vec().into_boxed_slice()
96    }
97
98    /// Create an ASN.1 DER encoded signature from big endian `r` and `s` scalars
99    pub(crate) fn from_scalar_bytes(r: &[u8], s: &[u8]) -> der::Result<Self> {
100        let r = UIntRef::new(r)?;
101        let s = UIntRef::new(s)?;
102
103        let mut bytes = SignatureBytes::<C>::default();
104        let mut writer = der::SliceWriter::new(&mut bytes);
105
106        writer.sequence((r.encoded_len()? + s.encoded_len()?)?, |seq| {
107            seq.encode(&r)?;
108            seq.encode(&s)
109        })?;
110
111        writer
112            .finish()?
113            .try_into()
114            .map_err(|_| der::Tag::Sequence.value_error())
115    }
116
117    /// Get the `r` component of the signature (leading zeros removed)
118    pub(crate) fn r(&self) -> &[u8] {
119        &self.bytes[self.r_range.clone()]
120    }
121
122    /// Get the `s` component of the signature (leading zeros removed)
123    pub(crate) fn s(&self) -> &[u8] {
124        &self.bytes[self.s_range.clone()]
125    }
126}
127
128impl<C> AsRef<[u8]> for Signature<C>
129where
130    C: PrimeCurve,
131    MaxSize<C>: ArrayLength<u8>,
132    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
133{
134    fn as_ref(&self) -> &[u8] {
135        self.as_bytes()
136    }
137}
138
139impl<C> fmt::Debug for Signature<C>
140where
141    C: PrimeCurve,
142    MaxSize<C>: ArrayLength<u8>,
143    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
144{
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        write!(f, "ecdsa::der::Signature<{:?}>(", C::default())?;
147
148        for &byte in self.as_ref() {
149            write!(f, "{:02X}", byte)?;
150        }
151
152        write!(f, ")")
153    }
154}
155
156impl<C> TryFrom<&[u8]> for Signature<C>
157where
158    C: PrimeCurve,
159    MaxSize<C>: ArrayLength<u8>,
160    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
161{
162    type Error = Error;
163
164    fn try_from(input: &[u8]) -> Result<Self> {
165        let (r, s) = decode_der(input).map_err(|_| Error::new())?;
166
167        if r.as_bytes().len() > C::UInt::BYTE_SIZE || s.as_bytes().len() > C::UInt::BYTE_SIZE {
168            return Err(Error::new());
169        }
170
171        let r_range = find_scalar_range(input, r.as_bytes())?;
172        let s_range = find_scalar_range(input, s.as_bytes())?;
173
174        if s_range.end != input.len() {
175            return Err(Error::new());
176        }
177
178        let mut bytes = SignatureBytes::<C>::default();
179        bytes[..s_range.end].copy_from_slice(input);
180
181        Ok(Signature {
182            bytes,
183            r_range,
184            s_range,
185        })
186    }
187}
188
189impl<C> TryFrom<Signature<C>> for super::Signature<C>
190where
191    C: PrimeCurve,
192    MaxSize<C>: ArrayLength<u8>,
193    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
194{
195    type Error = Error;
196
197    fn try_from(sig: Signature<C>) -> Result<super::Signature<C>> {
198        let mut bytes = super::SignatureBytes::<C>::default();
199        let r_begin = C::UInt::BYTE_SIZE.saturating_sub(sig.r().len());
200        let s_begin = bytes.len().saturating_sub(sig.s().len());
201        bytes[r_begin..C::UInt::BYTE_SIZE].copy_from_slice(sig.r());
202        bytes[s_begin..].copy_from_slice(sig.s());
203        Self::try_from(bytes.as_slice())
204    }
205}
206
207#[cfg(feature = "serde")]
208#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
209impl<C> Serialize for Signature<C>
210where
211    C: PrimeCurve,
212    MaxSize<C>: ArrayLength<u8>,
213    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
214{
215    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
216    where
217        S: ser::Serializer,
218    {
219        serdect::slice::serialize_hex_upper_or_bin(&self.as_bytes(), serializer)
220    }
221}
222
223#[cfg(feature = "serde")]
224#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
225impl<'de, C> Deserialize<'de> for Signature<C>
226where
227    C: PrimeCurve,
228    MaxSize<C>: ArrayLength<u8>,
229    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
230{
231    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
232    where
233        D: de::Deserializer<'de>,
234    {
235        let mut buf = SignatureBytes::<C>::default();
236        let slice = serdect::slice::deserialize_hex_or_bin(&mut buf, deserializer)?;
237        Self::try_from(slice).map_err(de::Error::custom)
238    }
239}
240
241/// Decode the `r` and `s` components of a DER-encoded ECDSA signature.
242fn decode_der(der_bytes: &[u8]) -> der::Result<(UIntRef<'_>, UIntRef<'_>)> {
243    let mut reader = der::SliceReader::new(der_bytes)?;
244    let header = der::Header::decode(&mut reader)?;
245    header.tag.assert_eq(der::Tag::Sequence)?;
246
247    let ret = reader.read_nested(header.length, |reader| {
248        let r = UIntRef::decode(reader)?;
249        let s = UIntRef::decode(reader)?;
250        Ok((r, s))
251    })?;
252
253    reader.finish(ret)
254}
255
256/// Locate the range within a slice at which a particular subslice is located
257fn find_scalar_range(outer: &[u8], inner: &[u8]) -> Result<Range<usize>> {
258    let outer_start = outer.as_ptr() as usize;
259    let inner_start = inner.as_ptr() as usize;
260    let start = inner_start
261        .checked_sub(outer_start)
262        .ok_or_else(Error::new)?;
263    let end = start.checked_add(inner.len()).ok_or_else(Error::new)?;
264    Ok(Range { start, end })
265}
266
267#[cfg(all(feature = "digest", feature = "hazmat"))]
268impl<C> signature::PrehashSignature for Signature<C>
269where
270    C: PrimeCurve + crate::hazmat::DigestPrimitive,
271    MaxSize<C>: ArrayLength<u8>,
272    <FieldSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
273{
274    type Digest = C::Digest;
275}
276
277#[cfg(all(test, feature = "arithmetic"))]
278mod tests {
279    use elliptic_curve::dev::MockCurve;
280    use signature::Signature as _;
281
282    type Signature = crate::Signature<MockCurve>;
283
284    const EXAMPLE_SIGNATURE: [u8; 64] = [
285        0xf3, 0xac, 0x80, 0x61, 0xb5, 0x14, 0x79, 0x5b, 0x88, 0x43, 0xe3, 0xd6, 0x62, 0x95, 0x27,
286        0xed, 0x2a, 0xfd, 0x6b, 0x1f, 0x6a, 0x55, 0x5a, 0x7a, 0xca, 0xbb, 0x5e, 0x6f, 0x79, 0xc8,
287        0xc2, 0xac, 0x8b, 0xf7, 0x78, 0x19, 0xca, 0x5, 0xa6, 0xb2, 0x78, 0x6c, 0x76, 0x26, 0x2b,
288        0xf7, 0x37, 0x1c, 0xef, 0x97, 0xb2, 0x18, 0xe9, 0x6f, 0x17, 0x5a, 0x3c, 0xcd, 0xda, 0x2a,
289        0xcc, 0x5, 0x89, 0x3,
290    ];
291
292    #[test]
293    fn test_fixed_to_asn1_signature_roundtrip() {
294        let signature1 = Signature::from_bytes(&EXAMPLE_SIGNATURE).unwrap();
295
296        // Convert to ASN.1 DER and back
297        let asn1_signature = signature1.to_der();
298        let signature2 = Signature::from_der(asn1_signature.as_ref()).unwrap();
299
300        assert_eq!(signature1, signature2);
301    }
302
303    #[test]
304    fn test_asn1_too_short_signature() {
305        assert!(Signature::from_der(&[]).is_err());
306        assert!(Signature::from_der(&[der::Tag::Sequence.into()]).is_err());
307        assert!(Signature::from_der(&[der::Tag::Sequence.into(), 0x00]).is_err());
308        assert!(Signature::from_der(&[
309            der::Tag::Sequence.into(),
310            0x03,
311            der::Tag::Integer.into(),
312            0x01,
313            0x01
314        ])
315        .is_err());
316    }
317
318    #[test]
319    fn test_asn1_non_der_signature() {
320        // A minimal 8-byte ASN.1 signature parses OK.
321        assert!(Signature::from_der(&[
322            der::Tag::Sequence.into(),
323            0x06, // length of below
324            der::Tag::Integer.into(),
325            0x01, // length of value
326            0x01, // value=1
327            der::Tag::Integer.into(),
328            0x01, // length of value
329            0x01, // value=1
330        ])
331        .is_ok());
332
333        // But length fields that are not minimally encoded should be rejected, as they are not
334        // valid DER, cf.
335        // https://github.com/google/wycheproof/blob/2196000605e4/testvectors/ecdsa_secp256k1_sha256_test.json#L57-L66
336        assert!(Signature::from_der(&[
337            der::Tag::Sequence.into(),
338            0x81, // extended length: 1 length byte to come
339            0x06, // length of below
340            der::Tag::Integer.into(),
341            0x01, // length of value
342            0x01, // value=1
343            der::Tag::Integer.into(),
344            0x01, // length of value
345            0x01, // value=1
346        ])
347        .is_err());
348    }
349}