Skip to main content

arcana/encoding/
der.rs

1//! Minimal ASN.1 DER encoder/decoder.
2//!
3//! Supports only the tags needed by PKCS#1, PKCS#8, SEC1 and SPKI:
4//! SEQUENCE, INTEGER, OCTET STRING, BIT STRING, OID, context-tagged
5//! (explicit), and NULL.
6
7// ====================================================================
8// Tag constants
9// ====================================================================
10
11/// ASN.1 tag bytes.
12pub const TAG_INTEGER: u8 = 0x02;
13/// BIT STRING tag.
14pub const TAG_BIT_STRING: u8 = 0x03;
15/// OCTET STRING tag.
16pub const TAG_OCTET_STRING: u8 = 0x04;
17/// NULL tag.
18pub const TAG_NULL: u8 = 0x05;
19/// OBJECT IDENTIFIER tag.
20pub const TAG_OID: u8 = 0x06;
21/// SEQUENCE (constructed) tag.
22pub const TAG_SEQUENCE: u8 = 0x30;
23
24// ====================================================================
25// Encoder
26// ====================================================================
27
28/// DER encoder: builds a byte vector by appending TLV items.
29pub struct DerEncoder {
30    buf: Vec<u8>,
31}
32
33impl DerEncoder {
34    /// Create a new empty encoder.
35    pub fn new() -> Self {
36        Self { buf: Vec::new() }
37    }
38
39    /// Return the encoded bytes.
40    pub fn finish(self) -> Vec<u8> {
41        self.buf
42    }
43
44    /// Append a raw DER length.
45    pub fn write_length(&mut self, len: usize) {
46        if len < 0x80 {
47            self.buf.push(len as u8);
48        } else if len < 0x100 {
49            self.buf.push(0x81);
50            self.buf.push(len as u8);
51        } else if len < 0x10000 {
52            self.buf.push(0x82);
53            self.buf.push((len >> 8) as u8);
54            self.buf.push((len & 0xFF) as u8);
55        } else {
56            self.buf.push(0x83);
57            self.buf.push((len >> 16) as u8);
58            self.buf.push(((len >> 8) & 0xFF) as u8);
59            self.buf.push((len & 0xFF) as u8);
60        }
61    }
62
63    /// Append a SEQUENCE wrapping the given content.
64    pub fn sequence(&mut self, content: &[u8]) {
65        self.buf.push(TAG_SEQUENCE);
66        self.write_length(content.len());
67        self.buf.extend_from_slice(content);
68    }
69
70    /// Append an INTEGER from big-endian unsigned bytes.
71    /// Adds a leading 0x00 if the MSB is set (to keep it positive).
72    pub fn integer(&mut self, value: &[u8]) {
73        // Strip leading zeros but keep at least one byte.
74        let mut start = 0;
75        while start < value.len() - 1 && value[start] == 0 {
76            start += 1;
77        }
78        let val = &value[start..];
79        let needs_pad = val[0] & 0x80 != 0;
80        let total = val.len() + if needs_pad { 1 } else { 0 };
81
82        self.buf.push(TAG_INTEGER);
83        self.write_length(total);
84        if needs_pad {
85            self.buf.push(0x00);
86        }
87        self.buf.extend_from_slice(val);
88    }
89
90    /// Append a small non-negative INTEGER from a u64 (for version fields).
91    pub fn integer_u64(&mut self, v: u64) {
92        if v == 0 {
93            self.buf.extend_from_slice(&[TAG_INTEGER, 1, 0]);
94            return;
95        }
96        let be = v.to_be_bytes();
97        let start = be.iter().position(|&b| b != 0).unwrap_or(7);
98        self.integer(&be[start..]);
99    }
100
101    /// Append an OCTET STRING.
102    pub fn octet_string(&mut self, data: &[u8]) {
103        self.buf.push(TAG_OCTET_STRING);
104        self.write_length(data.len());
105        self.buf.extend_from_slice(data);
106    }
107
108    /// Append a BIT STRING (with zero unused-bits prefix).
109    pub fn bit_string(&mut self, data: &[u8]) {
110        self.buf.push(TAG_BIT_STRING);
111        self.write_length(data.len() + 1);
112        self.buf.push(0x00); // unused bits
113        self.buf.extend_from_slice(data);
114    }
115
116    /// Append an OBJECT IDENTIFIER from its encoded byte form.
117    pub fn oid(&mut self, encoded: &[u8]) {
118        self.buf.push(TAG_OID);
119        self.write_length(encoded.len());
120        self.buf.extend_from_slice(encoded);
121    }
122
123    /// Append a NULL.
124    pub fn null(&mut self) {
125        self.buf.extend_from_slice(&[TAG_NULL, 0x00]);
126    }
127
128    /// Append an explicit context-tagged \[N\] CONSTRUCTED wrapping.
129    pub fn context_explicit(&mut self, tag_num: u8, content: &[u8]) {
130        self.buf.push(0xA0 | tag_num);
131        self.write_length(content.len());
132        self.buf.extend_from_slice(content);
133    }
134
135    /// Append raw bytes (for building inner content).
136    pub fn raw(&mut self, data: &[u8]) {
137        self.buf.extend_from_slice(data);
138    }
139}
140
141// ====================================================================
142// Decoder
143// ====================================================================
144
145/// DER decoder: reads TLV items from a byte slice.
146pub struct DerDecoder<'a> {
147    data: &'a [u8],
148    pos: usize,
149}
150
151impl<'a> DerDecoder<'a> {
152    /// Create a decoder over a byte slice.
153    pub fn new(data: &'a [u8]) -> Self {
154        Self { data, pos: 0 }
155    }
156
157    /// Bytes remaining.
158    pub fn remaining(&self) -> usize {
159        self.data.len() - self.pos
160    }
161
162    /// True if all bytes consumed.
163    pub fn is_empty(&self) -> bool {
164        self.pos >= self.data.len()
165    }
166
167    /// Peek at the next tag byte without advancing.
168    pub fn peek_tag(&self) -> Option<u8> {
169        self.data.get(self.pos).copied()
170    }
171
172    /// Read tag + length, return (tag, content_slice).
173    pub fn read_tlv(&mut self) -> Option<(u8, &'a [u8])> {
174        let tag = *self.data.get(self.pos)?;
175        self.pos += 1;
176        let len = self.read_length()?;
177        if self.pos + len > self.data.len() {
178            return None;
179        }
180        let content = &self.data[self.pos..self.pos + len];
181        self.pos += len;
182        Some((tag, content))
183    }
184
185    fn read_length(&mut self) -> Option<usize> {
186        let first = *self.data.get(self.pos)?;
187        self.pos += 1;
188        if first < 0x80 {
189            Some(first as usize)
190        } else if first == 0x81 {
191            let b = *self.data.get(self.pos)? as usize;
192            self.pos += 1;
193            Some(b)
194        } else if first == 0x82 {
195            let hi = *self.data.get(self.pos)? as usize;
196            let lo = *self.data.get(self.pos + 1)? as usize;
197            self.pos += 2;
198            Some((hi << 8) | lo)
199        } else if first == 0x83 {
200            let a = *self.data.get(self.pos)? as usize;
201            let b = *self.data.get(self.pos + 1)? as usize;
202            let c = *self.data.get(self.pos + 2)? as usize;
203            self.pos += 3;
204            Some((a << 16) | (b << 8) | c)
205        } else {
206            None
207        }
208    }
209
210    /// Read a SEQUENCE, return a sub-decoder over its content.
211    pub fn read_sequence(&mut self) -> Option<DerDecoder<'a>> {
212        let (tag, content) = self.read_tlv()?;
213        if tag != TAG_SEQUENCE {
214            return None;
215        }
216        Some(DerDecoder::new(content))
217    }
218
219    /// Read an INTEGER, return the big-endian unsigned bytes
220    /// (leading zero stripped).
221    pub fn read_integer(&mut self) -> Option<&'a [u8]> {
222        let (tag, content) = self.read_tlv()?;
223        if tag != TAG_INTEGER || content.is_empty() {
224            return None;
225        }
226        // Strip leading zero used for sign padding.
227        if content[0] == 0x00 && content.len() > 1 {
228            Some(&content[1..])
229        } else {
230            Some(content)
231        }
232    }
233
234    /// Read an INTEGER as u64 (small values like version fields).
235    pub fn read_integer_u64(&mut self) -> Option<u64> {
236        let bytes = self.read_integer()?;
237        let mut v = 0u64;
238        for &b in bytes {
239            v = (v << 8) | b as u64;
240        }
241        Some(v)
242    }
243
244    /// Read an OCTET STRING.
245    pub fn read_octet_string(&mut self) -> Option<&'a [u8]> {
246        let (tag, content) = self.read_tlv()?;
247        if tag != TAG_OCTET_STRING {
248            return None;
249        }
250        Some(content)
251    }
252
253    /// Read a BIT STRING, return the data bytes (skip unused-bits byte).
254    pub fn read_bit_string(&mut self) -> Option<&'a [u8]> {
255        let (tag, content) = self.read_tlv()?;
256        if tag != TAG_BIT_STRING || content.is_empty() {
257            return None;
258        }
259        // First byte = number of unused bits in the last byte.
260        Some(&content[1..])
261    }
262
263    /// Read an OID, return the encoded bytes.
264    pub fn read_oid(&mut self) -> Option<&'a [u8]> {
265        let (tag, content) = self.read_tlv()?;
266        if tag != TAG_OID {
267            return None;
268        }
269        Some(content)
270    }
271
272    /// Read a NULL.
273    pub fn read_null(&mut self) -> Option<()> {
274        let (tag, content) = self.read_tlv()?;
275        if tag != TAG_NULL || !content.is_empty() {
276            return None;
277        }
278        Some(())
279    }
280
281    /// Read an explicit context-tagged \[N\] CONSTRUCTED, return a
282    /// sub-decoder over its content. Returns None if the tag number
283    /// doesn't match or the tag is absent.
284    pub fn read_context_explicit(&mut self, tag_num: u8) -> Option<DerDecoder<'a>> {
285        let expected = 0xA0 | tag_num;
286        if self.peek_tag() != Some(expected) {
287            return None;
288        }
289        let (_, content) = self.read_tlv()?;
290        Some(DerDecoder::new(content))
291    }
292
293    /// Skip the next TLV element (any tag).
294    pub fn skip(&mut self) -> Option<()> {
295        self.read_tlv().map(|_| ())
296    }
297}
298
299// ====================================================================
300// Well-known OIDs (encoded form, without tag+length)
301// ====================================================================
302
303/// rsaEncryption (1.2.840.113549.1.1.1)
304pub const OID_RSA: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01];
305
306/// id-ecPublicKey (1.2.840.10045.2.1)
307pub const OID_EC_PUBLIC_KEY: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01];
308
309/// secp256r1 / P-256 (1.2.840.10045.3.1.7)
310pub const OID_SECP256R1: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
311
312/// secp384r1 / P-384 (1.3.132.0.34)
313pub const OID_SECP384R1: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x22];
314
315/// secp521r1 / P-521 (1.3.132.0.35)
316pub const OID_SECP521R1: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x23];
317
318/// secp256k1 (1.3.132.0.10)
319pub const OID_SECP256K1: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x0A];
320
321/// brainpoolP256r1 (1.3.36.3.3.2.8.1.1.7)
322pub const OID_BRAINPOOL_P256R1: &[u8] = &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07];
323
324/// brainpoolP384r1 (1.3.36.3.3.2.8.1.1.11)
325pub const OID_BRAINPOOL_P384R1: &[u8] = &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B];
326
327/// brainpoolP512r1 (1.3.36.3.3.2.8.1.1.13)
328pub const OID_BRAINPOOL_P512R1: &[u8] = &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D];
329
330/// id-Ed25519 (1.3.101.112)
331pub const OID_ED25519: &[u8] = &[0x2B, 0x65, 0x70];
332
333/// id-X25519 (1.3.101.110)
334pub const OID_X25519: &[u8] = &[0x2B, 0x65, 0x6E];
335
336/// id-X448 (1.3.101.111)
337pub const OID_X448: &[u8] = &[0x2B, 0x65, 0x6F];
338
339// ====================================================================
340// Tests
341// ====================================================================
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346
347    #[test]
348    fn roundtrip_integer() {
349        let val = [0x00, 0x80, 0xFF]; // needs leading-zero strip then pad
350        let mut enc = DerEncoder::new();
351        enc.integer(&val);
352        let der = enc.finish();
353        let mut dec = DerDecoder::new(&der);
354        let got = dec.read_integer().unwrap();
355        assert_eq!(got, &[0x80, 0xFF]);
356    }
357
358    #[test]
359    fn roundtrip_integer_u64() {
360        for v in [0u64, 1, 127, 128, 255, 256, 65535, 0xDEADBEEF] {
361            let mut enc = DerEncoder::new();
362            enc.integer_u64(v);
363            let der = enc.finish();
364            let mut dec = DerDecoder::new(&der);
365            assert_eq!(dec.read_integer_u64().unwrap(), v, "v={}", v);
366        }
367    }
368
369    #[test]
370    fn roundtrip_sequence() {
371        let mut inner = DerEncoder::new();
372        inner.integer_u64(42);
373        inner.octet_string(b"hello");
374        let content = inner.finish();
375
376        let mut outer = DerEncoder::new();
377        outer.sequence(&content);
378        let der = outer.finish();
379
380        let mut dec = DerDecoder::new(&der);
381        let mut seq = dec.read_sequence().unwrap();
382        assert_eq!(seq.read_integer_u64().unwrap(), 42);
383        assert_eq!(seq.read_octet_string().unwrap(), b"hello");
384        assert!(seq.is_empty());
385    }
386
387    #[test]
388    fn roundtrip_bit_string() {
389        let data = [0x04, 0x01, 0x02]; // uncompressed EC point prefix
390        let mut enc = DerEncoder::new();
391        enc.bit_string(&data);
392        let der = enc.finish();
393
394        let mut dec = DerDecoder::new(&der);
395        let got = dec.read_bit_string().unwrap();
396        assert_eq!(got, &data);
397    }
398
399    #[test]
400    fn roundtrip_oid() {
401        let mut enc = DerEncoder::new();
402        enc.oid(OID_RSA);
403        let der = enc.finish();
404
405        let mut dec = DerDecoder::new(&der);
406        assert_eq!(dec.read_oid().unwrap(), OID_RSA);
407    }
408
409    #[test]
410    fn context_explicit_tag() {
411        let mut inner = DerEncoder::new();
412        inner.integer_u64(1);
413        let content = inner.finish();
414
415        let mut enc = DerEncoder::new();
416        enc.context_explicit(0, &content);
417        let der = enc.finish();
418
419        let mut dec = DerDecoder::new(&der);
420        let mut ctx = dec.read_context_explicit(0).unwrap();
421        assert_eq!(ctx.read_integer_u64().unwrap(), 1);
422    }
423
424    #[test]
425    fn large_length_encoding() {
426        // 256 bytes → 0x82 0x01 0x00 encoding
427        let data = vec![0xABu8; 256];
428        let mut enc = DerEncoder::new();
429        enc.octet_string(&data);
430        let der = enc.finish();
431
432        let mut dec = DerDecoder::new(&der);
433        let got = dec.read_octet_string().unwrap();
434        assert_eq!(got, &data[..]);
435    }
436}