use crate::error::ErrorCode;
use crate::length::Encoding;
use crate::memory::{Memory, MemoryError, OwnedString};
mod owned;
mod text;
pub use owned::OwnedOid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Oid<'a>(&'a [u8]);
impl<'a> Oid<'a> {
#[must_use]
pub const fn from_bytes(bytes: &'a [u8]) -> Self {
assert!(
Self::well_formed(bytes),
"not a well formed object identifier"
);
Self(bytes)
}
const fn well_formed(bytes: &[u8]) -> bool {
if bytes.is_empty() {
return false;
}
let mut starting = true;
let mut at = 0;
while at < bytes.len() {
let byte = bytes[at];
if starting && byte == 0x80 {
return false;
}
starting = byte & 0x80 == 0;
at += 1;
}
starting
}
#[must_use]
pub fn as_bytes(self) -> &'a [u8] {
self.0
}
pub fn text(self, execution: &Memory) -> Result<OwnedString, MemoryError> {
text::oid(self, execution)
}
#[must_use]
pub fn arcs(self) -> Arcs<'a> {
Arcs {
bytes: self.0,
first: true,
pending: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Arc<'a> {
value: Option<u64>,
groups: &'a [u8],
offset: u64,
}
impl Arc<'_> {
pub fn text(self, execution: &Memory) -> Result<OwnedString, MemoryError> {
text::arc(self, execution)
}
#[must_use]
pub fn as_u64(self) -> Option<u64> {
self.value
}
}
const DECIMAL_BLOCK: u128 = 10_000_000_000_000_000_000;
const DECIMAL_BLOCK_DIGITS: usize = 19;
struct Groups<'a> {
encoded: &'a [u8],
offset: u64,
stop: usize,
}
impl<'a> Groups<'a> {
fn of(encoded: &'a [u8], offset: u64) -> Self {
let last = encoded.len() - 1;
let mut stop = last;
while stop > 0 {
let owed = if stop == last { offset } else { 1 };
if u64::from(encoded[stop] & 0x7f) >= owed {
break;
}
stop -= 1;
}
Self {
encoded,
offset,
stop,
}
}
fn count(&self) -> usize {
self.encoded.len()
}
fn iter_from(&self, first: usize) -> impl ExactSizeIterator<Item = u8> + '_ {
(first..self.encoded.len()).map(|at| self.at(at))
}
fn iter(&self) -> impl ExactSizeIterator<Item = u8> + '_ {
self.iter_from(0)
}
fn significant(&self) -> usize {
let leading = self.iter().take_while(|group| *group == 0).count();
self.encoded.len() - leading
}
fn at(&self, at: usize) -> u8 {
let last = self.encoded.len() - 1;
let group = u64::from(self.encoded[at] & 0x7f);
let owed = if at == last { self.offset } else { 1 };
let value = match at.cmp(&self.stop) {
core::cmp::Ordering::Less => group,
core::cmp::Ordering::Equal => group - owed,
core::cmp::Ordering::Greater => group + 128 - owed,
};
u8::try_from(value).expect("a group below the base")
}
}
#[derive(Debug, Clone)]
pub struct Arcs<'a> {
bytes: &'a [u8],
first: bool,
pending: Option<Arc<'a>>,
}
impl<'a> Iterator for Arcs<'a> {
type Item = Arc<'a>;
fn next(&mut self) -> Option<Arc<'a>> {
if let Some(second) = self.pending.take() {
return Some(second);
}
let mut value: Option<u128> = Some(0);
let mut taken = 0;
for byte in self.bytes {
taken += 1;
value = value
.and_then(|value| value.checked_mul(128)?.checked_add(u128::from(byte & 0x7f)));
if byte & 0x80 == 0 {
break;
}
}
let (groups, rest) = self.bytes.split_at(taken);
self.bytes = rest;
if taken == 0 {
return None;
}
let arc = |value: Option<u128>, offset: u64| Arc {
value: value.and_then(|value| u64::try_from(value - u128::from(offset)).ok()),
groups,
offset,
};
if self.first {
self.first = false;
let root = match value {
Some(0..=39) => 0,
Some(40..=79) => 1,
_ => 2,
};
self.pending = Some(arc(value, root * 40));
return Some(Arc {
value: Some(root),
groups: &[],
offset: 0,
});
}
Some(arc(value, 0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Integer<'a>(&'a [u8]);
impl<'a> Integer<'a> {
#[must_use]
pub fn as_bytes(self) -> &'a [u8] {
self.0
}
#[must_use]
pub fn is_negative(self) -> bool {
self.0.first().is_some_and(|first| first & 0x80 != 0)
}
#[must_use]
pub fn as_u64(self) -> Option<u64> {
if self.is_negative() {
return None;
}
let magnitude = match self.0 {
[0x00, rest @ ..] => rest,
all => all,
};
if magnitude.len() > 8 {
return None;
}
Some(
magnitude
.iter()
.fold(0u64, |value, byte| (value << 8) | u64::from(*byte)),
)
}
#[must_use]
pub fn as_i64(self) -> Option<i64> {
if self.0.len() > 8 {
return None;
}
let sign = if self.is_negative() { -1i64 } else { 0 };
Some(
self.0
.iter()
.fold(sign, |value, byte| (value << 8) | i64::from(*byte)),
)
}
#[must_use]
pub fn as_u32(self) -> Option<u32> {
self.as_u64().and_then(|value| u32::try_from(value).ok())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BitString<'a> {
bytes: &'a [u8],
unused: u8,
}
impl<'a> BitString<'a> {
#[must_use]
pub fn as_bytes(self) -> &'a [u8] {
self.bytes
}
#[must_use]
pub fn unused_bits(self) -> u8 {
self.unused
}
#[must_use]
pub fn len(self) -> u64 {
(self.bytes.len() as u64) * 8 - u64::from(self.unused)
}
#[must_use]
pub fn is_empty(self) -> bool {
self.bytes.is_empty()
}
#[must_use]
pub fn bit(self, position: u64) -> bool {
if position >= self.len() {
return false;
}
let within = (position % 8) as u32;
usize::try_from(position / 8)
.ok()
.and_then(|byte| self.bytes.get(byte))
.is_some_and(|value| value & (0x80u8 >> within) != 0)
}
#[must_use]
pub fn octets(self) -> Option<&'a [u8]> {
(self.unused == 0).then_some(self.bytes)
}
}
pub(crate) fn oid(contents: &[u8]) -> Result<Oid<'_>, ErrorCode> {
if contents.is_empty() {
return Err(ErrorCode::MalformedOid);
}
let mut starting = true;
for byte in contents {
if starting && *byte == 0x80 {
return Err(ErrorCode::MalformedOid);
}
starting = byte & 0x80 == 0;
}
if !starting {
return Err(ErrorCode::MalformedOid);
}
Ok(Oid(contents))
}
pub(crate) fn integer(contents: &[u8]) -> Result<Integer<'_>, ErrorCode> {
if contents.is_empty() {
return Err(ErrorCode::IntegerEmpty);
}
if redundant_sign_byte(contents) {
return Err(ErrorCode::IntegerNotMinimal);
}
Ok(Integer(contents))
}
fn redundant_sign_byte(contents: &[u8]) -> bool {
match contents {
[0x00, second, ..] => second & 0x80 == 0,
[0xff, second, ..] => second & 0x80 != 0,
_ => false,
}
}
pub(crate) fn recovered_integer(mut contents: &[u8]) -> Integer<'_> {
debug_assert!(contents.len() >= 2);
while redundant_sign_byte(contents) {
contents = &contents[1..];
}
Integer(contents)
}
pub(crate) fn bit_string(contents: &[u8], encoding: Encoding) -> Result<BitString<'_>, ErrorCode> {
let (unused, bytes) = contents
.split_first()
.ok_or(ErrorCode::MalformedBitString)?;
let unused = *unused;
if unused > 7 || (bytes.is_empty() && unused != 0) {
return Err(ErrorCode::MalformedBitString);
}
if encoding.is_der()
&& unused > 0
&& let Some(last) = bytes.last()
&& last & (0xffu8 >> (8 - unused)) != 0
{
return Err(ErrorCode::MalformedBitString);
}
Ok(BitString { bytes, unused })
}
pub(crate) fn boolean(contents: &[u8], encoding: Encoding) -> Result<bool, ErrorCode> {
match contents {
[0x00] => Ok(false),
[0xff] => Ok(true),
[_] if !encoding.is_der() => Ok(true),
_ => Err(ErrorCode::MalformedBoolean),
}
}
pub(crate) fn real(contents: &[u8], encoding: Encoding) -> Result<(), ErrorCode> {
let Some((first, rest)) = contents.split_first() else {
return Ok(());
};
match first & 0xc0 {
0x40 => (rest.is_empty() && matches!(first, 0x40..=0x43))
.then_some(())
.ok_or(ErrorCode::MalformedReal),
0x00 => decimal_real(*first, rest, encoding),
_ => binary_real(*first, rest, encoding),
}
}
fn decimal_real(first: u8, field: &[u8], encoding: Encoding) -> Result<(), ErrorCode> {
let malformed = || ErrorCode::MalformedReal;
let form = first & 0x3f;
if !matches!(form, 1..=3) || (encoding.is_der() && form != 3) {
return Err(malformed());
}
if !encoding.is_der() {
return (!field.is_empty()).then_some(()).ok_or_else(malformed);
}
let digits = field.strip_prefix(b"-").unwrap_or(field);
let (mantissa, exponent) = digits
.iter()
.position(|byte| *byte == b'.')
.map(|stop| digits.split_at(stop))
.ok_or_else(malformed)?;
let exponent = exponent.strip_prefix(b".E").ok_or_else(malformed)?;
let sound_mantissa = mantissa.first().is_some_and(|digit| *digit != b'0')
&& mantissa.last().is_some_and(|digit| *digit != b'0')
&& mantissa.iter().all(u8::is_ascii_digit);
let sound_exponent = exponent == b"+0"
|| exponent
.strip_prefix(b"-")
.unwrap_or(exponent)
.split_first()
.is_some_and(|(lead, rest)| {
lead.is_ascii_digit() && *lead != b'0' && rest.iter().all(u8::is_ascii_digit)
});
(sound_mantissa && sound_exponent)
.then_some(())
.ok_or_else(malformed)
}
fn binary_real(first: u8, rest: &[u8], encoding: Encoding) -> Result<(), ErrorCode> {
let malformed = || ErrorCode::MalformedReal;
let base = (first >> 4) & 0x03;
let scaling = (first >> 2) & 0x03;
if base == 0x03 || (encoding.is_der() && (base != 0 || scaling != 0)) {
return Err(malformed());
}
let (exponent, mantissa) = match first & 0x03 {
0 => rest.split_at_checked(1),
1 => rest.split_at_checked(2),
2 => rest.split_at_checked(3),
_ => rest.split_first().and_then(|(count, after)| {
let shortest = !encoding.is_der() || *count > 3;
(*count >= 1 && shortest).then(|| after.split_at_checked(usize::from(*count)))?
}),
}
.ok_or_else(malformed)?;
if let [lead, next, ..] = exponent {
let padded = *lead == 0x00 && next & 0x80 == 0;
let signed = *lead == 0xff && next & 0x80 != 0;
if padded || signed {
return Err(malformed());
}
}
match mantissa {
[] | [0x00, ..] => Err(malformed()),
[.., last] if encoding.is_der() && last & 0x01 == 0 => Err(malformed()),
_ => Ok(()),
}
}
pub(crate) fn null(contents: &[u8]) -> Result<(), ErrorCode> {
if contents.is_empty() {
Ok(())
} else {
Err(ErrorCode::MalformedNull)
}
}
#[cfg(test)]
mod tests;