Skip to main content

asn1/
tag.rs

1use crate::base128;
2use crate::parser::{ParseError, ParseErrorKind, ParseResult};
3use crate::writer::{WriteBuf, WriteResult};
4
5#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
6pub enum TagClass {
7    Universal = 0b00,
8    Application = 0b01,
9    ContextSpecific = 0b10,
10    Private = 0b11,
11}
12
13#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
14pub struct Tag {
15    value: u32,
16    constructed: bool,
17    class: TagClass,
18}
19
20pub(crate) const CONSTRUCTED: u8 = 0x20;
21
22impl Tag {
23    /// Parses a `Tag` from bytes and returns either the `Tag` and the
24    /// remaining bytes from the input or an error.
25    pub fn from_bytes(mut data: &[u8]) -> ParseResult<(Tag, &[u8])> {
26        let tag = match data.first() {
27            Some(&b) => b,
28            None => return Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
29        };
30        data = &data[1..];
31        let mut value = u32::from(tag & 0x1f);
32        let constructed = tag & CONSTRUCTED == CONSTRUCTED;
33
34        let tag_class_bits = tag >> 6;
35        let class = if tag_class_bits == TagClass::Universal as u8 {
36            TagClass::Universal
37        } else if tag_class_bits == TagClass::Application as u8 {
38            TagClass::Application
39        } else if tag_class_bits == TagClass::ContextSpecific as u8 {
40            TagClass::ContextSpecific
41        } else {
42            assert!(tag_class_bits == TagClass::Private as u8);
43            TagClass::Private
44        };
45
46        // Long form tag
47        if value == 0x1f {
48            let large_value;
49            (large_value, data) = base128::read_base128_int(data).map_err(|e| {
50                if matches!(e.kind(), ParseErrorKind::ShortData { .. }) {
51                    e
52                } else {
53                    ParseError::new(ParseErrorKind::InvalidTag)
54                }
55            })?;
56            value = large_value
57                .try_into()
58                .map_err(|_| ParseError::new(ParseErrorKind::InvalidTag))?;
59            // Tags must be encoded in minimal form.
60            if value < 0x1f {
61                return Err(ParseError::new(ParseErrorKind::InvalidTag));
62            }
63        }
64
65        Ok((
66            Tag {
67                value,
68                constructed,
69                class,
70            },
71            data,
72        ))
73    }
74
75    pub(crate) const fn new(tag: u32, class: TagClass, constructed: bool) -> Tag {
76        Tag {
77            value: tag,
78            constructed,
79            class,
80        }
81    }
82
83    /// This is `pub` for use in tests but is not considered part of the
84    /// supported API.
85    #[doc(hidden)]
86    pub const fn primitive(tag: u32) -> Tag {
87        Tag::new(tag, TagClass::Universal, false)
88    }
89
90    pub(crate) const fn constructed(tag: u32) -> Tag {
91        Tag::new(tag, TagClass::Universal, true)
92    }
93
94    /// Returns the tag's representation (including tag class and constructed
95    /// bits) as a `u8` if the `value` component fits in a short form
96    /// (value < 31) or `None` if this is a long-form tag.
97    pub fn as_u8(self) -> Option<u8> {
98        if self.value >= 0x1f {
99            return None;
100        }
101        Some(
102            ((self.class as u8) << 6)
103                | if self.constructed { CONSTRUCTED } else { 0 }
104                | (self.value as u8),
105        )
106    }
107
108    /// Writes the tag's encoded representation (including tag class and
109    /// constructed bits) to a `WriteBuf`.
110    pub(crate) fn write_to(self, dest: &mut WriteBuf) -> WriteResult {
111        let mut b = ((self.class as u8) << 6) | if self.constructed { CONSTRUCTED } else { 0 };
112        if self.value >= 0x1f {
113            b |= 0x1f;
114            dest.push_byte(b)?;
115            let len = base128::base128_length(self.value.into());
116            let orig_len = dest.len();
117            for _ in 0..len {
118                dest.push_byte(0)?;
119            }
120            base128::write_base128_int(&mut dest.as_mut_slice()[orig_len..], self.value.into());
121        } else {
122            b |= self.value as u8;
123            dest.push_byte(b)?;
124        }
125
126        Ok(())
127    }
128
129    pub const fn is_constructed(self) -> bool {
130        self.constructed
131    }
132
133    pub fn class(self) -> TagClass {
134        self.class
135    }
136
137    pub fn value(self) -> u32 {
138        self.value
139    }
140
141    /// Get the number of bytes needed to encode this tag.
142    pub(crate) fn encoded_length(self) -> usize {
143        if self.value >= 0x1f {
144            // Long form: 1 byte for the initial tag byte + base128 encoding of the value
145            1 + crate::base128::base128_length(self.value.into())
146        } else {
147            // Short form: 1 byte
148            1
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::{Tag, TagClass, CONSTRUCTED};
156
157    #[test]
158    fn test_constructed() {
159        for i in 0..31 {
160            let tag = Tag::constructed(u32::from(i));
161            assert_eq!(tag.as_u8(), Some(CONSTRUCTED | i));
162            assert!(tag.is_constructed());
163        }
164    }
165
166    #[test]
167    fn test_as_u8() {
168        for (t, expected) in &[
169            (Tag::new(5, TagClass::Application, true), Some(0x65)),
170            (Tag::new(5, TagClass::Universal, false), Some(0x05)),
171            (Tag::new(0x1f, TagClass::Universal, false), None),
172        ] {
173            assert_eq!(&t.as_u8(), expected);
174        }
175    }
176
177    #[test]
178    fn test_class() {
179        assert_eq!(
180            Tag::new(5, TagClass::Application, true).class(),
181            TagClass::Application
182        );
183    }
184
185    #[test]
186    fn test_value() {
187        assert_eq!(Tag::new(5, TagClass::Application, true).value(), 5);
188    }
189
190    #[test]
191    fn test_write_to() {
192        for (t, expected) in [
193            (Tag::new(5, TagClass::Application, true), b"\x65" as &[u8]),
194            (Tag::new(5, TagClass::Universal, false), b"\x05"),
195            (Tag::new(0x1f, TagClass::Universal, false), b"\x1f\x1f"),
196        ] {
197            let data = vec![];
198            let mut buf = super::WriteBuf::new(data);
199            t.write_to(&mut buf).unwrap();
200            assert_eq!(buf.as_slice(), expected);
201        }
202    }
203
204    #[test]
205    fn test_write_to_roundtrip() {
206        for tag in [
207            Tag::new(5, TagClass::Application, true),
208            Tag::new(5, TagClass::Universal, false),
209            Tag::new(0x1f, TagClass::Universal, false),
210            Tag::new(0, TagClass::ContextSpecific, true),
211            Tag::new(127, TagClass::Private, false),
212        ] {
213            let data = vec![];
214            let mut buf = super::WriteBuf::new(data);
215            tag.write_to(&mut buf).unwrap();
216            let (parsed, rest) = Tag::from_bytes(buf.as_slice()).unwrap();
217            assert_eq!(parsed, tag);
218            assert!(rest.is_empty());
219        }
220    }
221}