ot_certs/asn1/
der.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5//! This module provides an implementation of the `Builder` trait to produce
6//! a DER output. This module only supports literal values for variables
7//! since it needs to know the concrete values to produce the output.
8
9use anyhow::{Result, bail, ensure};
10use num_bigint_dig::BigUint;
11
12use crate::asn1::builder::Builder;
13use crate::asn1::{Oid, Tag};
14use crate::template::{Value, Variable};
15
16impl Tag {
17    // Return the DER representation of the identifier octet(s) of a tag.
18    pub fn to_der(&self) -> Result<Vec<u8>> {
19        const ASN1_TAG_CLASS_CONTEXT: u8 = 2 << 6;
20        const ASN1_TAG_FORM_PRIMITIVE: u8 = 0 << 5;
21        const ASN1_TAG_FORM_CONSTRUCTED: u8 = 1 << 5;
22        // The class and numbers of tags are defined in X.680 in textual
23        // form (https://www.itu.int/rec/T-REC-X.680). See section 18 and
24        // onwards.
25        let tag = match self {
26            Tag::Boolean => 0x01,
27            Tag::BitString => 0x03,
28            Tag::GeneralizedTime => 0x018,
29            Tag::Integer => 0x02,
30            Tag::OctetString => 0x04,
31            Tag::Oid => 0x06,
32            Tag::PrintableString => 0x13,
33            Tag::Sequence => 0x30,
34            Tag::Set => 0x31,
35            Tag::Utf8String => 0x0c,
36            &Tag::Context { constructed, value } => {
37                // Only support small numbers for now.
38                ensure!(value <= 30, "tag number above 30 are not supported for now");
39                let constructed = if constructed {
40                    ASN1_TAG_FORM_CONSTRUCTED
41                } else {
42                    ASN1_TAG_FORM_PRIMITIVE
43                };
44                ASN1_TAG_CLASS_CONTEXT | constructed | (value as u8)
45            }
46        };
47        Ok(vec![tag])
48    }
49}
50
51pub struct Der {
52    output: Vec<u8>,
53}
54
55impl Der {
56    fn new() -> Der {
57        Der { output: Vec::new() }
58    }
59
60    pub fn generate(build: impl FnOnce(&mut Self) -> Result<()>) -> Result<Vec<u8>> {
61        let mut der = Der::new();
62        build(&mut der)?;
63        Ok(der.output)
64    }
65
66    pub fn encode_size(size: usize) -> Vec<u8> {
67        // Push length, see X.690 section 8.1.3.
68        let mut encoded = Vec::<u8>::new();
69        if size <= 0x7f {
70            encoded.push(size as u8);
71        } else {
72            let mut remaining = size;
73            while remaining != 0 {
74                encoded.push((remaining & 0xff) as u8);
75                remaining >>= 8;
76            }
77            encoded.push(0x80 | (encoded.len() as u8));
78            encoded.reverse();
79        }
80        encoded
81    }
82
83    fn get_value_or_error<T>(val: &Value<T>) -> Result<&T> {
84        match val {
85            Value::Literal(x) => Ok(x),
86            Value::Variable(Variable { name, .. }) => bail!(
87                "cannot push value of variable {name}: the DER generator does not support variables"
88            ),
89        }
90    }
91
92    /// Push a byte into the ASN1 output.
93    fn push_bytes(&mut self, val: &[u8]) -> Result<()> {
94        self.output.extend(val);
95        Ok(())
96    }
97}
98
99impl Builder for Der {
100    /// Push a byte into the ASN1 output.
101    fn push_byte(&mut self, val: u8) -> Result<()> {
102        self.output.push(val);
103        Ok(())
104    }
105
106    /// Push a tagged boolean into the ASN1 output.
107    fn push_boolean(&mut self, tag: &Tag, val: &Value<bool>) -> Result<()> {
108        let val = Self::get_value_or_error(val)?;
109        let val = if *val { 0xffu8 } else { 0x00u8 };
110        self.push_tag(None, tag, |builder| builder.push_byte(val))
111    }
112
113    /// Push a tagged integer into the ASN1 output. The name hint can be used by
114    /// the implementation for documentation purpose, or completely ignored.
115    fn push_integer(
116        &mut self,
117        _name_hint: Option<String>,
118        tag: &Tag,
119        val: &Value<BigUint>,
120    ) -> Result<()> {
121        let mut bytes = Self::get_value_or_error(val)?.to_bytes_be();
122        // DER integers are always in two's complement so if the MSB of the MSB byte is set,
123        // we need add a 0 byte to make sure it is not mistaken for a negative integer.
124        if (bytes[0] >> 7) == 1 {
125            bytes.insert(0, 0x00);
126        }
127        let val = Value::Literal(bytes);
128        self.push_tag(None, tag, |builder| builder.push_byte_array(None, &val))
129    }
130
131    /// Push a byte array into the ASN1 output, representing an integer. If the provided
132    /// buffer is smaller than the provided size, it will be padded with zeroes. Note that this
133    /// function does not add a tag to the ASN1 output.
134    fn push_integer_pad(
135        &mut self,
136        _name_hint: Option<String>,
137        val: &Value<BigUint>,
138        size: usize,
139    ) -> Result<()> {
140        let val = Self::get_value_or_error(val)?;
141        let mut bytes = val.to_bytes_be();
142        while bytes.len() < size {
143            bytes.insert(0, 0x0);
144        }
145        let val = Value::Literal(bytes);
146        self.push_byte_array(None, &val)
147    }
148
149    /// Push a byte array of fixed length into the ASN1 output. Note that this function does not add a tag to
150    /// the ASN1 output.
151    fn push_byte_array(&mut self, _name_hint: Option<String>, val: &Value<Vec<u8>>) -> Result<()> {
152        let val = Self::get_value_or_error(val)?;
153        self.output.extend(val);
154        Ok(())
155    }
156
157    /// Push a tagged string into the ASN1 output.
158    fn push_string(
159        &mut self,
160        _name_hint: Option<String>,
161        str_type: &Tag,
162        val: &Value<String>,
163    ) -> Result<()> {
164        let val = Self::get_value_or_error(val)?;
165        let val = Value::Literal(val.as_bytes().to_vec());
166        self.push_tag(None, str_type, |builder| {
167            builder.push_byte_array(None, &val)
168        })
169    }
170
171    /// Push a tagged object identifier into the ASN1 output.
172    fn push_oid(&mut self, oid: &Oid) -> Result<()> {
173        let val = Value::Literal(oid.to_der()?);
174        self.push_tag(None, &Tag::Oid, |builder| {
175            builder.push_byte_array(None, &val)
176        })
177    }
178
179    fn push_bitstring(
180        &mut self,
181        name_hint: Option<String>,
182        tag: &Tag,
183        bits: &[Value<bool>],
184    ) -> Result<()> {
185        let bits = bits
186            .iter()
187            .map(Self::get_value_or_error)
188            .collect::<Result<Vec<_>>>()?;
189        // See X.690 spec section 8.6 for encoding details.
190        // Note: the encoding of an empty bitstring must be the number of unused bits to 0 and have no content.
191        let nr_bytes = bits.len().div_ceil(8);
192        let mut bytes = vec![0u8; nr_bytes];
193        for (i, bit) in bits.iter().enumerate() {
194            bytes[i / 8] |= (**bit as u8) << (7 - (i % 8));
195        }
196
197        self.push_as_bit_string(None, tag, bytes.len() * 8 - bits.len(), |builder| {
198            builder.push_byte_array(name_hint.clone(), &Value::Literal(bytes.clone()))
199        })
200    }
201
202    /// Push tagged content into the ASN1 output. The closure can use any available function of the builder
203    /// and produces the content of the tagged data.
204    fn push_tag(
205        &mut self,
206        _name_hint: Option<String>,
207        tag: &Tag,
208        build: impl FnOnce(&mut Self) -> Result<()>,
209    ) -> Result<()> {
210        let mut content = Der::new();
211        build(&mut content)?;
212        // Push identifier octets.
213        self.push_bytes(&tag.to_der()?)?;
214        // Push length, see X.690 section 8.1.3.
215        if content.output.len() <= 0x7f {
216            self.push_byte(content.output.len() as u8)?;
217        } else {
218            let len = content.output.len();
219            let bytes = Self::encode_size(len);
220            self.push_bytes(&bytes)?;
221        }
222        // Push content
223        self.push_bytes(&content.output)
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230    use num_traits::FromPrimitive;
231
232    #[test]
233    fn test_asn1_der_byte() -> Result<()> {
234        let der = Der::generate(|builder| builder.push_byte(0xa5))?;
235        const RESULT: &[u8] = &[0xa5];
236        assert_eq!(&der, RESULT);
237        Ok(())
238    }
239
240    #[test]
241    fn test_asn1_der_byte_array() -> Result<()> {
242        let bytes: Vec<u8> = vec![0x12, 0x34, 0x56, 0x78];
243        let der =
244            Der::generate(|builder| builder.push_byte_array(None, &Value::Literal(bytes.clone())))?;
245        assert_eq!(der, bytes);
246        Ok(())
247    }
248
249    #[test]
250    fn test_asn1_der_boolean() -> Result<()> {
251        let der = Der::generate(|builder| {
252            builder.push_boolean(&Tag::Boolean, &Value::Literal(true))?;
253            builder.push_boolean(&Tag::Integer, &Value::Literal(false))
254        })?;
255        const RESULT: &[u8] = &[
256            // Identifier octet (universal, boolean), length, content (can be any nonzero value, our DER generator uses 0xff).
257            0x01, 0x01, 0xff, // Identifier octet (universal, integer), length, content.
258            0x02, 0x01, 0x00,
259        ];
260        assert_eq!(&der, RESULT);
261        Ok(())
262    }
263
264    #[test]
265    fn test_asn1_der_integer() -> Result<()> {
266        let der = Der::generate(|builder| {
267            builder.push_integer(
268                None,
269                &Tag::Integer,
270                &Value::Literal(BigUint::from_u32(0).unwrap()),
271            )?;
272            builder.push_integer(
273                None,
274                &Tag::Integer,
275                &Value::Literal(BigUint::from_u32(0x1234).unwrap()),
276            )?;
277            builder.push_integer(
278                None,
279                &Tag::OctetString,
280                &Value::Literal(BigUint::from_u32(0x8000).unwrap()),
281            )
282        })?;
283        const RESULT: &[u8] = &[
284            // Identifier octet (universal, integer), length, content (minimal encoding, always uses one byte at least).
285            0x02, 0x01, 0x0,
286            // Identifier octet (universal, integer), length, content (minimal encoding).
287            0x02, 0x02, 0x12, 0x34,
288            // Identifier octet (universal, octet string), length, content (minimal encoding, needs 0x00 because MSB is set).
289            0x04, 0x03, 0x00, 0x80, 0x00,
290        ];
291        assert_eq!(&der, RESULT);
292        Ok(())
293    }
294
295    #[test]
296    fn test_asn1_der_integer_pad() -> Result<()> {
297        let der = Der::generate(|builder| {
298            builder.push_integer_pad(
299                None,
300                &Value::Literal(BigUint::from_u32(0x1234).unwrap()),
301                2,
302            )?;
303            builder.push_integer_pad(None, &Value::Literal(BigUint::from_u32(0x1234).unwrap()), 4)
304        })?;
305        const RESULT: &[u8] = &[
306            // Content.
307            0x12, 0x34, // Content.
308            0x00, 0x00, 0x12, 0x34,
309        ];
310        assert_eq!(&der, RESULT);
311        Ok(())
312    }
313
314    #[test]
315    fn test_asn1_der_string() -> Result<()> {
316        let der = Der::generate(|builder| {
317            builder.push_string(
318                None,
319                &Tag::PrintableString,
320                &Value::Literal("lowRISC".into()),
321            )?;
322            builder.push_string(None, &Tag::Utf8String, &Value::Literal("OpenTitan".into()))?;
323            builder.push_string(None, &Tag::OctetString, &Value::Literal("".into()))
324        })?;
325        const RESULT: &[u8] = &[
326            // Identifier octet (universal, printable string), length, content.
327            0x13, 0x07, 0x6c, 0x6f, 0x77, 0x52, 0x49, 0x53, 0x43,
328            // Identifier octet (universal, integer), length, content.
329            0x0c, 0x09, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x69, 0x74, 0x61, 0x6e,
330            // Identifier octet (universal, integer), length, no content.
331            0x04, 0x00,
332        ];
333        assert_eq!(&der, RESULT);
334        Ok(())
335    }
336
337    #[test]
338    fn test_asn1_der_oid() -> Result<()> {
339        let der = Der::generate(|builder| builder.push_oid(&Oid::Custom("2.999.3".into())))?;
340        const RESULT: &[u8] = &[
341            // Identifier octet (universal, OID), length, content (encoded as per X.690 section 8.19.5).
342            0x06, 0x03, 0x88, 0x037, 0x03,
343        ];
344        assert_eq!(&der, RESULT);
345        Ok(())
346    }
347
348    #[test]
349    fn test_asn1_der_bitstring() -> Result<()> {
350        let der = Der::generate(|builder| {
351            builder.push_bitstring(None, &Tag::BitString, &[])?;
352            builder.push_bitstring(None, &Tag::BitString, &[Value::Literal(true)])?;
353            builder.push_bitstring(None, &Tag::OctetString, &[Value::Literal(false)])?;
354            builder.push_bitstring(
355                None,
356                &Tag::BitString,
357                &[
358                    true, false, true, true, false, false, false, false, true, false, false, true,
359                ]
360                .into_iter()
361                .map(Value::Literal)
362                .collect::<Vec<_>>(),
363            )
364        })?;
365        const RESULT: &[u8] = &[
366            // Identifier octet (bitstring, OID), length, content (encoded as per X.690 section 8.6).
367            0x03, 0x01, 0x0, // Identifier octet (bitstring, OID), length, content.
368            0x03, 0x02, 0x7, 0x80, // Identifier octet (bitstring, OID), length, content.
369            0x04, 0x02, 0x7, 0x00,
370            // Identifier octet (bitstring, OID), length, content (written in binary to make it easier to read).
371            0x03, 0x03, 0x04, 0b10110000, 0b10010000,
372        ];
373        assert_eq!(&der, RESULT);
374        Ok(())
375    }
376
377    #[test]
378    fn test_asn1_der_tag() -> Result<()> {
379        // Pairs of (data, encoding of length)
380        let bytes_empty: (&[u8], &[u8]) = (&[], &[0x00]);
381        let bytes_short: (&[u8], &[u8]) = (&[0xa5u8; 0x7f], &[0x7f]);
382        let bytes_long_1a: (&[u8], &[u8]) = (&[0xb6u8; 0x80], &[0x81, 0x80]);
383        let bytes_long_1b: (&[u8], &[u8]) = (&[0xc7u8; 0xff], &[0x81, 0xff]);
384        let bytes_long_2a: (&[u8], &[u8]) = (&[0xd8u8; 0x100], &[0x82, 0x01, 0x00]);
385        let bytes_long_2b: (&[u8], &[u8]) = (&[0xe9u8; 0xffff], &[0x82, 0xff, 0xff]);
386        let seq = [
387            bytes_empty,
388            bytes_short,
389            bytes_long_1a,
390            bytes_long_1b,
391            bytes_long_2a,
392            bytes_long_2b,
393        ];
394        let der = Der::generate(|builder| {
395            for (bytes, _) in seq {
396                builder.push_tag(None, &Tag::Sequence, |builder| {
397                    builder.push_byte_array(None, &Value::Literal(bytes.to_vec()))
398                })?;
399            }
400            Ok(())
401        })?;
402        let mut result = Vec::<u8>::new();
403        for (bytes, len_enc) in seq {
404            result.push(0x30); // Identifier octet (universal, sequence).
405            result.extend(len_enc);
406            result.extend(bytes);
407        }
408        assert_eq!(der, result);
409        Ok(())
410    }
411}