// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub const BIT_STRING: u8 = 3;
pub const BOOLEAN: u8 = 1;
pub const CONSTRUCTED: u8 = 0x20;
pub const CONTEXT_SPECIFIC: u8 = 0x80;
pub const OCTET_STRING: u8 = 4;
pub const INTEGER: u8 = 2;
pub const NONE: u8 = 5;
pub const OBJECT_IDENTIFIER: u8 = 6;
pub const SEQUENCE: u8 = 0x30;
pub const UTC_TIME: u8 = 0x17;
pub const GENERALIZED_TIME: u8 = 0x18;
/// Get a byte from the front of a slice.
///
/// (This is the same as `split_first`, but returns the value rather than a
/// reference to it.)
pub fn u8_next(input: &[u8]) -> Option<(u8, &[u8])> {
let (first, rest) = input.split_first()?;
Some((*first, rest))
}
/// Treats the input as a series of big-endian bytes and returns a usize of
/// them. Since usize might only be 32 bits, the input must not be longer than
/// four bytes.
fn bytes_to_usize(bytes: &[u8]) -> usize {
assert!(bytes.len() <= 4);
let mut ret = 0usize;
for &byte in bytes {
ret <<= 8;
ret |= byte as usize
}
ret
}
/// Returns the tag, header length, and full element (including header) of the
/// next ASN.1 DER element from `input`, as well as the remainder of the
/// input.
pub fn next_element(orig_input: &[u8]) -> Option<(u8, usize, &[u8], &[u8])> {
let (tag_byte, input) = u8_next(orig_input)?;
if tag_byte & 0x1f == 0x1f {
// large-format tags are not supported.
return None;
}
let (length_byte, input) = u8_next(input)?;
let (payload_length, header_length) = if (length_byte & 0x80) == 0 {
(length_byte as usize, 2)
} else {
// The high bit indicate that this is the long form, while the next 7 bits
// encode the number of subsequent octets used to encode the length (ITU-T
// X.690 clause 8.1.3.5.b).
let num_bytes = (length_byte & 0x7f) as usize;
if num_bytes == 0 || num_bytes > 4 {
return None;
}
if input.len() < num_bytes {
return None;
}
let (length_bytes, _rest) = input.split_at(num_bytes);
let length = bytes_to_usize(length_bytes);
if length < 128 {
// Should have used short-form encoding.
return None;
}
if length >> ((num_bytes - 1) * 8) == 0 {
// Should have used fewer bytes to encode the length.
return None;
}
(length, 2 + num_bytes)
};
let element_length = header_length.checked_add(payload_length)?;
if orig_input.len() < element_length {
return None;
}
let (element, rest) = orig_input.split_at(element_length);
Some((tag_byte, header_length, element, rest))
}
/// Returns the tag and payload of the next ASN.1 DER element from `input`, as
/// well as the remainder of the input.
pub fn next(input: &[u8]) -> Option<(u8, &[u8], &[u8])> {
let (tag, header_length, element, rest) = next_element(input)?;
let (_header, payload) = element.split_at(header_length);
Some((tag, payload, rest))
}
/// Returns the payload of the next ASN.1 DER element from `input`, and the
/// remaining input, if the element has the expected tag. Otherwise `None`.
pub fn next_tagged(input: &[u8], expected_tag: u8) -> Option<(&[u8], &[u8])> {
let (tag, element, rest) = next(input)?;
if tag == expected_tag { Some((element, rest)) } else { None }
}
/// Returns the body of the next ASN.1 DER element, and the remainder, from
/// `input` if it's tag is as expected. Otherwise it returns `None` and the
/// original input.
pub fn next_optional(input: &[u8], expected_tag: u8) -> Option<(Option<&[u8]>, &[u8])> {
if input.is_empty() {
return Some((None, input));
}
let (tag, body, rest) = next(input)?;
if expected_tag == tag { Some((Some(body), rest)) } else { Some((None, input)) }
}