Skip to content

Commit

Permalink
BerObject and all similar structs now use Cow, and implement `to_ow…
Browse files Browse the repository at this point in the history
…ned` (#37)
  • Loading branch information
chifflier committed May 4, 2021
1 parent f2f0047 commit d0b3890
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 36 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
### Changed/Fixed

- BerObject: add method `as_bytes` and deprecate `as_slice`
- BerObject and all similar structs now use `Cow`, and implement `to_owned`
to return an object with static lifetime.

### Added

Expand Down
142 changes: 117 additions & 25 deletions src/ber/ber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ pub struct BerObjectHeader<'a> {
///
/// This is useful in some cases, where different representations of the same
/// BER tags have different meanings (BER only)
pub raw_tag: Option<&'a [u8]>,
pub raw_tag: Option<Cow<'a, [u8]>>,
}

/// BER object content
Expand Down Expand Up @@ -152,14 +152,14 @@ pub enum BerObjectContent<'a> {
/// UTF8String: decoded string
UTF8String(Cow<'a, str>),
/// T61String: raw object bytes
T61String(&'a [u8]),
T61String(Cow<'a, [u8]>),
/// VideotexString: raw object bytes
VideotexString(&'a [u8]),
VideotexString(Cow<'a, [u8]>),

/// BmpString: raw object bytes
BmpString(&'a [u8]),
BmpString(Cow<'a, [u8]>),
/// UniversalString: raw object bytes
UniversalString(&'a [u8]),
UniversalString(Cow<'a, [u8]>),

/// SEQUENCE: list of objects
Sequence(Vec<BerObject<'a>>),
Expand All @@ -172,11 +172,11 @@ pub enum BerObjectContent<'a> {
GeneralizedTime(Cow<'a, str>),

/// Object descriptor: raw object bytes
ObjectDescriptor(&'a [u8]),
ObjectDescriptor(Cow<'a, [u8]>),
/// GraphicString: raw object bytes
GraphicString(&'a [u8]),
GraphicString(Cow<'a, [u8]>),
/// GeneralString: raw object bytes
GeneralString(&'a [u8]),
GeneralString(Cow<'a, [u8]>),

/// Optional object
Optional(Option<Box<BerObject<'a>>>),
Expand Down Expand Up @@ -296,6 +296,7 @@ impl<'a> BerObjectHeader<'a> {
/// Update header to add reference to raw tag
#[inline]
pub fn with_raw_tag(self, raw_tag: Option<&'a [u8]>) -> Self {
let raw_tag = raw_tag.map(Cow::Borrowed);
BerObjectHeader { raw_tag, ..self }
}

Expand Down Expand Up @@ -330,6 +331,16 @@ impl<'a> BerObjectHeader<'a> {
pub fn is_constructed(&self) -> bool {
self.structured == 1
}

pub fn to_owned(&self) -> BerObjectHeader<'static> {
BerObjectHeader {
tag: self.tag,
structured: self.structured,
class: self.class,
len: self.len,
raw_tag: self.raw_tag.as_ref().map(|t| Cow::Owned(t.to_vec())),
}
}
}

impl<'a> BerObject<'a> {
Expand Down Expand Up @@ -373,6 +384,7 @@ impl<'a> BerObject<'a> {

/// Set a tag for the BER object
pub fn set_raw_tag(self, raw_tag: Option<&'a [u8]>) -> BerObject {
let raw_tag = raw_tag.map(Cow::Borrowed);
let header = BerObjectHeader {
raw_tag,
..self.header
Expand Down Expand Up @@ -555,6 +567,13 @@ impl<'a> BerObject<'a> {
pub fn is_constructed(&self) -> bool {
self.header.structured == 1
}

pub fn to_owned(&self) -> BerObject<'static> {
BerObject {
header: self.header.to_owned(),
content: self.content.to_owned(),
}
}
}

/// Build a DER object from an OID.
Expand Down Expand Up @@ -596,7 +615,7 @@ impl<'a> PartialEq<BerObjectHeader<'a>> for BerObjectHeader<'a> {
}
&& {
// it tag is present for both, compare it
if xor_option(self.raw_tag, other.raw_tag).is_none() {
if xor_option(self.raw_tag.as_ref(), other.raw_tag.as_ref()).is_none() {
self.raw_tag == other.raw_tag
} else {
true
Expand Down Expand Up @@ -733,8 +752,8 @@ impl<'a> BerObjectContent<'a> {
| BerObjectContent::UniversalString(s)
| BerObjectContent::ObjectDescriptor(s)
| BerObjectContent::GraphicString(s)
| BerObjectContent::GeneralString(s) => Ok(s),
BerObjectContent::BitString(_, BitStringObject { data: s })
| BerObjectContent::GeneralString(s)
| BerObjectContent::BitString(_, BitStringObject { data: s })
| BerObjectContent::Integer(s)
| BerObjectContent::OctetString(s)
| BerObjectContent::Unknown(_, s) => Ok(s.as_ref()),
Expand All @@ -748,40 +767,40 @@ impl<'a> BerObjectContent<'a> {
/// (consuming object prevents returning a reference in that case).
pub fn into_bytes(self) -> Result<&'a [u8], BerError> {
match self {
BerObjectContent::NumericString(Cow::Borrowed(s))
| BerObjectContent::VisibleString(Cow::Borrowed(s))
BerObjectContent::IA5String(Cow::Borrowed(s))
| BerObjectContent::NumericString(Cow::Borrowed(s))
| BerObjectContent::PrintableString(Cow::Borrowed(s))
| BerObjectContent::UTF8String(Cow::Borrowed(s))
| BerObjectContent::IA5String(Cow::Borrowed(s)) => Ok(s.as_ref()),
| BerObjectContent::VisibleString(Cow::Borrowed(s)) => Ok(s.as_ref()),
BerObjectContent::GeneralizedTime(Cow::Borrowed(s))
| BerObjectContent::UTCTime(Cow::Borrowed(s)) => Ok(s.as_bytes()),
BerObjectContent::T61String(s)
| BerObjectContent::VideotexString(s)
| BerObjectContent::BmpString(s)
| BerObjectContent::UniversalString(s)
| BerObjectContent::ObjectDescriptor(s)
| BerObjectContent::GraphicString(s)
| BerObjectContent::GeneralString(s) => Ok(s),
BerObjectContent::BitString(
_,
BitStringObject {
data: Cow::Borrowed(s),
},
)
| BerObjectContent::BmpString(Cow::Borrowed(s))
| BerObjectContent::GeneralString(Cow::Borrowed(s))
| BerObjectContent::GraphicString(Cow::Borrowed(s))
| BerObjectContent::Integer(Cow::Borrowed(s))
| BerObjectContent::ObjectDescriptor(Cow::Borrowed(s))
| BerObjectContent::OctetString(Cow::Borrowed(s))
| BerObjectContent::Unknown(_, Cow::Borrowed(s)) => Ok(s),
| BerObjectContent::T61String(Cow::Borrowed(s))
| BerObjectContent::Unknown(_, Cow::Borrowed(s))
| BerObjectContent::UniversalString(Cow::Borrowed(s))
| BerObjectContent::VideotexString(Cow::Borrowed(s)) => Ok(s),
_ => Err(BerError::BerTypeError),
}
}

pub fn as_str(&'a self) -> Result<&'a str, BerError> {
match self {
BerObjectContent::NumericString(s)
| BerObjectContent::VisibleString(s)
BerObjectContent::IA5String(s)
| BerObjectContent::NumericString(s)
| BerObjectContent::PrintableString(s)
| BerObjectContent::UTF8String(s)
| BerObjectContent::IA5String(s) => Ok(s),
| BerObjectContent::VisibleString(s) => Ok(s),
BerObjectContent::GeneralizedTime(s) | BerObjectContent::UTCTime(s) => Ok(s.as_ref()),
_ => Err(BerError::BerTypeError),
}
Expand Down Expand Up @@ -821,6 +840,73 @@ impl<'a> BerObjectContent<'a> {
BerObjectContent::Optional(None) => BerTag(0x00), // XXX invalid !
}
}

pub fn to_owned(&self) -> BerObjectContent<'static> {
match self {
BerObjectContent::BitString(n, s) => BerObjectContent::BitString(*n, s.to_owned()),
BerObjectContent::BmpString(s) => BerObjectContent::BmpString(Cow::Owned(s.to_vec())),
BerObjectContent::Boolean(b) => BerObjectContent::Boolean(*b),
BerObjectContent::EndOfContent => BerObjectContent::EndOfContent,
BerObjectContent::Enum(n) => BerObjectContent::Enum(*n),
BerObjectContent::GeneralString(s) => {
BerObjectContent::GeneralString(Cow::Owned(s.to_vec()))
}
BerObjectContent::GeneralizedTime(s) => {
BerObjectContent::GeneralizedTime(Cow::Owned(s.to_string()))
}
BerObjectContent::GraphicString(s) => {
BerObjectContent::GraphicString(Cow::Owned(s.to_vec()))
}
BerObjectContent::IA5String(s) => {
BerObjectContent::IA5String(Cow::Owned(s.to_string()))
}
BerObjectContent::Integer(s) => BerObjectContent::Integer(Cow::Owned(s.to_vec())),
BerObjectContent::Null => BerObjectContent::Null,
BerObjectContent::NumericString(s) => {
BerObjectContent::NumericString(Cow::Owned(s.to_string()))
}
BerObjectContent::OID(oid) => BerObjectContent::OID(oid.to_owned()),
BerObjectContent::ObjectDescriptor(s) => {
BerObjectContent::ObjectDescriptor(Cow::Owned(s.to_vec()))
}
BerObjectContent::OctetString(s) => {
BerObjectContent::OctetString(Cow::Owned(s.to_vec()))
}
BerObjectContent::Optional(o) => {
BerObjectContent::Optional(o.as_ref().map(|x| Box::new(x.as_ref().to_owned())))
}
BerObjectContent::PrintableString(s) => {
BerObjectContent::PrintableString(Cow::Owned(s.to_string()))
}
BerObjectContent::RelativeOID(oid) => BerObjectContent::RelativeOID(oid.to_owned()),
BerObjectContent::Sequence(v) => {
BerObjectContent::Sequence(v.iter().map(|o| o.to_owned()).collect::<Vec<_>>())
}
BerObjectContent::Set(v) => {
BerObjectContent::Set(v.iter().map(|o| o.to_owned()).collect::<Vec<_>>())
}
BerObjectContent::T61String(s) => BerObjectContent::T61String(Cow::Owned(s.to_vec())),
BerObjectContent::Tagged(c, t, o) => {
BerObjectContent::Tagged(*c, *t, Box::new(o.as_ref().to_owned()))
}
BerObjectContent::UTCTime(s) => BerObjectContent::UTCTime(Cow::Owned(s.to_string())),
BerObjectContent::UTF8String(s) => {
BerObjectContent::UTF8String(Cow::Owned(s.to_string()))
}
BerObjectContent::UniversalString(s) => {
BerObjectContent::UniversalString(Cow::Owned(s.to_vec()))
}
BerObjectContent::Unknown(t, b) => {
BerObjectContent::Unknown(*t, Cow::Owned(b.to_vec()))
}
BerObjectContent::VideotexString(s) => {
BerObjectContent::VideotexString(Cow::Owned(s.to_vec()))
}
BerObjectContent::VisibleString(s) => {
BerObjectContent::VisibleString(Cow::Owned(s.to_string()))
}
}
}
}

#[macro_export]
Expand Down Expand Up @@ -961,6 +1047,12 @@ impl<'a> BitStringObject<'a> {
}
}

pub fn to_owned(&self) -> BitStringObject<'static> {
BitStringObject {
data: Cow::Owned(self.data.to_vec()),
}
}

/// Test if bit `bitnum` is set
pub fn is_set(&self, bitnum: usize) -> bool {
let byte_pos = bitnum / 8;
Expand Down
24 changes: 17 additions & 7 deletions src/ber/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,14 @@ fn ber_read_content_printablestring<'a>(

#[inline]
fn ber_read_content_t61string(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::T61String)(i)
map(take(len), |b| BerObjectContent::T61String(Cow::Borrowed(b)))(i)
}

#[inline]
fn ber_read_content_videotexstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::VideotexString)(i)
map(take(len), |b| {
BerObjectContent::VideotexString(Cow::Borrowed(b))
})(i)
}

fn ber_read_content_ia5string<'a>(i: &'a [u8], len: usize) -> BerResult<BerObjectContent<'a>> {
Expand Down Expand Up @@ -458,27 +460,35 @@ fn ber_read_content_generalizedtime<'a>(

#[inline]
fn ber_read_content_objectdescriptor(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::ObjectDescriptor)(i)
map(take(len), |b| {
BerObjectContent::ObjectDescriptor(Cow::Borrowed(b))
})(i)
}

#[inline]
fn ber_read_content_graphicstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::GraphicString)(i)
map(take(len), |b| {
BerObjectContent::GraphicString(Cow::Borrowed(b))
})(i)
}

#[inline]
fn ber_read_content_generalstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::GeneralString)(i)
map(take(len), |b| {
BerObjectContent::GeneralString(Cow::Borrowed(b))
})(i)
}

#[inline]
fn ber_read_content_bmpstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::BmpString)(i)
map(take(len), |b| BerObjectContent::BmpString(Cow::Borrowed(b)))(i)
}

#[inline]
fn ber_read_content_universalstring(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
map(take(len), BerObjectContent::UniversalString)(i)
map(take(len), |b| {
BerObjectContent::UniversalString(Cow::Borrowed(b))
})(i)
}

/// Parse the next bytes as the *content* of a BER object.
Expand Down
21 changes: 18 additions & 3 deletions tests/ber_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,9 @@ fn test_ber_relativeoid() {
fn test_ber_bmpstring() {
let empty = &b""[..];
let bytes = hex!("1e 08 00 55 00 73 00 65 00 72");
let expected = BerObject::from_obj(BerObjectContent::BmpString(b"\x00U\x00s\x00e\x00r"));
let expected = BerObject::from_obj(BerObjectContent::BmpString(Cow::Borrowed(
b"\x00U\x00s\x00e\x00r",
)));
assert_eq!(parse_ber_bmpstring(&bytes), Ok((empty, expected)));
}

Expand All @@ -412,14 +414,14 @@ fn test_ber_customtags() {
.expect("ber_read_element_header")
.1;
// println!("{:?}", hdr);
let expected: &[u8] = &[0x8f];
let expected: Cow<[u8]> = Cow::Borrowed(&[0x8f]);
assert_eq!(hdr.raw_tag, Some(expected));
let bytes = hex!("9f 0f 02 12 34");
let hdr = ber_read_element_header(&bytes)
.expect("ber_read_element_header")
.1;
// println!("{:?}", hdr);
let expected: &[u8] = &[0x9f, 0x0f];
let expected: Cow<[u8]> = Cow::Borrowed(&[0x9f, 0x0f]);
assert_eq!(hdr.raw_tag, Some(expected));
}

Expand Down Expand Up @@ -475,3 +477,16 @@ fn test_parse_ber_content2() {
assert_eq!(tag, BerTag::Integer);
assert_eq!(content.as_u32(), Ok(0x10001));
}

#[test]
fn test_owned() {
fn test_static(obj: &BerObject<'static>) {
assert!(obj.is_primitive());
}
let ret = {
let v = vec![1, 2, 3, 4];
let obj = ber_int!(&v);
obj.to_owned()
};
test_static(&ret);
}
2 changes: 1 addition & 1 deletion tests/der_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ fn test_der_generalizedtime() {
fn test_der_generalstring() {
let empty = &b""[..];
let bytes = [0x1b, 0x04, 0x63, 0x69, 0x66, 0x73];
let expected = DerObject::from_obj(BerObjectContent::GeneralString(b"cifs"));
let expected = DerObject::from_obj(BerObjectContent::GeneralString(Cow::Borrowed(b"cifs")));
assert_eq!(parse_der_generalstring(&bytes), Ok((empty, expected)));
}

Expand Down

0 comments on commit d0b3890

Please sign in to comment.