blob: 7ffedc288aff846e82df5e856db22e557318391f [file] [log] [blame]
use crate::ber::*;
use crate::oid::Oid;
use cookie_factory::bytes::be_u8;
use cookie_factory::combinator::slice;
use cookie_factory::gen_simple;
use cookie_factory::multi::many_ref;
use cookie_factory::sequence::tuple;
use cookie_factory::{GenError, SerializeFn};
use std::io::Write;
// we do not use .copied() for compatibility with 1.34
#[allow(clippy::map_clone)]
fn encode_length<'a, W: Write + 'a, Len: Into<BerSize>>(len: Len) -> impl SerializeFn<W> + 'a {
let l = len.into();
move |out| {
match l {
BerSize::Definite(sz) => {
if sz <= 128 {
// definite, short form
be_u8(sz as u8)(out)
} else {
// definite, long form
let v: Vec<u8> = sz
.to_be_bytes()
.iter()
.map(|&x| x)
.skip_while(|&b| b == 0)
.collect();
let b0 = 0b1000_0000 | (v.len() as u8);
tuple((be_u8(b0 as u8), slice(v)))(out)
}
}
BerSize::Indefinite => be_u8(0b1000_0000)(out),
}
}
}
/// Encode header as object
///
/// The `len` field must be correct
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn ber_encode_header<'a, 'b: 'a, W: Write + 'a>(
hdr: &'b BerObjectHeader,
) -> impl SerializeFn<W> + 'a {
move |out| {
// identifier octets (X.690 8.1.2)
let class_u8 = (hdr.class as u8) << 6;
let pc_u8 = (hdr.structured & 1) << 5;
if hdr.tag.0 >= 30 {
unimplemented!();
}
let byte_0 = class_u8 | pc_u8 | (hdr.tag.0 as u8);
// length octets (X.690 8.1.3)
tuple((be_u8(byte_0), encode_length(hdr.len)))(out)
}
}
fn ber_encode_oid<'a, W: Write + 'a>(oid: &'a Oid) -> impl SerializeFn<W> + 'a {
move |out| {
// check oid.relative attribute ? this should not be necessary
slice(oid.bytes())(out)
}
}
fn ber_encode_sequence<'a, W: Write + Default + AsRef<[u8]> + 'a>(
v: &'a [BerObject],
) -> impl SerializeFn<W> + 'a {
many_ref(v, ber_encode_object)
}
/// Encode the provided object in an EXPLICIT tagged value, using the provided tag ans class
///
/// Note: `obj` should be the object to be encapsulated, not the `ContextSpecific` variant.
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn ber_encode_tagged_explicit<'a, W: Write + Default + AsRef<[u8]> + 'a>(
tag: BerTag,
class: BerClass,
obj: &'a BerObject,
) -> impl SerializeFn<W> + 'a {
move |out| {
// encode inner object
let v = gen_simple(ber_encode_object(obj), W::default())?;
let len = v.as_ref().len();
// encode the application header, using the tag
let hdr = BerObjectHeader::new(class, 1 /* X.690 8.14.2 */, tag, len);
let v_hdr = gen_simple(ber_encode_header(&hdr), W::default())?;
tuple((slice(v_hdr), slice(v)))(out)
}
}
/// Encode the provided object in an IMPLICIT tagged value, using the provided tag and class
///
/// Note: `obj` should be the object to be encapsulated, not the `ContextSpecific` variant.
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn ber_encode_tagged_implicit<'a, W: Write + Default + AsRef<[u8]> + 'a>(
tag: BerTag,
class: BerClass,
obj: &'a BerObject,
) -> impl SerializeFn<W> + 'a {
move |out| {
// encode inner object content
let v = gen_simple(ber_encode_object_content(&obj.content), W::default())?;
// but replace the tag (keep structured attribute)
let len = v.as_ref().len();
let hdr = BerObjectHeader::new(class, obj.header.structured, tag, len);
let v_hdr = gen_simple(ber_encode_header(&hdr), W::default())?;
tuple((slice(v_hdr), slice(v)))(out)
}
}
// we do not use .copied() for compatibility with 1.34
#[allow(clippy::map_clone)]
fn ber_encode_object_content<'a, W: Write + Default + AsRef<[u8]> + 'a>(
c: &'a BerObjectContent,
) -> impl SerializeFn<W> + 'a {
move |out| match c {
BerObjectContent::EndOfContent => be_u8(0)(out),
BerObjectContent::Boolean(b) => {
let b0 = if *b { 0xff } else { 0x00 };
be_u8(b0)(out)
}
BerObjectContent::Integer(s) => slice(s)(out),
BerObjectContent::BitString(ignored_bits, s) => {
tuple((be_u8(*ignored_bits), slice(s)))(out)
}
BerObjectContent::OctetString(s) => slice(s)(out),
BerObjectContent::Null => Ok(out),
BerObjectContent::Enum(i) => {
let v: Vec<u8> = i
.to_be_bytes()
.iter()
.map(|&x| x)
.skip_while(|&b| b == 0)
.collect();
slice(v)(out)
}
BerObjectContent::OID(oid) | BerObjectContent::RelativeOID(oid) => ber_encode_oid(oid)(out),
BerObjectContent::NumericString(s)
| BerObjectContent::UTCTime(s)
| BerObjectContent::GeneralizedTime(s)
| BerObjectContent::VisibleString(s)
| BerObjectContent::PrintableString(s)
| BerObjectContent::IA5String(s)
| BerObjectContent::UTF8String(s) => slice(s)(out),
BerObjectContent::T61String(s)
| BerObjectContent::VideotexString(s)
| BerObjectContent::BmpString(s)
| BerObjectContent::UniversalString(s)
| BerObjectContent::ObjectDescriptor(s)
| BerObjectContent::GraphicString(s)
| BerObjectContent::GeneralString(s) => slice(s)(out),
BerObjectContent::Sequence(v) | BerObjectContent::Set(v) => ber_encode_sequence(v)(out),
// best we can do is tagged-explicit, but we don't know
BerObjectContent::Optional(inner) => {
// directly encode inner object
match inner {
Some(obj) => ber_encode_object_content(&obj.content)(out),
None => slice(&[])(out), // XXX encode NOP ?
}
}
BerObjectContent::Tagged(_class, _tag, inner) => {
// directly encode inner object
// XXX wrong, we should wrap it!
ber_encode_object(inner)(out)
}
BerObjectContent::Unknown(_tag, s) => slice(s)(out),
}
}
/// Encode header and object content as BER, without any validation
///
/// Note that the encoding will not check *any* `field of the header (including length)
/// This can be used to craft invalid objects.
///
/// *This function is only available if the `serialize` feature is enabled.*
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn ber_encode_object_raw<'a, 'b: 'a, 'c: 'a, W: Write + Default + AsRef<[u8]> + 'a>(
hdr: &'b BerObjectHeader,
content: &'c BerObjectContent,
) -> impl SerializeFn<W> + 'a {
tuple((ber_encode_header(hdr), ber_encode_object_content(content)))
}
/// Encode object as BER
///
/// Note that the encoding will not check that the values of the `BerObject` fields are correct.
/// The length is automatically calculated, and the field is ignored.
///
/// `Tagged` objects will be encoded as EXPLICIT.
///
/// *This function is only available if the `serialize` feature is enabled.*
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn ber_encode_object<'a, 'b: 'a, W: Write + Default + AsRef<[u8]> + 'a>(
obj: &'b BerObject,
) -> impl SerializeFn<W> + 'a {
move |out| {
// XXX should we make an exception for tagged values here ?
let v = gen_simple(ber_encode_object_content(&obj.content), W::default())?;
let len = v.as_ref().len();
let hdr = obj.header.clone().with_len(len.into());
let v_hdr = gen_simple(ber_encode_header(&hdr), W::default())?;
tuple((slice(v_hdr), slice(v)))(out)
}
}
impl<'a> BerObject<'a> {
/// Attempt to encode object as BER
///
/// Note that the encoding will not check that the values of the `BerObject` fields are correct.
/// The length is automatically calculated, and the field is ignored.
///
/// `Tagged` objects will be encoded as EXPLICIT.
///
/// *This function is only available if the `serialize` feature is enabled.*
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn to_vec(&self) -> Result<Vec<u8>, GenError> {
gen_simple(ber_encode_object(self), Vec::new())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::error::BerResult;
use cookie_factory::gen_simple;
use hex_literal::hex;
macro_rules! encode_and_parse {
($obj:ident, $encode:ident, $parse:ident) => {{
let v = gen_simple($encode(&$obj), Vec::new()).expect("could not encode");
let (_, obj2) = $parse(&v).expect("could not re-parse");
assert_eq!($obj, obj2);
v
}};
}
#[test]
fn test_encode_length() {
let l = 38;
let v = gen_simple(encode_length(l), Vec::new()).expect("could not serialize");
assert_eq!(&v[..], &[38]);
let l = 201;
let v = gen_simple(encode_length(l), Vec::new()).expect("could not serialize");
assert_eq!(&v[..], &[129, 201]);
let l = 0x1234_5678;
let v = gen_simple(encode_length(l), Vec::new()).expect("could not serialize");
assert_eq!(&v[..], &[132, 0x12, 0x34, 0x56, 0x78]);
}
#[test]
fn test_encode_header() {
// simple header (integer)
let bytes = hex!("02 03 01 00 01");
let (_, hdr) = ber_read_element_header(&bytes).expect("could not parse");
let v = encode_and_parse!(hdr, ber_encode_header, ber_read_element_header);
assert_eq!(&v[..], &bytes[..2]);
}
#[test]
fn test_encode_bool() {
let b_true = BerObject::from_obj(BerObjectContent::Boolean(true));
let b_false = BerObject::from_obj(BerObjectContent::Boolean(false));
encode_and_parse!(b_true, ber_encode_object, parse_ber_bool);
encode_and_parse!(b_false, ber_encode_object, parse_ber_bool);
}
#[test]
fn test_encode_integer() {
let i = BerObject::from_obj(BerObjectContent::Integer(b"\x01\x00\x01"));
encode_and_parse!(i, ber_encode_object, parse_ber_integer);
}
#[test]
fn test_encode_bitstring() {
let bytes = hex!("03 04 06 6e 5d e0");
let b = BerObject::from_obj(BerObjectContent::BitString(
6,
BitStringObject { data: &bytes[3..] },
));
let v = encode_and_parse!(b, ber_encode_object, parse_ber_bitstring);
assert_eq!(&v[..], bytes)
}
#[test]
fn test_encode_octetstring() {
let i = BerObject::from_obj(BerObjectContent::OctetString(b"AAAAA"));
let v = encode_and_parse!(i, ber_encode_object, parse_ber_octetstring);
assert_eq!(&v[..], hex!("04 05 41 41 41 41 41"))
}
#[test]
fn test_encode_enum() {
let i = BerObject::from_obj(BerObjectContent::Enum(2));
let v = encode_and_parse!(i, ber_encode_object, parse_ber_enum);
assert_eq!(&v[..], hex!("0a 01 02"))
}
#[test]
fn test_encode_null() {
let i = BerObject::from_obj(BerObjectContent::Null);
encode_and_parse!(i, ber_encode_object, parse_ber_null);
}
#[test]
fn test_encode_oid() {
let bytes = hex!("06 09 2A 86 48 86 F7 0D 01 01 05");
let obj = BerObject::from_obj(BerObjectContent::OID(
Oid::from(&[1, 2, 840, 113_549, 1, 1, 5]).unwrap(),
));
let v = encode_and_parse!(obj, ber_encode_object, parse_ber_oid);
assert_eq!(&v[..], bytes);
}
#[test]
fn test_encode_relative_oid() {
let bytes = hex!("0d 04 c2 7b 03 02");
let obj = BerObject::from_obj(BerObjectContent::RelativeOID(
Oid::from_relative(&[8571, 3, 2]).unwrap(),
));
let v = encode_and_parse!(obj, ber_encode_object, parse_ber_relative_oid);
assert_eq!(&v[..], bytes);
}
#[test]
fn test_encode_sequence() {
let bytes = hex!("30 0a 02 03 01 00 01 02 03 01 00 00");
let obj = BerObject::from_seq(vec![
BerObject::from_int_slice(b"\x01\x00\x01"),
BerObject::from_int_slice(b"\x01\x00\x00"),
]);
let v = encode_and_parse!(obj, ber_encode_object, parse_ber_sequence);
assert_eq!(&v[..], bytes);
}
#[test]
fn test_encode_set() {
let bytes = hex!("31 0a 02 03 01 00 01 02 03 01 00 00");
let obj = BerObject::from_set(vec![
BerObject::from_int_slice(b"\x01\x00\x01"),
BerObject::from_int_slice(b"\x01\x00\x00"),
]);
let v = encode_and_parse!(obj, ber_encode_object, parse_ber_set);
assert_eq!(&v[..], bytes);
}
#[test]
fn test_encode_tagged_explicit() {
fn local_parse(i: &[u8]) -> BerResult {
parse_ber_explicit_optional(i, BerTag(0), parse_ber_integer)
}
let bytes = hex!("a0 03 02 01 02");
let obj = BerObject::from_int_slice(b"\x02");
let v = gen_simple(
ber_encode_tagged_explicit(BerTag(0), BerClass::ContextSpecific, &obj),
Vec::new(),
)
.expect("could not encode");
let (_, obj2) = local_parse(&v).expect("could not re-parse");
let obj2 = obj2
.as_optional()
.expect("tagged object not found")
.expect("optional object empty");
let (_class, tag, inner) = obj2.as_tagged().expect("not a tagged object");
assert_eq!(tag, BerTag(0));
assert_eq!(&obj, inner);
assert_eq!(&v[..], bytes);
}
#[test]
fn test_encode_tagged_implicit() {
fn der_read_integer_content<'a>(
i: &'a [u8],
hdr: &BerObjectHeader,
depth: usize,
) -> BerResult<'a, BerObjectContent<'a>> {
ber_read_element_content_as(i, BerTag::Integer, hdr.len, false, depth)
}
fn local_parse(i: &[u8]) -> BerResult<BerObject> {
parse_ber_implicit(i, BerTag(3), der_read_integer_content)
}
let obj = BerObject::from_int_slice(b"\x02");
let v = gen_simple(
ber_encode_tagged_implicit(BerTag(3), BerClass::ContextSpecific, &obj),
Vec::new(),
)
.expect("could not encode");
let (_, obj2) = local_parse(&v).expect("could not re-parse");
assert_eq!(obj2.header.tag, BerTag(3));
assert_eq!(&obj.content, &obj2.content);
let bytes = hex!("83 01 02");
assert_eq!(&v[..], bytes);
}
#[test]
fn test_encode_tagged_application() {
fn local_parse(i: &[u8]) -> BerResult {
parse_ber_explicit_optional(i, BerTag(2), parse_ber_integer)
}
let obj = BerObject::from_int_slice(b"\x02");
let v = gen_simple(
ber_encode_tagged_explicit(BerTag(2), BerClass::Application, &obj),
Vec::new(),
)
.expect("could not encode");
let (_, obj2) = local_parse(&v).expect("could not re-parse");
let obj2 = obj2
.as_optional()
.expect("tagged object not found")
.expect("optional object empty");
let (_class, tag, inner) = obj2.as_tagged().expect("not a tagged object");
assert_eq!(tag, BerTag(2));
assert_eq!(&obj, inner);
let bytes = hex!("62 03 02 01 02");
assert_eq!(&v[..], bytes);
}
}