Skip to main content

rs_matter/cert/
asn1_writer.rs

1/*
2 *
3 *    Copyright (c) 2022-2026 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use core::fmt::Write;
19
20use time::OffsetDateTime;
21
22use super::{CertConsumer, MAX_DEPTH};
23use crate::error::{Error, ErrorCode};
24use crate::utils::epoch::MATTER_EPOCH_SECS;
25
26#[derive(Debug)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28pub struct ASN1Writer<'a> {
29    buf: &'a mut [u8],
30    // The current write offset in the buffer
31    offset: usize,
32    // If multiple 'composite' structures are being written, their starts are
33    // captured in this
34    depth: [usize; MAX_DEPTH],
35    // The current depth of operation within the depth stack
36    current_depth: usize,
37}
38
39const RESERVE_LEN_BYTES: usize = 3;
40impl<'a> ASN1Writer<'a> {
41    pub fn new(buf: &'a mut [u8]) -> Self {
42        Self {
43            buf,
44            offset: 0,
45            depth: [0; MAX_DEPTH],
46            current_depth: 0,
47        }
48    }
49
50    pub fn append_with<F>(&mut self, size: usize, f: F) -> Result<(), Error>
51    where
52        F: FnOnce(&mut Self),
53    {
54        if self.offset + size <= self.buf.len() {
55            f(self);
56            self.offset += size;
57            return Ok(());
58        }
59        Err(ErrorCode::BufferTooSmall.into())
60    }
61
62    pub fn append_tlv<F>(&mut self, tag: u8, len: usize, f: F) -> Result<(), Error>
63    where
64        F: FnOnce(&mut Self),
65    {
66        let total_len = 1 + ASN1Writer::bytes_to_encode_len(len)? + len;
67        if self.offset + total_len <= self.buf.len() {
68            self.buf[self.offset] = tag;
69            self.offset += 1;
70            self.offset = self.encode_len(self.offset, len)?;
71            f(self);
72            self.offset += len;
73            return Ok(());
74        }
75        Err(ErrorCode::BufferTooSmall.into())
76    }
77
78    fn add_compound(&mut self, val: u8) -> Result<(), Error> {
79        // We reserve 3 bytes for encoding the length
80        // If a shorter length is actually required, we will move everything back
81        self.append_with(1 + RESERVE_LEN_BYTES, |t| t.buf[t.offset] = val)?;
82        self.depth[self.current_depth] = self.offset;
83        self.current_depth += 1;
84        if self.current_depth >= MAX_DEPTH {
85            Err(ErrorCode::BufferTooSmall.into())
86        } else {
87            Ok(())
88        }
89    }
90
91    fn encode_len(&mut self, mut at_offset: usize, len: usize) -> Result<usize, Error> {
92        let mut bytes_of_len = ASN1Writer::bytes_to_encode_len(len)?;
93        if bytes_of_len > 1 {
94            self.buf[at_offset] = (0x80 | (bytes_of_len - 1)) as u8;
95            at_offset += 1;
96            bytes_of_len -= 1;
97        }
98
99        // At this point bytes_of_len is the actual number of bytes for the length encoding
100        // after the 0x80 (if it was present)
101        let mut octet_number = bytes_of_len - 1;
102        // We start encoding the highest octest first
103        loop {
104            self.buf[at_offset] = ((len >> (octet_number * 8)) & 0xff) as u8;
105
106            at_offset += 1;
107            if octet_number == 0 {
108                break;
109            }
110            octet_number -= 1;
111        }
112
113        Ok(at_offset)
114    }
115
116    fn end_compound(&mut self) -> Result<(), Error> {
117        if self.current_depth == 0 {
118            Err(ErrorCode::Invalid)?;
119        }
120        let seq_len = self.get_compound_len();
121        let write_offset = self.get_length_encoding_offset();
122
123        let mut write_offset = self.encode_len(write_offset, seq_len)?;
124
125        // Shift everything by as much
126        let shift_len = self.depth[self.current_depth - 1] - write_offset;
127        if shift_len > 0 {
128            for _i in 0..seq_len {
129                self.buf[write_offset] = self.buf[write_offset + shift_len];
130                write_offset += 1;
131            }
132        }
133        self.current_depth -= 1;
134        self.offset -= shift_len;
135        Ok(())
136    }
137
138    fn get_compound_len(&self) -> usize {
139        self.offset - self.depth[self.current_depth - 1]
140    }
141
142    fn bytes_to_encode_len(len: usize) -> Result<usize, Error> {
143        let len = if len < 128 {
144            // This is directly encoded
145            1
146        } else if len < 256 {
147            // This is done with an 0xA1 followed by actual len
148            2
149        } else if len < 65536 {
150            // This is done with an 0xA2 followed by 2 bytes of actual len
151            3
152        } else {
153            Err(ErrorCode::BufferTooSmall)?
154        };
155        Ok(len)
156    }
157
158    fn get_length_encoding_offset(&self) -> usize {
159        self.depth[self.current_depth - 1] - RESERVE_LEN_BYTES
160    }
161
162    pub fn as_slice(&self) -> &[u8] {
163        &self.buf[..self.offset]
164    }
165
166    fn write_str(&mut self, vtype: u8, s: &[u8]) -> Result<(), Error> {
167        self.append_tlv(vtype, s.len(), |t| {
168            let end_offset = t.offset + s.len();
169            t.buf[t.offset..end_offset].copy_from_slice(s);
170        })
171    }
172}
173
174impl CertConsumer for ASN1Writer<'_> {
175    fn start_seq(&mut self, _tag: &str) -> Result<(), Error> {
176        self.add_compound(0x30)
177    }
178
179    fn end_seq(&mut self) -> Result<(), Error> {
180        self.end_compound()
181    }
182
183    fn integer(&mut self, _tag: &str, i: &[u8]) -> Result<(), Error> {
184        self.write_str(0x02, i)
185    }
186
187    fn printstr(&mut self, _tag: &str, s: &str) -> Result<(), Error> {
188        // Note: ASN1 has multiple strings, this is PrintableString
189        self.write_str(0x13, s.as_bytes())
190    }
191
192    fn utf8str(&mut self, _tag: &str, s: &str) -> Result<(), Error> {
193        // Note: ASN1 has multiple strings, this is UTF8String
194        self.write_str(0x0c, s.as_bytes())
195    }
196
197    fn bitstr(&mut self, _tag: &str, truncate: bool, s: &[u8]) -> Result<(), Error> {
198        // Note: ASN1 has multiple strings, this is BIT String
199
200        // Strip off the end zeroes
201        let mut last_byte = s.len() - 1;
202        let mut num_of_zero = 0;
203        if truncate {
204            while s[last_byte] == 0 {
205                last_byte -= 1;
206            }
207            // For the last valid byte, identifying the number of last bits
208            // that are 0s
209            num_of_zero = s[last_byte].trailing_zeros() as u8;
210        }
211        let s = &s[..(last_byte + 1)];
212        self.append_tlv(0x03, s.len() + 1, |t| {
213            t.buf[t.offset] = num_of_zero;
214            let end_offset = t.offset + 1 + s.len();
215            t.buf[(t.offset + 1)..end_offset].copy_from_slice(s);
216        })
217    }
218
219    fn ostr(&mut self, _tag: &str, s: &[u8]) -> Result<(), Error> {
220        // Note: ASN1 has multiple strings, this is Octet String
221        self.write_str(0x04, s)
222    }
223
224    fn start_compound_ostr(&mut self, _tag: &str) -> Result<(), Error> {
225        // Note: ASN1 has 3 string, this is compound Octet String
226        self.add_compound(0x04)
227    }
228
229    fn end_compound_ostr(&mut self) -> Result<(), Error> {
230        self.end_compound()
231    }
232
233    fn bool(&mut self, _tag: &str, b: bool) -> Result<(), Error> {
234        self.append_tlv(0x01, 1, |t| {
235            if b {
236                t.buf[t.offset] = 0xFF;
237            } else {
238                t.buf[t.offset] = 0x00;
239            }
240        })
241    }
242
243    fn start_set(&mut self, _tag: &str) -> Result<(), Error> {
244        self.add_compound(0x31)
245    }
246
247    fn end_set(&mut self) -> Result<(), Error> {
248        self.end_compound()
249    }
250
251    fn ctx(&mut self, _tag: &str, id: u8, val: &[u8]) -> Result<(), Error> {
252        self.write_str(0x80 | id, val)
253    }
254
255    fn start_ctx(&mut self, _tag: &str, val: u8) -> Result<(), Error> {
256        self.add_compound(0xA0 | val)
257    }
258
259    fn end_ctx(&mut self) -> Result<(), Error> {
260        self.end_compound()
261    }
262
263    fn oid(&mut self, _tag: &str, oid: &[u8]) -> Result<(), Error> {
264        self.write_str(0x06, oid)
265    }
266
267    fn utctime(&mut self, _tag: &str, epoch: u64) -> Result<(), Error> {
268        let matter_epoch = MATTER_EPOCH_SECS + epoch;
269
270        let dt = unwrap!(
271            OffsetDateTime::from_unix_timestamp(matter_epoch as _),
272            "DateTimeError"
273        );
274
275        let mut time_str: heapless::String<32> = heapless::String::<32>::new();
276
277        if dt.year() >= 2050 {
278            // If year is >= 2050, ASN.1 requires it to be Generalised Time
279            write_unwrap!(
280                &mut time_str,
281                "{:04}{:02}{:02}{:02}{:02}{:02}Z",
282                dt.year(),
283                dt.month() as u8,
284                dt.day(),
285                dt.hour(),
286                dt.minute(),
287                dt.second()
288            );
289            self.write_str(0x18, time_str.as_bytes())
290        } else {
291            write_unwrap!(
292                &mut time_str,
293                "{:02}{:02}{:02}{:02}{:02}{:02}Z",
294                dt.year() % 100,
295                dt.month() as u8,
296                dt.day(),
297                dt.hour(),
298                dt.minute(),
299                dt.second()
300            );
301            self.write_str(0x17, time_str.as_bytes())
302        }
303    }
304
305    fn raw(&mut self, _tag: &str, data: &[u8]) -> Result<(), Error> {
306        self.append_with(data.len(), |t| {
307            let end_offset = t.offset + data.len();
308            t.buf[t.offset..end_offset].copy_from_slice(data);
309        })
310    }
311}