Skip to content

Commit e5bcd4f

Browse files
authored
der: improve constructed bit handling (#1919)
Adds an internal decoding method which returns the constructed bit in addition to the `Tag`. This is currently used for validating the constructed bit at decode time, however in future work it might be interesting to retain, either on the `Tag` type (where we do store it for certain variants) or in the `Header` type.
1 parent 61eaf9e commit e5bcd4f

File tree

2 files changed

+64
-63
lines changed

2 files changed

+64
-63
lines changed

der/src/header.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ impl<'a> Decode<'a> for Header {
3636
type Error = Error;
3737

3838
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Header> {
39-
let is_constructed = Tag::peek_is_constructed(reader)?;
40-
let tag = Tag::decode(reader)?;
39+
let (tag, is_constructed) = Tag::decode_with_constructed_bit(reader)?;
4140

4241
let length = Length::decode(reader).map_err(|e| {
4342
if e.kind() == ErrorKind::Overlength {

der/src/tag.rs

Lines changed: 63 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -164,19 +164,75 @@ impl Tag {
164164
/// rules implemented by this crate.
165165
pub(crate) const MAX_SIZE: usize = 6;
166166

167+
/// Decode a [`Tag`] in addition to returning the value of the constructed bit.
168+
pub(crate) fn decode_with_constructed_bit<'a>(
169+
reader: &mut impl Reader<'a>,
170+
) -> Result<(Self, bool)> {
171+
let first_byte = reader.read_byte()?;
172+
let is_constructed = first_byte & CONSTRUCTED_FLAG != 0;
173+
174+
let tag = match first_byte {
175+
0x01 => Tag::Boolean,
176+
0x02 => Tag::Integer,
177+
0x03 => Tag::BitString,
178+
0x04 => Tag::OctetString,
179+
0x05 => Tag::Null,
180+
0x06 => Tag::ObjectIdentifier,
181+
0x09 => Tag::Real,
182+
0x0A => Tag::Enumerated,
183+
0x0C => Tag::Utf8String,
184+
0x12 => Tag::NumericString,
185+
0x13 => Tag::PrintableString,
186+
0x14 => Tag::TeletexString,
187+
0x15 => Tag::VideotexString,
188+
0x16 => Tag::Ia5String,
189+
0x17 => Tag::UtcTime,
190+
0x18 => Tag::GeneralizedTime,
191+
0x1A => Tag::VisibleString,
192+
0x1B => Tag::GeneralString,
193+
0x1E => Tag::BmpString,
194+
0x24 if reader.encoding_rules() == EncodingRules::Ber => Tag::OctetString,
195+
0x30 => Tag::Sequence, // constructed
196+
0x31 => Tag::Set, // constructed
197+
0x40..=0x7F => {
198+
let (constructed, number) = parse_parts(first_byte, reader)?;
199+
200+
Tag::Application {
201+
constructed,
202+
number,
203+
}
204+
}
205+
0x80..=0xBF => {
206+
let (constructed, number) = parse_parts(first_byte, reader)?;
207+
208+
Tag::ContextSpecific {
209+
constructed,
210+
number,
211+
}
212+
}
213+
0xC0..=0xFF => {
214+
let (constructed, number) = parse_parts(first_byte, reader)?;
215+
216+
Tag::Private {
217+
constructed,
218+
number,
219+
}
220+
}
221+
// universal tag in long form
222+
0x1F => return Err(reader.error(ErrorKind::TagNumberInvalid)),
223+
byte => return Err(reader.error(ErrorKind::TagUnknown { byte })),
224+
};
225+
226+
Ok((tag, is_constructed))
227+
}
228+
167229
/// Peek at the next byte in the reader and attempt to decode it as a [`Tag`] value.
168230
///
169231
/// Does not modify the reader's state.
170232
pub fn peek<'a>(reader: &impl Reader<'a>) -> Result<Self> {
171233
Self::decode(&mut reader.clone())
172234
}
173235

174-
/// Peek at whether the next byte in the reader has the constructed bit set.
175-
pub(crate) fn peek_is_constructed<'a>(reader: &impl Reader<'a>) -> Result<bool> {
176-
let octet = reader.clone().read_byte()?;
177-
Ok(octet & CONSTRUCTED_FLAG != 0)
178-
}
179-
180236
/// Returns true if given context-specific (or any given class) tag number matches the peeked tag.
181237
pub(crate) fn peek_matches<'a, R: Reader<'a>>(
182238
reader: &mut R,
@@ -304,61 +360,7 @@ impl<'a> Decode<'a> for Tag {
304360
type Error = Error;
305361

306362
fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Self> {
307-
let first_byte = reader.read_byte()?;
308-
309-
let tag = match first_byte {
310-
0x01 => Tag::Boolean,
311-
0x02 => Tag::Integer,
312-
0x03 => Tag::BitString,
313-
0x04 => Tag::OctetString,
314-
0x05 => Tag::Null,
315-
0x06 => Tag::ObjectIdentifier,
316-
0x09 => Tag::Real,
317-
0x0A => Tag::Enumerated,
318-
0x0C => Tag::Utf8String,
319-
0x12 => Tag::NumericString,
320-
0x13 => Tag::PrintableString,
321-
0x14 => Tag::TeletexString,
322-
0x15 => Tag::VideotexString,
323-
0x16 => Tag::Ia5String,
324-
0x17 => Tag::UtcTime,
325-
0x18 => Tag::GeneralizedTime,
326-
0x1A => Tag::VisibleString,
327-
0x1B => Tag::GeneralString,
328-
0x1E => Tag::BmpString,
329-
0x24 if reader.encoding_rules() == EncodingRules::Ber => Tag::OctetString,
330-
0x30 => Tag::Sequence, // constructed
331-
0x31 => Tag::Set, // constructed
332-
0x40..=0x7F => {
333-
let (constructed, number) = parse_parts(first_byte, reader)?;
334-
335-
Tag::Application {
336-
constructed,
337-
number,
338-
}
339-
}
340-
0x80..=0xBF => {
341-
let (constructed, number) = parse_parts(first_byte, reader)?;
342-
343-
Tag::ContextSpecific {
344-
constructed,
345-
number,
346-
}
347-
}
348-
0xC0..=0xFF => {
349-
let (constructed, number) = parse_parts(first_byte, reader)?;
350-
351-
Tag::Private {
352-
constructed,
353-
number,
354-
}
355-
}
356-
// universal tag in long form
357-
0x1F => return Err(reader.error(ErrorKind::TagNumberInvalid)),
358-
byte => return Err(reader.error(ErrorKind::TagUnknown { byte })),
359-
};
360-
361-
Ok(tag)
363+
Self::decode_with_constructed_bit(reader).map(|(tag, _)| tag)
362364
}
363365
}
364366

0 commit comments

Comments
 (0)