ecdsa_flow/
der.rs

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