blob: 7ff8f0132f171de3e656c1a2bfd97e688b53ad59 [file] [log] [blame]
//! OID encoder with `const` support.
use crate::{
arcs::{ARC_MAX_FIRST, ARC_MAX_SECOND},
Arc, Error, ObjectIdentifier, Result,
};
/// BER/DER encoder
pub(crate) struct Encoder {
/// Current state
state: State,
/// Bytes of the OID being encoded in-progress
bytes: [u8; ObjectIdentifier::MAX_SIZE],
/// Current position within the byte buffer
cursor: usize,
}
/// Current state of the encoder
enum State {
/// Initial state - no arcs yet encoded
Initial,
/// First arc parsed
FirstArc(Arc),
/// Encoding base 128 body of the OID
Body,
}
impl Encoder {
/// Create a new encoder initialized to an empty default state
pub(crate) const fn new() -> Self {
Self {
state: State::Initial,
bytes: [0u8; ObjectIdentifier::MAX_SIZE],
cursor: 0,
}
}
/// Encode an [`Arc`] as base 128 into the internal buffer
pub(crate) const fn encode(mut self, arc: Arc) -> Self {
match self.state {
State::Initial => {
const_assert!(arc <= ARC_MAX_FIRST, "invalid first arc (must be 0-2)");
self.state = State::FirstArc(arc);
self
}
State::FirstArc(first_arc) => {
const_assert!(arc <= ARC_MAX_SECOND, "invalid second arc (must be 0-39)");
self.state = State::Body;
self.bytes[0] = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + arc as u8;
self.cursor = 1;
self
}
State::Body => {
// Total number of bytes in encoded arc - 1
let nbytes = base128_len(arc);
const_assert!(
self.cursor + nbytes + 1 < ObjectIdentifier::MAX_SIZE,
"OID too long (exceeded max DER bytes)"
);
let new_cursor = self.cursor + nbytes + 1;
let mut result = self.encode_base128_byte(arc, nbytes, false);
result.cursor = new_cursor;
result
}
}
}
/// Finish encoding an OID
pub(crate) const fn finish(self) -> ObjectIdentifier {
const_assert!(self.cursor >= 2, "OID too short (minimum 3 arcs)");
ObjectIdentifier {
bytes: self.bytes,
length: self.cursor as u8,
}
}
/// Encode a single byte of a base128 value
const fn encode_base128_byte(mut self, mut n: u32, i: usize, continued: bool) -> Self {
let mask = if continued { 0b10000000 } else { 0 };
if n > 0x80 {
self.bytes[self.cursor + i] = (n & 0b1111111) as u8 | mask;
n >>= 7;
const_assert!(i > 0, "Base 128 offset miscalculation");
self.encode_base128_byte(n, i.saturating_sub(1), true)
} else {
self.bytes[self.cursor] = n as u8 | mask;
self
}
}
}
/// Compute the length - 1 of an arc when encoded in base 128
const fn base128_len(arc: Arc) -> usize {
match arc {
0..=0x7f => 0,
0x80..=0x3fff => 1,
0x4000..=0x1fffff => 2,
0x200000..=0x1fffffff => 3,
_ => 4,
}
}
/// Write the given unsigned integer in base 128
// TODO(tarcieri): consolidate encoding logic with `encode_base128_byte`
pub(crate) fn write_base128(bytes: &mut [u8], mut n: Arc) -> Result<usize> {
let nbytes = base128_len(n);
let mut i = nbytes;
let mut mask = 0;
while n > 0x80 {
let byte = bytes.get_mut(i).ok_or(Error)?;
*byte = (n & 0b1111111 | mask) as u8;
n >>= 7;
i = i.checked_sub(1).expect("overflow");
mask = 0b10000000;
}
bytes[0] = (n | mask) as u8;
Ok(nbytes + 1)
}
#[cfg(test)]
mod tests {
use super::Encoder;
use hex_literal::hex;
/// OID `1.2.840.10045.2.1` encoded as ASN.1 BER/DER
const EXAMPLE_OID_BER: &[u8] = &hex!("2A8648CE3D0201");
#[test]
fn encode() {
let encoder = Encoder::new();
let encoder = encoder.encode(1);
let encoder = encoder.encode(2);
let encoder = encoder.encode(840);
let encoder = encoder.encode(10045);
let encoder = encoder.encode(2);
let encoder = encoder.encode(1);
assert_eq!(&encoder.bytes[..encoder.cursor], EXAMPLE_OID_BER);
}
}