181 lines
4.7 KiB
Rust
181 lines
4.7 KiB
Rust
|
|
use crate::{DynTagged, Error, Result, Tag};
|
||
|
|
#[cfg(feature = "std")]
|
||
|
|
use crate::{SerializeResult, ToDer};
|
||
|
|
use core::ops;
|
||
|
|
|
||
|
|
/// BER Object Length
|
||
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||
|
|
pub enum Length {
|
||
|
|
/// Definite form (X.690 8.1.3.3)
|
||
|
|
Definite(usize),
|
||
|
|
/// Indefinite form (X.690 8.1.3.6)
|
||
|
|
Indefinite,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Length {
|
||
|
|
/// Return true if length is definite and equal to 0
|
||
|
|
#[inline]
|
||
|
|
pub fn is_null(&self) -> bool {
|
||
|
|
*self == Length::Definite(0)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get length of primitive object
|
||
|
|
#[inline]
|
||
|
|
pub fn definite(&self) -> Result<usize> {
|
||
|
|
match self {
|
||
|
|
Length::Definite(sz) => Ok(*sz),
|
||
|
|
Length::Indefinite => Err(Error::IndefiniteLengthUnexpected),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Return true if length is definite
|
||
|
|
#[inline]
|
||
|
|
pub const fn is_definite(&self) -> bool {
|
||
|
|
matches!(self, Length::Definite(_))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Return error if length is not definite
|
||
|
|
#[inline]
|
||
|
|
pub const fn assert_definite(&self) -> Result<()> {
|
||
|
|
match self {
|
||
|
|
Length::Definite(_) => Ok(()),
|
||
|
|
Length::Indefinite => Err(Error::IndefiniteLengthUnexpected),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl From<usize> for Length {
|
||
|
|
fn from(l: usize) -> Self {
|
||
|
|
Length::Definite(l)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl ops::Add<Length> for Length {
|
||
|
|
type Output = Self;
|
||
|
|
|
||
|
|
fn add(self, rhs: Length) -> Self::Output {
|
||
|
|
match self {
|
||
|
|
Length::Indefinite => self,
|
||
|
|
Length::Definite(lhs) => match rhs {
|
||
|
|
Length::Indefinite => rhs,
|
||
|
|
Length::Definite(rhs) => Length::Definite(lhs + rhs),
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl ops::Add<usize> for Length {
|
||
|
|
type Output = Self;
|
||
|
|
|
||
|
|
fn add(self, rhs: usize) -> Self::Output {
|
||
|
|
match self {
|
||
|
|
Length::Definite(lhs) => Length::Definite(lhs + rhs),
|
||
|
|
Length::Indefinite => self,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl ops::AddAssign<usize> for Length {
|
||
|
|
fn add_assign(&mut self, rhs: usize) {
|
||
|
|
match self {
|
||
|
|
Length::Definite(ref mut lhs) => *lhs += rhs,
|
||
|
|
Length::Indefinite => (),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl DynTagged for Length {
|
||
|
|
fn tag(&self) -> Tag {
|
||
|
|
Tag(0)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(feature = "std")]
|
||
|
|
impl ToDer for Length {
|
||
|
|
fn to_der_len(&self) -> Result<usize> {
|
||
|
|
match self {
|
||
|
|
Length::Indefinite => Ok(1),
|
||
|
|
Length::Definite(l) => match l {
|
||
|
|
0..=0x7f => Ok(1),
|
||
|
|
0x80..=0xff => Ok(2),
|
||
|
|
0x100..=0xffff => Ok(3),
|
||
|
|
0x1_0000..=0xffff_ffff => Ok(4),
|
||
|
|
_ => Err(Error::InvalidLength),
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
|
||
|
|
match *self {
|
||
|
|
Length::Indefinite => {
|
||
|
|
let sz = writer.write(&[0b1000_0000])?;
|
||
|
|
Ok(sz)
|
||
|
|
}
|
||
|
|
Length::Definite(l) => {
|
||
|
|
if l <= 127 {
|
||
|
|
// Short form
|
||
|
|
let sz = writer.write(&[l as u8])?;
|
||
|
|
Ok(sz)
|
||
|
|
} else {
|
||
|
|
// Long form
|
||
|
|
let b = l.to_be_bytes();
|
||
|
|
// skip leading zeroes
|
||
|
|
// we do not have to test for length, l cannot be 0
|
||
|
|
let mut idx = 0;
|
||
|
|
while b[idx] == 0 {
|
||
|
|
idx += 1;
|
||
|
|
}
|
||
|
|
let b = &b[idx..];
|
||
|
|
// first byte: 0x80 + length of length
|
||
|
|
let b0 = 0x80 | (b.len() as u8);
|
||
|
|
let sz = writer.write(&[b0])?;
|
||
|
|
let sz = sz + writer.write(b)?;
|
||
|
|
Ok(sz)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
|
||
|
|
Ok(0)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use crate::*;
|
||
|
|
|
||
|
|
/// Generic and coverage tests
|
||
|
|
#[test]
|
||
|
|
fn methods_length() {
|
||
|
|
let l = Length::from(2);
|
||
|
|
assert_eq!(l.definite(), Ok(2));
|
||
|
|
assert!(l.assert_definite().is_ok());
|
||
|
|
|
||
|
|
let l = Length::Indefinite;
|
||
|
|
assert!(l.definite().is_err());
|
||
|
|
assert!(l.assert_definite().is_err());
|
||
|
|
|
||
|
|
let l = Length::from(2);
|
||
|
|
assert_eq!(l + 2, Length::from(4));
|
||
|
|
assert_eq!(l + Length::Indefinite, Length::Indefinite);
|
||
|
|
|
||
|
|
let l = Length::Indefinite;
|
||
|
|
assert_eq!(l + 2, Length::Indefinite);
|
||
|
|
|
||
|
|
let l = Length::from(2);
|
||
|
|
assert_eq!(l + Length::from(2), Length::from(4));
|
||
|
|
|
||
|
|
let l = Length::Indefinite;
|
||
|
|
assert_eq!(l + Length::from(2), Length::Indefinite);
|
||
|
|
|
||
|
|
let mut l = Length::from(2);
|
||
|
|
l += 2;
|
||
|
|
assert_eq!(l.definite(), Ok(4));
|
||
|
|
|
||
|
|
let mut l = Length::Indefinite;
|
||
|
|
l += 2;
|
||
|
|
assert_eq!(l, Length::Indefinite);
|
||
|
|
}
|
||
|
|
}
|