flexiber/
tag.rs

1use crate::{
2    Decodable, Decoder, Encodable, Encoder, Error, ErrorKind, Length, Result, TaggedValue,
3};
4use core::{
5    convert::{TryFrom, TryInto},
6    fmt,
7};
8
9const CLASS_OFFSET: usize = 6;
10const CONSTRUCTED_OFFSET: usize = 5;
11
12/// Indicator bit for constructed form encoding (i.e. vs primitive form)
13const CONSTRUCTED_FLAG: u8 = 1u8 << CONSTRUCTED_OFFSET;
14
15/// Indicator bit for constructed form encoding (i.e. vs primitive form)
16const NOT_LAST_TAG_OCTET_FLAG: u8 = 1u8 << 7;
17
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19#[repr(u8)]
20/// Class of BER tag.
21pub enum Class {
22    Universal = 0b00,
23    Application = 0b01,
24    Context = 0b10,
25    Private = 0b11,
26}
27
28impl TryFrom<u8> for Class {
29    type Error = Error;
30    fn try_from(value: u8) -> Result<Self> {
31        use Class::*;
32        Ok(match value {
33            0b00 => Universal,
34            0b01 => Application,
35            0b10 => Context,
36            0b11 => Private,
37            _ => return Err(ErrorKind::InvalidClass { value }.into()),
38        })
39    }
40}
41
42/// The tag field consists of a single byte encoding a tag number from 1 to 254. The values '00' and 'FF' are invalid.
43#[derive(Clone, Copy, Eq, PartialEq)]
44pub struct Tag {
45    pub class: Class,
46    pub constructed: bool,
47    pub number: u16,
48}
49
50impl Tag {
51    pub const BOOLEAN: Self = Self::universal(0x1);
52    pub const INTEGER: Self = Self::universal(0x1);
53    pub const BIT_STRING: Self = Self::universal(0x3);
54    pub const OCTET_STRING: Self = Self::universal(0x4);
55    pub const NULL: Self = Self::universal(0x5);
56    pub const OBJECT_IDENTIFIER: Self = Self::universal(0x6);
57    pub const UTF8_STRING: Self = Self::universal(0xC);
58    pub const PRINTABLE_STRING: Self = Self::universal(0x13);
59    pub const UTC_TIME: Self = Self::universal(0x17);
60    pub const GENERALIZED_TIME: Self = Self::universal(0x18);
61    pub const SEQUENCE: Self = Self::universal(0x10).constructed();
62    pub const SET: Self = Self::universal(0x11).constructed();
63
64    pub fn from(class: Class, constructed: bool, number: u16) -> Self {
65        Self {
66            class,
67            constructed,
68            number,
69        }
70    }
71    pub const fn universal(number: u16) -> Self {
72        Self {
73            class: Class::Universal,
74            constructed: false,
75            number,
76        }
77    }
78
79    pub const fn application(number: u16) -> Self {
80        Self {
81            class: Class::Application,
82            constructed: false,
83            number,
84        }
85    }
86
87    pub const fn context(number: u16) -> Self {
88        Self {
89            class: Class::Context,
90            constructed: false,
91            number,
92        }
93    }
94
95    pub const fn private(number: u16) -> Self {
96        Self {
97            class: Class::Private,
98            constructed: false,
99            number,
100        }
101    }
102
103    pub const fn constructed(self) -> Self {
104        let Self {
105            class,
106            constructed: _,
107            number,
108        } = self;
109        Self {
110            class,
111            constructed: true,
112            number,
113        }
114    }
115}
116
117impl TryFrom<&'_ [u8]> for Tag {
118    type Error = Error;
119    fn try_from(encoding: &[u8]) -> Result<Self> {
120        let mut decoder = Decoder::new(encoding);
121        decoder.decode()
122    }
123}
124
125impl TryFrom<u8> for Tag {
126    type Error = Error;
127    fn try_from(encoded_value: u8) -> Result<Self> {
128        [encoded_value].as_ref().try_into()
129    }
130}
131
132/// This is the common trait that types to be used as tags
133/// are supposed to implement.
134pub trait TagLike: Copy + PartialEq + Sized {
135    /// To stick with one Error type, make sure the tag type can somehow
136    /// or other be coerced into a BerTag.
137    fn embedding(self) -> Tag;
138
139    /// Assert that this [`Tag`] matches the provided expected tag.
140    ///
141    /// On mismatch, returns an [`Error`] with [`ErrorKind::UnexpectedTag`].
142    fn assert_eq(self, expected: Self) -> Result<Self> {
143        if self == expected {
144            Ok(self)
145        } else {
146            Err(ErrorKind::UnexpectedTag {
147                expected: Some(expected.embedding()),
148                actual: self.embedding(),
149            }
150            .into())
151        }
152    }
153
154    /// Ergonomic way to get a TaggedValue for a given tag and value
155    fn with_value<V>(self, value: V) -> TaggedValue<V, Self> {
156        TaggedValue::new(self, value)
157    }
158}
159
160impl TagLike for Tag {
161    fn embedding(self) -> Tag {
162        self
163    }
164}
165
166impl fmt::Display for Tag {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        // f.write_str(self.type_name())
169        // write!(f, "Tag('{:02x}')", self.0)
170        core::fmt::Debug::fmt(self, f)
171    }
172}
173
174impl fmt::Debug for Tag {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        let mut buf = [0u8; 3];
177        let mut encoder = Encoder::new(&mut buf);
178        encoder.encode(self).unwrap();
179        write!(
180            f,
181            "Tag(class = {:?}, constructed = {}, number = {})",
182            self.class, self.constructed, self.number
183        )
184    }
185}
186
187impl Encodable for Tag {
188    fn encoded_length(&self) -> Result<Length> {
189        match self.number {
190            0..=0x1E => Ok(Length(1)),
191            0x1F..=0x7F => Ok(Length(2)),
192            0x80..=0x3FFF => Ok(Length(3)),
193            0x4000..=0xFFFF => Ok(Length(4)),
194        }
195    }
196
197    fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
198        let first_byte =
199            ((self.class as u8) << CLASS_OFFSET) | ((self.constructed as u8) << CONSTRUCTED_OFFSET);
200
201        match self.number {
202            0..=0x1E => encoder.byte(first_byte | (self.number as u8)),
203            0x1F..=0x7F => {
204                encoder.byte(first_byte | 0x1F)?;
205                encoder.byte(self.number as u8)
206            }
207            0x80..=0x3FFF => {
208                encoder.byte(first_byte | 0x1F)?;
209                encoder.byte(NOT_LAST_TAG_OCTET_FLAG | (self.number >> 7) as u8)?;
210                encoder.byte((self.number & 0x7F) as u8)
211            }
212            0x4000..=0xFFFF => Err(Error::from(ErrorKind::UnsupportedTagSize)),
213        }
214    }
215}
216
217impl Decodable<'_> for Tag {
218    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
219        let first_byte = decoder.byte()?;
220        let class = (first_byte >> 6).try_into()?;
221        let constructed = first_byte & CONSTRUCTED_FLAG != 0;
222        // remove class and primitive/constructed bits
223        let first_byte_masked = first_byte & ((1 << 5) - 1);
224
225        let number = match first_byte_masked {
226            number @ 0..=0x1E => number as u16,
227            _ => {
228                let second_byte = decoder.byte()?;
229                if second_byte & NOT_LAST_TAG_OCTET_FLAG == 0 {
230                    let number = second_byte;
231                    number as u16
232                } else {
233                    let number = second_byte & (!NOT_LAST_TAG_OCTET_FLAG);
234                    let third_byte = decoder.byte()?;
235                    if third_byte & NOT_LAST_TAG_OCTET_FLAG == 0 {
236                        ((number as u16) << 7) | (third_byte as u16)
237                    } else {
238                        // todo()
239                        return Err(Error::from(ErrorKind::InvalidLength));
240                    }
241                }
242            }
243        };
244        Ok(Self {
245            class,
246            constructed,
247            number,
248        })
249    }
250}
251
252#[cfg(test)]
253mod tests {
254    use crate::{Decodable, Encodable, Tag};
255
256    #[test]
257    fn reconstruct() {
258        let mut buf = [0u8; 32];
259
260        let tag = Tag::universal(30);
261        let encoded = tag.encode_to_slice(&mut buf).unwrap();
262        assert_eq!(encoded, &[0x1E]);
263        let tag2 = Tag::from_bytes(encoded).unwrap();
264        assert_eq!(tag, tag2);
265
266        let tag = Tag::universal(31);
267        let encoded = tag.encode_to_slice(&mut buf).unwrap();
268        assert_eq!(encoded, &[0x1F, 0x1F]);
269        let tag2 = Tag::from_bytes(encoded).unwrap();
270        assert_eq!(tag, tag2);
271
272        let tag = Tag::universal(0xAA);
273        let encoded = tag.encode_to_slice(&mut buf).unwrap();
274        assert_eq!(encoded, &[0x1F, 0x81, 0x2A]);
275        let tag2 = Tag::from_bytes(encoded).unwrap();
276        assert_eq!(tag, tag2);
277
278        let tag = Tag::universal(0x10).constructed();
279        let encoded = tag.encode_to_slice(&mut buf).unwrap();
280        // assert_eq!(encoded, &[0x1F, 0x81, 0x2A]);
281        assert_eq!(encoded, &[super::CONSTRUCTED_FLAG + 0x10]);
282        let tag2 = Tag::from_bytes(encoded).unwrap();
283        assert_eq!(tag, tag2);
284    }
285}