Skip to main content

crypto/encoding/
der.rs

1//! Minimal DER (ASN.1 Distinguished Encoding Rules) reader.
2//!
3//! This implements just enough DER to parse the key material handled by
4//! [`pkcs8`](super::pkcs8) and the DER-encoded ECDSA signatures handled by
5//! [`ecdsa`](super::ecdsa): definite lengths, single-byte tags, and the
6//! primitive encodings those structures require.
7//!
8//! It intentionally rejects BER indefinite-length encoding, constructed
9//! string forms, and high-tag-number forms. Inputs using them return
10//! [`DerError::Unsupported`] or [`DerError::UnexpectedTag`].
11
12/// Errors returned while reading DER-encoded data.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum DerError {
15    /// The input ended before a complete value could be read.
16    Truncated,
17    /// A length used an unsupported or non-minimal encoding.
18    InvalidLength,
19    /// A value had an unexpected tag or empty content.
20    UnexpectedTag,
21    /// The input had trailing bytes after the expected value(s).
22    TrailingData,
23    /// A value used a DER feature this reader does not support.
24    Unsupported,
25}
26
27impl core::fmt::Display for DerError {
28    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
29        match self {
30            DerError::Truncated => write!(f, "truncated DER input"),
31            DerError::InvalidLength => write!(f, "invalid DER length"),
32            DerError::UnexpectedTag => write!(f, "unexpected DER tag"),
33            DerError::TrailingData => write!(f, "trailing DER data"),
34            DerError::Unsupported => write!(f, "unsupported DER encoding"),
35        }
36    }
37}
38
39impl core::error::Error for DerError {}
40
41const TAG_INTEGER: u8 = 0x02;
42const TAG_BIT_STRING: u8 = 0x03;
43const TAG_OCTET_STRING: u8 = 0x04;
44const TAG_OID: u8 = 0x06;
45const TAG_SEQUENCE: u8 = 0x30;
46
47/// A cursor over a DER input that yields borrowed value contents.
48///
49/// The reader is checked: every method validates the tag, the length, and the
50/// available input before returning, so it never panics on malformed data.
51#[derive(Debug, Clone)]
52pub struct Reader<'a> {
53    data: &'a [u8],
54    pos: usize,
55}
56
57impl<'a> Reader<'a> {
58    /// Creates a reader over `data`.
59    pub const fn new(data: &'a [u8]) -> Self {
60        Self {
61            data,
62            pos: 0,
63        }
64    }
65
66    /// Returns `true` when all input has been consumed.
67    pub fn is_empty(&self) -> bool {
68        self.pos >= self.data.len()
69    }
70
71    /// Returns the number of unread bytes.
72    pub fn remaining(&self) -> usize {
73        self.data.len() - self.pos
74    }
75
76    /// Returns the tag of the next value without consuming it.
77    ///
78    /// Returns [`DerError::Truncated`] when no value remains.
79    pub fn peek_tag(&self) -> Result<u8, DerError> {
80        self.data.get(self.pos).copied().ok_or(DerError::Truncated)
81    }
82
83    /// Reads one tag-length-value, returning its tag and content.
84    ///
85    /// Returns [`DerError`] when the value is truncated, uses an indefinite or
86    /// non-minimal length, or has a high-tag-number form.
87    pub fn read_tlv(&mut self) -> Result<(u8, &'a [u8]), DerError> {
88        let tag = *self.data.get(self.pos).ok_or(DerError::Truncated)?;
89        if tag & 0x1f == 0x1f {
90            return Err(DerError::Unsupported);
91        }
92        self.pos += 1;
93
94        let first = *self.data.get(self.pos).ok_or(DerError::Truncated)?;
95        self.pos += 1;
96
97        let len = match first {
98            0x00..=0x7f => first as usize,
99            // Indefinite length is BER, not DER.
100            0x80 => return Err(DerError::InvalidLength),
101            0x81..=0x84 => {
102                let n = (first & 0x7f) as usize;
103                let mut len: usize = 0;
104                for _ in 0..n {
105                    let b = *self.data.get(self.pos).ok_or(DerError::Truncated)?;
106                    self.pos += 1;
107                    len = (len << 8) | b as usize;
108                }
109                // DER requires the minimal number of length bytes.
110                if len < 0x80 {
111                    return Err(DerError::InvalidLength);
112                }
113                len
114            }
115            _ => return Err(DerError::InvalidLength),
116        };
117
118        let end = self.pos.checked_add(len).ok_or(DerError::InvalidLength)?;
119        if end > self.data.len() {
120            return Err(DerError::Truncated);
121        }
122        let content = &self.data[self.pos..end];
123        self.pos = end;
124        Ok((tag, content))
125    }
126
127    /// Reads one value and requires it to have `tag`, returning its content.
128    pub fn read_expected(&mut self, tag: u8) -> Result<&'a [u8], DerError> {
129        let (actual, content) = self.read_tlv()?;
130        if actual != tag {
131            return Err(DerError::UnexpectedTag);
132        }
133        Ok(content)
134    }
135
136    /// Reads a `SEQUENCE` and returns a reader over its content.
137    pub fn read_sequence(&mut self) -> Result<Reader<'a>, DerError> {
138        Ok(Reader::new(self.read_expected(TAG_SEQUENCE)?))
139    }
140
141    /// Reads an `INTEGER`, returning its canonical content bytes.
142    ///
143    /// The content may start with a single `0x00` when required to keep the
144    /// sign bit clear. Empty or non-minimal encodings are rejected.
145    pub fn read_integer(&mut self) -> Result<&'a [u8], DerError> {
146        let content = self.read_expected(TAG_INTEGER)?;
147        if content.is_empty() {
148            return Err(DerError::UnexpectedTag);
149        }
150        // Reject a redundant leading zero (canonical DER).
151        if content.len() > 1 && content[0] == 0 && content[1] & 0x80 == 0 {
152            return Err(DerError::InvalidLength);
153        }
154        Ok(content)
155    }
156
157    /// Reads an `OCTET STRING`, returning its content bytes.
158    pub fn read_octet_string(&mut self) -> Result<&'a [u8], DerError> {
159        self.read_expected(TAG_OCTET_STRING)
160    }
161
162    /// Reads a `BIT STRING`, returning its bytes with the unused-bits count
163    /// byte removed.
164    ///
165    /// Returns [`DerError::Unsupported`] when the value declares unused bits
166    /// (only byte-aligned bit strings are supported).
167    pub fn read_bit_string(&mut self) -> Result<&'a [u8], DerError> {
168        let content = self.read_expected(TAG_BIT_STRING)?;
169        let (unused, rest) = content.split_first().ok_or(DerError::UnexpectedTag)?;
170        if *unused != 0 {
171            return Err(DerError::Unsupported);
172        }
173        Ok(rest)
174    }
175
176    /// Reads an `OBJECT IDENTIFIER`, returning its content bytes (without the
177    /// leading tag/length). Compare against known OID byte strings.
178    pub fn read_oid(&mut self) -> Result<&'a [u8], DerError> {
179        let content = self.read_expected(TAG_OID)?;
180        if content.is_empty() {
181            return Err(DerError::UnexpectedTag);
182        }
183        Ok(content)
184    }
185
186    /// Reads a context-specific `[tag]` explicit wrapper and returns a reader
187    /// over the wrapped value. `tag` is the full identifier byte, e.g. `0xa0`.
188    pub fn read_explicit(&mut self, tag: u8) -> Result<Reader<'a>, DerError> {
189        Ok(Reader::new(self.read_expected(tag)?))
190    }
191
192    /// Asserts that all input was consumed.
193    ///
194    /// Returns [`DerError::TrailingData`] otherwise.
195    pub fn finish(self) -> Result<(), DerError> {
196        if self.pos == self.data.len() {
197            Ok(())
198        } else {
199            Err(DerError::TrailingData)
200        }
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207
208    #[test]
209    fn reads_short_form_sequence() {
210        // SEQUENCE { INTEGER 1 }
211        let der = [0x30, 0x03, 0x02, 0x01, 0x01];
212        let mut r = Reader::new(&der);
213        let mut seq = r.read_sequence().unwrap();
214        r.finish().unwrap();
215        assert_eq!(seq.read_integer().unwrap(), &[0x01]);
216        seq.finish().unwrap();
217    }
218
219    #[test]
220    fn reads_long_form_length() {
221        // OCTET STRING of 200 zero bytes: 04 81 c8 ...
222        let mut der = vec![0x04, 0x81, 0xc8];
223        der.extend_from_slice(&[0u8; 200]);
224        let mut r = Reader::new(&der);
225        assert_eq!(r.read_octet_string().unwrap().len(), 200);
226        r.finish().unwrap();
227    }
228
229    #[test]
230    fn rejects_indefinite_length() {
231        let der = [0x30, 0x80, 0x00, 0x00];
232        assert_eq!(Reader::new(&der).read_sequence().unwrap_err(), DerError::InvalidLength);
233    }
234
235    #[test]
236    fn rejects_non_minimal_length() {
237        // Length 0x01 encoded in long form (0x81 0x01) is not DER.
238        let der = [0x04, 0x81, 0x01, 0xff];
239        assert_eq!(Reader::new(&der).read_octet_string().unwrap_err(), DerError::InvalidLength);
240    }
241
242    #[test]
243    fn rejects_truncated_content() {
244        let der = [0x04, 0x05, 0x00, 0x00];
245        assert_eq!(Reader::new(&der).read_octet_string().unwrap_err(), DerError::Truncated);
246    }
247
248    #[test]
249    fn rejects_wrong_tag() {
250        let der = [0x02, 0x01, 0x01];
251        assert_eq!(Reader::new(&der).read_octet_string().unwrap_err(), DerError::UnexpectedTag);
252    }
253
254    #[test]
255    fn rejects_trailing_data() {
256        let der = [0x02, 0x01, 0x01, 0xff];
257        let mut r = Reader::new(&der);
258        r.read_integer().unwrap();
259        assert_eq!(r.finish().unwrap_err(), DerError::TrailingData);
260    }
261
262    #[test]
263    fn rejects_redundant_integer_zero() {
264        // INTEGER 0x0001 with a redundant leading zero.
265        let der = [0x02, 0x02, 0x00, 0x01];
266        assert_eq!(Reader::new(&der).read_integer().unwrap_err(), DerError::InvalidLength);
267    }
268
269    #[test]
270    fn reads_bit_string_ignoring_unused_bits_byte() {
271        // BIT STRING, unused=0, 0x04 0x01
272        let der = [0x03, 0x03, 0x00, 0x04, 0x01];
273        assert_eq!(Reader::new(&der).read_bit_string().unwrap(), &[0x04, 0x01]);
274    }
275
276    #[test]
277    fn rejects_non_zero_unused_bits() {
278        let der = [0x03, 0x03, 0x07, 0x04, 0x01];
279        assert_eq!(Reader::new(&der).read_bit_string().unwrap_err(), DerError::Unsupported);
280    }
281
282    #[test]
283    fn reads_explicit_context_tag() {
284        // [0] { INTEGER 2 }
285        let der = [0xa0, 0x03, 0x02, 0x01, 0x02];
286        let mut r = Reader::new(&der);
287        let mut inner = r.read_explicit(0xa0).unwrap();
288        assert_eq!(inner.read_integer().unwrap(), &[0x02]);
289    }
290}