Skip to main content

structured_asn1/
tag.rs

1//! Identifier octets: the class, whether the element is constructed, and the
2//! number.
3
4use crate::error::ErrorCode;
5
6/// The two bits that say who defines the tag number's meaning.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum Class {
9    /// Defined by X.680 itself: INTEGER, SEQUENCE and the rest.
10    Universal,
11    /// Defined by the application.
12    Application,
13    /// Defined by the position within the enclosing type: `[0]`, `[1]`.
14    ContextSpecific,
15    /// Defined privately.
16    Private,
17}
18
19impl Class {
20    fn from_bits(bits: u8) -> Self {
21        match bits >> 6 {
22            0 => Self::Universal,
23            1 => Self::Application,
24            2 => Self::ContextSpecific,
25            _ => Self::Private,
26        }
27    }
28}
29
30/// Universal tag numbers this parser names. Others are read as numbers.
31pub mod universal {
32    /// BOOLEAN.
33    pub const BOOLEAN: u32 = 1;
34    /// INTEGER.
35    pub const INTEGER: u32 = 2;
36    /// BIT STRING.
37    pub const BIT_STRING: u32 = 3;
38    /// OCTET STRING.
39    pub const OCTET_STRING: u32 = 4;
40    /// NULL.
41    pub const NULL: u32 = 5;
42    /// OBJECT IDENTIFIER.
43    pub const OID: u32 = 6;
44    /// `UTF8String`.
45    pub const UTF8_STRING: u32 = 12;
46    /// SEQUENCE and SEQUENCE OF.
47    pub const SEQUENCE: u32 = 16;
48    /// SET and SET OF.
49    pub const SET: u32 = 17;
50    /// `PrintableString`.
51    pub const PRINTABLE_STRING: u32 = 19;
52    /// `IA5String`.
53    pub const IA5_STRING: u32 = 22;
54    /// `UTCTime`.
55    pub const UTC_TIME: u32 = 23;
56    /// `GeneralizedTime`.
57    pub const GENERALIZED_TIME: u32 = 24;
58}
59
60/// One element's identity.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub struct Tag {
63    class: Class,
64    constructed: bool,
65    /// The number, or `None` for one wider than this reader names. The
66    /// encoding bounds no tag number (X.690 8.1.2.4.2), and a schema assigns
67    /// small ones, so a wide number is not one anything names: it is read
68    /// and kept apart from every named tag rather than refused, because
69    /// refusing it would block a certificate over an element nothing was
70    /// going to interpret anyway.
71    ///
72    /// Every wide number is the same `None` here, so two tags that are both
73    /// beyond naming are equal, which is what the accessor says about them.
74    /// What tells two documents apart is their bytes.
75    number: Option<u32>,
76}
77
78impl Tag {
79    /// A tag built from its parts.
80    #[must_use]
81    pub fn new(class: Class, constructed: bool, number: u32) -> Self {
82        Self {
83            class,
84            constructed,
85            number: Some(number),
86        }
87    }
88
89    /// A tag whose number is wider than this reader names.
90    ///
91    /// Every such tag is the same tag here, which is what its class and
92    /// construction say and nothing more. Two documents writing different
93    /// wide numbers are still told apart by their bytes, which is what
94    /// identity is compared on; what this decides is only that neither
95    /// matches a tag a schema names.
96    #[must_use]
97    pub fn beyond_naming(class: Class, constructed: bool) -> Self {
98        Self {
99            class,
100            constructed,
101            number: None,
102        }
103    }
104
105    /// A universal primitive tag, the common case for values.
106    #[must_use]
107    pub fn primitive(number: u32) -> Self {
108        Self::new(Class::Universal, false, number)
109    }
110
111    /// A universal constructed tag, the common case for containers.
112    #[must_use]
113    pub fn constructed(number: u32) -> Self {
114        Self::new(Class::Universal, true, number)
115    }
116
117    /// A context-specific tag, as written `[n]` in a module.
118    #[must_use]
119    pub fn context(constructed: bool, number: u32) -> Self {
120        Self::new(Class::ContextSpecific, constructed, number)
121    }
122
123    /// Who defines what this number means.
124    #[must_use]
125    pub fn class(self) -> Class {
126        self.class
127    }
128
129    /// Whether the contents are further elements rather than a value.
130    #[must_use]
131    pub fn is_constructed(self) -> bool {
132        self.constructed
133    }
134
135    /// The number within the class, where it is one this reader names.
136    ///
137    /// `None` is a number too wide for that, which no schema assigns and
138    /// nothing here interprets. A caller matching against the numbers it
139    /// knows gets no match, which is the answer.
140    #[must_use]
141    pub fn number(self) -> Option<u32> {
142        self.number
143    }
144
145    /// Whether this is the end-of-contents marker that closes an
146    /// indefinite-length element.
147    #[must_use]
148    pub fn is_end_of_contents(self) -> bool {
149        self.class == Class::Universal && !self.constructed && self.number == Some(0)
150    }
151}
152
153/// Reads the identifier octets at the front of `input`.
154///
155/// Returns the tag and how many bytes it took. The high-tag-number form is
156/// accepted, and two spellings of it are refused: a first byte of `0x80`,
157/// which pads the number with a leading zero, and the long form used for a
158/// number the short one already writes. Both are ways to write the same
159/// number in different bytes, which is how two parsers are made to disagree
160/// about what they read.
161///
162/// A number wider than the reader names is not one of those: it is read, and
163/// kept apart from every named tag.
164pub(crate) fn read_accounted(
165    input: &[u8],
166    offset: u64,
167    mut charge: impl FnMut(u64) -> Result<(), ErrorCode>,
168) -> Result<(Tag, usize), (ErrorCode, u64)> {
169    let fail = |code| (code, offset);
170
171    charge(1).map_err(fail)?;
172
173    let first = *input
174        .first()
175        .ok_or_else(|| fail(ErrorCode::UnexpectedEnd))?;
176    let class = Class::from_bits(first);
177    let constructed = first & 0x20 != 0;
178    let low = first & 0x1f;
179
180    if low != 0x1f {
181        // Universal zero belongs to the end-of-contents marker, which is
182        // primitive. Constructed, it is neither that marker nor a tag any
183        // encoding defines, and returning it hands the reader an element that
184        // does not exist.
185        if class == Class::Universal && low == 0 && constructed {
186            return Err(fail(ErrorCode::MalformedTag));
187        }
188        return Ok((Tag::new(class, constructed, u32::from(low)), 1));
189    }
190
191    // The number, where it fits, and `None` for one wider than that. The
192    // encoding bounds no tag number (X.690 8.1.2.4.2), and a schema assigns
193    // small ones, so a wide number is not a tag anything names; refusing it
194    // would block a certificate over an element nothing was going to
195    // interpret. What is still refused is a header spending more bytes on
196    // the number than any document has reason to, which is a limit on the
197    // size of the header rather than on the size of the number.
198    let mut number: Option<u64> = Some(0);
199    let mut taken = 1;
200    loop {
201        charge(1).map_err(fail)?;
202        let byte = *input
203            .get(taken)
204            .ok_or_else(|| fail(ErrorCode::UnexpectedEnd))?;
205        taken += 1;
206
207        // A first continuation byte of 0x80 means the number starts with a zero
208        // seven-bit group, which is a longer spelling of a shorter number.
209        if taken == 2 && byte == 0x80 {
210            return Err(fail(ErrorCode::MalformedTag));
211        }
212
213        number =
214            number.and_then(|value| value.checked_mul(128)?.checked_add(u64::from(byte & 0x7f)));
215
216        if byte & 0x80 == 0 {
217            break;
218        }
219
220        // Continuing past this would read the rest of the document looking
221        // for a byte without the high bit set.
222        if taken > TAG_NUMBER_BYTES {
223            return Err(fail(ErrorCode::TagTooLarge));
224        }
225    }
226
227    // The high-tag-number form is only for numbers that do not fit the low form.
228    if number.is_some_and(|number| number < 0x1f) {
229        return Err(fail(ErrorCode::MalformedTag));
230    }
231
232    let tag = match number.and_then(|number| u32::try_from(number).ok()) {
233        Some(number) => Tag::new(class, constructed, number),
234        None => Tag::beyond_naming(class, constructed),
235    };
236    Ok((tag, taken))
237}
238
239#[cfg(test)]
240fn read(input: &[u8], offset: u64) -> Result<(Tag, usize), (ErrorCode, u64)> {
241    read_accounted(input, offset, |_| Ok(()))
242}
243
244/// How many bytes a tag's number may be written in.
245///
246/// Not a bound on the number, which the encoding does not bound: a number
247/// wider than this reader names is read and kept apart from the named ones.
248/// This bounds the header instead. Without it a run of continuation bytes
249/// would be followed to the end of the document, so it is the same kind of
250/// limit as the one on how deeply elements may nest, and sixteen bytes is
251/// several times what any assigned number has ever needed.
252const TAG_NUMBER_BYTES: usize = 16;
253
254#[cfg(test)]
255mod tests;