chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

493
vendor/asn1-rs/src/asn1_types/any.rs vendored Normal file
View File

@@ -0,0 +1,493 @@
use crate::ber::*;
use crate::*;
use alloc::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
use core::convert::{TryFrom, TryInto};
use self::debug::trace;
/// The `Any` object is not strictly an ASN.1 type, but holds a generic description of any object
/// that could be encoded.
///
/// It contains a header, and either a reference to or owned data for the object content.
///
/// Note: this type is only provided in **borrowed** version (*i.e.* it cannot own the inner data).
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Any<'a> {
/// The object header
pub header: Header<'a>,
/// The object contents
pub data: &'a [u8],
}
impl<'a> Any<'a> {
/// Create a new `Any` from BER/DER header and content
#[inline]
pub const fn new(header: Header<'a>, data: &'a [u8]) -> Self {
Any { header, data }
}
/// Create a new `Any` from a tag, and BER/DER content
#[inline]
pub const fn from_tag_and_data(tag: Tag, data: &'a [u8]) -> Self {
let constructed = matches!(tag, Tag::Sequence | Tag::Set);
Any {
header: Header {
tag,
constructed,
class: Class::Universal,
length: Length::Definite(data.len()),
raw_tag: None,
},
data,
}
}
/// Return the `Class` of this object
#[inline]
pub const fn class(&self) -> Class {
self.header.class
}
/// Update the class of the current object
#[inline]
pub fn with_class(self, class: Class) -> Self {
Any {
header: self.header.with_class(class),
..self
}
}
/// Return the `Tag` of this object
#[inline]
pub const fn tag(&self) -> Tag {
self.header.tag
}
/// Update the tag of the current object
#[inline]
pub fn with_tag(self, tag: Tag) -> Self {
Any {
header: self.header.with_tag(tag),
data: self.data,
}
}
/// Get the bytes representation of the *content*
#[inline]
pub fn as_bytes(&self) -> &'a [u8] {
self.data
}
#[inline]
pub fn parse_ber<T>(&self) -> ParseResult<'a, T>
where
T: FromBer<'a>,
{
T::from_ber(self.data)
}
/// Parse a BER value and apply the provided parsing function to content
///
/// After parsing, the sequence object and header are discarded.
pub fn from_ber_and_then<F, T, E>(
class: Class,
tag: u32,
bytes: &'a [u8],
op: F,
) -> ParseResult<'a, T, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?;
any.tag()
.assert_eq(Tag(tag))
.map_err(|e| Err::Error(e.into()))?;
any.class()
.assert_eq(class)
.map_err(|e| Err::Error(e.into()))?;
let (_, res) = op(any.data)?;
Ok((rem, res))
}
/// Parse a DER value and apply the provided parsing function to content
///
/// After parsing, the sequence object and header are discarded.
pub fn from_der_and_then<F, T, E>(
class: Class,
tag: u32,
bytes: &'a [u8],
op: F,
) -> ParseResult<'a, T, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?;
any.tag()
.assert_eq(Tag(tag))
.map_err(|e| Err::Error(e.into()))?;
any.class()
.assert_eq(class)
.map_err(|e| Err::Error(e.into()))?;
let (_, res) = op(any.data)?;
Ok((rem, res))
}
#[inline]
pub fn parse_der<T>(&self) -> ParseResult<'a, T>
where
T: FromDer<'a>,
{
T::from_der(self.data)
}
/// Get the content following a BER header
#[inline]
pub fn parse_ber_content<'i>(i: &'i [u8], header: &'_ Header) -> ParseResult<'i, &'i [u8]> {
header.parse_ber_content(i)
}
/// Get the content following a DER header
#[inline]
pub fn parse_der_content<'i>(i: &'i [u8], header: &'_ Header) -> ParseResult<'i, &'i [u8]> {
header.assert_definite()?;
DerParser::get_object_content(i, header, 8)
}
}
macro_rules! impl_any_into {
(IMPL $sname:expr, $fn_name:ident => $ty:ty, $asn1:expr) => {
#[doc = "Attempt to convert object to `"]
#[doc = $sname]
#[doc = "` (ASN.1 type: `"]
#[doc = $asn1]
#[doc = "`)."]
pub fn $fn_name(self) -> Result<$ty> {
self.try_into()
}
};
($fn_name:ident => $ty:ty, $asn1:expr) => {
impl_any_into! {
IMPL stringify!($ty), $fn_name => $ty, $asn1
}
};
}
macro_rules! impl_any_as {
(IMPL $sname:expr, $fn_name:ident => $ty:ty, $asn1:expr) => {
#[doc = "Attempt to create ASN.1 type `"]
#[doc = $asn1]
#[doc = "` from this object."]
#[inline]
pub fn $fn_name(&self) -> Result<$ty> {
TryFrom::try_from(self)
}
};
($fn_name:ident => $ty:ty, $asn1:expr) => {
impl_any_as! {
IMPL stringify!($ty), $fn_name => $ty, $asn1
}
};
}
impl<'a> Any<'a> {
impl_any_into!(bitstring => BitString<'a>, "BIT STRING");
impl_any_into!(bmpstring => BmpString<'a>, "BMPString");
impl_any_into!(bool => bool, "BOOLEAN");
impl_any_into!(boolean => Boolean, "BOOLEAN");
impl_any_into!(embedded_pdv => EmbeddedPdv<'a>, "EMBEDDED PDV");
impl_any_into!(enumerated => Enumerated, "ENUMERATED");
impl_any_into!(generalizedtime => GeneralizedTime, "GeneralizedTime");
impl_any_into!(generalstring => GeneralString<'a>, "GeneralString");
impl_any_into!(graphicstring => GraphicString<'a>, "GraphicString");
impl_any_into!(i8 => i8, "INTEGER");
impl_any_into!(i16 => i16, "INTEGER");
impl_any_into!(i32 => i32, "INTEGER");
impl_any_into!(i64 => i64, "INTEGER");
impl_any_into!(i128 => i128, "INTEGER");
impl_any_into!(ia5string => Ia5String<'a>, "IA5String");
impl_any_into!(integer => Integer<'a>, "INTEGER");
impl_any_into!(null => Null, "NULL");
impl_any_into!(numericstring => NumericString<'a>, "NumericString");
impl_any_into!(objectdescriptor => ObjectDescriptor<'a>, "ObjectDescriptor");
impl_any_into!(octetstring => OctetString<'a>, "OCTET STRING");
impl_any_into!(oid => Oid<'a>, "OBJECT IDENTIFIER");
impl_any_into!(real => Real, "REAL");
/// Attempt to convert object to `Oid` (ASN.1 type: `RELATIVE-OID`).
pub fn relative_oid(self) -> Result<Oid<'a>> {
self.header.assert_tag(Tag::RelativeOid)?;
let asn1 = Cow::Borrowed(self.data);
Ok(Oid::new_relative(asn1))
}
impl_any_into!(printablestring => PrintableString<'a>, "PrintableString");
// XXX REAL
impl_any_into!(sequence => Sequence<'a>, "SEQUENCE");
impl_any_into!(set => Set<'a>, "SET");
impl_any_into!(str => &'a str, "UTF8String");
impl_any_into!(string => String, "UTF8String");
impl_any_into!(teletexstring => TeletexString<'a>, "TeletexString");
impl_any_into!(u8 => u8, "INTEGER");
impl_any_into!(u16 => u16, "INTEGER");
impl_any_into!(u32 => u32, "INTEGER");
impl_any_into!(u64 => u64, "INTEGER");
impl_any_into!(u128 => u128, "INTEGER");
impl_any_into!(universalstring => UniversalString<'a>, "UniversalString");
impl_any_into!(utctime => UtcTime, "UTCTime");
impl_any_into!(utf8string => Utf8String<'a>, "UTF8String");
impl_any_into!(videotexstring => VideotexString<'a>, "VideotexString");
impl_any_into!(visiblestring => VisibleString<'a>, "VisibleString");
impl_any_as!(as_bitstring => BitString, "BITSTRING");
impl_any_as!(as_bmpstring => BmpString, "BMPString");
impl_any_as!(as_bool => bool, "BOOLEAN");
impl_any_as!(as_boolean => Boolean, "BOOLEAN");
impl_any_as!(as_embedded_pdv => EmbeddedPdv, "EMBEDDED PDV");
impl_any_as!(as_endofcontent => EndOfContent, "END OF CONTENT (not a real ASN.1 type)");
impl_any_as!(as_enumerated => Enumerated, "ENUMERATED");
impl_any_as!(as_generalizedtime => GeneralizedTime, "GeneralizedTime");
impl_any_as!(as_generalstring => GeneralString, "GeneralString");
impl_any_as!(as_graphicstring => GraphicString, "GraphicString");
impl_any_as!(as_i8 => i8, "INTEGER");
impl_any_as!(as_i16 => i16, "INTEGER");
impl_any_as!(as_i32 => i32, "INTEGER");
impl_any_as!(as_i64 => i64, "INTEGER");
impl_any_as!(as_i128 => i128, "INTEGER");
impl_any_as!(as_ia5string => Ia5String, "IA5String");
impl_any_as!(as_integer => Integer, "INTEGER");
impl_any_as!(as_null => Null, "NULL");
impl_any_as!(as_numericstring => NumericString, "NumericString");
impl_any_as!(as_objectdescriptor => ObjectDescriptor, "OBJECT IDENTIFIER");
impl_any_as!(as_octetstring => OctetString, "OCTET STRING");
impl_any_as!(as_oid => Oid, "OBJECT IDENTIFIER");
impl_any_as!(as_real => Real, "REAL");
/// Attempt to create ASN.1 type `RELATIVE-OID` from this object.
pub fn as_relative_oid(&self) -> Result<Oid<'a>> {
self.header.assert_tag(Tag::RelativeOid)?;
let asn1 = Cow::Borrowed(self.data);
Ok(Oid::new_relative(asn1))
}
impl_any_as!(as_printablestring => PrintableString, "PrintableString");
impl_any_as!(as_sequence => Sequence, "SEQUENCE");
impl_any_as!(as_set => Set, "SET");
impl_any_as!(as_str => &str, "UTF8String");
impl_any_as!(as_string => String, "UTF8String");
impl_any_as!(as_teletexstring => TeletexString, "TeletexString");
impl_any_as!(as_u8 => u8, "INTEGER");
impl_any_as!(as_u16 => u16, "INTEGER");
impl_any_as!(as_u32 => u32, "INTEGER");
impl_any_as!(as_u64 => u64, "INTEGER");
impl_any_as!(as_u128 => u128, "INTEGER");
impl_any_as!(as_universalstring => UniversalString, "UniversalString");
impl_any_as!(as_utctime => UtcTime, "UTCTime");
impl_any_as!(as_utf8string => Utf8String, "UTF8String");
impl_any_as!(as_videotexstring => VideotexString, "VideotexString");
impl_any_as!(as_visiblestring => VisibleString, "VisibleString");
/// Attempt to create an `Option<T>` from this object.
pub fn as_optional<'b, T>(&'b self) -> Result<Option<T>>
where
T: TryFrom<&'b Any<'a>, Error = Error>,
'a: 'b,
{
match TryFrom::try_from(self) {
Ok(t) => Ok(Some(t)),
Err(Error::UnexpectedTag { .. }) => Ok(None),
Err(e) => Err(e),
}
}
/// Attempt to create a tagged value (EXPLICIT) from this object.
pub fn as_tagged_explicit<T, E, const CLASS: u8, const TAG: u32>(
&self,
) -> Result<TaggedValue<T, E, Explicit, CLASS, TAG>, E>
where
T: FromBer<'a, E>,
E: From<Error>,
{
TryFrom::try_from(self)
}
/// Attempt to create a tagged value (IMPLICIT) from this object.
pub fn as_tagged_implicit<T, E, const CLASS: u8, const TAG: u32>(
&self,
) -> Result<TaggedValue<T, E, Implicit, CLASS, TAG>, E>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
TryFrom::try_from(self)
}
/// Attempt to get value as `str`, for all known string types
///
/// This function does not allocate data, so it supports all string types except
/// `UniversalString`.
pub fn as_any_str(&self) -> Result<String> {
match self.tag() {
Tag::GeneralString
| Tag::GraphicString
| Tag::Ia5String
| Tag::NumericString
| Tag::PrintableString
| Tag::T61String
| Tag::Utf8String
| Tag::VideotexString
| Tag::VisibleString => {
let res = core::str::from_utf8(self.data)?;
Ok(res.to_string())
}
Tag::UniversalString => {
let us = UniversalString::try_from(self)?;
Ok(us.string())
}
_ => todo!(),
}
}
/// Attempt to get value as `String`, for all known string types
///
/// This function allocates data
pub fn as_any_string(&self) -> Result<&str> {
match self.tag() {
Tag::GeneralString
| Tag::GraphicString
| Tag::Ia5String
| Tag::NumericString
| Tag::PrintableString
| Tag::T61String
//| Tag::UniversalString // UCS-4, cannot be converted
| Tag::Utf8String
| Tag::VideotexString
| Tag::VisibleString => {
let res = core::str::from_utf8(self.data)?;
Ok(res)
}
_ => todo!(),
}
}
}
pub(crate) fn parse_ber_any(input: &[u8]) -> ParseResult<Any> {
let (i, header) = Header::from_ber(input)?;
let (i, data) = BerParser::get_object_content(i, &header, MAX_RECURSION)?;
Ok((i, Any { header, data }))
}
pub(crate) fn parse_der_any(input: &[u8]) -> ParseResult<Any> {
let (i, header) = Header::from_der(input)?;
// X.690 section 10.1: The definite form of length encoding shall be used
header.length.assert_definite()?;
let (i, data) = DerParser::get_object_content(i, &header, MAX_RECURSION)?;
Ok((i, Any { header, data }))
}
impl<'a> FromBer<'a> for Any<'a> {
#[inline]
fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> {
trace("Any", parse_ber_any, bytes)
}
}
impl<'a> FromDer<'a> for Any<'a> {
#[inline]
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> {
trace("Any", parse_der_any, bytes)
}
}
impl CheckDerConstraints for Any<'_> {
fn check_constraints(any: &Any) -> Result<()> {
any.header.length().assert_definite()?;
// if len < 128, must use short form (10.1: minimum number of octets)
Ok(())
}
}
impl DerAutoDerive for Any<'_> {}
impl DynTagged for Any<'_> {
fn tag(&self) -> Tag {
self.tag()
}
}
// impl<'a> ToStatic for Any<'a> {
// type Owned = Any<'static>;
// fn to_static(&self) -> Self::Owned {
// Any {
// header: self.header.to_static(),
// data: Cow::Owned(self.data.to_vec()),
// }
// }
// }
#[cfg(feature = "std")]
impl ToDer for Any<'_> {
fn to_der_len(&self) -> Result<usize> {
let hdr_len = self.header.to_der_len()?;
Ok(hdr_len + self.data.len())
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// create fake header to have correct length
let header = Header::new(
self.header.class,
self.header.constructed,
self.header.tag,
Length::Definite(self.data.len()),
);
let sz = header.write_der_header(writer)?;
Ok(sz)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(self.data).map_err(Into::into)
}
/// Similar to using `to_der`, but uses header without computing length value
fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let sz = self.header.write_der_header(writer)?;
let sz = sz + writer.write(self.data)?;
Ok(sz)
}
}
#[cfg(test)]
mod tests {
use crate::*;
use hex_literal::hex;
#[test]
fn methods_any() {
let header = Header::new_simple(Tag::Integer);
let any = Any::new(header, &[])
.with_class(Class::ContextSpecific)
.with_tag(Tag(0));
assert_eq!(any.as_bytes(), &[]);
let input = &hex! {"80 03 02 01 01"};
let (_, any) = Any::from_ber(input).expect("parsing failed");
let (_, r) = any.parse_ber::<Integer>().expect("parse_ber failed");
assert_eq!(r.as_u32(), Ok(1));
let (_, r) = any.parse_der::<Integer>().expect("parse_der failed");
assert_eq!(r.as_u32(), Ok(1));
let header = &any.header;
let (_, content) = Any::parse_ber_content(&input[2..], header).unwrap();
assert_eq!(content.len(), 3);
let (_, content) = Any::parse_der_content(&input[2..], header).unwrap();
assert_eq!(content.len(), 3);
let (_, any) = Any::from_der(&input[2..]).unwrap();
Any::check_constraints(&any).unwrap();
assert_eq!(<Any as DynTagged>::tag(&any), any.tag());
let int = any.integer().unwrap();
assert_eq!(int.as_u16(), Ok(1));
}
}

View File

@@ -0,0 +1,157 @@
use crate::*;
use alloc::borrow::Cow;
#[cfg(feature = "bits")]
use bitvec::{order::Msb0, slice::BitSlice};
use core::convert::TryFrom;
/// ASN.1 `BITSTRING` type
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BitString<'a> {
pub unused_bits: u8,
pub data: Cow<'a, [u8]>,
}
impl<'a> BitString<'a> {
// Length must be >= 1 (first byte is number of ignored bits)
pub const fn new(unused_bits: u8, s: &'a [u8]) -> Self {
BitString {
unused_bits,
data: Cow::Borrowed(s),
}
}
/// Test if bit `bitnum` is set
pub fn is_set(&self, bitnum: usize) -> bool {
let byte_pos = bitnum / 8;
if byte_pos >= self.data.len() {
return false;
}
let b = 7 - (bitnum % 8);
(self.data[byte_pos] & (1 << b)) != 0
}
/// Constructs a shared `&BitSlice` reference over the object data.
#[cfg(feature = "bits")]
pub fn as_bitslice(&self) -> Option<&BitSlice<u8, Msb0>> {
BitSlice::<_, Msb0>::try_from_slice(&self.data).ok()
}
}
impl AsRef<[u8]> for BitString<'_> {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
impl<'a> TryFrom<Any<'a>> for BitString<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<BitString<'a>> {
TryFrom::try_from(&any)
}
}
// non-consuming version
impl<'a, 'b> TryFrom<&'b Any<'a>> for BitString<'a> {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<BitString<'a>> {
any.tag().assert_eq(Self::TAG)?;
if any.data.is_empty() {
return Err(Error::InvalidLength);
}
let s = any.data;
let (unused_bits, data) = (s[0], Cow::Borrowed(&s[1..]));
Ok(BitString { unused_bits, data })
}
}
impl CheckDerConstraints for BitString<'_> {
fn check_constraints(any: &Any) -> Result<()> {
// X.690 section 10.2
any.header.assert_primitive()?;
// Check that padding bits are all 0 (X.690 section 11.2.1)
match any.data.len() {
0 => Err(Error::InvalidLength),
1 => {
// X.690 section 11.2.2 Note 2
if any.data[0] == 0 {
Ok(())
} else {
Err(Error::InvalidLength)
}
}
len => {
let unused_bits = any.data[0];
let last_byte = any.data[len - 1];
if last_byte.trailing_zeros() < unused_bits as u32 {
return Err(Error::DerConstraintFailed(DerConstraint::UnusedBitsNotZero));
}
Ok(())
}
}
}
}
impl DerAutoDerive for BitString<'_> {}
impl Tagged for BitString<'_> {
const TAG: Tag = Tag::BitString;
}
#[cfg(feature = "std")]
impl ToDer for BitString<'_> {
fn to_der_len(&self) -> Result<usize> {
let sz = self.data.len();
if sz < 127 {
// 1 (class+tag) + 1 (length) + 1 (unused bits) + len
Ok(3 + sz)
} else {
// 1 (class+tag) + n (length) + 1 (unused bits) + len
let n = Length::Definite(sz + 1).to_der_len()?;
Ok(2 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(1 + self.data.len()),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let sz = writer.write(&[self.unused_bits])?;
let sz = sz + writer.write(&self.data)?;
Ok(sz)
}
}
#[cfg(test)]
mod tests {
use super::BitString;
#[test]
fn test_bitstring_is_set() {
let obj = BitString::new(0, &[0x0f, 0x00, 0x40]);
assert!(!obj.is_set(0));
assert!(obj.is_set(7));
assert!(!obj.is_set(9));
assert!(obj.is_set(17));
}
#[cfg(feature = "bits")]
#[test]
fn test_bitstring_to_bitvec() {
let obj = BitString::new(0, &[0x0f, 0x00, 0x40]);
let bv = obj.as_bitslice().expect("could not get bitslice");
assert_eq!(bv.get(0).as_deref(), Some(&false));
assert_eq!(bv.get(7).as_deref(), Some(&true));
assert_eq!(bv.get(9).as_deref(), Some(&false));
assert_eq!(bv.get(17).as_deref(), Some(&true));
}
}

147
vendor/asn1-rs/src/asn1_types/boolean.rs vendored Normal file
View File

@@ -0,0 +1,147 @@
use crate::*;
use core::convert::TryFrom;
/// ASN.1 `BOOLEAN` type
///
/// BER objects consider any non-zero value as `true`, and `0` as `false`.
///
/// DER objects must use value `0x0` (`false`) or `0xff` (`true`).
#[derive(Debug, PartialEq, Eq)]
pub struct Boolean {
pub value: u8,
}
impl Boolean {
/// `BOOLEAN` object for value `false`
pub const FALSE: Boolean = Boolean::new(0);
/// `BOOLEAN` object for value `true`
pub const TRUE: Boolean = Boolean::new(0xff);
/// Create a new `Boolean` from the provided logical value.
#[inline]
pub const fn new(value: u8) -> Self {
Boolean { value }
}
/// Return the `bool` value from this object.
#[inline]
pub const fn bool(&self) -> bool {
self.value != 0
}
}
impl<'a> TryFrom<Any<'a>> for Boolean {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Boolean> {
TryFrom::try_from(&any)
}
}
// non-consuming version
impl<'a, 'b> TryFrom<&'b Any<'a>> for Boolean {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Boolean> {
any.tag().assert_eq(Self::TAG)?;
// X.690 section 8.2.1:
// The encoding of a boolean value shall be primitive. The contents octets shall consist of a single octet
if any.header.length != Length::Definite(1) {
return Err(Error::InvalidLength);
}
let value = any.data[0];
Ok(Boolean { value })
}
}
impl CheckDerConstraints for Boolean {
fn check_constraints(any: &Any) -> Result<()> {
let c = any.data[0];
// X.690 section 11.1
if !(c == 0 || c == 0xff) {
return Err(Error::DerConstraintFailed(DerConstraint::InvalidBoolean));
}
Ok(())
}
}
impl DerAutoDerive for Boolean {}
impl Tagged for Boolean {
const TAG: Tag = Tag::Boolean;
}
#[cfg(feature = "std")]
impl ToDer for Boolean {
fn to_der_len(&self) -> Result<usize> {
// 3 = 1 (tag) + 1 (length) + 1 (value)
Ok(3)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let b = if self.value != 0 { 0xff } else { 0x00 };
writer.write(&[b]).map_err(Into::into)
}
/// Similar to using `to_der`, but uses header without computing length value
fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let sz = writer.write(&[Self::TAG.0 as u8, 0x01, self.value])?;
Ok(sz)
}
}
impl<'a> TryFrom<Any<'a>> for bool {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<bool> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for bool {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<bool> {
any.tag().assert_eq(Self::TAG)?;
let b = Boolean::try_from(any)?;
Ok(b.bool())
}
}
impl CheckDerConstraints for bool {
fn check_constraints(any: &Any) -> Result<()> {
let c = any.data[0];
// X.690 section 11.1
if !(c == 0 || c == 0xff) {
return Err(Error::DerConstraintFailed(DerConstraint::InvalidBoolean));
}
Ok(())
}
}
impl DerAutoDerive for bool {}
impl Tagged for bool {
const TAG: Tag = Tag::Boolean;
}
#[cfg(feature = "std")]
impl ToDer for bool {
fn to_der_len(&self) -> Result<usize> {
// 3 = 1 (tag) + 1 (length) + 1 (value)
Ok(3)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let b = if *self { 0xff } else { 0x00 };
writer.write(&[b]).map_err(Into::into)
}
}

21
vendor/asn1-rs/src/asn1_types/choice.rs vendored Normal file
View File

@@ -0,0 +1,21 @@
use crate::{FromBer, FromDer, Tag, Tagged};
pub trait Choice {
/// Is the provided [`Tag`] decodable as a variant of this `CHOICE`?
fn can_decode(tag: Tag) -> bool;
}
/// This blanket impl allows any [`Tagged`] type to function as a [`Choice`]
/// with a single alternative.
impl<T> Choice for T
where
T: Tagged,
{
fn can_decode(tag: Tag) -> bool {
T::TAG == tag
}
}
pub trait BerChoice<'a>: Choice + FromBer<'a> {}
pub trait DerChoice<'a>: Choice + FromDer<'a> {}

View File

@@ -0,0 +1,125 @@
use crate::*;
use core::convert::TryFrom;
#[derive(Debug, PartialEq, Eq)]
pub struct EmbeddedPdv<'a> {
pub identification: PdvIdentification<'a>,
pub data_value_descriptor: Option<ObjectDescriptor<'a>>,
pub data_value: &'a [u8],
}
#[derive(Debug, PartialEq, Eq)]
pub enum PdvIdentification<'a> {
Syntaxes {
s_abstract: Oid<'a>,
s_transfer: Oid<'a>,
},
Syntax(Oid<'a>),
PresentationContextId(Integer<'a>),
ContextNegotiation {
presentation_context_id: Integer<'a>,
presentation_syntax: Oid<'a>,
},
TransferSyntax(Oid<'a>),
Fixed,
}
impl<'a> TryFrom<Any<'a>> for EmbeddedPdv<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for EmbeddedPdv<'a> {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Self> {
let data = any.data;
// AUTOMATIC TAGS means all values will be tagged (IMPLICIT)
// [0] -> identification
let (rem, seq0) =
TaggedParser::<Explicit, Any>::parse_ber(Class::ContextSpecific, Tag(0), data)?;
let inner = seq0.inner;
let identification = match inner.tag() {
Tag(0) => {
// syntaxes SEQUENCE {
// abstract OBJECT IDENTIFIER,
// transfer OBJECT IDENTIFIER
// },
// AUTOMATIC tags -> implicit! Hopefully, Oid does not check tag value!
let (rem, s_abstract) = Oid::from_ber(inner.data)?;
let (_, s_transfer) = Oid::from_ber(rem)?;
PdvIdentification::Syntaxes {
s_abstract,
s_transfer,
}
}
Tag(1) => {
// syntax OBJECT IDENTIFIER
let oid = Oid::new(inner.data.into());
PdvIdentification::Syntax(oid)
}
Tag(2) => {
// presentation-context-id INTEGER
let i = Integer::new(inner.data);
PdvIdentification::PresentationContextId(i)
}
Tag(3) => {
// context-negotiation SEQUENCE {
// presentation-context-id INTEGER,
// transfer-syntax OBJECT IDENTIFIER
// },
// AUTOMATIC tags -> implicit!
let (rem, any) = Any::from_ber(inner.data)?;
let presentation_context_id = Integer::new(any.data);
let (_, presentation_syntax) = Oid::from_ber(rem)?;
PdvIdentification::ContextNegotiation {
presentation_context_id,
presentation_syntax,
}
}
Tag(4) => {
// transfer-syntax OBJECT IDENTIFIER
let oid = Oid::new(inner.data.into());
PdvIdentification::TransferSyntax(oid)
}
Tag(5) => {
// fixed NULL
PdvIdentification::Fixed
}
_ => {
return Err(inner
.tag()
.invalid_value("Invalid identification tag in EMBEDDED PDV"))
}
};
// [1] -> data-value-descriptor ObjectDescriptor OPTIONAL
// *BUT* WITH COMPONENTS data-value-descriptor ABSENT
// XXX this should be parse_ber?
// let (rem, data_value_descriptor) =
// TaggedOptional::from(1).parse_der(rem, |_, inner| ObjectDescriptor::from_ber(inner))?;
let (rem, data_value_descriptor) = (rem, None);
// [2] -> data-value OCTET STRING
let (_, data_value) =
TaggedParser::<Implicit, &[u8]>::parse_ber(Class::ContextSpecific, Tag(2), rem)?;
let data_value = data_value.inner;
let obj = EmbeddedPdv {
identification,
data_value_descriptor,
data_value,
};
Ok(obj)
}
}
impl CheckDerConstraints for EmbeddedPdv<'_> {
fn check_constraints(any: &Any) -> Result<()> {
any.header.length().assert_definite()?;
any.header.assert_constructed()?;
Ok(())
}
}
impl DerAutoDerive for EmbeddedPdv<'_> {}

View File

@@ -0,0 +1,61 @@
use crate::{Any, Error, Result, Tag, Tagged};
use core::convert::TryFrom;
/// End-of-contents octets
///
/// `EndOfContent` is not a BER type, but represents a marked to indicate the end of contents
/// of an object, when the length is `Indefinite` (see X.690 section 8.1.5).
///
/// This type cannot exist in DER, and so provides no `FromDer`/`ToDer` implementation.
#[derive(Debug)]
pub struct EndOfContent {}
impl Default for EndOfContent {
fn default() -> Self {
Self::new()
}
}
impl EndOfContent {
pub const fn new() -> Self {
EndOfContent {}
}
}
impl<'a> TryFrom<Any<'a>> for EndOfContent {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<EndOfContent> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for EndOfContent {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<EndOfContent> {
any.tag().assert_eq(Self::TAG)?;
if !any.header.length.is_null() {
return Err(Error::InvalidLength);
}
Ok(EndOfContent {})
}
}
impl Tagged for EndOfContent {
const TAG: Tag = Tag::EndOfContent;
}
// impl ToDer for EndOfContent {
// fn to_der_len(&self) -> Result<usize> {
// Ok(2)
// }
// fn write_der_header(&self, writer: &mut dyn std::io::Write) -> crate::SerializeResult<usize> {
// writer.write(&[Self::TAG.0 as u8, 0x00]).map_err(Into::into)
// }
// fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> crate::SerializeResult<usize> {
// Ok(0)
// }
// }

View File

@@ -0,0 +1,72 @@
use crate::ber::bytes_to_u64;
use crate::*;
use core::convert::TryFrom;
/// ASN.1 `ENUMERATED` type
///
/// # Limitations
///
/// Supported values are limited to 0 .. 2^32
#[derive(Debug, PartialEq, Eq)]
pub struct Enumerated(pub u32);
impl Enumerated {
pub const fn new(value: u32) -> Self {
Enumerated(value)
}
}
impl<'a> TryFrom<Any<'a>> for Enumerated {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Enumerated> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Enumerated {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Enumerated> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_primitive()?;
let res_u64 = bytes_to_u64(any.data)?;
if res_u64 > (<u32>::MAX as u64) {
return Err(Error::IntegerTooLarge);
}
let value = res_u64 as u32;
Ok(Enumerated(value))
}
}
impl CheckDerConstraints for Enumerated {
fn check_constraints(any: &Any) -> Result<()> {
any.header.length.assert_definite()?;
Ok(())
}
}
impl DerAutoDerive for Enumerated {}
impl Tagged for Enumerated {
const TAG: Tag = Tag::Enumerated;
}
#[cfg(feature = "std")]
impl ToDer for Enumerated {
fn to_der_len(&self) -> Result<usize> {
Integer::from(self.0).to_der_len()
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let i = Integer::from(self.0);
let len = i.data.len();
let header = Header::new(Class::Universal, false, Self::TAG, Length::Definite(len));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(self.0);
int.write_der_content(writer)
}
}

View File

@@ -0,0 +1,303 @@
use crate::*;
use alloc::format;
#[cfg(not(feature = "std"))]
use alloc::string::String;
use core::convert::TryFrom;
use core::fmt;
#[cfg(feature = "datetime")]
use time::OffsetDateTime;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct GeneralizedTime(pub ASN1DateTime);
impl GeneralizedTime {
pub const fn new(datetime: ASN1DateTime) -> Self {
GeneralizedTime(datetime)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
// X.680 section 42 defines a GeneralizedTime as a VisibleString restricted to:
//
// a) a string representing the calendar date, as specified in ISO 8601, with a four-digit representation of the
// year, a two-digit representation of the month and a two-digit representation of the day, without use of
// separators, followed by a string representing the time of day, as specified in ISO 8601, without separators
// other than decimal comma or decimal period (as provided for in ISO 8601), and with no terminating Z (as
// provided for in ISO 8601); or
// b) the characters in a) above followed by an upper-case letter Z ; or
// c) he characters in a) above followed by a string representing a local time differential, as specified in
// ISO 8601, without separators.
let (year, month, day, hour, minute, rem) = match bytes {
[year1, year2, year3, year4, mon1, mon2, day1, day2, hour1, hour2, min1, min2, rem @ ..] =>
{
let year_hi = decode_decimal(Self::TAG, *year1, *year2)?;
let year_lo = decode_decimal(Self::TAG, *year3, *year4)?;
let year = (year_hi as u32) * 100 + (year_lo as u32);
let month = decode_decimal(Self::TAG, *mon1, *mon2)?;
let day = decode_decimal(Self::TAG, *day1, *day2)?;
let hour = decode_decimal(Self::TAG, *hour1, *hour2)?;
let minute = decode_decimal(Self::TAG, *min1, *min2)?;
(year, month, day, hour, minute, rem)
}
_ => return Err(Self::TAG.invalid_value("malformed time string (not yymmddhhmm)")),
};
if rem.is_empty() {
return Err(Self::TAG.invalid_value("malformed time string"));
}
// check for seconds
let (second, rem) = match rem {
[sec1, sec2, rem @ ..] => {
let second = decode_decimal(Self::TAG, *sec1, *sec2)?;
(second, rem)
}
_ => (0, rem),
};
if month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59 {
// eprintln!("GeneralizedTime: time checks failed");
// eprintln!(" month:{}", month);
// eprintln!(" day:{}", day);
// eprintln!(" hour:{}", hour);
// eprintln!(" minute:{}", minute);
// eprintln!(" second:{}", second);
return Err(Self::TAG.invalid_value("time components with invalid values"));
}
if rem.is_empty() {
// case a): no fractional seconds part, and no terminating Z
return Ok(GeneralizedTime(ASN1DateTime::new(
year,
month,
day,
hour,
minute,
second,
None,
ASN1TimeZone::Undefined,
)));
}
// check for fractional seconds
let (millisecond, rem) = match rem {
[b'.' | b',', rem @ ..] => {
let mut fsecond = 0;
let mut rem = rem;
let mut digits = 0;
for idx in 0..=4 {
if rem.is_empty() {
if idx == 0 {
// dot or comma, but no following digit
return Err(Self::TAG.invalid_value(
"malformed time string (dot or comma but no digits)",
));
}
digits = idx;
break;
}
if idx == 4 {
return Err(
Self::TAG.invalid_value("malformed time string (invalid milliseconds)")
);
}
match rem[0] {
b'0'..=b'9' => {
// cannot overflow, max 4 digits will be read
fsecond = fsecond * 10 + (rem[0] - b'0') as u16;
}
b'Z' | b'+' | b'-' => {
digits = idx;
break;
}
_ => {
return Err(Self::TAG.invalid_value(
"malformed time string (invalid milliseconds/timezone)",
))
}
}
rem = &rem[1..];
}
// fix fractional seconds depending on the number of digits
// for ex, date "xxxx.3" means 3000 milliseconds, not 3
let fsecond = match digits {
1 => fsecond * 100,
2 => fsecond * 10,
_ => fsecond,
};
(Some(fsecond), rem)
}
_ => (None, rem),
};
// check timezone
if rem.is_empty() {
// case a): fractional seconds part, and no terminating Z
return Ok(GeneralizedTime(ASN1DateTime::new(
year,
month,
day,
hour,
minute,
second,
millisecond,
ASN1TimeZone::Undefined,
)));
}
let tz = match rem {
[b'Z'] => ASN1TimeZone::Z,
[b'+', h1, h2, m1, m2] => {
let hh = decode_decimal(Self::TAG, *h1, *h2)?;
let mm = decode_decimal(Self::TAG, *m1, *m2)?;
ASN1TimeZone::Offset(hh as i8, mm as i8)
}
[b'-', h1, h2, m1, m2] => {
let hh = decode_decimal(Self::TAG, *h1, *h2)?;
let mm = decode_decimal(Self::TAG, *m1, *m2)?;
ASN1TimeZone::Offset(-(hh as i8), mm as i8)
}
_ => return Err(Self::TAG.invalid_value("malformed time string: no time zone")),
};
Ok(GeneralizedTime(ASN1DateTime::new(
year,
month,
day,
hour,
minute,
second,
millisecond,
tz,
)))
}
/// Return a ISO 8601 combined date and time with time zone.
#[cfg(feature = "datetime")]
#[cfg_attr(docsrs, doc(cfg(feature = "datetime")))]
pub fn utc_datetime(&self) -> Result<OffsetDateTime> {
self.0.to_datetime()
}
}
impl<'a> TryFrom<Any<'a>> for GeneralizedTime {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<GeneralizedTime> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for GeneralizedTime {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<GeneralizedTime> {
any.tag().assert_eq(Self::TAG)?;
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_visible(b: &u8) -> bool {
0x20 <= *b && *b <= 0x7f
}
if !any.data.iter().all(is_visible) {
return Err(Error::StringInvalidCharset);
}
GeneralizedTime::from_bytes(any.data)
}
}
impl fmt::Display for GeneralizedTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let dt = &self.0;
let fsec = match self.0.millisecond {
Some(v) => format!(".{}", v),
None => String::new(),
};
match dt.tz {
ASN1TimeZone::Undefined => write!(
f,
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}",
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec
),
ASN1TimeZone::Z => write!(
f,
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}Z",
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec
),
ASN1TimeZone::Offset(hh, mm) => {
let (s, hh) = if hh > 0 { ('+', hh) } else { ('-', -hh) };
write!(
f,
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}{}{:02}{:02}",
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec, s, hh, mm
)
}
}
}
}
impl CheckDerConstraints for GeneralizedTime {
fn check_constraints(any: &Any) -> Result<()> {
// X.690 section 11.7.1: The encoding shall terminate with a "Z"
if any.data.last() != Some(&b'Z') {
return Err(Error::DerConstraintFailed(DerConstraint::MissingTimeZone));
}
// X.690 section 11.7.2: The seconds element shall always be present.
// XXX
// X.690 section 11.7.4: The decimal point element, if present, shall be the point option "."
if any.data.contains(&b',') {
return Err(Error::DerConstraintFailed(DerConstraint::MissingSeconds));
}
Ok(())
}
}
impl DerAutoDerive for GeneralizedTime {}
impl Tagged for GeneralizedTime {
const TAG: Tag = Tag::GeneralizedTime;
}
#[cfg(feature = "std")]
impl ToDer for GeneralizedTime {
fn to_der_len(&self) -> Result<usize> {
// data:
// - 8 bytes for YYYYMMDD
// - 6 for hhmmss in DER (X.690 section 11.7.2)
// - (variable) the fractional part, without trailing zeros, with a point "."
// - 1 for the character Z in DER (X.690 section 11.7.1)
// data length: 15 + fractional part
//
// thus, length will always be on 1 byte (short length) and
// class+structure+tag also on 1
//
// total: = 1 (class+constructed+tag) + 1 (length) + 15 + fractional
let num_digits = match self.0.millisecond {
None => 0,
Some(v) => 1 + v.to_string().len(),
};
Ok(2 + 15 + num_digits)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// see above for length value
let num_digits = match self.0.millisecond {
None => 0,
Some(v) => 1 + v.to_string().len() as u8,
};
writer
.write(&[Self::TAG.0 as u8, 15 + num_digits])
.map_err(Into::into)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let fractional = match self.0.millisecond {
None => "".to_string(),
Some(v) => format!(".{}", v),
};
let num_digits = fractional.len();
write!(
writer,
"{:04}{:02}{:02}{:02}{:02}{:02}{}Z",
self.0.year,
self.0.month,
self.0.day,
self.0.hour,
self.0.minute,
self.0.second,
fractional,
)?;
// write_fmt returns (), see above for length value
Ok(15 + num_digits)
}
}

727
vendor/asn1-rs/src/asn1_types/integer.rs vendored Normal file
View File

@@ -0,0 +1,727 @@
use crate::*;
use alloc::borrow::Cow;
use alloc::vec;
use core::convert::{TryFrom, TryInto};
#[cfg(feature = "bigint")]
#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
pub use num_bigint::{BigInt, BigUint, Sign};
/// Decode an unsigned integer into a big endian byte slice with all leading
/// zeroes removed (if positive) and extra 0xff remove (if negative)
fn trim_slice<'a>(any: &'a Any<'_>) -> Result<&'a [u8]> {
let bytes = any.data;
if bytes.is_empty() || (bytes[0] != 0x00 && bytes[0] != 0xff) {
return Ok(bytes);
}
match bytes.iter().position(|&b| b != 0) {
// first byte is not 0
Some(0) => (),
// all bytes are 0
None => return Ok(&bytes[bytes.len() - 1..]),
Some(first) => return Ok(&bytes[first..]),
}
// same for negative integers : skip byte 0->n if byte 0->n = 0xff AND byte n+1 >= 0x80
match bytes.windows(2).position(|s| match s {
&[a, b] => !(a == 0xff && b >= 0x80),
_ => true,
}) {
// first byte is not 0xff
Some(0) => (),
// all bytes are 0xff
None => return Ok(&bytes[bytes.len() - 1..]),
Some(first) => return Ok(&bytes[first..]),
}
Ok(bytes)
}
/// Decode an unsigned integer into a byte array of the requested size
/// containing a big endian integer.
fn decode_array_uint<const N: usize>(any: &Any<'_>) -> Result<[u8; N]> {
if is_highest_bit_set(any.data) {
return Err(Error::IntegerNegative);
}
let input = trim_slice(any)?;
if input.len() > N {
return Err(Error::IntegerTooLarge);
}
// Input has leading zeroes removed, so we need to add them back
let mut output = [0u8; N];
assert!(input.len() <= N);
output[N.saturating_sub(input.len())..].copy_from_slice(input);
Ok(output)
}
/// Decode an unsigned integer of the specified size.
///
/// Returns a byte array of the requested size containing a big endian integer.
fn decode_array_int<const N: usize>(any: &Any<'_>) -> Result<[u8; N]> {
if any.data.len() > N {
return Err(Error::IntegerTooLarge);
}
// any.tag().assert_eq(Tag::Integer)?;
let mut output = [0xFFu8; N];
let offset = N.saturating_sub(any.as_bytes().len());
output[offset..].copy_from_slice(any.as_bytes());
Ok(output)
}
/// Is the highest bit of the first byte in the slice 1? (if present)
#[inline]
fn is_highest_bit_set(bytes: &[u8]) -> bool {
bytes
.first()
.map(|byte| byte & 0b10000000 != 0)
.unwrap_or(false)
}
macro_rules! impl_int {
($uint:ty => $int:ty) => {
impl<'a> TryFrom<Any<'a>> for $int {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for $int {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Self> {
$crate::debug::trace_generic(
core::any::type_name::<$int>(),
"Conversion to int",
|any| {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_primitive()?;
let uint = if is_highest_bit_set(any.as_bytes()) {
<$uint>::from_be_bytes(decode_array_int(&any)?)
} else {
// read as uint, but check if the value will fit in a signed integer
let u = <$uint>::from_be_bytes(decode_array_uint(&any)?);
if u > <$int>::MAX as $uint {
return Err(Error::IntegerTooLarge);
}
u
};
Ok(uint as $int)
},
any,
)
}
}
impl CheckDerConstraints for $int {
fn check_constraints(any: &Any) -> Result<()> {
check_der_int_constraints(any)
}
}
impl DerAutoDerive for $int {}
impl Tagged for $int {
const TAG: Tag = Tag::Integer;
}
#[cfg(feature = "std")]
impl ToDer for $int {
fn to_der_len(&self) -> Result<usize> {
let int = Integer::from(*self);
int.to_der_len()
}
fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der(writer)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der_content(writer)
}
}
};
}
macro_rules! impl_uint {
($ty:ty) => {
impl<'a> TryFrom<Any<'a>> for $ty {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for $ty {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Self> {
$crate::debug::trace_generic(
core::any::type_name::<$ty>(),
"Conversion to uint",
|any| {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_primitive()?;
let result = Self::from_be_bytes(decode_array_uint(any)?);
Ok(result)
},
any,
)
}
}
impl CheckDerConstraints for $ty {
fn check_constraints(any: &Any) -> Result<()> {
check_der_int_constraints(any)
}
}
impl DerAutoDerive for $ty {}
impl Tagged for $ty {
const TAG: Tag = Tag::Integer;
}
#[cfg(feature = "std")]
impl ToDer for $ty {
fn to_der_len(&self) -> Result<usize> {
let int = Integer::from(*self);
int.to_der_len()
}
fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der(writer)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der_content(writer)
}
}
};
}
impl_uint!(u8);
impl_uint!(u16);
impl_uint!(u32);
impl_uint!(u64);
impl_uint!(u128);
impl_int!(u8 => i8);
impl_int!(u16 => i16);
impl_int!(u32 => i32);
impl_int!(u64 => i64);
impl_int!(u128 => i128);
/// ASN.1 `INTEGER` type
///
/// Generic representation for integer types.
/// BER/DER integers can be of any size, so it is not possible to store them as simple integers (they
/// are stored as raw bytes).
///
/// The internal representation can be obtained using `.as_ref()`.
///
/// # Note
///
/// Methods from/to BER and DER encodings are also implemented for primitive types
/// (`u8`, `u16` to `u128`, and `i8` to `i128`).
/// In most cases, it is easier to use these types directly.
///
/// # Examples
///
/// Creating an `Integer`
///
/// ```
/// use asn1_rs::Integer;
///
/// // unsigned
/// let i = Integer::from(4);
/// assert_eq!(i.as_ref(), &[4]);
/// // signed
/// let j = Integer::from(-2);
/// assert_eq!(j.as_ref(), &[0xfe]);
/// ```
///
/// Converting an `Integer` to a primitive type (using the `TryInto` trait)
///
/// ```
/// use asn1_rs::{Error, Integer};
/// use std::convert::TryInto;
///
/// let i = Integer::new(&[0x12, 0x34, 0x56, 0x78]);
/// // converts to an u32
/// let n: u32 = i.try_into().unwrap();
///
/// // Same, but converting to an u16: will fail, value cannot fit into an u16
/// let i = Integer::new(&[0x12, 0x34, 0x56, 0x78]);
/// assert_eq!(i.try_into() as Result<u16, _>, Err(Error::IntegerTooLarge));
/// ```
///
/// Encoding an `Integer` to DER
///
#[cfg_attr(feature = "std", doc = r#"```"#)]
#[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)]
/// use asn1_rs::{Integer, ToDer};
///
/// let i = Integer::from(4);
/// let v = i.to_der_vec().unwrap();
/// assert_eq!(&v, &[2, 1, 4]);
///
/// // same, with primitive types
/// let v = 4.to_der_vec().unwrap();
/// assert_eq!(&v, &[2, 1, 4]);
/// ```
#[derive(Debug, Eq, PartialEq)]
pub struct Integer<'a> {
pub(crate) data: Cow<'a, [u8]>,
}
impl<'a> Integer<'a> {
/// Creates a new `Integer` containing the given value (borrowed).
#[inline]
pub const fn new(s: &'a [u8]) -> Self {
Integer {
data: Cow::Borrowed(s),
}
}
/// Creates a borrowed `Any` for this object
#[inline]
pub fn any(&'a self) -> Any<'a> {
Any::from_tag_and_data(Self::TAG, &self.data)
}
/// Returns a `BigInt` built from this `Integer` value.
#[cfg(feature = "bigint")]
#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
pub fn as_bigint(&self) -> BigInt {
BigInt::from_signed_bytes_be(&self.data)
}
/// Returns a `BigUint` built from this `Integer` value.
#[cfg(feature = "bigint")]
#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
pub fn as_biguint(&self) -> Result<BigUint> {
if is_highest_bit_set(&self.data) {
Err(Error::IntegerNegative)
} else {
Ok(BigUint::from_bytes_be(&self.data))
}
}
/// Build an `Integer` from a constant array of bytes representation of an integer.
pub fn from_const_array<const N: usize>(b: [u8; N]) -> Self {
// if high bit set -> add leading 0 to ensure unsigned
if is_highest_bit_set(&b) {
let mut bytes = vec![0];
bytes.extend_from_slice(&b);
Integer {
data: Cow::Owned(bytes),
}
}
// otherwise -> remove 0 unless next has high bit set
else {
let mut idx = 0;
while idx < b.len() - 1 {
if b[idx] == 0 && b[idx + 1] < 0x80 {
idx += 1;
continue;
}
break;
}
Integer {
data: Cow::Owned(b[idx..].to_vec()),
}
}
}
fn from_const_array_negative<const N: usize>(b: [u8; N]) -> Self {
let mut idx = 0;
// Skip leading FF unless next has high bit clear
while idx < b.len() - 1 {
if b[idx] == 0xFF && b[idx + 1] >= 0x80 {
idx += 1;
continue;
}
break;
}
if idx == b.len() {
Integer {
data: Cow::Borrowed(&[0]),
}
} else {
Integer {
data: Cow::Owned(b[idx..].to_vec()),
}
}
}
}
macro_rules! impl_from_to {
($ty:ty, $sty:expr, $from:ident, $to:ident) => {
impl From<$ty> for Integer<'_> {
fn from(i: $ty) -> Self {
Self::$from(i)
}
}
impl TryFrom<Integer<'_>> for $ty {
type Error = Error;
fn try_from(value: Integer<'_>) -> Result<Self> {
value.$to()
}
}
impl Integer<'_> {
#[doc = "Attempts to convert an `Integer` to a `"]
#[doc = $sty]
#[doc = "`."]
#[doc = ""]
#[doc = "This function returns an `IntegerTooLarge` error if the integer will not fit into the output type."]
pub fn $to(&self) -> Result<$ty> {
self.any().try_into()
}
}
};
(IMPL SIGNED $ty:ty, $sty:expr, $from:ident, $to:ident) => {
impl_from_to!($ty, $sty, $from, $to);
impl Integer<'_> {
#[doc = "Converts a `"]
#[doc = $sty]
#[doc = "` to an `Integer`"]
#[doc = ""]
#[doc = "Note: this function allocates data."]
pub fn $from(i: $ty) -> Self {
let b = i.to_be_bytes();
if i >= 0 {
Self::from_const_array(b)
} else {
Self::from_const_array_negative(b)
}
}
}
};
(IMPL UNSIGNED $ty:ty, $sty:expr, $from:ident, $to:ident) => {
impl_from_to!($ty, $sty, $from, $to);
impl Integer<'_> {
#[doc = "Converts a `"]
#[doc = $sty]
#[doc = "` to an `Integer`"]
#[doc = ""]
#[doc = "Note: this function allocates data."]
pub fn $from(i: $ty) -> Self {
Self::from_const_array(i.to_be_bytes())
}
}
};
(SIGNED $ty:ty, $from:ident, $to:ident) => {
impl_from_to!(IMPL SIGNED $ty, stringify!($ty), $from, $to);
};
(UNSIGNED $ty:ty, $from:ident, $to:ident) => {
impl_from_to!(IMPL UNSIGNED $ty, stringify!($ty), $from, $to);
};
}
impl_from_to!(SIGNED i8, from_i8, as_i8);
impl_from_to!(SIGNED i16, from_i16, as_i16);
impl_from_to!(SIGNED i32, from_i32, as_i32);
impl_from_to!(SIGNED i64, from_i64, as_i64);
impl_from_to!(SIGNED i128, from_i128, as_i128);
impl_from_to!(UNSIGNED u8, from_u8, as_u8);
impl_from_to!(UNSIGNED u16, from_u16, as_u16);
impl_from_to!(UNSIGNED u32, from_u32, as_u32);
impl_from_to!(UNSIGNED u64, from_u64, as_u64);
impl_from_to!(UNSIGNED u128, from_u128, as_u128);
impl AsRef<[u8]> for Integer<'_> {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
impl<'a> TryFrom<Any<'a>> for Integer<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Integer<'a>> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Integer<'a> {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Integer<'a>> {
any.tag().assert_eq(Self::TAG)?;
Ok(Integer {
data: Cow::Borrowed(any.data),
})
}
}
impl CheckDerConstraints for Integer<'_> {
fn check_constraints(any: &Any) -> Result<()> {
check_der_int_constraints(any)
}
}
fn check_der_int_constraints(any: &Any) -> Result<()> {
any.header.assert_primitive()?;
any.header.length.assert_definite()?;
match any.as_bytes() {
[] => Err(Error::DerConstraintFailed(DerConstraint::IntegerEmpty)),
[0] => Ok(()),
// leading zeroes
[0, byte, ..] if *byte < 0x80 => Err(Error::DerConstraintFailed(
DerConstraint::IntegerLeadingZeroes,
)),
// negative integer with non-minimal encoding
[0xff, byte, ..] if *byte >= 0x80 => {
Err(Error::DerConstraintFailed(DerConstraint::IntegerLeadingFF))
}
_ => Ok(()),
}
}
impl DerAutoDerive for Integer<'_> {}
impl Tagged for Integer<'_> {
const TAG: Tag = Tag::Integer;
}
#[cfg(feature = "std")]
impl ToDer for Integer<'_> {
fn to_der_len(&self) -> Result<usize> {
let sz = self.data.len();
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// hmm, a very long integer. anyway:
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(self.data.len()),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&self.data).map_err(Into::into)
}
}
/// Helper macro to declare integers at compile-time
///
/// [`Integer`] stores the encoded representation of the integer, so declaring
/// an integer requires to either use a runtime function or provide the encoded value.
/// This macro simplifies this task by encoding the value.
/// It can be used the following ways:
///
/// - `int!(1234)`: Create a const expression for the corresponding `Integer<'static>`
/// - `int!(raw 1234)`: Return the DER encoded form as a byte array (hex-encoded, big-endian
/// representation from the integer, with leading zeroes removed).
///
/// # Examples
///
/// ```rust
/// use asn1_rs::{int, Integer};
///
/// const INT0: Integer = int!(1234);
/// ```
#[macro_export]
macro_rules! int {
(raw $item:expr) => {
$crate::exports::asn1_rs_impl::encode_int!($item)
};
(rel $item:expr) => {
$crate::exports::asn1_rs_impl::encode_int!(rel $item)
};
($item:expr) => {
$crate::Integer::new(
&$crate::int!(raw $item),
)
};
}
#[cfg(all(test, feature = "std"))]
mod tests {
use crate::{Any, FromDer, Header, Tag, ToDer};
use std::convert::TryInto;
// Vectors from Section 5.7 of:
// https://luca.ntop.org/Teaching/Appunti/asn1.html
pub(crate) const I0_BYTES: &[u8] = &[0x02, 0x01, 0x00];
pub(crate) const I127_BYTES: &[u8] = &[0x02, 0x01, 0x7F];
pub(crate) const I128_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0x80];
pub(crate) const I256_BYTES: &[u8] = &[0x02, 0x02, 0x01, 0x00];
pub(crate) const INEG128_BYTES: &[u8] = &[0x02, 0x01, 0x80];
pub(crate) const INEG129_BYTES: &[u8] = &[0x02, 0x02, 0xFF, 0x7F];
// Additional vectors
pub(crate) const I255_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0xFF];
pub(crate) const I32767_BYTES: &[u8] = &[0x02, 0x02, 0x7F, 0xFF];
pub(crate) const I65535_BYTES: &[u8] = &[0x02, 0x03, 0x00, 0xFF, 0xFF];
pub(crate) const INEG32768_BYTES: &[u8] = &[0x02, 0x02, 0x80, 0x00];
#[test]
fn decode_i8() {
assert_eq!(0, i8::from_der(I0_BYTES).unwrap().1);
assert_eq!(127, i8::from_der(I127_BYTES).unwrap().1);
assert_eq!(-128, i8::from_der(INEG128_BYTES).unwrap().1);
}
#[test]
fn encode_i8() {
assert_eq!(0i8.to_der_vec().unwrap(), I0_BYTES);
assert_eq!(127i8.to_der_vec().unwrap(), I127_BYTES);
assert_eq!((-128i8).to_der_vec().unwrap(), INEG128_BYTES);
}
#[test]
fn decode_i16() {
assert_eq!(0, i16::from_der(I0_BYTES).unwrap().1);
assert_eq!(127, i16::from_der(I127_BYTES).unwrap().1);
assert_eq!(128, i16::from_der(I128_BYTES).unwrap().1);
assert_eq!(255, i16::from_der(I255_BYTES).unwrap().1);
assert_eq!(256, i16::from_der(I256_BYTES).unwrap().1);
assert_eq!(32767, i16::from_der(I32767_BYTES).unwrap().1);
assert_eq!(-128, i16::from_der(INEG128_BYTES).unwrap().1);
assert_eq!(-129, i16::from_der(INEG129_BYTES).unwrap().1);
assert_eq!(-32768, i16::from_der(INEG32768_BYTES).unwrap().1);
}
#[test]
fn encode_i16() {
assert_eq!(0i16.to_der_vec().unwrap(), I0_BYTES);
assert_eq!(127i16.to_der_vec().unwrap(), I127_BYTES);
assert_eq!(128i16.to_der_vec().unwrap(), I128_BYTES);
assert_eq!(255i16.to_der_vec().unwrap(), I255_BYTES);
assert_eq!(256i16.to_der_vec().unwrap(), I256_BYTES);
assert_eq!(32767i16.to_der_vec().unwrap(), I32767_BYTES);
assert_eq!((-128i16).to_der_vec().unwrap(), INEG128_BYTES);
assert_eq!((-129i16).to_der_vec().unwrap(), INEG129_BYTES);
assert_eq!((-32768i16).to_der_vec().unwrap(), INEG32768_BYTES);
}
#[test]
fn decode_u8() {
assert_eq!(0, u8::from_der(I0_BYTES).unwrap().1);
assert_eq!(127, u8::from_der(I127_BYTES).unwrap().1);
assert_eq!(255, u8::from_der(I255_BYTES).unwrap().1);
}
#[test]
fn encode_u8() {
assert_eq!(0u8.to_der_vec().unwrap(), I0_BYTES);
assert_eq!(127u8.to_der_vec().unwrap(), I127_BYTES);
assert_eq!(255u8.to_der_vec().unwrap(), I255_BYTES);
}
#[test]
fn decode_u16() {
assert_eq!(0, u16::from_der(I0_BYTES).unwrap().1);
assert_eq!(127, u16::from_der(I127_BYTES).unwrap().1);
assert_eq!(255, u16::from_der(I255_BYTES).unwrap().1);
assert_eq!(256, u16::from_der(I256_BYTES).unwrap().1);
assert_eq!(32767, u16::from_der(I32767_BYTES).unwrap().1);
assert_eq!(65535, u16::from_der(I65535_BYTES).unwrap().1);
}
#[test]
fn encode_u16() {
assert_eq!(0u16.to_der_vec().unwrap(), I0_BYTES);
assert_eq!(127u16.to_der_vec().unwrap(), I127_BYTES);
assert_eq!(255u16.to_der_vec().unwrap(), I255_BYTES);
assert_eq!(256u16.to_der_vec().unwrap(), I256_BYTES);
assert_eq!(32767u16.to_der_vec().unwrap(), I32767_BYTES);
assert_eq!(65535u16.to_der_vec().unwrap(), I65535_BYTES);
}
/// Integers must be encoded with a minimum number of octets
#[test]
fn reject_non_canonical() {
assert!(i8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
assert!(i16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
assert!(u8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
assert!(u16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err());
}
#[test]
fn declare_int() {
let int = super::int!(1234);
assert_eq!(int.try_into(), Ok(1234));
}
#[test]
fn trim_slice() {
use super::trim_slice;
let h = Header::new_simple(Tag(0));
// no zero nor ff - nothing to remove
let input: &[u8] = &[0x7f, 0xff, 0x00, 0x02];
assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input)));
//
// 0x00
//
// empty - nothing to remove
let input: &[u8] = &[];
assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input)));
// one zero - nothing to remove
let input: &[u8] = &[0];
assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input)));
// all zeroes - keep only one
let input: &[u8] = &[0, 0, 0];
assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input)));
// some zeroes - keep only the non-zero part
let input: &[u8] = &[0, 0, 1];
assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input)));
//
// 0xff
//
// one ff - nothing to remove
let input: &[u8] = &[0xff];
assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input)));
// all ff - keep only one
let input: &[u8] = &[0xff, 0xff, 0xff];
assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input)));
// some ff - keep only the non-zero part
let input: &[u8] = &[0xff, 0xff, 1];
assert_eq!(Ok(&input[1..]), trim_slice(&Any::new(h.clone(), input)));
// some ff and a MSB 1 - keep only the non-zero part
let input: &[u8] = &[0xff, 0xff, 0x80, 1];
assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input)));
}
}

26
vendor/asn1-rs/src/asn1_types/mod.rs vendored Normal file
View File

@@ -0,0 +1,26 @@
mod any;
mod bitstring;
mod boolean;
mod choice;
mod embedded_pdv;
mod end_of_content;
mod enumerated;
mod generalizedtime;
mod integer;
mod null;
mod object_descriptor;
mod octetstring;
mod oid;
mod optional;
mod real;
mod sequence;
mod set;
mod strings;
mod tagged;
mod utctime;
pub use {
any::*, bitstring::*, boolean::*, choice::*, embedded_pdv::*, end_of_content::*, enumerated::*,
generalizedtime::*, integer::*, null::*, object_descriptor::*, octetstring::*, oid::*, real::*,
sequence::*, set::*, strings::*, tagged::*, utctime::*,
};

105
vendor/asn1-rs/src/asn1_types/null.rs vendored Normal file
View File

@@ -0,0 +1,105 @@
use crate::*;
use core::convert::TryFrom;
/// ASN.1 `NULL` type
#[derive(Debug, PartialEq, Eq)]
pub struct Null {}
impl Default for Null {
fn default() -> Self {
Self::new()
}
}
impl Null {
pub const fn new() -> Self {
Null {}
}
}
impl<'a> TryFrom<Any<'a>> for Null {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Null> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Null {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Null> {
any.tag().assert_eq(Self::TAG)?;
if !any.header.length.is_null() {
return Err(Error::InvalidLength);
}
Ok(Null {})
}
}
impl CheckDerConstraints for Null {
fn check_constraints(_any: &Any) -> Result<()> {
Ok(())
}
}
impl DerAutoDerive for Null {}
impl Tagged for Null {
const TAG: Tag = Tag::Null;
}
#[cfg(feature = "std")]
impl ToDer for Null {
fn to_der_len(&self) -> Result<usize> {
Ok(2)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[0x05, 0x00]).map_err(Into::into)
}
fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
Ok(0)
}
}
impl<'a> TryFrom<Any<'a>> for () {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_primitive()?;
if !any.header.length.is_null() {
return Err(Error::InvalidLength);
}
Ok(())
}
}
impl CheckDerConstraints for () {
fn check_constraints(_any: &Any) -> Result<()> {
Ok(())
}
}
impl DerAutoDerive for () {}
impl Tagged for () {
const TAG: Tag = Tag::Null;
}
#[cfg(feature = "std")]
impl ToDer for () {
fn to_der_len(&self) -> Result<usize> {
Ok(2)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[0x05, 0x00]).map_err(Into::into)
}
fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
Ok(0)
}
}

View File

@@ -0,0 +1,18 @@
use crate::{asn1_string, TestValidCharset};
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::String;
// X.680 section 44.3
// ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString
asn1_string!(ObjectDescriptor);
impl TestValidCharset for ObjectDescriptor<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
if !i.iter().all(u8::is_ascii) {
return Err(Error::StringInvalidCharset);
}
Ok(())
}
}

View File

@@ -0,0 +1,157 @@
use crate::*;
use alloc::borrow::Cow;
use core::convert::TryFrom;
/// ASN.1 `OCTETSTRING` type
#[derive(Debug, PartialEq, Eq)]
pub struct OctetString<'a> {
data: Cow<'a, [u8]>,
}
impl<'a> OctetString<'a> {
pub const fn new(s: &'a [u8]) -> Self {
OctetString {
data: Cow::Borrowed(s),
}
}
/// Get the bytes representation of the *content*
pub fn as_cow(&'a self) -> &'a Cow<'a, [u8]> {
&self.data
}
/// Get the bytes representation of the *content*
pub fn into_cow(self) -> Cow<'a, [u8]> {
self.data
}
}
impl AsRef<[u8]> for OctetString<'_> {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
impl<'a> From<&'a [u8]> for OctetString<'a> {
fn from(b: &'a [u8]) -> Self {
OctetString {
data: Cow::Borrowed(b),
}
}
}
impl<'a> TryFrom<Any<'a>> for OctetString<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<OctetString<'a>> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for OctetString<'a> {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<OctetString<'a>> {
any.tag().assert_eq(Self::TAG)?;
Ok(OctetString {
data: Cow::Borrowed(any.data),
})
}
}
impl CheckDerConstraints for OctetString<'_> {
fn check_constraints(any: &Any) -> Result<()> {
// X.690 section 10.2
any.header.assert_primitive()?;
Ok(())
}
}
impl DerAutoDerive for OctetString<'_> {}
impl Tagged for OctetString<'_> {
const TAG: Tag = Tag::OctetString;
}
#[cfg(feature = "std")]
impl ToDer for OctetString<'_> {
fn to_der_len(&self) -> Result<usize> {
let sz = self.data.len();
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(self.data.len()),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&self.data).map_err(Into::into)
}
}
impl<'a> TryFrom<Any<'a>> for &'a [u8] {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<&'a [u8]> {
any.tag().assert_eq(Self::TAG)?;
let s = OctetString::try_from(any)?;
match s.data {
Cow::Borrowed(s) => Ok(s),
Cow::Owned(_) => Err(Error::LifetimeError),
}
}
}
impl CheckDerConstraints for &'_ [u8] {
fn check_constraints(any: &Any) -> Result<()> {
// X.690 section 10.2
any.header.assert_primitive()?;
Ok(())
}
}
impl DerAutoDerive for &'_ [u8] {}
impl Tagged for &'_ [u8] {
const TAG: Tag = Tag::OctetString;
}
#[cfg(feature = "std")]
impl ToDer for &'_ [u8] {
fn to_der_len(&self) -> Result<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(self.len()),
);
Ok(header.to_der_len()? + self.len())
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(self.len()),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(self).map_err(Into::into)
}
}

528
vendor/asn1-rs/src/asn1_types/oid.rs vendored Normal file
View File

@@ -0,0 +1,528 @@
use crate::*;
use alloc::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::format;
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::{
convert::TryFrom, fmt, iter::FusedIterator, marker::PhantomData, ops::Shl, str::FromStr,
};
use displaydoc::Display;
use num_traits::Num;
use thiserror::Error;
/// An error for OID parsing functions.
#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Error)]
pub enum OidParseError {
/// Encoded data length too short
TooShort,
/** Signalizes that the first or second component is too large.
* The first must be within the range 0 to 6 (inclusive).
* The second component must be less than 40.
*/
FirstComponentsTooLarge,
/// a
ParseIntError,
}
/// Object ID (OID) representation which can be relative or non-relative.
///
/// An example for an OID in string representation is `"1.2.840.113549.1.1.5"`.
///
/// For non-relative OIDs restrictions apply to the first two components.
///
/// This library contains a procedural macro `oid` which can be used to
/// create oids. For example `oid!(1.2.44.233)` or `oid!(rel 44.233)`
/// for relative oids. See the [module documentation](index.html) for more information.
#[derive(Hash, PartialEq, Eq, Clone)]
pub struct Oid<'a> {
asn1: Cow<'a, [u8]>,
relative: bool,
}
impl<'a> TryFrom<Any<'a>> for Oid<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Oid<'a> {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Self> {
// check that any.data.last().unwrap() >> 7 == 0u8
let asn1 = Cow::Borrowed(any.data);
Ok(Oid::new(asn1))
}
}
impl CheckDerConstraints for Oid<'_> {
fn check_constraints(any: &Any) -> Result<()> {
any.header.assert_primitive()?;
any.header.length.assert_definite()?;
Ok(())
}
}
impl DerAutoDerive for Oid<'_> {}
impl Tagged for Oid<'_> {
const TAG: Tag = Tag::Oid;
}
#[cfg(feature = "std")]
impl ToDer for Oid<'_> {
fn to_der_len(&self) -> Result<usize> {
// OID/REL-OID tag will not change header size, so we don't care here
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(self.asn1.len()),
);
Ok(header.to_der_len()? + self.asn1.len())
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let tag = if self.relative {
Tag::RelativeOid
} else {
Tag::Oid
};
let header = Header::new(
Class::Universal,
false,
tag,
Length::Definite(self.asn1.len()),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&self.asn1).map_err(Into::into)
}
}
fn encode_relative(ids: &'_ [u64]) -> impl Iterator<Item = u8> + '_ {
ids.iter().flat_map(|id| {
let bit_count = 64 - id.leading_zeros();
let octets_needed = ((bit_count + 6) / 7).max(1);
(0..octets_needed).map(move |i| {
let flag = if i == octets_needed - 1 { 0 } else { 1 << 7 };
((id >> (7 * (octets_needed - 1 - i))) & 0b111_1111) as u8 | flag
})
})
}
impl<'a> Oid<'a> {
/// Create an OID from the ASN.1 DER encoded form. See the [module documentation](index.html)
/// for other ways to create oids.
pub const fn new(asn1: Cow<'a, [u8]>) -> Oid<'a> {
Oid {
asn1,
relative: false,
}
}
/// Create a relative OID from the ASN.1 DER encoded form. See the [module documentation](index.html)
/// for other ways to create relative oids.
pub const fn new_relative(asn1: Cow<'a, [u8]>) -> Oid<'a> {
Oid {
asn1,
relative: true,
}
}
/// Build an OID from an array of object identifier components.
/// This method allocates memory on the heap.
pub fn from(s: &[u64]) -> core::result::Result<Oid<'static>, OidParseError> {
if s.len() < 2 {
if s.len() == 1 && s[0] == 0 {
return Ok(Oid {
asn1: Cow::Borrowed(&[0]),
relative: false,
});
}
return Err(OidParseError::TooShort);
}
if s[0] >= 7 || s[1] >= 40 {
return Err(OidParseError::FirstComponentsTooLarge);
}
let asn1_encoded: Vec<u8> = [(s[0] * 40 + s[1]) as u8]
.iter()
.copied()
.chain(encode_relative(&s[2..]))
.collect();
Ok(Oid {
asn1: Cow::from(asn1_encoded),
relative: false,
})
}
/// Build a relative OID from an array of object identifier components.
pub fn from_relative(s: &[u64]) -> core::result::Result<Oid<'static>, OidParseError> {
if s.is_empty() {
return Err(OidParseError::TooShort);
}
let asn1_encoded: Vec<u8> = encode_relative(s).collect();
Ok(Oid {
asn1: Cow::from(asn1_encoded),
relative: true,
})
}
/// Create a deep copy of the oid.
///
/// This method allocates data on the heap. The returned oid
/// can be used without keeping the ASN.1 representation around.
///
/// Cloning the returned oid does again allocate data.
pub fn to_owned(&self) -> Oid<'static> {
Oid {
asn1: Cow::from(self.asn1.to_vec()),
relative: self.relative,
}
}
/// Get the encoded oid without the header.
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.asn1.as_ref()
}
/// Get the encoded oid without the header.
#[deprecated(since = "0.2.0", note = "Use `as_bytes` instead")]
#[inline]
pub fn bytes(&self) -> &[u8] {
self.as_bytes()
}
/// Get the bytes representation of the encoded oid
pub fn into_cow(self) -> Cow<'a, [u8]> {
self.asn1
}
/// Convert the OID to a string representation.
/// The string contains the IDs separated by dots, for ex: "1.2.840.113549.1.1.5"
#[cfg(feature = "bigint")]
pub fn to_id_string(&self) -> String {
let ints: Vec<String> = self.iter_bigint().map(|i| i.to_string()).collect();
ints.join(".")
}
#[cfg(not(feature = "bigint"))]
/// Convert the OID to a string representation.
///
/// If every arc fits into a u64 a string like "1.2.840.113549.1.1.5"
/// is returned, otherwise a hex representation.
///
/// See also the "bigint" feature of this crate.
pub fn to_id_string(&self) -> String {
if let Some(arcs) = self.iter() {
let ints: Vec<String> = arcs.map(|i| i.to_string()).collect();
ints.join(".")
} else {
let mut ret = String::with_capacity(self.asn1.len() * 3);
for (i, o) in self.asn1.iter().enumerate() {
ret.push_str(&format!("{:02x}", o));
if i + 1 != self.asn1.len() {
ret.push(' ');
}
}
ret
}
}
/// Return an iterator over the sub-identifiers (arcs).
#[cfg(feature = "bigint")]
pub fn iter_bigint(&'_ self) -> impl FusedIterator<Item = BigUint> + ExactSizeIterator + '_ {
SubIdentifierIterator {
oid: self,
pos: 0,
first: false,
n: PhantomData,
}
}
/// Return an iterator over the sub-identifiers (arcs).
/// Returns `None` if at least one arc does not fit into `u64`.
pub fn iter(&'_ self) -> Option<impl FusedIterator<Item = u64> + ExactSizeIterator + '_> {
// Check that every arc fits into u64
let bytes = if self.relative {
&self.asn1
} else if self.asn1.is_empty() {
&[]
} else {
&self.asn1[1..]
};
let max_bits = bytes
.iter()
.fold((0usize, 0usize), |(max, cur), c| {
let is_end = (c >> 7) == 0u8;
if is_end {
(max.max(cur + 7), 0)
} else {
(max, cur + 7)
}
})
.0;
if max_bits > 64 {
return None;
}
Some(SubIdentifierIterator {
oid: self,
pos: 0,
first: false,
n: PhantomData,
})
}
pub fn from_ber_relative(bytes: &'a [u8]) -> ParseResult<'a, Self> {
let (rem, any) = Any::from_ber(bytes)?;
any.header.assert_primitive()?;
any.header.assert_tag(Tag::RelativeOid)?;
let asn1 = Cow::Borrowed(any.data);
Ok((rem, Oid::new_relative(asn1)))
}
pub fn from_der_relative(bytes: &'a [u8]) -> ParseResult<'a, Self> {
let (rem, any) = Any::from_der(bytes)?;
any.header.assert_tag(Tag::RelativeOid)?;
Self::check_constraints(&any)?;
let asn1 = Cow::Borrowed(any.data);
Ok((rem, Oid::new_relative(asn1)))
}
/// Returns true if `needle` is a prefix of the OID.
pub fn starts_with(&self, needle: &Oid) -> bool {
self.asn1.len() >= needle.asn1.len() && self.asn1.starts_with(needle.as_bytes())
}
}
trait Repr: Num + Shl<usize, Output = Self> + From<u8> {}
impl<N> Repr for N where N: Num + Shl<usize, Output = N> + From<u8> {}
struct SubIdentifierIterator<'a, N: Repr> {
oid: &'a Oid<'a>,
pos: usize,
first: bool,
n: PhantomData<&'a N>,
}
impl<N: Repr> Iterator for SubIdentifierIterator<'_, N> {
type Item = N;
fn next(&mut self) -> Option<Self::Item> {
use num_traits::identities::Zero;
if self.pos == self.oid.asn1.len() {
return None;
}
if !self.oid.relative {
if !self.first {
debug_assert!(self.pos == 0);
self.first = true;
return Some((self.oid.asn1[0] / 40).into());
} else if self.pos == 0 {
self.pos += 1;
if self.oid.asn1[0] == 0 && self.oid.asn1.len() == 1 {
return None;
}
return Some((self.oid.asn1[0] % 40).into());
}
}
// decode objet sub-identifier according to the asn.1 standard
let mut res = <N as Zero>::zero();
for o in self.oid.asn1[self.pos..].iter() {
self.pos += 1;
res = (res << 7) + (o & 0b111_1111).into();
let flag = o >> 7;
if flag == 0u8 {
break;
}
}
Some(res)
}
}
impl<N: Repr> FusedIterator for SubIdentifierIterator<'_, N> {}
impl<N: Repr> ExactSizeIterator for SubIdentifierIterator<'_, N> {
fn len(&self) -> usize {
if self.oid.relative {
self.oid.asn1.iter().filter(|o| (*o >> 7) == 0u8).count()
} else if self.oid.asn1.is_empty() {
0
} else if self.oid.asn1.len() == 1 {
if self.oid.asn1[0] == 0 {
1
} else {
2
}
} else {
2 + self.oid.asn1[2..]
.iter()
.filter(|o| (*o >> 7) == 0u8)
.count()
}
}
}
impl fmt::Display for Oid<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.relative {
f.write_str("rel. ")?;
}
f.write_str(&self.to_id_string())
}
}
impl fmt::Debug for Oid<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("OID(")?;
<Oid as fmt::Display>::fmt(self, f)?;
f.write_str(")")
}
}
impl FromStr for Oid<'_> {
type Err = OidParseError;
fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
let v: core::result::Result<Vec<_>, _> = s.split('.').map(|c| c.parse::<u64>()).collect();
v.map_err(|_| OidParseError::ParseIntError)
.and_then(|v| Oid::from(&v))
}
}
/// Helper macro to declare integers at compile-time
///
/// Since the DER encoded oids are not very readable we provide a
/// procedural macro `oid!`. The macro can be used the following ways:
///
/// - `oid!(1.4.42.23)`: Create a const expression for the corresponding `Oid<'static>`
/// - `oid!(rel 42.23)`: Create a const expression for the corresponding relative `Oid<'static>`
/// - `oid!(raw 1.4.42.23)`/`oid!(raw rel 42.23)`: Obtain the DER encoded form as a byte array.
///
/// # Comparing oids
///
/// Comparing a parsed oid to a static oid is probably the most common
/// thing done with oids in your code. The `oid!` macro can be used in expression positions for
/// this purpose. For example
/// ```
/// use asn1_rs::{oid, Oid};
///
/// # let some_oid: Oid<'static> = oid!(1.2.456);
/// const SOME_STATIC_OID: Oid<'static> = oid!(1.2.456);
/// assert_eq!(some_oid, SOME_STATIC_OID)
/// ```
/// To get a relative Oid use `oid!(rel 1.2)`.
///
/// Because of limitations for procedural macros ([rust issue](https://github.com/rust-lang/rust/issues/54727))
/// and constants used in patterns ([rust issue](https://github.com/rust-lang/rust/issues/31434))
/// the `oid` macro can not directly be used in patterns, also not through constants.
/// You can do this, though:
/// ```
/// # use asn1_rs::{oid, Oid};
/// # let some_oid: Oid<'static> = oid!(1.2.456);
/// const SOME_OID: Oid<'static> = oid!(1.2.456);
/// if some_oid == SOME_OID || some_oid == oid!(1.2.456) {
/// println!("match");
/// }
///
/// // Alternatively, compare the DER encoded form directly:
/// const SOME_OID_RAW: &[u8] = &oid!(raw 1.2.456);
/// match some_oid.as_bytes() {
/// SOME_OID_RAW => println!("match"),
/// _ => panic!("no match"),
/// }
/// ```
/// *Attention*, be aware that the latter version might not handle the case of a relative oid correctly. An
/// extra check might be necessary.
#[macro_export]
macro_rules! oid {
(raw $( $item:literal ).*) => {
$crate::exports::asn1_rs_impl::encode_oid!( $( $item ).* )
};
(raw $items:expr) => {
$crate::exports::asn1_rs_impl::encode_oid!($items)
};
(rel $($item:literal ).*) => {
$crate::Oid::new_relative($crate::exports::borrow::Cow::Borrowed(
&$crate::exports::asn1_rs_impl::encode_oid!(rel $( $item ).*),
))
};
($($item:literal ).*) => {
$crate::Oid::new($crate::exports::borrow::Cow::Borrowed(
&$crate::oid!(raw $( $item ).*),
))
};
}
#[cfg(all(test, feature = "std"))]
mod tests {
use crate::{FromDer, Oid, ToDer};
use hex_literal::hex;
#[test]
fn declare_oid() {
let oid = super::oid! {1.2.840.113549.1};
assert_eq!(oid.to_string(), "1.2.840.113549.1");
}
const OID_RSA_ENCRYPTION: &[u8] = &oid! {raw 1.2.840.113549.1.1.1};
const OID_EC_PUBLIC_KEY: &[u8] = &oid! {raw 1.2.840.10045.2.1};
#[allow(clippy::match_like_matches_macro)]
fn compare_oid(oid: &Oid) -> bool {
match oid.as_bytes() {
OID_RSA_ENCRYPTION => true,
OID_EC_PUBLIC_KEY => true,
_ => false,
}
}
#[test]
fn test_compare_oid() {
let oid = Oid::from(&[1, 2, 840, 113_549, 1, 1, 1]).unwrap();
assert_eq!(oid, oid! {1.2.840.113549.1.1.1});
let oid = Oid::from(&[1, 2, 840, 113_549, 1, 1, 1]).unwrap();
assert!(compare_oid(&oid));
}
#[test]
fn oid_to_der() {
let oid = super::oid! {1.2.840.113549.1};
assert_eq!(oid.to_der_len(), Ok(9));
let v = oid.to_der_vec().expect("could not serialize");
assert_eq!(&v, &hex! {"06 07 2a 86 48 86 f7 0d 01"});
let (_, oid2) = Oid::from_der(&v).expect("could not re-parse");
assert_eq!(&oid, &oid2);
}
#[test]
fn oid_starts_with() {
const OID_RSA_ENCRYPTION: Oid = oid! {1.2.840.113549.1.1.1};
const OID_EC_PUBLIC_KEY: Oid = oid! {1.2.840.10045.2.1};
let oid = super::oid! {1.2.840.113549.1};
assert!(OID_RSA_ENCRYPTION.starts_with(&oid));
assert!(!OID_EC_PUBLIC_KEY.starts_with(&oid));
}
#[test]
fn oid_macro_parameters() {
// Code inspired from https://github.com/rusticata/der-parser/issues/68
macro_rules! foo {
($a:literal $b:literal $c:literal) => {
super::oid!($a.$b.$c)
};
}
let oid = foo!(1 2 3);
assert_eq!(oid, oid! {1.2.3});
}
}

View File

@@ -0,0 +1,123 @@
use crate::*;
// note: we cannot implement `TryFrom<Any<'a>> with generic errors for Option<T>`,
// because this conflicts with generic `T` implementation in
// `src/traits.rs`, since `T` always satisfies `T: Into<Option<T>>`
//
// for the same reason, we cannot use a generic error type here
impl<'a, T> FromBer<'a> for Option<T>
where
T: FromBer<'a>,
T: Tagged,
{
fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> {
if bytes.is_empty() {
return Ok((bytes, None));
}
if let Ok((_, header)) = Header::from_ber(bytes) {
if T::TAG != header.tag {
// not the expected tag, early return
return Ok((bytes, None));
}
}
match T::from_ber(bytes) {
Ok((rem, t)) => Ok((rem, Some(t))),
Err(e) => Err(e),
}
}
}
impl<'a> FromBer<'a> for Option<Any<'a>> {
fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> {
if bytes.is_empty() {
return Ok((bytes, None));
}
match Any::from_ber(bytes) {
Ok((rem, t)) => Ok((rem, Some(t))),
Err(e) => Err(e),
}
}
}
impl<'a, T> FromDer<'a> for Option<T>
where
T: FromDer<'a>,
T: Tagged,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> {
if bytes.is_empty() {
return Ok((bytes, None));
}
if let Ok((_, header)) = Header::from_der(bytes) {
if T::TAG != header.tag {
// not the expected tag, early return
return Ok((bytes, None));
}
}
match T::from_der(bytes) {
Ok((rem, t)) => Ok((rem, Some(t))),
Err(e) => Err(e),
}
}
}
impl<'a> FromDer<'a> for Option<Any<'a>> {
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> {
if bytes.is_empty() {
return Ok((bytes, None));
}
match Any::from_der(bytes) {
Ok((rem, t)) => Ok((rem, Some(t))),
Err(e) => Err(e),
}
}
}
impl<T> CheckDerConstraints for Option<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
T::check_constraints(any)
}
}
impl<T> DynTagged for Option<T>
where
T: DynTagged,
{
fn tag(&self) -> Tag {
if self.is_some() {
self.tag()
} else {
Tag(0)
}
}
}
#[cfg(feature = "std")]
impl<T> ToDer for Option<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
match self {
None => Ok(0),
Some(t) => t.to_der_len(),
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
match self {
None => Ok(0),
Some(t) => t.write_der_header(writer),
}
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
match self {
None => Ok(0),
Some(t) => t.write_der_content(writer),
}
}
}

466
vendor/asn1-rs/src/asn1_types/real.rs vendored Normal file
View File

@@ -0,0 +1,466 @@
use crate::*;
use alloc::format;
use core::convert::TryFrom;
mod f32;
mod f64;
/// ASN.1 `REAL` type
///
/// # Limitations
///
/// When encoding binary values, only base 2 is supported
#[derive(Debug, PartialEq)]
pub enum Real {
/// Non-special values
Binary {
mantissa: f64,
base: u32,
exponent: i32,
enc_base: u8,
},
/// Infinity (∞).
Infinity,
/// Negative infinity (−∞).
NegInfinity,
/// Zero
Zero,
}
impl Real {
/// Create a new `REAL` from the `f64` value.
pub fn new(f: f64) -> Self {
if f.is_infinite() {
if f.is_sign_positive() {
Self::Infinity
} else {
Self::NegInfinity
}
} else if f.abs() == 0.0 {
Self::Zero
} else {
let mut e = 0;
let mut f = f;
while f.fract() != 0.0 {
f *= 10.0_f64;
e -= 1;
}
Real::Binary {
mantissa: f,
base: 10,
exponent: e,
enc_base: 10,
}
.normalize_base10()
}
}
pub const fn with_enc_base(self, enc_base: u8) -> Self {
match self {
Real::Binary {
mantissa,
base,
exponent,
..
} => Real::Binary {
mantissa,
base,
exponent,
enc_base,
},
e => e,
}
}
fn normalize_base10(self) -> Self {
match self {
Real::Binary {
mantissa,
base: 10,
exponent,
enc_base: _enc_base,
} => {
let mut m = mantissa;
let mut e = exponent;
while m.abs() > f64::EPSILON && m.rem_euclid(10.0).abs() < f64::EPSILON {
m /= 10.0;
e += 1;
}
Real::Binary {
mantissa: m,
base: 10,
exponent: e,
enc_base: _enc_base,
}
}
_ => self,
}
}
/// Create a new binary `REAL`
#[inline]
pub const fn binary(mantissa: f64, base: u32, exponent: i32) -> Self {
Self::Binary {
mantissa,
base,
exponent,
enc_base: 2,
}
}
/// Returns `true` if this value is positive infinity or negative infinity, and
/// `false` otherwise.
#[inline]
pub fn is_infinite(&self) -> bool {
matches!(self, Real::Infinity | Real::NegInfinity)
}
/// Returns `true` if this number is not infinite.
#[inline]
pub fn is_finite(&self) -> bool {
matches!(self, Real::Zero | Real::Binary { .. })
}
/// Returns the 'f64' value of this `REAL`.
///
/// Returned value is a float, and may be infinite.
pub fn f64(&self) -> f64 {
match self {
Real::Binary {
mantissa,
base,
exponent,
..
} => {
let f = mantissa;
let exp = (*base as f64).powi(*exponent);
f * exp
}
Real::Zero => 0.0_f64,
Real::Infinity => f64::INFINITY,
Real::NegInfinity => f64::NEG_INFINITY,
}
}
/// Returns the 'f32' value of this `REAL`.
///
/// This functions casts the result of [`Real::f64`] to a `f32`, and loses precision.
pub fn f32(&self) -> f32 {
self.f64() as f32
}
}
impl<'a> TryFrom<Any<'a>> for Real {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Real {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Self> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_primitive()?;
let data = &any.data;
if data.is_empty() {
return Ok(Real::Zero);
}
// code inspired from pyasn1
let first = data[0];
let rem = &data[1..];
if first & 0x80 != 0 {
// binary encoding (X.690 section 8.5.6)
// format of exponent
let (n, rem) = match first & 0x03 {
4 => {
let (b, rem) = rem
.split_first()
.ok_or_else(|| Error::Incomplete(Needed::new(1)))?;
(*b as usize, rem)
}
b => (b as usize + 1, rem),
};
if n >= rem.len() {
return Err(any.tag().invalid_value("Invalid float value(exponent)"));
}
// n cannot be 0 (see the +1 above)
let (eo, rem) = rem.split_at(n);
// so 'eo' cannot be empty
let mut e = if eo[0] & 0x80 != 0 { -1 } else { 0 };
// safety check: 'eo' length must be <= container type for 'e'
if eo.len() > 4 {
return Err(any.tag().invalid_value("Exponent too large (REAL)"));
}
for b in eo {
e = (e << 8) | (*b as i32);
}
// base bits
let b = (first >> 4) & 0x03;
let _enc_base = match b {
0 => 2,
1 => 8,
2 => 16,
_ => return Err(any.tag().invalid_value("Illegal REAL encoding base")),
};
let e = match b {
// base 2
0 => e,
// base 8
1 => e * 3,
// base 16
2 => e * 4,
_ => return Err(any.tag().invalid_value("Illegal REAL base")),
};
if rem.len() > 8 {
return Err(any.tag().invalid_value("Mantissa too large (REAL)"));
}
let mut p = 0;
for b in rem {
p = (p << 8) | (*b as i64);
}
// sign bit
let p = if first & 0x40 != 0 { -p } else { p };
// scale bits
let sf = (first >> 2) & 0x03;
let p = match sf {
0 => p as f64,
sf => {
// 2^sf: cannot overflow, sf is between 0 and 3
let scale = 2_f64.powi(sf as _);
(p as f64) * scale
}
};
Ok(Real::Binary {
mantissa: p,
base: 2,
exponent: e,
enc_base: _enc_base,
})
} else if first & 0x40 != 0 {
// special real value (X.690 section 8.5.8)
// there shall be only one contents octet,
if any.header.length != Length::Definite(1) {
return Err(Error::InvalidLength);
}
// with values as follows
match first {
0x40 => Ok(Real::Infinity),
0x41 => Ok(Real::NegInfinity),
_ => Err(any.tag().invalid_value("Invalid float special value")),
}
} else {
// decimal encoding (X.690 section 8.5.7)
let s = alloc::str::from_utf8(rem)?;
match first & 0x03 {
0x1 => {
// NR1
match s.parse::<u32>() {
Err(_) => Err(any.tag().invalid_value("Invalid float string encoding")),
Ok(v) => Ok(Real::new(v.into())),
}
}
0x2 /* NR2 */ | 0x3 /* NR3 */=> {
match s.parse::<f64>() {
Err(_) => Err(any.tag().invalid_value("Invalid float string encoding")),
Ok(v) => Ok(Real::new(v)),
}
}
c => {
Err(any.tag().invalid_value(&format!("Invalid NR ({})", c)))
}
}
}
}
}
impl CheckDerConstraints for Real {
fn check_constraints(any: &Any) -> Result<()> {
any.header.assert_primitive()?;
any.header.length.assert_definite()?;
// XXX more checks
Ok(())
}
}
impl DerAutoDerive for Real {}
impl Tagged for Real {
const TAG: Tag = Tag::RealType;
}
#[cfg(feature = "std")]
impl ToDer for Real {
fn to_der_len(&self) -> Result<usize> {
match self {
Real::Zero => Ok(0),
Real::Infinity | Real::NegInfinity => Ok(1),
Real::Binary { .. } => {
let mut sink = std::io::sink();
let n = self
.write_der_content(&mut sink)
.map_err(|_| Self::TAG.invalid_value("Serialization of REAL failed"))?;
Ok(n)
}
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(self.to_der_len()?),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
match self {
Real::Zero => Ok(0),
Real::Infinity => writer.write(&[0x40]).map_err(Into::into),
Real::NegInfinity => writer.write(&[0x41]).map_err(Into::into),
Real::Binary {
mantissa,
base,
exponent,
enc_base: _enc_base,
} => {
if *base == 10 {
// using character form
let sign = if *exponent == 0 { "+" } else { "" };
let s = format!("\x03{}E{}{}", mantissa, sign, exponent);
return writer.write(s.as_bytes()).map_err(Into::into);
}
if *base != 2 {
return Err(Self::TAG.invalid_value("Invalid base for REAL").into());
}
let mut first: u8 = 0x80;
// choose encoding base
let enc_base = *_enc_base;
let (ms, mut m, enc_base, mut e) =
drop_floating_point(*mantissa, enc_base, *exponent);
assert!(m != 0);
if ms < 0 {
first |= 0x40
};
// exponent & mantissa normalization
match enc_base {
2 => {
while m & 0x1 == 0 {
m >>= 1;
e += 1;
}
}
8 => {
while m & 0x7 == 0 {
m >>= 3;
e += 1;
}
first |= 0x10;
}
_ /* 16 */ => {
while m & 0xf == 0 {
m >>= 4;
e += 1;
}
first |= 0x20;
}
}
// scale factor
// XXX in DER, sf is always 0 (11.3.1)
let mut sf = 0;
while m & 0x1 == 0 && sf < 4 {
m >>= 1;
sf += 1;
}
first |= sf << 2;
// exponent length and bytes
let len_e = match e.abs() {
0..=0xff => 1,
0x100..=0xffff => 2,
0x1_0000..=0xff_ffff => 3,
// e is an `i32` so it can't be longer than 4 bytes
// use 4, so `first` is ORed with 3
_ => 4,
};
first |= (len_e - 1) & 0x3;
// write first byte
let mut n = writer.write(&[first])?;
// write exponent
// special case: number of bytes from exponent is > 3 and cannot fit in 2 bits
#[allow(clippy::identity_op)]
if len_e == 4 {
let b = len_e & 0xff;
n += writer.write(&[b])?;
}
// we only need to write e.len() bytes
let bytes = e.to_be_bytes();
n += writer.write(&bytes[(4 - len_e) as usize..])?;
// write mantissa
let bytes = m.to_be_bytes();
let mut idx = 0;
for &b in bytes.iter() {
if b != 0 {
break;
}
idx += 1;
}
n += writer.write(&bytes[idx..])?;
Ok(n)
}
}
}
}
impl From<f32> for Real {
fn from(f: f32) -> Self {
Real::new(f.into())
}
}
impl From<f64> for Real {
fn from(f: f64) -> Self {
Real::new(f)
}
}
impl From<Real> for f32 {
fn from(r: Real) -> Self {
r.f32()
}
}
impl From<Real> for f64 {
fn from(r: Real) -> Self {
r.f64()
}
}
#[cfg(feature = "std")]
fn drop_floating_point(m: f64, b: u8, e: i32) -> (i8, u64, u8, i32) {
let ms = if m.is_sign_positive() { 1 } else { -1 };
let es = if e.is_positive() { 1 } else { -1 };
let mut m = m.abs();
let mut e = e;
//
if b == 8 {
m *= 2_f64.powi((e.abs() / 3) * es);
e = (e.abs() / 3) * es;
} else if b == 16 {
m *= 2_f64.powi((e.abs() / 4) * es);
e = (e.abs() / 4) * es;
}
//
while m.abs() > f64::EPSILON {
if m.fract() != 0.0 {
m *= b as f64;
e -= 1;
} else {
break;
}
}
(ms, m as u64, b, e)
}

View File

@@ -0,0 +1,27 @@
use crate::{Any, CheckDerConstraints, DerAutoDerive, Error, Real, Result, Tag, Tagged};
use core::convert::{TryFrom, TryInto};
impl<'a> TryFrom<Any<'a>> for f32 {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<f32> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_primitive()?;
let real: Real = any.try_into()?;
Ok(real.f32())
}
}
impl CheckDerConstraints for f32 {
fn check_constraints(any: &Any) -> Result<()> {
any.header.assert_primitive()?;
any.header.length.assert_definite()?;
Ok(())
}
}
impl DerAutoDerive for f32 {}
impl Tagged for f32 {
const TAG: Tag = Tag::RealType;
}

View File

@@ -0,0 +1,27 @@
use crate::{Any, CheckDerConstraints, DerAutoDerive, Error, Real, Result, Tag, Tagged};
use core::convert::{TryFrom, TryInto};
impl<'a> TryFrom<Any<'a>> for f64 {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<f64> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_primitive()?;
let real: Real = any.try_into()?;
Ok(real.f64())
}
}
impl CheckDerConstraints for f64 {
fn check_constraints(any: &Any) -> Result<()> {
any.header.assert_primitive()?;
any.header.length.assert_definite()?;
Ok(())
}
}
impl DerAutoDerive for f64 {}
impl Tagged for f64 {
const TAG: Tag = Tag::RealType;
}

View File

@@ -0,0 +1,400 @@
use crate::*;
use alloc::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::convert::TryFrom;
mod iterator;
mod sequence_of;
mod vec;
pub use iterator::*;
pub use sequence_of::*;
/// The `SEQUENCE` object is an ordered list of heteregeneous types.
///
/// Sequences can usually be of 2 types:
/// - a list of different objects (`SEQUENCE`, usually parsed as a `struct`)
/// - a list of similar objects (`SEQUENCE OF`, usually parsed as a `Vec<T>`)
///
/// The current object covers the former. For the latter, see the [`SequenceOf`] documentation.
///
/// The `Sequence` object contains the (*unparsed*) encoded representation of its content. It provides
/// methods to parse and iterate contained objects, or convert the sequence to other types.
///
/// # Building a Sequence
///
/// To build a DER sequence:
/// - if the sequence is composed of objects of the same type, the [`Sequence::from_iter_to_der`] method can be used
/// - otherwise, the [`ToDer`] trait can be used to create content incrementally
///
#[cfg_attr(feature = "std", doc = r#"```"#)]
#[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)]
/// use asn1_rs::{Integer, Sequence, SerializeResult, ToDer};
///
/// fn build_seq<'a>() -> SerializeResult<Sequence<'a>> {
/// let mut v = Vec::new();
/// // add an Integer object (construct type):
/// let i = Integer::from_u32(4);
/// let _ = i.write_der(&mut v)?;
/// // some primitive objects also implement `ToDer`. A string will be mapped as `Utf8String`:
/// let _ = "abcd".write_der(&mut v)?;
/// // return the sequence built from the DER content
/// Ok(Sequence::new(v.into()))
/// }
///
/// let seq = build_seq().unwrap();
///
/// ```
///
/// # Examples
///
#[cfg_attr(feature = "std", doc = r#"```"#)]
#[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)]
/// use asn1_rs::{Error, Sequence};
///
/// // build sequence
/// let it = [2, 3, 4].iter();
/// let seq = Sequence::from_iter_to_der(it).unwrap();
///
/// // `seq` now contains the serialized DER representation of the array
///
/// // iterate objects
/// let mut sum = 0;
/// for item in seq.der_iter::<u32, Error>() {
/// // item has type `Result<u32>`, since parsing the serialized bytes could fail
/// sum += item.expect("parsing list item failed");
/// }
/// assert_eq!(sum, 9);
///
/// ```
///
/// Note: the above example encodes a `SEQUENCE OF INTEGER` object, the [`SequenceOf`] object could
/// be used to provide a simpler API.
///
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Sequence<'a> {
/// Serialized DER representation of the sequence content
pub content: Cow<'a, [u8]>,
}
impl<'a> Sequence<'a> {
/// Build a sequence, given the provided content
pub const fn new(content: Cow<'a, [u8]>) -> Self {
Sequence { content }
}
/// Consume the sequence and return the content
#[inline]
pub fn into_content(self) -> Cow<'a, [u8]> {
self.content
}
/// Apply the parsing function to the sequence content, consuming the sequence
///
/// Note: this function expects the caller to take ownership of content.
/// In some cases, handling the lifetime of objects is not easy (when keeping only references on
/// data). Other methods are provided (depending on the use case):
/// - [`Sequence::parse`] takes a reference on the sequence data, but does not consume it,
/// - [`Sequence::from_der_and_then`] does the parsing of the sequence and applying the function
/// in one step, ensuring there are only references (and dropping the temporary sequence).
pub fn and_then<U, F, E>(self, op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(Cow<'a, [u8]>) -> ParseResult<'a, U, E>,
{
op(self.content)
}
/// Same as [`Sequence::from_der_and_then`], but using BER encoding (no constraints).
pub fn from_ber_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>,
E: From<Error>,
{
let (rem, seq) = Sequence::from_ber(bytes).map_err(Err::convert)?;
let data = match seq.content {
Cow::Borrowed(b) => b,
// Since 'any' is built from 'bytes', it is borrowed by construction
Cow::Owned(_) => unreachable!(),
};
let (_, res) = op(data)?;
Ok((rem, res))
}
/// Parse a DER sequence and apply the provided parsing function to content
///
/// After parsing, the sequence object and header are discarded.
///
/// ```
/// use asn1_rs::{FromDer, ParseResult, Sequence};
///
/// // Parse a SEQUENCE {
/// // a INTEGER (0..255),
/// // b INTEGER (0..4294967296)
/// // }
/// // and return only `(a,b)
/// fn parser(i: &[u8]) -> ParseResult<(u8, u32)> {
/// Sequence::from_der_and_then(i, |i| {
/// let (i, a) = u8::from_der(i)?;
/// let (i, b) = u32::from_der(i)?;
/// Ok((i, (a, b)))
/// }
/// )
/// }
/// ```
pub fn from_der_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>,
E: From<Error>,
{
let (rem, seq) = Sequence::from_der(bytes).map_err(Err::convert)?;
let data = match seq.content {
Cow::Borrowed(b) => b,
// Since 'any' is built from 'bytes', it is borrowed by construction
Cow::Owned(_) => unreachable!(),
};
let (_, res) = op(data)?;
Ok((rem, res))
}
/// Apply the parsing function to the sequence content (non-consuming version)
pub fn parse<F, T, E>(&'a self, mut f: F) -> ParseResult<'a, T, E>
where
F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>,
{
let input: &[u8] = &self.content;
f(input)
}
/// Apply the parsing function to the sequence content (consuming version)
///
/// Note: to parse and apply a parsing function in one step, use the
/// [`Sequence::from_der_and_then`] method.
///
/// # Limitations
///
/// This function fails if the sequence contains `Owned` data, because the parsing function
/// takes a reference on data (which is dropped).
pub fn parse_into<F, T, E>(self, mut f: F) -> ParseResult<'a, T, E>
where
F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
match self.content {
Cow::Borrowed(b) => f(b),
_ => Err(Err::Error(Error::LifetimeError.into())),
}
}
/// Return an iterator over the sequence content, attempting to decode objects as BER
///
/// This method can be used when all objects from the sequence have the same type.
pub fn ber_iter<T, E>(&'a self) -> SequenceIterator<'a, T, BerParser, E>
where
T: FromBer<'a, E>,
{
SequenceIterator::new(&self.content)
}
/// Return an iterator over the sequence content, attempting to decode objects as DER
///
/// This method can be used when all objects from the sequence have the same type.
pub fn der_iter<T, E>(&'a self) -> SequenceIterator<'a, T, DerParser, E>
where
T: FromDer<'a, E>,
{
SequenceIterator::new(&self.content)
}
/// Attempt to parse the sequence as a `SEQUENCE OF` items (BER), and return the parsed items as a `Vec`.
pub fn ber_sequence_of<T, E>(&'a self) -> Result<Vec<T>, E>
where
T: FromBer<'a, E>,
E: From<Error>,
{
self.ber_iter().collect()
}
/// Attempt to parse the sequence as a `SEQUENCE OF` items (DER), and return the parsed items as a `Vec`.
pub fn der_sequence_of<T, E>(&'a self) -> Result<Vec<T>, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
self.der_iter().collect()
}
/// Attempt to parse the sequence as a `SEQUENCE OF` items (BER) (consuming input),
/// and return the parsed items as a `Vec`.
///
/// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects.
pub fn into_ber_sequence_of<T, U, E>(self) -> Result<Vec<T>, E>
where
for<'b> T: FromBer<'b, E>,
E: From<Error>,
T: ToStatic<Owned = T>,
{
match self.content {
Cow::Borrowed(bytes) => SequenceIterator::<T, BerParser, E>::new(bytes).collect(),
Cow::Owned(data) => {
let v1 = SequenceIterator::<T, BerParser, E>::new(&data)
.collect::<Result<Vec<T>, E>>()?;
let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
Ok(v2)
}
}
}
/// Attempt to parse the sequence as a `SEQUENCE OF` items (DER) (consuming input),
/// and return the parsed items as a `Vec`.
///
/// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects.
pub fn into_der_sequence_of<T, U, E>(self) -> Result<Vec<T>, E>
where
for<'b> T: FromDer<'b, E>,
E: From<Error>,
T: ToStatic<Owned = T>,
{
match self.content {
Cow::Borrowed(bytes) => SequenceIterator::<T, DerParser, E>::new(bytes).collect(),
Cow::Owned(data) => {
let v1 = SequenceIterator::<T, DerParser, E>::new(&data)
.collect::<Result<Vec<T>, E>>()?;
let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
Ok(v2)
}
}
}
pub fn into_der_sequence_of_ref<T, E>(self) -> Result<Vec<T>, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
match self.content {
Cow::Borrowed(bytes) => SequenceIterator::<T, DerParser, E>::new(bytes).collect(),
Cow::Owned(_) => Err(Error::LifetimeError.into()),
}
}
}
impl ToStatic for Sequence<'_> {
type Owned = Sequence<'static>;
fn to_static(&self) -> Self::Owned {
Sequence {
content: Cow::Owned(self.content.to_vec()),
}
}
}
impl<T, U> ToStatic for Vec<T>
where
T: ToStatic<Owned = U>,
U: 'static,
{
type Owned = Vec<U>;
fn to_static(&self) -> Self::Owned {
self.iter().map(|t| t.to_static()).collect()
}
}
impl AsRef<[u8]> for Sequence<'_> {
fn as_ref(&self) -> &[u8] {
&self.content
}
}
impl<'a> TryFrom<Any<'a>> for Sequence<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Sequence<'a>> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Sequence<'a> {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Sequence<'a>> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
Ok(Sequence {
content: Cow::Borrowed(any.data),
})
}
}
impl CheckDerConstraints for Sequence<'_> {
fn check_constraints(_any: &Any) -> Result<()> {
// TODO: iterate on ANY objects and check constraints? -> this will not be exhaustive
// test, for ex INTEGER encoding will not be checked
Ok(())
}
}
impl DerAutoDerive for Sequence<'_> {}
impl Tagged for Sequence<'_> {
const TAG: Tag = Tag::Sequence;
}
#[cfg(feature = "std")]
impl ToDer for Sequence<'_> {
fn to_der_len(&self) -> Result<usize> {
let sz = self.content.len();
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
true,
Self::TAG,
Length::Definite(self.content.len()),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&self.content).map_err(Into::into)
}
}
#[cfg(feature = "std")]
impl Sequence<'_> {
/// Attempt to create a `Sequence` from an iterator over serializable objects (to DER)
///
/// # Examples
///
/// ```
/// use asn1_rs::Sequence;
///
/// // build sequence
/// let it = [2, 3, 4].iter();
/// let seq = Sequence::from_iter_to_der(it).unwrap();
/// ```
pub fn from_iter_to_der<T, IT>(it: IT) -> SerializeResult<Self>
where
IT: Iterator<Item = T>,
T: ToDer,
T: Tagged,
{
let mut v = Vec::new();
for item in it {
let item_v = <T as ToDer>::to_der_vec(&item)?;
v.extend_from_slice(&item_v);
}
Ok(Sequence {
content: Cow::Owned(v),
})
}
}

View File

@@ -0,0 +1,106 @@
use crate::{ASN1Parser, BerParser, DerParser, Error, FromBer, FromDer};
use core::marker::PhantomData;
/// An Iterator over binary data, parsing elements of type `T`
///
/// This helps parsing `SEQUENCE OF` items of type `T`. The type of parser
/// (BER/DER) is specified using the generic parameter `F` of this struct.
///
/// Note: the iterator must start on the sequence *contents*, not the sequence itself.
///
/// # Examples
///
/// ```rust
/// use asn1_rs::{DerParser, Integer, SequenceIterator};
///
/// let data = &[0x30, 0x6, 0x2, 0x1, 0x1, 0x2, 0x1, 0x2];
/// for (idx, item) in SequenceIterator::<Integer, DerParser>::new(&data[2..]).enumerate() {
/// let item = item.unwrap(); // parsing could have failed
/// let i = item.as_u32().unwrap(); // integer can be negative, or too large to fit into u32
/// assert_eq!(i as usize, idx + 1);
/// }
/// ```
#[derive(Debug)]
pub struct SequenceIterator<'a, T, F, E = Error>
where
F: ASN1Parser,
{
data: &'a [u8],
has_error: bool,
_t: PhantomData<T>,
_f: PhantomData<F>,
_e: PhantomData<E>,
}
impl<'a, T, F, E> SequenceIterator<'a, T, F, E>
where
F: ASN1Parser,
{
pub fn new(data: &'a [u8]) -> Self {
SequenceIterator {
data,
has_error: false,
_t: PhantomData,
_f: PhantomData,
_e: PhantomData,
}
}
}
impl<'a, T, E> Iterator for SequenceIterator<'a, T, BerParser, E>
where
T: FromBer<'a, E>,
E: From<Error>,
{
type Item = Result<T, E>;
fn next(&mut self) -> Option<Self::Item> {
if self.has_error || self.data.is_empty() {
return None;
}
match T::from_ber(self.data) {
Ok((rem, obj)) => {
self.data = rem;
Some(Ok(obj))
}
Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => {
self.has_error = true;
Some(Err(e))
}
Err(nom::Err::Incomplete(n)) => {
self.has_error = true;
Some(Err(Error::Incomplete(n).into()))
}
}
}
}
impl<'a, T, E> Iterator for SequenceIterator<'a, T, DerParser, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
type Item = Result<T, E>;
fn next(&mut self) -> Option<Self::Item> {
if self.has_error || self.data.is_empty() {
return None;
}
match T::from_der(self.data) {
Ok((rem, obj)) => {
self.data = rem;
Some(Ok(obj))
}
Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => {
self.has_error = true;
Some(Err(e))
}
Err(nom::Err::Incomplete(n)) => {
self.has_error = true;
Some(Err(Error::Incomplete(n).into()))
}
}
}
}

View File

@@ -0,0 +1,197 @@
use crate::*;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::convert::TryFrom;
use core::fmt::{Debug, Display};
use core::iter::FromIterator;
use core::ops::{Deref, DerefMut};
use self::debug::{trace, trace_generic};
/// The `SEQUENCE OF` object is an ordered list of homogeneous types.
///
/// This type implements `Deref<Target = [T]>` and `DerefMut<Target = [T]>`, so all methods
/// like `.iter()`, `.len()` and others can be used transparently as if using a vector.
///
/// # Examples
///
/// ```
/// use asn1_rs::SequenceOf;
/// use std::iter::FromIterator;
///
/// // build set
/// let seq = SequenceOf::from_iter([2, 3, 4]);
///
/// // `seq` now contains the serialized DER representation of the array
///
/// // iterate objects
/// let mut sum = 0;
/// for item in seq.iter() {
/// // item has type `Result<u32>`, since parsing the serialized bytes could fail
/// sum += *item;
/// }
/// assert_eq!(sum, 9);
///
/// ```
#[derive(Debug, PartialEq)]
pub struct SequenceOf<T> {
pub(crate) items: Vec<T>,
}
impl<T> SequenceOf<T> {
/// Builds a `SEQUENCE OF` from the provided content
#[inline]
pub const fn new(items: Vec<T>) -> Self {
SequenceOf { items }
}
/// Converts `self` into a vector without clones or allocation.
#[inline]
pub fn into_vec(self) -> Vec<T> {
self.items
}
/// Appends an element to the back of a collection
#[inline]
pub fn push(&mut self, item: T) {
self.items.push(item)
}
}
impl<T> AsRef<[T]> for SequenceOf<T> {
fn as_ref(&self) -> &[T] {
&self.items
}
}
impl<T> AsMut<[T]> for SequenceOf<T> {
fn as_mut(&mut self) -> &mut [T] {
&mut self.items
}
}
impl<T> Deref for SequenceOf<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.items
}
}
impl<T> DerefMut for SequenceOf<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.items
}
}
impl<T> From<SequenceOf<T>> for Vec<T> {
fn from(seq: SequenceOf<T>) -> Self {
seq.items
}
}
impl<T> FromIterator<T> for SequenceOf<T> {
fn from_iter<IT: IntoIterator<Item = T>>(iter: IT) -> Self {
let items = Vec::from_iter(iter);
SequenceOf::new(items)
}
}
impl<'a, T> TryFrom<Any<'a>> for SequenceOf<T>
where
T: FromBer<'a>,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
any.tag().assert_eq(Self::TAG)?;
if !any.header.is_constructed() {
return Err(Error::ConstructExpected);
}
let items = SequenceIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?;
Ok(SequenceOf::new(items))
}
}
impl<T> CheckDerConstraints for SequenceOf<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SequenceIterator::<Any, DerParser>::new(any.data) {
let item = item?;
T::check_constraints(&item)?;
}
Ok(())
}
}
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for SequenceOf<T>
where
T: FromDer<'a, E>,
E: From<Error> + Display + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"SequenceOf::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), parse_der_any, bytes)
.map_err(Err::convert)?;
any.header
.assert_tag(Self::TAG)
.map_err(|e| Err::Error(e.into()))?;
let items = SequenceIterator::<T, DerParser, E>::new(any.data)
.collect::<Result<Vec<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, SequenceOf::new(items)))
},
bytes,
)
}
}
impl<T> Tagged for SequenceOf<T> {
const TAG: Tag = Tag::Sequence;
}
#[cfg(feature = "std")]
impl<T> ToDer for SequenceOf<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
self.items.to_der_len()
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
self.items.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
self.items.write_der_content(writer)
}
}
#[cfg(test)]
mod tests {
use crate::SequenceOf;
use core::iter::FromIterator;
/// Test use of object, available methods and syntax for different use cases
#[test]
fn use_sequence_of() {
let mut set = SequenceOf::from_iter([1, 2, 3]);
set.push(4);
// deref as slice
let sum: i32 = set.iter().sum();
assert_eq!(sum, 10);
// range operator
assert_eq!(&set[1..3], &[2, 3]);
}
}

View File

@@ -0,0 +1,165 @@
use crate::*;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::convert::TryFrom;
use core::fmt::Debug;
use self::debug::{macros::debug_eprintln, trace, trace_generic};
// // XXX this compiles but requires bound TryFrom :/
// impl<'a, 'b, T> TryFrom<&'b Any<'a>> for Vec<T>
// where
// T: TryFrom<&'b Any<'a>>,
// for<'e> <T as TryFrom<&'b Any<'a>>>::Error: From<Error>,
// T: FromBer<'a, <T as TryFrom<&'b Any<'a>>>::Error>,
// // T: FromBer<'a, E>,
// // E: From<Error>,
// {
// type Error = <T as TryFrom<&'b Any<'a>>>::Error;
// fn try_from(any: &'b Any<'a>) -> Result<Vec<T>, Self::Error> {
// any.tag().assert_eq(Self::TAG)?;
// any.header.assert_constructed()?;
// let v = SequenceIterator::<T, BerParser, Self::Error>::new(any.data)
// .collect::<Result<Vec<T>, Self::Error>>()?;
// Ok(v)
// }
// }
// // XXX this compiles but requires bound TryFrom :/
// impl<'a, 'b, T> TryFrom<&'b Any<'a>> for Vec<T>
// where
// T: TryFrom<&'b Any<'a>>,
// <T as TryFrom<&'b Any<'a>>>::Error: From<Error>,
// T: FromBer<'a, <T as TryFrom<&'b Any<'a>>>::Error>,
// // T: FromBer<'a, E>,
// // E: From<Error>,
// {
// type Error = <T as TryFrom<&'b Any<'a>>>::Error;
// fn try_from(any: &'b Any<'a>) -> Result<Vec<T>, Self::Error> {
// any.tag().assert_eq(Self::TAG)?;
// any.header.assert_constructed()?;
// let v = SequenceIterator::<T, BerParser, Self::Error>::new(any.data)
// .collect::<Result<Vec<T>, Self::Error>>()?;
// Ok(v)
// }
// }
impl<'a, T> TryFrom<Any<'a>> for Vec<T>
where
T: FromBer<'a>,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
trace_generic(
core::any::type_name::<Self>(),
"T::from(Any)",
|any| {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
let res_items: Result<Vec<T>> =
SetIterator::<T, BerParser>::new(any.data).collect();
if res_items.is_err() {
debug_eprintln!(
core::any::type_name::<T>(),
"≠ {}",
"Conversion from Any failed".red()
);
}
res_items
},
any,
)
}
}
impl<T> CheckDerConstraints for Vec<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SequenceIterator::<Any, DerParser>::new(any.data) {
let item = item?;
<T as CheckDerConstraints>::check_constraints(&item)?;
}
Ok(())
}
}
impl<T> Tagged for Vec<T> {
const TAG: Tag = Tag::Sequence;
}
// impl<'a, T> FromBer<'a> for Vec<T>
// where
// T: FromBer<'a>,
// {
// fn from_ber(bytes: &'a [u8]) -> ParseResult<Self> {
// let (rem, any) = Any::from_ber(bytes)?;
// any.header.assert_tag(Self::TAG)?;
// let v = SequenceIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?;
// Ok((rem, v))
// }
// }
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for Vec<T>
where
T: FromDer<'a, E>,
E: From<Error> + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"Sequence::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), parse_der_any, bytes)
.map_err(Err::convert)?;
any.header
.assert_tag(Self::TAG)
.map_err(|e| Err::Error(e.into()))?;
let v = SequenceIterator::<T, DerParser, E>::new(any.data)
.collect::<Result<Vec<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, v))
},
bytes,
)
}
}
#[cfg(feature = "std")]
impl<T> ToDer for Vec<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
let mut len = 0;
for t in self.iter() {
len += t.to_der_len()?;
}
let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len));
Ok(header.to_der_len()? + len)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut len = 0;
for t in self.iter() {
len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?;
}
let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut sz = 0;
for t in self.iter() {
sz += t.write_der(writer)?;
}
Ok(sz)
}
}

387
vendor/asn1-rs/src/asn1_types/set.rs vendored Normal file
View File

@@ -0,0 +1,387 @@
use crate::*;
use alloc::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::convert::TryFrom;
mod btreeset;
mod hashset;
mod iterator;
mod set_of;
pub use iterator::*;
pub use set_of::*;
/// The `SET` object is an unordered list of heteregeneous types.
///
/// Sets can usually be of 2 types:
/// - a list of different objects (`SET`, usually parsed as a `struct`)
/// - a list of similar objects (`SET OF`, usually parsed as a `BTreeSet<T>` or `HashSet<T>`)
///
/// The current object covers the former. For the latter, see the [`SetOf`] documentation.
///
/// The `Set` object contains the (*unparsed*) encoded representation of its content. It provides
/// methods to parse and iterate contained objects, or convert the sequence to other types.
///
/// # Building a Set
///
/// To build a DER set:
/// - if the set is composed of objects of the same type, the [`Set::from_iter_to_der`] method can be used
/// - otherwise, the [`ToDer`] trait can be used to create content incrementally
///
#[cfg_attr(feature = "std", doc = r#"```"#)]
#[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)]
/// use asn1_rs::{Integer, Set, SerializeResult, ToDer};
///
/// fn build_set<'a>() -> SerializeResult<Set<'a>> {
/// let mut v = Vec::new();
/// // add an Integer object (construct type):
/// let i = Integer::from_u32(4);
/// let _ = i.write_der(&mut v)?;
/// // some primitive objects also implement `ToDer`. A string will be mapped as `Utf8String`:
/// let _ = "abcd".write_der(&mut v)?;
/// // return the set built from the DER content
/// Ok(Set::new(v.into()))
/// }
///
/// let seq = build_set().unwrap();
///
/// ```
///
/// # Examples
///
#[cfg_attr(feature = "std", doc = r#"```"#)]
#[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)]
/// use asn1_rs::{Error, Set};
///
/// // build set
/// let it = [2, 3, 4].iter();
/// let set = Set::from_iter_to_der(it).unwrap();
///
/// // `set` now contains the serialized DER representation of the array
///
/// // iterate objects
/// let mut sum = 0;
/// for item in set.der_iter::<u32, Error>() {
/// // item has type `Result<u32>`, since parsing the serialized bytes could fail
/// sum += item.expect("parsing list item failed");
/// }
/// assert_eq!(sum, 9);
///
/// ```
///
/// Note: the above example encodes a `SET OF INTEGER` object, the [`SetOf`] object could
/// be used to provide a simpler API.
///
#[derive(Clone, Debug)]
pub struct Set<'a> {
/// Serialized DER representation of the set content
pub content: Cow<'a, [u8]>,
}
impl<'a> Set<'a> {
/// Build a set, given the provided content
pub const fn new(content: Cow<'a, [u8]>) -> Self {
Set { content }
}
/// Consume the set and return the content
#[inline]
pub fn into_content(self) -> Cow<'a, [u8]> {
self.content
}
/// Apply the parsing function to the set content, consuming the set
///
/// Note: this function expects the caller to take ownership of content.
/// In some cases, handling the lifetime of objects is not easy (when keeping only references on
/// data). Other methods are provided (depending on the use case):
/// - [`Set::parse`] takes a reference on the set data, but does not consume it,
/// - [`Set::from_der_and_then`] does the parsing of the set and applying the function
/// in one step, ensuring there are only references (and dropping the temporary set).
pub fn and_then<U, F, E>(self, op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(Cow<'a, [u8]>) -> ParseResult<'a, U, E>,
{
op(self.content)
}
/// Same as [`Set::from_der_and_then`], but using BER encoding (no constraints).
pub fn from_ber_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>,
E: From<Error>,
{
let (rem, seq) = Set::from_ber(bytes).map_err(Err::convert)?;
let data = match seq.content {
Cow::Borrowed(b) => b,
// Since 'any' is built from 'bytes', it is borrowed by construction
Cow::Owned(_) => unreachable!(),
};
let (_, res) = op(data)?;
Ok((rem, res))
}
/// Parse a DER set and apply the provided parsing function to content
///
/// After parsing, the set object and header are discarded.
///
/// ```
/// use asn1_rs::{FromDer, ParseResult, Set};
///
/// // Parse a SET {
/// // a INTEGER (0..255),
/// // b INTEGER (0..4294967296)
/// // }
/// // and return only `(a,b)
/// fn parser(i: &[u8]) -> ParseResult<(u8, u32)> {
/// Set::from_der_and_then(i, |i| {
/// let (i, a) = u8::from_der(i)?;
/// let (i, b) = u32::from_der(i)?;
/// Ok((i, (a, b)))
/// }
/// )
/// }
/// ```
pub fn from_der_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>,
E: From<Error>,
{
let (rem, seq) = Set::from_der(bytes).map_err(Err::convert)?;
let data = match seq.content {
Cow::Borrowed(b) => b,
// Since 'any' is built from 'bytes', it is borrowed by construction
Cow::Owned(_) => unreachable!(),
};
let (_, res) = op(data)?;
Ok((rem, res))
}
/// Apply the parsing function to the set content (non-consuming version)
pub fn parse<F, T, E>(&'a self, mut f: F) -> ParseResult<'a, T, E>
where
F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>,
{
let input: &[u8] = &self.content;
f(input)
}
/// Apply the parsing function to the set content (consuming version)
///
/// Note: to parse and apply a parsing function in one step, use the
/// [`Set::from_der_and_then`] method.
///
/// # Limitations
///
/// This function fails if the set contains `Owned` data, because the parsing function
/// takes a reference on data (which is dropped).
pub fn parse_into<F, T, E>(self, mut f: F) -> ParseResult<'a, T, E>
where
F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
match self.content {
Cow::Borrowed(b) => f(b),
_ => Err(Err::Error(Error::LifetimeError.into())),
}
}
/// Return an iterator over the set content, attempting to decode objects as BER
///
/// This method can be used when all objects from the set have the same type.
pub fn ber_iter<T, E>(&'a self) -> SetIterator<'a, T, BerParser, E>
where
T: FromBer<'a, E>,
{
SetIterator::new(&self.content)
}
/// Return an iterator over the set content, attempting to decode objects as DER
///
/// This method can be used when all objects from the set have the same type.
pub fn der_iter<T, E>(&'a self) -> SetIterator<'a, T, DerParser, E>
where
T: FromDer<'a, E>,
{
SetIterator::new(&self.content)
}
/// Attempt to parse the set as a `SET OF` items (BER), and return the parsed items as a `Vec`.
pub fn ber_set_of<T, E>(&'a self) -> Result<Vec<T>, E>
where
T: FromBer<'a, E>,
E: From<Error>,
{
self.ber_iter().collect()
}
/// Attempt to parse the set as a `SET OF` items (DER), and return the parsed items as a `Vec`.
pub fn der_set_of<T, E>(&'a self) -> Result<Vec<T>, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
self.der_iter().collect()
}
/// Attempt to parse the set as a `SET OF` items (BER) (consuming input),
/// and return the parsed items as a `Vec`.
///
/// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects.
pub fn into_ber_set_of<T, E>(self) -> Result<Vec<T>, E>
where
for<'b> T: FromBer<'b, E>,
E: From<Error>,
T: ToStatic<Owned = T>,
{
match self.content {
Cow::Borrowed(bytes) => SetIterator::<T, BerParser, E>::new(bytes).collect(),
Cow::Owned(data) => {
let v1 =
SetIterator::<T, BerParser, E>::new(&data).collect::<Result<Vec<T>, E>>()?;
let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
Ok(v2)
}
}
}
/// Attempt to parse the set as a `SET OF` items (DER) (consuming input),
/// and return the parsed items as a `Vec`.
///
/// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects.
pub fn into_der_set_of<T, E>(self) -> Result<Vec<T>, E>
where
for<'b> T: FromDer<'b, E>,
E: From<Error>,
T: ToStatic<Owned = T>,
{
match self.content {
Cow::Borrowed(bytes) => SetIterator::<T, DerParser, E>::new(bytes).collect(),
Cow::Owned(data) => {
let v1 =
SetIterator::<T, DerParser, E>::new(&data).collect::<Result<Vec<T>, E>>()?;
let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
Ok(v2)
}
}
}
pub fn into_der_set_of_ref<T, E>(self) -> Result<Vec<T>, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
match self.content {
Cow::Borrowed(bytes) => SetIterator::<T, DerParser, E>::new(bytes).collect(),
Cow::Owned(_) => Err(Error::LifetimeError.into()),
}
}
}
impl ToStatic for Set<'_> {
type Owned = Set<'static>;
fn to_static(&self) -> Self::Owned {
Set {
content: Cow::Owned(self.content.to_vec()),
}
}
}
impl AsRef<[u8]> for Set<'_> {
fn as_ref(&self) -> &[u8] {
&self.content
}
}
impl<'a> TryFrom<Any<'a>> for Set<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Set<'a>> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Set<'a> {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Set<'a>> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
Ok(Set {
content: Cow::Borrowed(any.data),
})
}
}
impl CheckDerConstraints for Set<'_> {
fn check_constraints(_any: &Any) -> Result<()> {
Ok(())
}
}
impl DerAutoDerive for Set<'_> {}
impl Tagged for Set<'_> {
const TAG: Tag = Tag::Set;
}
#[cfg(feature = "std")]
impl ToDer for Set<'_> {
fn to_der_len(&self) -> Result<usize> {
let sz = self.content.len();
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
true,
Self::TAG,
Length::Definite(self.content.len()),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&self.content).map_err(Into::into)
}
}
#[cfg(feature = "std")]
impl Set<'_> {
/// Attempt to create a `Set` from an iterator over serializable objects (to DER)
///
/// # Examples
///
/// ```
/// use asn1_rs::Set;
///
/// // build set
/// let it = [2, 3, 4].iter();
/// let seq = Set::from_iter_to_der(it).unwrap();
/// ```
pub fn from_iter_to_der<T, IT>(it: IT) -> SerializeResult<Self>
where
IT: Iterator<Item = T>,
T: ToDer,
T: Tagged,
{
let mut v = Vec::new();
for item in it {
let item_v = <T as ToDer>::to_der_vec(&item)?;
v.extend_from_slice(&item_v);
}
Ok(Set {
content: Cow::Owned(v),
})
}
}

View File

@@ -0,0 +1,142 @@
use crate::*;
use alloc::collections::BTreeSet;
use core::{convert::TryFrom, fmt::Debug};
use self::debug::{trace, trace_generic};
impl<T> Tagged for BTreeSet<T> {
const TAG: Tag = Tag::Set;
}
impl<'a, T> TryFrom<Any<'a>> for BTreeSet<T>
where
T: FromBer<'a>,
T: Ord,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
trace_generic(
core::any::type_name::<Self>(),
"BTreeSet::from_der",
|any| {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
let items =
SetIterator::<T, BerParser>::new(any.data).collect::<Result<BTreeSet<T>>>()?;
Ok(items)
},
any,
)
}
}
impl<T> CheckDerConstraints for BTreeSet<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SetIterator::<Any, DerParser>::new(any.data) {
let item = item?;
T::check_constraints(&item)?;
}
Ok(())
}
}
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for BTreeSet<T>
where
T: FromDer<'a, E>,
T: Ord,
E: From<Error> + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"BTreeSet::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), Any::from_der, bytes)
.map_err(Err::convert)?;
any.tag()
.assert_eq(Self::TAG)
.map_err(|e| Err::Error(e.into()))?;
any.header
.assert_constructed()
.map_err(|e| Err::Error(e.into()))?;
let items = SetIterator::<T, DerParser, E>::new(any.data)
.collect::<Result<BTreeSet<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, items))
},
bytes,
)
}
}
#[cfg(feature = "std")]
impl<T> ToDer for BTreeSet<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
let mut len = 0;
for t in self.iter() {
len += t.to_der_len()?;
}
let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len));
Ok(header.to_der_len()? + len)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut len = 0;
for t in self.iter() {
len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?;
}
let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut sz = 0;
for t in self.iter() {
sz += t.write_der(writer)?;
}
Ok(sz)
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use crate::*;
use core::convert::TryFrom;
use hex_literal::hex;
use std::collections::BTreeSet;
#[test]
fn ber_btreeset() {
let input = &hex! {"31 06 02 01 00 02 01 01"};
let (_, any) = Any::from_ber(input).expect("parsing hashset failed");
<BTreeSet<u32>>::check_constraints(&any).unwrap();
let h = <BTreeSet<u32>>::try_from(any).unwrap();
assert_eq!(h.len(), 2);
}
#[test]
fn der_btreeset() {
let input = &hex! {"31 06 02 01 00 02 01 01"};
let r: IResult<_, _, Error> = BTreeSet::<u32>::from_der(input);
let (_, h) = r.expect("parsing hashset failed");
assert_eq!(h.len(), 2);
assert_eq!(h.to_der_len(), Ok(8));
let v = h.to_der_vec().expect("could not serialize");
let (_, h2) = SetOf::<u32>::from_der(&v).unwrap();
assert!(h.iter().eq(h2.iter()));
}
}

View File

@@ -0,0 +1,136 @@
#![cfg(feature = "std")]
use crate::*;
use core::fmt::Debug;
use std::collections::HashSet;
use std::convert::TryFrom;
use std::hash::Hash;
use self::debug::{trace, trace_generic};
impl<T> Tagged for HashSet<T> {
const TAG: Tag = Tag::Set;
}
impl<'a, T> TryFrom<Any<'a>> for HashSet<T>
where
T: FromBer<'a>,
T: Hash + Eq,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
let items = SetIterator::<T, BerParser>::new(any.data).collect::<Result<HashSet<T>>>()?;
Ok(items)
}
}
impl<T> CheckDerConstraints for HashSet<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SetIterator::<Any, DerParser>::new(any.data) {
let item = item?;
T::check_constraints(&item)?;
}
Ok(())
}
}
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for HashSet<T>
where
T: FromDer<'a, E>,
T: Hash + Eq,
E: From<Error> + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"BTreeSet::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), Any::from_der, bytes)
.map_err(Err::convert)?;
any.tag()
.assert_eq(Self::TAG)
.map_err(|e| Err::Error(e.into()))?;
any.header
.assert_constructed()
.map_err(|e| Err::Error(e.into()))?;
let items = SetIterator::<T, DerParser, E>::new(any.data)
.collect::<Result<HashSet<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, items))
},
bytes,
)
}
}
impl<T> ToDer for HashSet<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
let mut len = 0;
for t in self.iter() {
len += t.to_der_len()?;
}
let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len));
Ok(header.to_der_len()? + len)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut len = 0;
for t in self.iter() {
len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?;
}
let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut sz = 0;
for t in self.iter() {
sz += t.write_der(writer)?;
}
Ok(sz)
}
}
#[cfg(test)]
mod tests {
use crate::*;
use core::convert::TryFrom;
use hex_literal::hex;
use std::collections::HashSet;
#[test]
fn ber_hashset() {
let input = &hex! {"31 06 02 01 00 02 01 01"};
let (_, any) = Any::from_ber(input).expect("parsing hashset failed");
<HashSet<u32>>::check_constraints(&any).unwrap();
let h = <HashSet<u32>>::try_from(any).unwrap();
assert_eq!(h.len(), 2);
}
#[test]
fn der_hashset() {
let input = &hex! {"31 06 02 01 00 02 01 01"};
let r: IResult<_, _, Error> = HashSet::<u32>::from_der(input);
let (_, h) = r.expect("parsing hashset failed");
assert_eq!(h.len(), 2);
assert_eq!(h.to_der_len(), Ok(8));
let v = h.to_der_vec().expect("could not serialize");
let (_, h2) = SetOf::<u32>::from_der(&v).unwrap();
assert!(h.iter().eq(h2.iter()));
}
}

View File

@@ -0,0 +1,22 @@
pub use crate::{Error, SequenceIterator};
/// An Iterator over binary data, parsing elements of type `T`
///
/// This helps parsing `SET OF` items of type `T`. The type of parser
/// (BER/DER) is specified using the generic parameter `F` of this struct.
///
/// Note: the iterator must start on the set *contents*, not the set itself.
///
/// # Examples
///
/// ```rust
/// use asn1_rs::{DerParser, Integer, SetIterator};
///
/// let data = &[0x30, 0x6, 0x2, 0x1, 0x1, 0x2, 0x1, 0x2];
/// for (idx, item) in SetIterator::<Integer, DerParser>::new(&data[2..]).enumerate() {
/// let item = item.unwrap(); // parsing could have failed
/// let i = item.as_u32().unwrap(); // integer can be negative, or too large to fit into u32
/// assert_eq!(i as usize, idx + 1);
/// }
/// ```
pub type SetIterator<'a, T, F, E = Error> = SequenceIterator<'a, T, F, E>;

View File

@@ -0,0 +1,183 @@
use crate::*;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::convert::TryFrom;
use core::fmt::{Debug, Display};
use core::iter::FromIterator;
use core::ops::{Deref, DerefMut};
use self::debug::{trace, trace_generic};
/// The `SET OF` object is an unordered list of homogeneous types.
///
/// This type implements `Deref<Target = [T]>` and `DerefMut<Target = [T]>`, so all methods
/// like `.iter()`, `.len()` and others can be used transparently as if using a vector.
///
/// # Examples
///
/// ```
/// use asn1_rs::SetOf;
/// use std::iter::FromIterator;
///
/// // build set
/// let set = SetOf::from_iter([2, 3, 4]);
///
/// // `set` now contains the serialized DER representation of the array
///
/// // iterate objects
/// let mut sum = 0;
/// for item in set.iter() {
/// // item has type `Result<u32>`, since parsing the serialized bytes could fail
/// sum += *item;
/// }
/// assert_eq!(sum, 9);
///
/// ```
#[derive(Debug, PartialEq)]
pub struct SetOf<T> {
items: Vec<T>,
}
impl<T> SetOf<T> {
/// Builds a `SET OF` from the provided content
#[inline]
pub const fn new(items: Vec<T>) -> Self {
SetOf { items }
}
/// Converts `self` into a vector without clones or allocation.
#[inline]
pub fn into_vec(self) -> Vec<T> {
self.items
}
/// Appends an element to the back of a collection
#[inline]
pub fn push(&mut self, item: T) {
self.items.push(item)
}
}
impl<T> AsRef<[T]> for SetOf<T> {
fn as_ref(&self) -> &[T] {
&self.items
}
}
impl<T> AsMut<[T]> for SetOf<T> {
fn as_mut(&mut self) -> &mut [T] {
&mut self.items
}
}
impl<T> Deref for SetOf<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.items
}
}
impl<T> DerefMut for SetOf<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.items
}
}
impl<T> From<SetOf<T>> for Vec<T> {
fn from(set: SetOf<T>) -> Self {
set.items
}
}
impl<T> FromIterator<T> for SetOf<T> {
fn from_iter<IT: IntoIterator<Item = T>>(iter: IT) -> Self {
let items = Vec::from_iter(iter);
SetOf::new(items)
}
}
impl<'a, T> TryFrom<Any<'a>> for SetOf<T>
where
T: FromBer<'a>,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
any.tag().assert_eq(Self::TAG)?;
if !any.header.is_constructed() {
return Err(Error::ConstructExpected);
}
let items = SetIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?;
Ok(SetOf::new(items))
}
}
impl<T> CheckDerConstraints for SetOf<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SetIterator::<Any, DerParser>::new(any.data) {
let item = item?;
T::check_constraints(&item)?;
}
Ok(())
}
}
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for SetOf<T>
where
T: FromDer<'a, E>,
E: From<Error> + Display + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"SetOf::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), Any::from_der, bytes)
.map_err(Err::convert)?;
any.header
.assert_tag(Self::TAG)
.map_err(|e| Err::Error(e.into()))?;
let items = SetIterator::<T, DerParser, E>::new(any.data)
.collect::<Result<Vec<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, SetOf::new(items)))
},
bytes,
)
}
}
impl<T> Tagged for SetOf<T> {
const TAG: Tag = Tag::Set;
}
#[cfg(feature = "std")]
impl<T> ToDer for SetOf<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
self.items.to_der_len()
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// Do not call self.items.write_der_header(), this will encode the wrong tag (items is a Vec)
let mut len = 0;
for t in self.items.iter() {
len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?;
}
let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
self.items.write_der_content(writer)
}
}

170
vendor/asn1-rs/src/asn1_types/strings.rs vendored Normal file
View File

@@ -0,0 +1,170 @@
mod bmpstring;
mod generalstring;
mod graphicstring;
mod ia5string;
mod numericstring;
mod printablestring;
mod str;
mod string;
mod teletexstring;
mod universalstring;
mod utf8string;
mod videotexstring;
mod visiblestring;
pub use bmpstring::*;
pub use generalstring::*;
pub use graphicstring::*;
pub use ia5string::*;
pub use numericstring::*;
pub use printablestring::*;
pub use teletexstring::*;
pub use universalstring::*;
pub use utf8string::*;
pub use videotexstring::*;
pub use visiblestring::*;
/// Base trait for BER string objects and character set validation
///
/// This trait is implemented by several types, and is used to determine if some bytes
/// would be valid for the given type.
///
/// # Example
///
/// ```rust
/// use asn1_rs::{PrintableString, TestValidCharset, VisibleString};
///
/// let bytes: &[u8] = b"abcd*4";
/// let res = PrintableString::test_valid_charset(bytes);
/// assert!(res.is_err());
/// let res = VisibleString::test_valid_charset(bytes);
/// assert!(res.is_ok());
/// ```
pub trait TestValidCharset {
/// Check character set for this object type.
fn test_valid_charset(i: &[u8]) -> crate::Result<()>;
}
#[doc(hidden)]
#[macro_export]
macro_rules! asn1_string {
(IMPL $name:ident, $sname:expr) => {
#[doc="ASN.1 restricted character string type (`"]
#[doc = $sname]
#[doc = "`)"]
#[derive(Debug, PartialEq, Eq)]
pub struct $name<'a> {
pub(crate) data: alloc::borrow::Cow<'a, str>,
}
impl<'a> $name<'a> {
pub const fn new(s: &'a str) -> Self {
$name {
data: alloc::borrow::Cow::Borrowed(s),
}
}
pub fn string(&self) -> String {
use alloc::string::ToString;
self.data.to_string()
}
}
impl<'a> AsRef<str> for $name<'a> {
fn as_ref(&self) -> &str {
&self.data
}
}
impl<'a> From<&'a str> for $name<'a> {
fn from(s: &'a str) -> Self {
Self::new(s)
}
}
impl From<String> for $name<'_> {
fn from(s: String) -> Self {
Self {
data: alloc::borrow::Cow::Owned(s),
}
}
}
impl<'a> core::convert::TryFrom<$crate::Any<'a>> for $name<'a> {
type Error = $crate::Error;
fn try_from(any: $crate::Any<'a>) -> $crate::Result<$name<'a>> {
use core::convert::TryFrom;
TryFrom::try_from(&any)
}
}
impl<'a, 'b> core::convert::TryFrom<&'b $crate::Any<'a>> for $name<'a> {
type Error = $crate::Error;
fn try_from(any: &'b $crate::Any<'a>) -> $crate::Result<$name<'a>> {
use $crate::traits::Tagged;
use alloc::borrow::Cow;
any.tag().assert_eq(Self::TAG)?;
<$name>::test_valid_charset(any.data)?;
let s = alloc::str::from_utf8(any.data)?;
let data = Cow::Borrowed(s);
Ok($name { data })
}
}
impl<'a> $crate::CheckDerConstraints for $name<'a> {
fn check_constraints(any: &$crate::Any) -> $crate::Result<()> {
any.header.assert_primitive()?;
Ok(())
}
}
impl $crate::DerAutoDerive for $name<'_> {}
impl<'a> $crate::Tagged for $name<'a> {
const TAG: $crate::Tag = $crate::Tag::$name;
}
#[cfg(feature = "std")]
impl $crate::ToDer for $name<'_> {
fn to_der_len(&self) -> Result<usize> {
let sz = self.data.as_bytes().len();
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = $crate::Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(
&self,
writer: &mut dyn std::io::Write,
) -> $crate::SerializeResult<usize> {
use $crate::Tagged;
let header = $crate::Header::new(
$crate::Class::Universal,
false,
Self::TAG,
$crate::Length::Definite(self.data.len()),
);
header.write_der_header(writer).map_err(Into::into)
}
fn write_der_content(
&self,
writer: &mut dyn std::io::Write,
) -> $crate::SerializeResult<usize> {
writer.write(self.data.as_bytes()).map_err(Into::into)
}
}
};
($name:ident) => {
asn1_string!(IMPL $name, stringify!($name));
};
}

View File

@@ -0,0 +1,143 @@
// do not use the `asn1_string` macro, since types are not the same
// X.680 section 37.15
use crate::*;
use alloc::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
/// ASN.1 `BMPSTRING` type
///
/// Note: parsing a `BmpString` allocates memory since the UTF-16 to UTF-8 conversion requires a memory allocation.
/// (see `String::from_utf16` method).
#[derive(Debug, PartialEq, Eq)]
pub struct BmpString<'a> {
pub(crate) data: Cow<'a, str>,
}
impl<'a> BmpString<'a> {
pub const fn new(s: &'a str) -> Self {
BmpString {
data: Cow::Borrowed(s),
}
}
pub fn string(&self) -> String {
self.data.to_string()
}
}
impl AsRef<str> for BmpString<'_> {
fn as_ref(&self) -> &str {
&self.data
}
}
impl<'a> From<&'a str> for BmpString<'a> {
fn from(s: &'a str) -> Self {
Self::new(s)
}
}
impl From<String> for BmpString<'_> {
fn from(s: String) -> Self {
Self {
data: Cow::Owned(s),
}
}
}
impl<'a, 'r> core::convert::TryFrom<&'r Any<'a>> for BmpString<'a> {
type Error = Error;
fn try_from(any: &'r Any<'a>) -> Result<BmpString<'a>> {
any.tag().assert_eq(Self::TAG)?;
// read slice as big-endian UTF-16 string
let v = &any
.data
.chunks(2)
.map(|s| match s {
[a, b] => ((*a as u16) << 8) | (*b as u16),
[a] => *a as u16,
_ => unreachable!(),
})
.collect::<Vec<_>>();
let s = String::from_utf16(v)?;
let data = Cow::Owned(s);
Ok(BmpString { data })
}
}
impl<'a> core::convert::TryFrom<Any<'a>> for BmpString<'a> {
type Error = Error;
#[inline]
fn try_from(any: Any<'a>) -> Result<BmpString<'a>> {
BmpString::try_from(&any)
}
}
impl CheckDerConstraints for BmpString<'_> {
fn check_constraints(any: &Any) -> Result<()> {
any.header.assert_primitive()?;
Ok(())
}
}
impl DerAutoDerive for BmpString<'_> {}
impl Tagged for BmpString<'_> {
const TAG: Tag = Tag::BmpString;
}
impl TestValidCharset for BmpString<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
if i.len() % 2 != 0 {
return Err(Error::StringInvalidCharset);
}
let iter = i.chunks(2).map(|s| ((s[0] as u16) << 8) | (s[1] as u16));
for c in char::decode_utf16(iter) {
if c.is_err() {
return Err(Error::StringInvalidCharset);
}
}
Ok(())
}
}
#[cfg(feature = "std")]
impl ToDer for BmpString<'_> {
fn to_der_len(&self) -> Result<usize> {
// compute the UTF-16 length
let sz = self.data.encode_utf16().count() * 2;
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// compute the UTF-16 length
let l = self.data.encode_utf16().count() * 2;
let header = Header::new(Class::Universal, false, Self::TAG, Length::Definite(l));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut v = Vec::new();
for u in self.data.encode_utf16() {
v.push((u >> 8) as u8);
v.push((u & 0xff) as u8);
}
writer.write(&v).map_err(Into::into)
}
}

View File

@@ -0,0 +1,15 @@
use crate::{asn1_string, TestValidCharset};
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::String;
asn1_string!(GeneralString);
impl TestValidCharset for GeneralString<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
if !i.iter().all(u8::is_ascii) {
return Err(Error::StringInvalidCharset);
}
Ok(())
}
}

View File

@@ -0,0 +1,15 @@
use crate::{asn1_string, TestValidCharset};
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::String;
asn1_string!(GraphicString);
impl TestValidCharset for GraphicString<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
if !i.iter().all(u8::is_ascii) {
return Err(Error::StringInvalidCharset);
}
Ok(())
}
}

View File

@@ -0,0 +1,15 @@
use crate::{asn1_string, TestValidCharset};
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::String;
asn1_string!(Ia5String);
impl TestValidCharset for Ia5String<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
if !i.iter().all(u8::is_ascii) {
return Err(Error::StringInvalidCharset);
}
Ok(())
}
}

View File

@@ -0,0 +1,19 @@
use crate::{asn1_string, TestValidCharset};
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::String;
asn1_string!(NumericString);
impl TestValidCharset for NumericString<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_numeric(b: &u8) -> bool {
matches!(*b, b'0'..=b'9' | b' ')
}
if !i.iter().all(is_numeric) {
return Err(Error::StringInvalidCharset);
}
Ok(())
}
}

View File

@@ -0,0 +1,36 @@
use crate::{asn1_string, TestValidCharset};
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::String;
asn1_string!(PrintableString);
impl TestValidCharset for PrintableString<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
// Argument must be a reference, because of the .iter().all(F) call below
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_printable(b: &u8) -> bool {
matches!(*b,
b'a'..=b'z'
| b'A'..=b'Z'
| b'0'..=b'9'
| b' '
| b'\''
| b'('
| b')'
| b'+'
| b','
| b'-'
| b'.'
| b'/'
| b':'
| b'='
| b'?')
}
if !i.iter().all(is_printable) {
return Err(Error::StringInvalidCharset);
}
Ok(())
}
}

View File

@@ -0,0 +1,67 @@
use crate::*;
use alloc::borrow::Cow;
use core::convert::TryFrom;
impl<'a> TryFrom<Any<'a>> for &'a str {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<&'a str> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for &'a str {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<&'a str> {
any.tag().assert_eq(Self::TAG)?;
let s = Utf8String::try_from(any)?;
match s.data {
Cow::Borrowed(s) => Ok(s),
Cow::Owned(_) => Err(Error::LifetimeError),
}
}
}
impl CheckDerConstraints for &'_ str {
fn check_constraints(any: &Any) -> Result<()> {
// X.690 section 10.2
any.header.assert_primitive()?;
Ok(())
}
}
impl DerAutoDerive for &'_ str {}
impl Tagged for &'_ str {
const TAG: Tag = Tag::Utf8String;
}
#[cfg(feature = "std")]
impl ToDer for &'_ str {
fn to_der_len(&self) -> Result<usize> {
let sz = self.len();
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(self.len()),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(self.as_bytes()).map_err(Into::into)
}
}

View File

@@ -0,0 +1,65 @@
use crate::*;
#[cfg(not(feature = "std"))]
use alloc::string::String;
use core::convert::TryFrom;
impl<'a> TryFrom<Any<'a>> for String {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<String> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for String {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<String> {
any.tag().assert_eq(Self::TAG)?;
let s = Utf8String::try_from(any)?;
Ok(s.data.into_owned())
}
}
impl CheckDerConstraints for String {
fn check_constraints(any: &Any) -> Result<()> {
// X.690 section 10.2
any.header.assert_primitive()?;
Ok(())
}
}
impl DerAutoDerive for String {}
impl Tagged for String {
const TAG: Tag = Tag::Utf8String;
}
#[cfg(feature = "std")]
impl ToDer for String {
fn to_der_len(&self) -> Result<usize> {
let sz = self.len();
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(self.len()),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(self.as_ref()).map_err(Into::into)
}
}

View File

@@ -0,0 +1,19 @@
use crate::{asn1_string, TestValidCharset};
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::String;
asn1_string!(TeletexString);
impl TestValidCharset for TeletexString<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_visible(b: &u8) -> bool {
0x20 <= *b && *b <= 0x7f
}
if !i.iter().all(is_visible) {
return Err(Error::StringInvalidCharset);
}
Ok(())
}
}

View File

@@ -0,0 +1,139 @@
// do not use the `asn1_string` macro, since types are not the same
// X.680 section 37.6 and X.690 section 8.21.7
use crate::*;
use alloc::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::convert::TryFrom;
use core::iter::FromIterator;
/// ASN.1 `UniversalString` type
///
/// Note: parsing a `UniversalString` allocates memory since the UCS-4 to UTF-8 conversion requires a memory allocation.
#[derive(Debug, PartialEq, Eq)]
pub struct UniversalString<'a> {
pub(crate) data: Cow<'a, str>,
}
impl<'a> UniversalString<'a> {
pub const fn new(s: &'a str) -> Self {
UniversalString {
data: Cow::Borrowed(s),
}
}
pub fn string(&self) -> String {
self.data.to_string()
}
}
impl AsRef<str> for UniversalString<'_> {
fn as_ref(&self) -> &str {
&self.data
}
}
impl<'a> From<&'a str> for UniversalString<'a> {
fn from(s: &'a str) -> Self {
Self::new(s)
}
}
impl From<String> for UniversalString<'_> {
fn from(s: String) -> Self {
Self {
data: Cow::Owned(s),
}
}
}
impl<'a> TryFrom<Any<'a>> for UniversalString<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<UniversalString<'a>> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for UniversalString<'a> {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<UniversalString<'a>> {
any.tag().assert_eq(Self::TAG)?;
if any.data.len() % 4 != 0 {
return Err(Error::StringInvalidCharset);
}
// read slice as big-endian UCS-4 string
let v = &any
.data
.chunks(4)
.map(|s| match s {
[a, b, c, d] => {
let u32_val = ((*a as u32) << 24)
| ((*b as u32) << 16)
| ((*c as u32) << 8)
| (*d as u32);
char::from_u32(u32_val)
}
_ => unreachable!(),
})
.collect::<Option<Vec<_>>>()
.ok_or(Error::StringInvalidCharset)?;
let s = String::from_iter(v);
let data = Cow::Owned(s);
Ok(UniversalString { data })
}
}
impl CheckDerConstraints for UniversalString<'_> {
fn check_constraints(any: &Any) -> Result<()> {
any.header.assert_primitive()?;
Ok(())
}
}
impl DerAutoDerive for UniversalString<'_> {}
impl Tagged for UniversalString<'_> {
const TAG: Tag = Tag::UniversalString;
}
#[cfg(feature = "std")]
impl ToDer for UniversalString<'_> {
fn to_der_len(&self) -> Result<usize> {
// UCS-4: 4 bytes per character
let sz = self.data.len() * 4;
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let header = Header::new(
Class::Universal,
false,
Self::TAG,
Length::Definite(self.data.len() * 4),
);
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
self.data
.chars()
.try_for_each(|c| writer.write(&(c as u32).to_be_bytes()[..]).map(|_| ()))?;
Ok(self.data.len() * 4)
}
}

View File

@@ -0,0 +1,14 @@
use crate::asn1_string;
use crate::Result;
use crate::TestValidCharset;
#[cfg(not(feature = "std"))]
use alloc::string::String;
asn1_string!(Utf8String);
impl TestValidCharset for Utf8String<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
let _ = core::str::from_utf8(i)?;
Ok(())
}
}

View File

@@ -0,0 +1,20 @@
use crate::{asn1_string, TestValidCharset};
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::String;
asn1_string!(VideotexString);
impl TestValidCharset for VideotexString<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_visible(b: &u8) -> bool {
// XXX
0x20 <= *b && *b <= 0x7f
}
if !i.iter().all(is_visible) {
return Err(Error::StringInvalidCharset);
}
Ok(())
}
}

View File

@@ -0,0 +1,19 @@
use crate::{asn1_string, TestValidCharset};
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::String;
asn1_string!(VisibleString);
impl TestValidCharset for VisibleString<'_> {
fn test_valid_charset(i: &[u8]) -> Result<()> {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_visible(b: &u8) -> bool {
0x20 <= *b && *b <= 0x7f
}
if !i.iter().all(is_visible) {
return Err(Error::StringInvalidCharset);
}
Ok(())
}
}

128
vendor/asn1-rs/src/asn1_types/tagged.rs vendored Normal file
View File

@@ -0,0 +1,128 @@
use crate::{Class, Error, Tag, Tagged};
use core::marker::PhantomData;
mod application;
mod builder;
mod explicit;
mod helpers;
mod implicit;
mod optional;
mod parser;
mod private;
pub use application::*;
pub use builder::*;
pub use explicit::*;
pub use helpers::*;
pub use implicit::*;
pub use optional::*;
pub use parser::*;
pub use private::*;
pub(crate) const CONTEXT_SPECIFIC: u8 = Class::ContextSpecific as u8;
/// A type parameter for `IMPLICIT` tagged values.
#[derive(Debug, PartialEq, Eq)]
pub enum Implicit {}
/// A type parameter for `EXPLICIT` tagged values.
#[derive(Debug, PartialEq, Eq)]
pub enum Explicit {}
/// A type parameter for tagged values either [`Explicit`] or [`Implicit`].
pub trait TagKind {}
impl TagKind for Implicit {}
impl TagKind for Explicit {}
/// Helper object for creating `FromBer`/`FromDer` types for TAGGED OPTIONAL types
///
/// When parsing `ContextSpecific` (the most common class), see [`TaggedExplicit`] and
/// [`TaggedImplicit`] alias types.
///
/// # Notes
///
/// `CLASS` must be between 0 and 4. See [`Class`] for possible values for the `CLASS` parameter.
/// Constants from this class can be used, but they must be wrapped in braces due to
/// [Rust syntax for generics](https://doc.rust-lang.org/reference/items/generics.html)
/// (see example below).
///
/// # Examples
///
/// To parse a `[APPLICATION 0] EXPLICIT INTEGER` object:
///
/// ```rust
/// use asn1_rs::{Class, Error, Explicit, FromBer, Integer, TaggedValue};
///
/// let bytes = &[0x60, 0x03, 0x2, 0x1, 0x2];
///
/// // If tagged object is present (and has expected tag), parsing succeeds:
/// let (_, tagged) =
/// TaggedValue::<Integer, Error, Explicit, {Class::APPLICATION}, 0>::from_ber(bytes)
/// .unwrap();
/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2)));
/// ```
#[derive(Debug, PartialEq, Eq)]
pub struct TaggedValue<T, E, TagKind, const CLASS: u8, const TAG: u32> {
pub(crate) inner: T,
tag_kind: PhantomData<TagKind>,
_e: PhantomData<E>,
}
impl<T, E, TagKind, const CLASS: u8, const TAG: u32> TaggedValue<T, E, TagKind, CLASS, TAG> {
/// Consumes the `TaggedParser`, returning the wrapped value.
#[inline]
pub fn into_inner(self) -> T {
self.inner
}
/// Return the (outer) tag of this object
pub const fn tag(&self) -> Tag {
Self::TAG
}
/// Return the (outer) class of this object
#[inline]
pub const fn class(&self) -> u8 {
CLASS
}
}
impl<T, E, const CLASS: u8, const TAG: u32> TaggedValue<T, E, Explicit, CLASS, TAG> {
/// Constructs a new `EXPLICIT TaggedParser` with the provided value
#[inline]
pub const fn explicit(inner: T) -> Self {
TaggedValue {
inner,
tag_kind: PhantomData,
_e: PhantomData,
}
}
}
impl<T, E, const CLASS: u8, const TAG: u32> TaggedValue<T, E, Implicit, CLASS, TAG> {
/// Constructs a new `IMPLICIT TaggedParser` with the provided value
#[inline]
pub const fn implicit(inner: T) -> Self {
TaggedValue {
inner,
tag_kind: PhantomData,
_e: PhantomData,
}
}
}
impl<T, E, TagKind, const CLASS: u8, const TAG: u32> AsRef<T>
for TaggedValue<T, E, TagKind, CLASS, TAG>
{
fn as_ref(&self) -> &T {
&self.inner
}
}
impl<T, E, TagKind, const CLASS: u8, const TAG: u32> Tagged
for TaggedValue<T, E, TagKind, CLASS, TAG>
{
const TAG: Tag = Tag(TAG);
}

View File

@@ -0,0 +1,42 @@
use crate::{Class, Explicit, Implicit, TaggedValue};
/// A helper object to parse `[APPLICATION n] EXPLICIT T`
///
/// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to
/// parse explicit application-tagged values.
///
/// # Examples
///
/// To parse a `[APPLICATION 0] EXPLICIT INTEGER` object:
///
/// ```rust
/// use asn1_rs::{ApplicationExplicit, Error, FromBer, Integer, TaggedValue};
///
/// let bytes = &[0x60, 0x03, 0x2, 0x1, 0x2];
///
/// // If tagged object is present (and has expected tag), parsing succeeds:
/// let (_, tagged) = ApplicationExplicit::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2)));
/// ```
pub type ApplicationExplicit<T, E, const TAG: u32> =
TaggedValue<T, E, Explicit, { Class::APPLICATION }, TAG>;
/// A helper object to parse `[APPLICATION n] IMPLICIT T`
///
/// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to
/// parse explicit application-tagged values.
///
/// # Examples
///
/// To parse a `[APPLICATION 0] IMPLICIT INTEGER` object:
///
/// ```rust
/// use asn1_rs::{ApplicationImplicit, Error, FromBer, Integer, TaggedValue};
///
/// let bytes = &[0x60, 0x1, 0x2];
///
/// let (_, tagged) = ApplicationImplicit::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2_u8)));
/// ```
pub type ApplicationImplicit<T, E, const TAG: u32> =
TaggedValue<T, E, Implicit, { Class::APPLICATION }, TAG>;

View File

@@ -0,0 +1,110 @@
use super::{Error, Explicit, Implicit, TaggedParser};
use crate::{Class, FromBer, FromDer, ParseResult, Tag};
use core::marker::PhantomData;
/// A builder for parsing tagged values (`IMPLICIT` or `EXPLICIT`)
///
/// # Examples
///
/// ```
/// use asn1_rs::{Class, Tag, TaggedParserBuilder};
///
/// let parser = TaggedParserBuilder::explicit()
/// .with_class(Class::ContextSpecific)
/// .with_tag(Tag(0))
/// .der_parser::<u32>();
///
/// let input = &[0xa0, 0x03, 0x02, 0x01, 0x02];
/// let (rem, tagged) = parser(input).expect("parsing failed");
///
/// assert!(rem.is_empty());
/// assert_eq!(tagged.tag(), Tag(0));
/// assert_eq!(tagged.as_ref(), &2);
/// ```
#[derive(Clone, Copy, Debug)]
pub struct TaggedParserBuilder<TagKind, E = Error> {
class: Class,
tag: Tag,
tag_kind: PhantomData<TagKind>,
_e: PhantomData<E>,
}
impl<TagKind, E> Default for TaggedParserBuilder<TagKind, E> {
fn default() -> Self {
Self::new()
}
}
impl<TagKind, E> TaggedParserBuilder<TagKind, E> {
/// Create a default `TaggedParserBuilder` builder
///
/// `TagKind` must be specified as either [`Explicit`] or [`Implicit`]
///
/// ```
/// use asn1_rs::{Explicit, TaggedParserBuilder};
///
/// let builder = TaggedParserBuilder::<Explicit>::new();
/// ```
pub const fn new() -> Self {
TaggedParserBuilder {
class: Class::Universal,
tag: Tag(0),
tag_kind: PhantomData,
_e: PhantomData,
}
}
/// Set the expected `Class` for the builder
pub const fn with_class(self, class: Class) -> Self {
Self { class, ..self }
}
/// Set the expected `Tag` for the builder
pub const fn with_tag(self, tag: Tag) -> Self {
Self { tag, ..self }
}
}
impl<E> TaggedParserBuilder<Explicit, E> {
/// Create a `TagParser` builder for `EXPLICIT` tagged values
pub const fn explicit() -> Self {
TaggedParserBuilder::new()
}
}
impl<E> TaggedParserBuilder<Implicit, E> {
/// Create a `TagParser` builder for `IMPLICIT` tagged values
pub const fn implicit() -> Self {
TaggedParserBuilder::new()
}
}
impl<TagKind, E> TaggedParserBuilder<TagKind, E> {
/// Create the BER parser from the builder parameters
///
/// This method will consume the builder and return a parser (to be used as a function).
pub fn ber_parser<'a, T>(
self,
) -> impl Fn(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, TagKind, T, E>, E>
where
TaggedParser<'a, TagKind, T, E>: FromBer<'a, E>,
E: From<Error>,
{
move |bytes: &[u8]| TaggedParser::<TagKind, T, E>::parse_ber(self.class, self.tag, bytes)
}
}
impl<TagKind, E> TaggedParserBuilder<TagKind, E> {
/// Create the DER parser from the builder parameters
///
/// This method will consume the builder and return a parser (to be used as a function).
pub fn der_parser<'a, T>(
self,
) -> impl Fn(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, TagKind, T, E>, E>
where
TaggedParser<'a, TagKind, T, E>: FromDer<'a, E>,
E: From<Error>,
{
move |bytes: &[u8]| TaggedParser::<TagKind, T, E>::parse_der(self.class, self.tag, bytes)
}
}

View File

@@ -0,0 +1,262 @@
use crate::*;
use core::convert::TryFrom;
use core::marker::PhantomData;
impl<'a, T, E, const CLASS: u8, const TAG: u32> TryFrom<Any<'a>>
for TaggedValue<T, E, Explicit, CLASS, TAG>
where
T: FromBer<'a, E>,
E: From<Error>,
{
type Error = E;
fn try_from(any: Any<'a>) -> Result<Self, E> {
Self::try_from(&any)
}
}
impl<'a, 'b, T, E, const CLASS: u8, const TAG: u32> TryFrom<&'b Any<'a>>
for TaggedValue<T, E, Explicit, CLASS, TAG>
where
T: FromBer<'a, E>,
E: From<Error>,
{
type Error = E;
fn try_from(any: &'b Any<'a>) -> Result<Self, E> {
any.tag().assert_eq(Tag(TAG))?;
any.header.assert_constructed()?;
if any.class() as u8 != CLASS {
let class = Class::try_from(CLASS).ok();
return Err(Error::unexpected_class(class, any.class()).into());
}
let (_, inner) = match T::from_ber(any.data) {
Ok((rem, res)) => (rem, res),
Err(Err::Error(e)) | Err(Err::Failure(e)) => return Err(e),
Err(Err::Incomplete(n)) => return Err(Error::Incomplete(n).into()),
};
Ok(TaggedValue::explicit(inner))
}
}
impl<'a, T, E, const CLASS: u8, const TAG: u32> FromDer<'a, E>
for TaggedValue<T, E, Explicit, CLASS, TAG>
where
T: FromDer<'a, E>,
E: From<Error>,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?;
any.tag()
.assert_eq(Tag(TAG))
.map_err(|e| Err::Error(e.into()))?;
any.header
.assert_constructed()
.map_err(|e| Err::Error(e.into()))?;
if any.class() as u8 != CLASS {
let class = Class::try_from(CLASS).ok();
return Err(Err::Error(
Error::unexpected_class(class, any.class()).into(),
));
}
let (_, inner) = T::from_der(any.data)?;
Ok((rem, TaggedValue::explicit(inner)))
}
}
impl<T, E, const CLASS: u8, const TAG: u32> CheckDerConstraints
for TaggedValue<T, E, Explicit, CLASS, TAG>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.header.length.assert_definite()?;
let (_, inner) = Any::from_ber(any.data)?;
T::check_constraints(&inner)?;
Ok(())
}
}
#[cfg(feature = "std")]
impl<T, E, const CLASS: u8, const TAG: u32> ToDer for TaggedValue<T, E, Explicit, CLASS, TAG>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
let sz = self.inner.to_der_len()?;
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let inner_len = self.inner.to_der_len()?;
let class =
Class::try_from(CLASS).map_err(|_| SerializeError::InvalidClass { class: CLASS })?;
let header = Header::new(class, true, self.tag(), Length::Definite(inner_len));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
self.inner.write_der(writer)
}
}
/// A helper object to parse `[ n ] EXPLICIT T`
///
/// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged
/// optional values.
///
/// This helper expects context-specific tags.
/// See [`TaggedValue`] or [`TaggedParser`] for more generic implementations if needed.
///
/// # Examples
///
/// To parse a `[0] EXPLICIT INTEGER` object:
///
/// ```rust
/// use asn1_rs::{Error, FromBer, Integer, TaggedExplicit, TaggedValue};
///
/// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2];
///
/// // If tagged object is present (and has expected tag), parsing succeeds:
/// let (_, tagged) = TaggedExplicit::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2)));
/// ```
pub type TaggedExplicit<T, E, const TAG: u32> = TaggedValue<T, E, Explicit, CONTEXT_SPECIFIC, TAG>;
// implementations for TaggedParser
impl<'a, T, E> TaggedParser<'a, Explicit, T, E> {
pub const fn new_explicit(class: Class, tag: u32, inner: T) -> Self {
Self {
header: Header::new(class, true, Tag(tag), Length::Definite(0)),
inner,
tag_kind: PhantomData,
_e: PhantomData,
}
}
/// Parse a BER tagged value and apply the provided parsing function to content
///
/// After parsing, the sequence object and header are discarded.
///
/// Note: this function is provided for `Explicit`, but there is not difference between
/// explicit or implicit tags. The `op` function is responsible of handling the content.
#[inline]
pub fn from_ber_and_then<F>(
class: Class,
tag: u32,
bytes: &'a [u8],
op: F,
) -> ParseResult<'a, T, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
Any::from_ber_and_then(class, tag, bytes, op)
}
/// Parse a DER tagged value and apply the provided parsing function to content
///
/// After parsing, the sequence object and header are discarded.
///
/// Note: this function is provided for `Explicit`, but there is not difference between
/// explicit or implicit tags. The `op` function is responsible of handling the content.
#[inline]
pub fn from_der_and_then<F>(
class: Class,
tag: u32,
bytes: &'a [u8],
op: F,
) -> ParseResult<'a, T, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
Any::from_der_and_then(class, tag, bytes, op)
}
}
impl<'a, T, E> FromBer<'a, E> for TaggedParser<'a, Explicit, T, E>
where
T: FromBer<'a, E>,
E: From<Error>,
{
fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?;
let header = any.header;
let (_, inner) = T::from_ber(any.data)?;
let tagged = TaggedParser {
header,
inner,
tag_kind: PhantomData,
_e: PhantomData,
};
Ok((rem, tagged))
}
}
impl<'a, T, E> FromDer<'a, E> for TaggedParser<'a, Explicit, T, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?;
let header = any.header;
let (_, inner) = T::from_der(any.data)?;
let tagged = TaggedParser {
header,
inner,
tag_kind: PhantomData,
_e: PhantomData,
};
Ok((rem, tagged))
}
}
impl<T> CheckDerConstraints for TaggedParser<'_, Explicit, T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.header.length.assert_definite()?;
let (_, inner_any) = Any::from_der(any.data)?;
T::check_constraints(&inner_any)?;
Ok(())
}
}
#[cfg(feature = "std")]
impl<T> ToDer for TaggedParser<'_, Explicit, T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
let sz = self.inner.to_der_len()?;
if sz < 127 {
// 1 (class+tag) + 1 (length) + len
Ok(2 + sz)
} else {
// 1 (class+tag) + n (length) + len
let n = Length::Definite(sz).to_der_len()?;
Ok(1 + n + sz)
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let inner_len = self.inner.to_der_len()?;
let header = Header::new(self.class(), true, self.tag(), Length::Definite(inner_len));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
self.inner.write_der(writer)
}
}

View File

@@ -0,0 +1,101 @@
use super::{Explicit, Implicit, TaggedParser};
use crate::{Any, Error, FromDer, Header, ParseResult, Tag, Tagged};
use nom::error::ParseError;
use nom::{Err, IResult};
// helper functions for parsing tagged objects
pub fn parse_der_tagged_explicit<'a, IntoTag, T, E>(
tag: IntoTag,
) -> impl FnMut(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, Explicit, T, E>, E>
where
IntoTag: Into<Tag>,
TaggedParser<'a, Explicit, T, E>: FromDer<'a, E>,
E: From<Error>,
{
let tag = tag.into();
move |i| {
let (rem, tagged) = TaggedParser::from_der(i)?;
tagged.assert_tag(tag).map_err(|e| Err::Error(e.into()))?;
Ok((rem, tagged))
}
}
pub fn parse_der_tagged_explicit_g<'a, IntoTag, T, F, E>(
tag: IntoTag,
f: F,
) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T, E>
where
F: Fn(&'a [u8], Header<'a>) -> IResult<&'a [u8], T, E>,
E: ParseError<&'a [u8]> + From<Error>,
IntoTag: Into<Tag>,
{
let tag = tag.into();
parse_der_container(tag, move |any: Any<'a>| {
any.header
.assert_tag(tag)
.map_err(|e| Err::convert(e.into()))?;
f(any.data, any.header)
})
}
pub fn parse_der_tagged_implicit<'a, IntoTag, T, E>(
tag: IntoTag,
) -> impl FnMut(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, Implicit, T, E>, E>
where
IntoTag: Into<Tag>,
// T: TryFrom<Any<'a>, Error = Error> + Tagged,
TaggedParser<'a, Implicit, T, E>: FromDer<'a, E>,
E: From<Error>,
{
let tag = tag.into();
move |i| {
let (rem, tagged) = TaggedParser::from_der(i)?;
tagged.assert_tag(tag).map_err(|e| Err::convert(e.into()))?;
Ok((rem, tagged))
}
}
pub fn parse_der_tagged_implicit_g<'a, IntoTag, T, F, E>(
tag: IntoTag,
f: F,
) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T, E>
where
F: Fn(&'a [u8], Tag, Header<'a>) -> IResult<&'a [u8], T, E>,
E: ParseError<&'a [u8]> + From<Error>,
IntoTag: Into<Tag>,
T: Tagged,
{
let tag = tag.into();
parse_der_container(tag, move |any: Any<'a>| {
// verify tag of external header
any.header
.assert_tag(tag)
.map_err(|e| Err::convert(e.into()))?;
// build a fake header with the expected tag
let Any { header, data } = any;
let header = Header {
tag: T::TAG,
..header.clone()
};
f(data, tag, header)
})
}
fn parse_der_container<'a, T, F, E>(
tag: Tag,
f: F,
) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T, E>
where
F: Fn(Any<'a>) -> IResult<&'a [u8], T, E>,
E: ParseError<&'a [u8]> + From<Error>,
{
move |i: &[u8]| {
let (rem, any) = Any::from_der(i).map_err(Err::convert)?;
any.header
.assert_tag(tag)
.map_err(|e| Err::convert(e.into()))?;
let (_, output) = f(any)?;
Ok((rem, output))
}
}

View File

@@ -0,0 +1,287 @@
use crate::*;
use core::convert::TryFrom;
use core::marker::PhantomData;
impl<'a, T, E, const CLASS: u8, const TAG: u32> TryFrom<Any<'a>>
for TaggedValue<T, E, Implicit, CLASS, TAG>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
type Error = E;
fn try_from(any: Any<'a>) -> Result<Self, E> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b, E, T, const CLASS: u8, const TAG: u32> TryFrom<&'b Any<'a>>
for TaggedValue<T, E, Implicit, CLASS, TAG>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
type Error = E;
fn try_from(any: &'b Any<'a>) -> Result<Self, E> {
any.tag().assert_eq(Tag(TAG))?;
// XXX if input is empty, this function is not called
if any.class() as u8 != CLASS {
let class = Class::try_from(CLASS).ok();
return Err(Error::unexpected_class(class, any.class()).into());
}
let any = Any {
header: Header {
tag: T::TAG,
..any.header.clone()
},
data: any.data,
};
match T::try_from(any) {
Ok(inner) => Ok(TaggedValue::implicit(inner)),
Err(e) => Err(e),
}
}
}
impl<'a, T, E, const CLASS: u8, const TAG: u32> FromDer<'a, E>
for TaggedValue<T, E, Implicit, CLASS, TAG>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?;
any.tag()
.assert_eq(Tag(TAG))
.map_err(|e| Err::Error(e.into()))?;
if any.class() as u8 != CLASS {
let class = Class::try_from(CLASS).ok();
return Err(Err::Error(
Error::unexpected_class(class, any.class()).into(),
));
}
let any = Any {
header: Header {
tag: T::TAG,
..any.header.clone()
},
data: any.data,
};
match T::try_from(any) {
Ok(inner) => Ok((rem, TaggedValue::implicit(inner))),
Err(e) => Err(Err::Error(e)),
}
}
}
impl<T, E, const CLASS: u8, const TAG: u32> CheckDerConstraints
for TaggedValue<T, E, Implicit, CLASS, TAG>
where
T: CheckDerConstraints,
T: Tagged,
{
fn check_constraints(any: &Any) -> Result<()> {
any.header.length.assert_definite()?;
let header = any.header.clone().with_tag(T::TAG);
let inner = Any::new(header, any.data);
T::check_constraints(&inner)?;
Ok(())
}
}
#[cfg(feature = "std")]
impl<T, E, const CLASS: u8, const TAG: u32> ToDer for TaggedValue<T, E, Implicit, CLASS, TAG>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
self.inner.to_der_len()
}
fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let class =
Class::try_from(CLASS).map_err(|_| SerializeError::InvalidClass { class: CLASS })?;
let mut v = Vec::new();
let inner_len = self.inner.write_der_content(&mut v)?;
// XXX X.690 section 8.14.3: if implicing tagging was used [...]:
// XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise
let constructed = matches!(self.inner.tag(), Tag::Sequence | Tag::Set);
let header = Header::new(class, constructed, self.tag(), Length::Definite(inner_len));
let sz = header.write_der_header(writer)?;
let sz = sz + writer.write(&v)?;
Ok(sz)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut sink = std::io::sink();
let class =
Class::try_from(CLASS).map_err(|_| SerializeError::InvalidClass { class: CLASS })?;
let inner_len = self.inner.write_der_content(&mut sink)?;
// XXX X.690 section 8.14.3: if implicing tagging was used [...]:
// XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise
let constructed = matches!(self.inner.tag(), Tag::Sequence | Tag::Set);
let header = Header::new(class, constructed, self.tag(), Length::Definite(inner_len));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
self.inner.write_der_content(writer)
}
}
/// A helper object to parse `[ n ] IMPLICIT T`
///
/// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged
/// optional values.
///
/// This helper expects context-specific tags.
/// See [`TaggedValue`] or [`TaggedParser`] for more generic implementations if needed.
///
/// # Examples
///
/// To parse a `[0] IMPLICIT INTEGER OPTIONAL` object:
///
/// ```rust
/// use asn1_rs::{Error, FromBer, Integer, TaggedImplicit, TaggedValue};
///
/// let bytes = &[0xa0, 0x1, 0x2];
///
/// let (_, tagged) = TaggedImplicit::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2)));
/// ```
pub type TaggedImplicit<T, E, const TAG: u32> = TaggedValue<T, E, Implicit, CONTEXT_SPECIFIC, TAG>;
impl<'a, T, E> FromBer<'a, E> for TaggedParser<'a, Implicit, T, E>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?;
let Any { header, data } = any;
let any = Any {
header: Header {
tag: T::TAG,
..header.clone()
},
data,
};
match T::try_from(any) {
Ok(t) => {
let tagged_value = TaggedParser {
header,
inner: t,
tag_kind: PhantomData,
_e: PhantomData,
};
Ok((rem, tagged_value))
}
Err(e) => Err(Err::Error(e)),
}
}
}
// implementations for TaggedParser
impl<T, E> TaggedParser<'_, Implicit, T, E> {
pub const fn new_implicit(class: Class, constructed: bool, tag: u32, inner: T) -> Self {
Self {
header: Header::new(class, constructed, Tag(tag), Length::Definite(0)),
inner,
tag_kind: PhantomData,
_e: PhantomData,
}
}
}
impl<'a, T, E> FromDer<'a, E> for TaggedParser<'a, Implicit, T, E>
where
T: TryFrom<Any<'a>, Error = E>,
T: CheckDerConstraints,
T: Tagged,
E: From<Error>,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?;
let Any { header, data } = any;
let any = Any {
header: Header {
tag: T::TAG,
..header.clone()
},
data,
};
T::check_constraints(&any).map_err(|e| Err::Error(e.into()))?;
match T::try_from(any) {
Ok(t) => {
let tagged_value = TaggedParser {
header,
inner: t,
tag_kind: PhantomData,
_e: PhantomData,
};
Ok((rem, tagged_value))
}
Err(e) => Err(Err::Error(e)),
}
}
}
impl<T> CheckDerConstraints for TaggedParser<'_, Implicit, T>
where
T: CheckDerConstraints,
T: Tagged,
{
fn check_constraints(any: &Any) -> Result<()> {
any.header.length.assert_definite()?;
let any = Any {
header: Header {
tag: T::TAG,
..any.header.clone()
},
data: any.data,
};
T::check_constraints(&any)?;
Ok(())
}
}
#[cfg(feature = "std")]
impl<T> ToDer for TaggedParser<'_, Implicit, T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
self.inner.to_der_len()
}
fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut v = Vec::new();
let inner_len = self.inner.write_der_content(&mut v)?;
// XXX X.690 section 8.14.3: if implicing tagging was used [...]:
// XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise
let header = Header::new(self.class(), false, self.tag(), Length::Definite(inner_len));
let sz = header.write_der_header(writer)?;
let sz = sz + writer.write(&v)?;
Ok(sz)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let mut sink = std::io::sink();
let inner_len = self.inner.write_der_content(&mut sink)?;
// XXX X.690 section 8.14.3: if implicing tagging was used [...]:
// XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise
let header = Header::new(self.class(), false, self.tag(), Length::Definite(inner_len));
header.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
self.inner.write_der_content(writer)
}
}

View File

@@ -0,0 +1,239 @@
use crate::*;
/// Helper object to parse TAGGED OPTIONAL types (explicit or implicit)
///
/// This object can be used similarly to a builder pattern, to specify the expected class and
/// tag of the object to parse, and the content parsing function.
///
/// The content parsing function takes two arguments: the outer header, and the data.
///
/// It can be used for both EXPLICIT or IMPLICIT tagged objects by using parsing functions that
/// expect a header (or not) in the contents.
///
/// The [`OptTaggedParser::from`] method is a shortcut to build an object with `ContextSpecific`
/// class and the given tag. The [`OptTaggedParser::new`] method is more generic.
///
/// See also [`OptTaggedExplicit`] and [`OptTaggedImplicit`] for alternatives that implement [`FromBer`]/
/// [`FromDer`].
///
/// # Examples
///
/// To parse a `[APPLICATION 0] EXPLICIT INTEGER OPTIONAL` object:
///
/// ```rust
/// use asn1_rs::{Class, FromDer, Integer, Tag, OptTaggedParser};
///
/// let bytes = &[0x60, 0x03, 0x2, 0x1, 0x2];
///
/// let (_, tagged) = OptTaggedParser::new(Class::Application, Tag(0))
/// .parse_der(bytes, |_, data| Integer::from_der(data))
/// .unwrap();
///
/// assert_eq!(tagged, Some(Integer::from(2)));
/// ```
///
/// To parse a `[0] IMPLICIT INTEGER OPTIONAL` object:
///
/// ```rust
/// use asn1_rs::{Error, Integer, OptTaggedParser};
///
/// let bytes = &[0xa0, 0x1, 0x2];
///
/// let (_, tagged) = OptTaggedParser::from(0)
/// .parse_der::<_, Error, _>(bytes, |_, data| Ok((&[], Integer::new(data))))
/// .unwrap();
///
/// assert_eq!(tagged, Some(Integer::from(2)));
/// ```
#[derive(Debug)]
pub struct OptTaggedParser {
/// The expected class for the object to parse
pub class: Class,
/// The expected tag for the object to parse
pub tag: Tag,
}
impl OptTaggedParser {
/// Build a new `OptTaggedParser` object.
///
/// If using `Class::ContextSpecific`, using [`OptTaggedParser::from`] with either a `Tag` or `u32` is
/// a shorter way to build this object.
pub const fn new(class: Class, tag: Tag) -> Self {
OptTaggedParser { class, tag }
}
pub const fn universal(tag: u32) -> Self {
Self::new(Class::Universal, Tag(tag))
}
pub const fn tagged(tag: u32) -> Self {
Self::new(Class::ContextSpecific, Tag(tag))
}
pub const fn application(tag: u32) -> Self {
Self::new(Class::Application, Tag(tag))
}
pub const fn private(tag: u32) -> Self {
Self::new(Class::Private, Tag(tag))
}
/// Parse input as BER, and apply the provided function to parse object.
///
/// Returns the remaining bytes, and `Some(T)` if expected tag was found, else `None`.
///
/// This function returns an error if tag was found but has a different class, or if parsing fails.
///
/// # Examples
///
/// To parse a `[0] EXPLICIT INTEGER OPTIONAL` object:
///
/// ```rust
/// use asn1_rs::{FromBer, Integer, OptTaggedParser};
///
/// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2];
///
/// let (_, tagged) = OptTaggedParser::from(0)
/// .parse_ber(bytes, |_, data| Integer::from_ber(data))
/// .unwrap();
///
/// assert_eq!(tagged, Some(Integer::from(2)));
/// ```
pub fn parse_ber<'a, T, E, F>(&self, bytes: &'a [u8], f: F) -> ParseResult<'a, Option<T>, E>
where
F: Fn(Header, &'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
if bytes.is_empty() {
return Ok((bytes, None));
}
let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?;
if any.tag() != self.tag {
return Ok((bytes, None));
}
if any.class() != self.class {
return Err(Err::Error(
Error::unexpected_class(Some(self.class), any.class()).into(),
));
}
let Any { header, data } = any;
let (_, res) = f(header, data)?;
Ok((rem, Some(res)))
}
/// Parse input as DER, and apply the provided function to parse object.
///
/// Returns the remaining bytes, and `Some(T)` if expected tag was found, else `None`.
///
/// This function returns an error if tag was found but has a different class, or if parsing fails.
///
/// # Examples
///
/// To parse a `[0] EXPLICIT INTEGER OPTIONAL` object:
///
/// ```rust
/// use asn1_rs::{FromDer, Integer, OptTaggedParser};
///
/// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2];
///
/// let (_, tagged) = OptTaggedParser::from(0)
/// .parse_der(bytes, |_, data| Integer::from_der(data))
/// .unwrap();
///
/// assert_eq!(tagged, Some(Integer::from(2)));
/// ```
pub fn parse_der<'a, T, E, F>(&self, bytes: &'a [u8], f: F) -> ParseResult<'a, Option<T>, E>
where
F: Fn(Header, &'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
if bytes.is_empty() {
return Ok((bytes, None));
}
let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?;
if any.tag() != self.tag {
return Ok((bytes, None));
}
if any.class() != self.class {
return Err(Err::Error(
Error::unexpected_class(Some(self.class), any.class()).into(),
));
}
let Any { header, data } = any;
let (_, res) = f(header, data)?;
Ok((rem, Some(res)))
}
}
impl From<Tag> for OptTaggedParser {
/// Build a `TaggedOptional` object with class `ContextSpecific` and given tag
#[inline]
fn from(tag: Tag) -> Self {
OptTaggedParser::new(Class::ContextSpecific, tag)
}
}
impl From<u32> for OptTaggedParser {
/// Build a `TaggedOptional` object with class `ContextSpecific` and given tag
#[inline]
fn from(tag: u32) -> Self {
OptTaggedParser::new(Class::ContextSpecific, Tag(tag))
}
}
/// A helper object to parse `[ n ] EXPLICIT T OPTIONAL`
///
/// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged
/// optional values.
///
/// This helper expects context-specific tags.
/// Use `Option<` [`TaggedValue`] `>` for a more generic implementation.
///
/// # Examples
///
/// To parse a `[0] EXPLICIT INTEGER OPTIONAL` object:
///
/// ```rust
/// use asn1_rs::{Error, FromBer, Integer, OptTaggedExplicit, TaggedValue};
///
/// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2];
///
/// // If tagged object is present (and has expected tag), parsing succeeds:
/// let (_, tagged) = OptTaggedExplicit::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, Some(TaggedValue::explicit(Integer::from(2))));
///
/// // If tagged object is not present or has different tag, parsing
/// // also succeeds (returning None):
/// let (_, tagged) = OptTaggedExplicit::<Integer, Error, 0>::from_ber(&[]).unwrap();
/// assert_eq!(tagged, None);
/// let (_, tagged) = OptTaggedExplicit::<Integer, Error, 1>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, None);
/// ```
pub type OptTaggedExplicit<T, E, const TAG: u32> = Option<TaggedExplicit<T, E, TAG>>;
/// A helper object to parse `[ n ] IMPLICIT T OPTIONAL`
///
/// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged
/// optional values.
///
/// This helper expects context-specific tags.
/// Use `Option<` [`TaggedValue`] `>` for a more generic implementation.
///
/// # Examples
///
/// To parse a `[0] IMPLICIT INTEGER OPTIONAL` object:
///
/// ```rust
/// use asn1_rs::{Error, FromBer, Integer, OptTaggedImplicit, TaggedValue};
///
/// let bytes = &[0xa0, 0x1, 0x2];
///
/// let (_, tagged) = OptTaggedImplicit::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, Some(TaggedValue::implicit(Integer::from(2))));
///
/// // If tagged object is not present or has different tag, parsing
/// // also succeeds (returning None):
/// let (_, tagged) = OptTaggedImplicit::<Integer, Error, 0>::from_ber(&[]).unwrap();
/// assert_eq!(tagged, None);
/// ```
pub type OptTaggedImplicit<T, E, const TAG: u32> = Option<TaggedImplicit<T, E, TAG>>;

View File

@@ -0,0 +1,78 @@
use crate::*;
use core::marker::PhantomData;
#[derive(Debug, PartialEq, Eq)]
pub struct TaggedParser<'a, TagKind, T, E = Error> {
pub header: Header<'a>,
pub inner: T,
pub(crate) tag_kind: PhantomData<TagKind>,
pub(crate) _e: PhantomData<E>,
}
impl<'a, TagKind, T, E> TaggedParser<'a, TagKind, T, E> {
pub const fn new(header: Header<'a>, inner: T) -> Self {
TaggedParser {
header,
inner,
tag_kind: PhantomData,
_e: PhantomData,
}
}
pub const fn assert_class(&self, class: Class) -> Result<()> {
self.header.assert_class(class)
}
pub const fn assert_tag(&self, tag: Tag) -> Result<()> {
self.header.assert_tag(tag)
}
#[inline]
pub const fn class(&self) -> Class {
self.header.class
}
#[inline]
pub const fn tag(&self) -> Tag {
self.header.tag
}
}
impl<TagKind, T, E> AsRef<T> for TaggedParser<'_, TagKind, T, E> {
fn as_ref(&self) -> &T {
&self.inner
}
}
impl<'a, TagKind, T, E> TaggedParser<'a, TagKind, T, E>
where
Self: FromBer<'a, E>,
E: From<Error>,
{
pub fn parse_ber(class: Class, tag: Tag, bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, t) = TaggedParser::<TagKind, T, E>::from_ber(bytes)?;
t.assert_class(class).map_err(|e| Err::Error(e.into()))?;
t.assert_tag(tag).map_err(|e| Err::Error(e.into()))?;
Ok((rem, t))
}
}
impl<'a, TagKind, T, E> TaggedParser<'a, TagKind, T, E>
where
Self: FromDer<'a, E>,
E: From<Error>,
{
pub fn parse_der(class: Class, tag: Tag, bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, t) = TaggedParser::<TagKind, T, E>::from_der(bytes)?;
t.assert_class(class).map_err(|e| Err::Error(e.into()))?;
t.assert_tag(tag).map_err(|e| Err::Error(e.into()))?;
Ok((rem, t))
}
}
impl<TagKind, T, E> DynTagged for TaggedParser<'_, TagKind, T, E> {
fn tag(&self) -> Tag {
self.tag()
}
}

View File

@@ -0,0 +1,42 @@
use crate::{Class, Explicit, Implicit, TaggedValue};
/// A helper object to parse `[PRIVATE n] EXPLICIT T`
///
/// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to
/// parse explicit private-tagged values.
///
/// # Examples
///
/// To parse a `[PRIVATE 0] EXPLICIT INTEGER` object:
///
/// ```rust
/// use asn1_rs::{Error, FromBer, Integer, PrivateExplicit, TaggedValue};
///
/// let bytes = &[0xe0, 0x03, 0x2, 0x1, 0x2];
///
/// // If tagged object is present (and has expected tag), parsing succeeds:
/// let (_, tagged) = PrivateExplicit::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2)));
/// ```
pub type PrivateExplicit<T, E, const TAG: u32> =
TaggedValue<T, E, Explicit, { Class::PRIVATE }, TAG>;
/// A helper object to parse `[PRIVATE n] IMPLICIT T`
///
/// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to
/// parse implicit private-tagged values.
///
/// # Examples
///
/// To parse a `[PRIVATE 0] IMPLICIT INTEGER` object:
///
/// ```rust
/// use asn1_rs::{Error, FromBer, Integer, PrivateImplicit, TaggedValue};
///
/// let bytes = &[0xe0, 0x1, 0x2];
///
/// let (_, tagged) = PrivateImplicit::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2_u8)));
/// ```
pub type PrivateImplicit<T, E, const TAG: u32> =
TaggedValue<T, E, Implicit, { Class::PRIVATE }, TAG>;

221
vendor/asn1-rs/src/asn1_types/utctime.rs vendored Normal file
View File

@@ -0,0 +1,221 @@
use crate::*;
use core::convert::TryFrom;
use core::fmt;
#[cfg(feature = "datetime")]
use time::OffsetDateTime;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct UtcTime(pub ASN1DateTime);
impl UtcTime {
pub const fn new(datetime: ASN1DateTime) -> Self {
UtcTime(datetime)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
// X.680 section 43 defines a UniversalTime as a VisibleString restricted to:
//
// a) the six digits YYMMDD where YY is the two low-order digits of the Christian year, MM is the month
// (counting January as 01), and DD is the day of the month (01 to 31); and
// b) either:
// 1) the four digits hhmm where hh is hour (00 to 23) and mm is minutes (00 to 59); or
// 2) the six digits hhmmss where hh and mm are as in 1) above, and ss is seconds (00 to 59); and
// c) either:
// 1) the character Z ; or
// 2) one of the characters + or - , followed by hhmm, where hh is hour and mm is minutes.
//
// XXX // RFC 5280 requires mandatory seconds and Z-normalized time zone
let (year, month, day, hour, minute, rem) = match bytes {
[year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, rem @ ..] => {
let year = decode_decimal(Self::TAG, *year1, *year2)?;
let month = decode_decimal(Self::TAG, *mon1, *mon2)?;
let day = decode_decimal(Self::TAG, *day1, *day2)?;
let hour = decode_decimal(Self::TAG, *hour1, *hour2)?;
let minute = decode_decimal(Self::TAG, *min1, *min2)?;
(year, month, day, hour, minute, rem)
}
_ => return Err(Self::TAG.invalid_value("malformed time string (not yymmddhhmm)")),
};
if rem.is_empty() {
return Err(Self::TAG.invalid_value("malformed time string"));
}
// check for seconds
let (second, rem) = match rem {
[sec1, sec2, rem @ ..] => {
let second = decode_decimal(Self::TAG, *sec1, *sec2)?;
(second, rem)
}
_ => (0, rem),
};
if month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59 {
return Err(Self::TAG.invalid_value("time components with invalid values"));
}
if rem.is_empty() {
return Err(Self::TAG.invalid_value("malformed time string"));
}
let tz = match rem {
[b'Z'] => ASN1TimeZone::Z,
[b'+', h1, h2, m1, m2] => {
let hh = decode_decimal(Self::TAG, *h1, *h2)?;
let mm = decode_decimal(Self::TAG, *m1, *m2)?;
ASN1TimeZone::Offset(hh as i8, mm as i8)
}
[b'-', h1, h2, m1, m2] => {
let hh = decode_decimal(Self::TAG, *h1, *h2)?;
let mm = decode_decimal(Self::TAG, *m1, *m2)?;
ASN1TimeZone::Offset(-(hh as i8), mm as i8)
}
_ => return Err(Self::TAG.invalid_value("malformed time string: no time zone")),
};
Ok(UtcTime(ASN1DateTime::new(
year as u32,
month,
day,
hour,
minute,
second,
None,
tz,
)))
// match *bytes {
// [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => {
// let year = decode_decimal(Self::TAG, year1, year2)?;
// let month = decode_decimal(Self::TAG, mon1, mon2)?;
// let day = decode_decimal(Self::TAG, day1, day2)?;
// let hour = decode_decimal(Self::TAG, hour1, hour2)?;
// let minute = decode_decimal(Self::TAG, min1, min2)?;
// let second = decode_decimal(Self::TAG, sec1, sec2)?;
// // RFC 5280 rules for interpreting the year
// let year = if year >= 50 { year + 1900 } else { year + 2000 };
// Ok(UtcTime::new(year, month, day, hour, minute, second))
// }
// _ => Err(Error::InvalidValue),
// }
}
/// Return a ISO 8601 combined date and time with time zone.
#[cfg(feature = "datetime")]
#[cfg_attr(docsrs, doc(cfg(feature = "datetime")))]
#[inline]
pub fn utc_datetime(&self) -> Result<OffsetDateTime> {
self.0.to_datetime()
}
/// Return an adjusted ISO 8601 combined date and time with time zone.
/// According to Universal time definition in X.680 we add 2000 years
/// from 0 to 49 year and 1900 otherwise.
#[cfg(feature = "datetime")]
#[cfg_attr(docsrs, doc(cfg(feature = "datetime")))]
#[inline]
pub fn utc_adjusted_datetime(&self) -> Result<OffsetDateTime> {
self.0.to_datetime().and_then(|dt| {
let year = dt.year();
// We follow the Universal time definition in X.680 for interpreting
// the adjusted year
let year = if year >= 50 { year + 1900 } else { year + 2000 };
time::Date::from_calendar_date(year, dt.month(), dt.day())
.map(|d| dt.replace_date(d))
.map_err(|_e| Self::TAG.invalid_value("Invalid adjusted date"))
})
}
/// Returns the number of non-leap seconds since the midnight on January 1, 1970.
#[cfg(feature = "datetime")]
#[cfg_attr(docsrs, doc(cfg(feature = "datetime")))]
pub fn timestamp(&self) -> Result<i64> {
let dt = self.0.to_datetime()?;
Ok(dt.unix_timestamp())
}
}
impl<'a> TryFrom<Any<'a>> for UtcTime {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<UtcTime> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for UtcTime {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<UtcTime> {
any.tag().assert_eq(Self::TAG)?;
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_visible(b: &u8) -> bool {
0x20 <= *b && *b <= 0x7f
}
if !any.data.iter().all(is_visible) {
return Err(Error::StringInvalidCharset);
}
UtcTime::from_bytes(any.data)
}
}
impl fmt::Display for UtcTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let dt = &self.0;
match dt.tz {
ASN1TimeZone::Z | ASN1TimeZone::Undefined => write!(
f,
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}Z",
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second
),
ASN1TimeZone::Offset(hh, mm) => {
let (s, hh) = if hh > 0 { ('+', hh) } else { ('-', -hh) };
write!(
f,
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}{:02}{:02}",
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, s, hh, mm
)
}
}
}
}
impl CheckDerConstraints for UtcTime {
fn check_constraints(_any: &Any) -> Result<()> {
Ok(())
}
}
impl DerAutoDerive for UtcTime {}
impl Tagged for UtcTime {
const TAG: Tag = Tag::UtcTime;
}
#[cfg(feature = "std")]
impl ToDer for UtcTime {
fn to_der_len(&self) -> Result<usize> {
// data:
// - 6 bytes for YYMMDD
// - 6 for hhmmss in DER (X.690 section 11.8.2)
// - 1 for the character Z in DER (X.690 section 11.8.1)
// data length: 13
//
// thus, length will always be on 1 byte (short length) and
// class+structure+tag also on 1
//
// total: 15 = 1 (class+constructed+tag) + 1 (length) + 13
Ok(15)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// see above for length value
writer.write(&[Self::TAG.0 as u8, 13]).map_err(Into::into)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
write!(
writer,
"{:02}{:02}{:02}{:02}{:02}{:02}Z",
self.0.year, self.0.month, self.0.day, self.0.hour, self.0.minute, self.0.second,
)?;
// write_fmt returns (), see above for length value
Ok(13)
}
}

3
vendor/asn1-rs/src/ber/mod.rs vendored Normal file
View File

@@ -0,0 +1,3 @@
mod parser;
pub use parser::*;

169
vendor/asn1-rs/src/ber/parser.rs vendored Normal file
View File

@@ -0,0 +1,169 @@
use crate::error::*;
use crate::header::*;
use crate::{BerParser, DerParser, FromBer, Length, Tag};
use nom::bytes::streaming::take;
use nom::{Err, Needed, Offset};
use rusticata_macros::custom_check;
/// Default maximum recursion limit
pub const MAX_RECURSION: usize = 50;
// /// Default maximum object size (2^32)
// pub const MAX_OBJECT_SIZE: usize = 4_294_967_295;
pub trait GetObjectContent {
/// Return the raw content (bytes) of the next ASN.1 encoded object
///
/// Note: if using BER and length is indefinite, terminating End-Of-Content is NOT included
fn get_object_content<'a>(
i: &'a [u8],
hdr: &'_ Header,
max_depth: usize,
) -> ParseResult<'a, &'a [u8]>;
}
impl GetObjectContent for BerParser {
fn get_object_content<'a>(
i: &'a [u8],
hdr: &'_ Header,
max_depth: usize,
) -> ParseResult<'a, &'a [u8]> {
let start_i = i;
let (i, _) = ber_skip_object_content(i, hdr, max_depth)?;
let len = start_i.offset(i);
let (content, i) = start_i.split_at(len);
// if len is indefinite, there are 2 extra bytes for EOC
if hdr.length == Length::Indefinite {
let len = content.len();
assert!(len >= 2);
Ok((i, &content[..len - 2]))
} else {
Ok((i, content))
}
}
}
impl GetObjectContent for DerParser {
/// Skip object content, accepting only DER
///
/// This this function is for DER only, it cannot go into recursion (no indefinite length)
fn get_object_content<'a>(
i: &'a [u8],
hdr: &'_ Header,
_max_depth: usize,
) -> ParseResult<'a, &'a [u8]> {
match hdr.length {
Length::Definite(l) => take(l)(i),
Length::Indefinite => Err(Err::Error(Error::DerConstraintFailed(
DerConstraint::IndefiniteLength,
))),
}
}
}
/// Skip object content, and return true if object was End-Of-Content
fn ber_skip_object_content<'a>(
i: &'a [u8],
hdr: &Header,
max_depth: usize,
) -> ParseResult<'a, bool> {
if max_depth == 0 {
return Err(Err::Error(Error::BerMaxDepth));
}
match hdr.length {
Length::Definite(l) => {
if l == 0 && hdr.tag == Tag::EndOfContent {
return Ok((i, true));
}
let (i, _) = take(l)(i)?;
Ok((i, false))
}
Length::Indefinite => {
hdr.assert_constructed()?;
// read objects until EndOfContent (00 00)
// this is recursive
let mut i = i;
loop {
let (i2, header2) = Header::from_ber(i)?;
let (i3, eoc) = ber_skip_object_content(i2, &header2, max_depth - 1)?;
if eoc {
// return false, since top object was not EndOfContent
return Ok((i3, false));
}
i = i3;
}
}
}
}
/// Try to parse input bytes as u64
#[inline]
pub(crate) fn bytes_to_u64(s: &[u8]) -> core::result::Result<u64, Error> {
let mut u: u64 = 0;
for &c in s {
if u & 0xff00_0000_0000_0000 != 0 {
return Err(Error::IntegerTooLarge);
}
u <<= 8;
u |= u64::from(c);
}
Ok(u)
}
pub(crate) fn parse_identifier(i: &[u8]) -> ParseResult<(u8, u8, u32, &[u8])> {
if i.is_empty() {
Err(Err::Incomplete(Needed::new(1)))
} else {
let a = i[0] >> 6;
let b = u8::from(i[0] & 0b0010_0000 != 0);
let mut c = u32::from(i[0] & 0b0001_1111);
let mut tag_byte_count = 1;
if c == 0x1f {
c = 0;
loop {
// Make sure we don't read past the end of our data.
custom_check!(i, tag_byte_count >= i.len(), Error::InvalidTag)?;
// With tag defined as u32 the most we can fit in is four tag bytes.
// (X.690 doesn't actually specify maximum tag width.)
custom_check!(i, tag_byte_count > 5, Error::InvalidTag)?;
c = (c << 7) | (u32::from(i[tag_byte_count]) & 0x7f);
let done = i[tag_byte_count] & 0x80 == 0;
tag_byte_count += 1;
if done {
break;
}
}
}
let (raw_tag, rem) = i.split_at(tag_byte_count);
Ok((rem, (a, b, c, raw_tag)))
}
}
/// Return the MSB and the rest of the first byte, or an error
pub(crate) fn parse_ber_length_byte(i: &[u8]) -> ParseResult<(u8, u8)> {
if i.is_empty() {
Err(Err::Incomplete(Needed::new(1)))
} else {
let a = i[0] >> 7;
let b = i[0] & 0b0111_1111;
Ok((&i[1..], (a, b)))
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! der_constraint_fail_if(
($slice:expr, $cond:expr, $constraint:expr) => (
{
if $cond {
return Err(::nom::Err::Error(Error::DerConstraintFailed($constraint)));
}
}
);
);

94
vendor/asn1-rs/src/class.rs vendored Normal file
View File

@@ -0,0 +1,94 @@
use core::convert::TryFrom;
use core::fmt;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BerClassFromIntError(pub(crate) ());
/// BER Object class of tag
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u8)]
pub enum Class {
/// `Universal` class of tags (`0b00`)
Universal = 0b00,
/// `Application` class of tags (`0b01`)
Application = 0b01,
/// `Context-Specific` class of tags (`0b10`)
ContextSpecific = 0b10,
/// `Private` class of tags (`0b11`)
Private = 0b11,
}
impl Class {
/// `Universal` class of tags (`0b00`)
pub const UNIVERSAL: u8 = 0b00;
/// `Application` class of tags (`0b01`)
pub const APPLICATION: u8 = 0b01;
/// `Context-Specific` class of tags (`0b10`)
pub const CONTEXT_SPECIFIC: u8 = 0b10;
/// `Private` class of tags (`0b11`)
pub const PRIVATE: u8 = 0b11;
pub const fn assert_eq(&self, class: Class) -> Result<(), crate::error::Error> {
if *self as u8 == class as u8 {
Ok(())
} else {
Err(crate::error::Error::UnexpectedClass {
expected: Some(class),
actual: *self,
})
}
}
}
impl fmt::Display for Class {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Class::Universal => "UNIVERSAL",
Class::Application => "APPLICATION",
Class::ContextSpecific => "CONTEXT-SPECIFIC",
Class::Private => "PRIVATE",
};
write!(f, "{}", s)
}
}
impl TryFrom<u8> for Class {
type Error = BerClassFromIntError;
#[inline]
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0b00 => Ok(Class::Universal),
0b01 => Ok(Class::Application),
0b10 => Ok(Class::ContextSpecific),
0b11 => Ok(Class::Private),
_ => Err(BerClassFromIntError(())),
}
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
#[test]
fn methods_class() {
let c = Class::Universal;
assert!(c.assert_eq(Class::Universal).is_ok());
assert!(c.assert_eq(Class::Private).is_err());
assert_eq!(Class::Universal.to_string().as_str(), "UNIVERSAL");
assert_eq!(Class::Application.to_string().as_str(), "APPLICATION");
assert_eq!(
Class::ContextSpecific.to_string().as_str(),
"CONTEXT-SPECIFIC"
);
assert_eq!(Class::Private.to_string().as_str(), "PRIVATE");
assert!(Class::try_from(0b00).is_ok());
assert!(Class::try_from(0b01).is_ok());
assert!(Class::try_from(0b10).is_ok());
assert!(Class::try_from(0b11).is_ok());
assert!(Class::try_from(4).is_err());
}
}

43
vendor/asn1-rs/src/const_int.rs vendored Normal file
View File

@@ -0,0 +1,43 @@
use crate::{Tag, Tagged};
#[derive(Debug)]
pub struct ConstInt {
buffer: [u8; 10],
n: usize,
}
// XXX only ToBer/ToDer trait supported?
impl Tagged for ConstInt {
const TAG: Tag = Tag::Integer;
}
#[derive(Debug)]
pub struct IntBuilder {}
impl IntBuilder {
pub const fn build(&self, i: u64) -> ConstInt {
let b = i.to_be_bytes();
let mut out = [0u8; 10];
out[0] = 0x4;
let src_len = b.len();
let mut src_index = 0;
while src_index < src_len && b[src_index] == 0 {
src_index += 1;
}
out[1] = (src_len - src_index) as u8;
let mut dst_index = 2;
while src_index < src_len {
out[dst_index] = b[src_index];
src_index += 1;
dst_index += 1;
}
// XXX will not work: we need to allocate a Vec
// also, we cannot just store the bytes (there are extra zeroes at end)
// Integer::new(&out[..dst_index])
ConstInt {
buffer: out,
n: dst_index,
}
}
}

109
vendor/asn1-rs/src/datetime.rs vendored Normal file
View File

@@ -0,0 +1,109 @@
use crate::{Result, Tag};
use alloc::format;
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
use core::fmt;
#[cfg(feature = "datetime")]
use time::OffsetDateTime;
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum ASN1TimeZone {
/// No timezone provided
Undefined,
/// Coordinated universal time
Z,
/// Local zone, with offset to coordinated universal time
///
/// `(offset_hour, offset_minute)`
Offset(i8, i8),
}
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct ASN1DateTime {
pub year: u32,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub millisecond: Option<u16>,
pub tz: ASN1TimeZone,
}
impl ASN1DateTime {
#[allow(clippy::too_many_arguments)]
pub const fn new(
year: u32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
millisecond: Option<u16>,
tz: ASN1TimeZone,
) -> Self {
ASN1DateTime {
year,
month,
day,
hour,
minute,
second,
millisecond,
tz,
}
}
#[cfg(feature = "datetime")]
fn to_time_datetime(
&self,
) -> core::result::Result<OffsetDateTime, time::error::ComponentRange> {
use std::convert::TryFrom;
use time::{Date, Month, PrimitiveDateTime, Time, UtcOffset};
let month = Month::try_from(self.month)?;
let date = Date::from_calendar_date(self.year as i32, month, self.day)?;
let time = Time::from_hms_milli(
self.hour,
self.minute,
self.second,
self.millisecond.unwrap_or(0),
)?;
let primitive_date = PrimitiveDateTime::new(date, time);
let offset = match self.tz {
ASN1TimeZone::Offset(h, m) => UtcOffset::from_hms(h, m, 0)?,
ASN1TimeZone::Undefined | ASN1TimeZone::Z => UtcOffset::UTC,
};
Ok(primitive_date.assume_offset(offset))
}
#[cfg(feature = "datetime")]
pub fn to_datetime(&self) -> Result<OffsetDateTime> {
use crate::Error;
self.to_time_datetime().map_err(|_| Error::InvalidDateTime)
}
}
impl fmt::Display for ASN1DateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let fractional = match self.millisecond {
None => "".to_string(),
Some(v) => format!(".{}", v),
};
write!(
f,
"{:04}{:02}{:02}{:02}{:02}{:02}{}Z",
self.year, self.month, self.day, self.hour, self.minute, self.second, fractional,
)
}
}
/// Decode 2-digit decimal value
pub(crate) fn decode_decimal(tag: Tag, hi: u8, lo: u8) -> Result<u8> {
if hi.is_ascii_digit() && lo.is_ascii_digit() {
Ok((hi - b'0') * 10 + (lo - b'0'))
} else {
Err(tag.invalid_value("expected digit"))
}
}

264
vendor/asn1-rs/src/debug.rs vendored Normal file
View File

@@ -0,0 +1,264 @@
#![allow(unused_imports)]
use crate::ParseResult;
pub(crate) mod macros {
macro_rules! debug_eprintln {
($msg: expr, $( $args:expr ),* ) => {
#[cfg(feature = "debug")]
{
use colored::Colorize;
let s = $msg.to_string().green();
eprintln!("{} {}", s, format!($($args),*));
}
};
}
#[allow(unused_macros)]
macro_rules! trace_eprintln {
($msg: expr, $( $args:expr ),* ) => {
#[cfg(feature = "trace")]
{
use colored::Colorize;
let s = $msg.to_string().green();
eprintln!("{} {}", s, format!($($args),*));
}
};
}
pub(crate) use debug_eprintln;
pub(crate) use trace_eprintln;
}
use macros::*;
#[cfg(feature = "debug")]
fn eprintln_hex_dump(bytes: &[u8], max_len: usize) {
use core::cmp::min;
use nom::HexDisplay;
let m = min(bytes.len(), max_len);
eprint!("{}", &bytes[..m].to_hex(16));
if bytes.len() > max_len {
eprintln!("... <continued>");
}
}
#[cfg(not(feature = "debug"))]
#[inline]
pub fn trace_generic<F, I, O, E>(_msg: &str, _fname: &str, f: F, input: I) -> Result<O, E>
where
F: Fn(I) -> Result<O, E>,
{
f(input)
}
#[cfg(feature = "debug")]
pub fn trace_generic<F, I, O, E>(msg: &str, fname: &str, f: F, input: I) -> Result<O, E>
where
F: Fn(I) -> Result<O, E>,
E: core::fmt::Display,
{
trace_eprintln!(msg, "⤷ {}", fname);
let output = f(input);
match &output {
Err(e) => {
debug_eprintln!(msg, "↯ {} failed: {}", fname, e.to_string().red());
}
_ => {
debug_eprintln!(msg, "⤶ {}", fname);
}
}
output
}
#[cfg(not(feature = "debug"))]
#[inline]
pub fn trace<'a, T, E, F>(_msg: &str, f: F, input: &'a [u8]) -> ParseResult<'a, T, E>
where
F: Fn(&'a [u8]) -> ParseResult<'a, T, E>,
{
f(input)
}
#[cfg(feature = "debug")]
pub fn trace<'a, T, E, F>(msg: &str, f: F, input: &'a [u8]) -> ParseResult<'a, T, E>
where
F: Fn(&'a [u8]) -> ParseResult<'a, T, E>,
{
trace_eprintln!(
msg,
"⤷ input (len={}, type={})",
input.len(),
core::any::type_name::<T>()
);
let res = f(input);
match &res {
Ok((_rem, _)) => {
trace_eprintln!(
msg,
"⤶ Parsed {} bytes, {} remaining",
input.len() - _rem.len(),
_rem.len()
);
}
Err(_) => {
// NOTE: we do not need to print error, caller should print it
debug_eprintln!(msg, "↯ Parsing failed at location:");
eprintln_hex_dump(input, 16);
}
}
res
}
#[cfg(feature = "debug")]
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use crate::*;
use alloc::collections::BTreeSet;
use hex_literal::hex;
#[test]
fn debug_from_ber_any() {
assert!(Any::from_ber(&hex!("01 01 ff")).is_ok());
}
#[test]
fn debug_from_ber_failures() {
// wrong type
eprintln!("--");
assert!(<Vec<u16>>::from_ber(&hex!("02 01 00")).is_err());
}
#[test]
fn debug_from_ber_sequence_indefinite() {
let input = &hex!("30 80 02 03 01 00 01 00 00");
let (rem, result) = Sequence::from_ber(input).expect("parsing failed");
assert_eq!(result.as_ref(), &input[2..7]);
assert_eq!(rem, &[]);
eprintln!("--");
let (rem, result) = <Vec<u32>>::from_ber(input).expect("parsing failed");
assert_eq!(&result, &[65537]);
assert_eq!(rem, &[]);
}
#[test]
fn debug_from_ber_sequence_of() {
// parsing failure (wrong type)
let input = &hex!("30 03 01 01 00");
eprintln!("--");
let _ = <SequenceOf<u32>>::from_ber(input).expect_err("parsing should fail");
eprintln!("--");
let _ = <Vec<u32>>::from_ber(input).expect_err("parsing should fail");
}
#[test]
fn debug_from_ber_u32() {
assert!(u32::from_ber(&hex!("02 01 01")).is_ok());
}
#[test]
fn debug_from_der_any() {
assert!(Any::from_der(&hex!("01 01 ff")).is_ok());
}
#[test]
fn debug_from_der_bool() {
eprintln!("** first test is ok**");
assert!(<bool>::from_der(&hex!("01 01 ff")).is_ok());
eprintln!("** second test fails when parsing ANY (eof)**");
assert!(<bool>::from_der(&hex!("01 02 ff")).is_err());
eprintln!("** second test fails when checking DER constraints**");
assert!(<bool>::from_der(&hex!("01 01 f0")).is_err());
eprintln!("** second test fails during TryFrom**");
assert!(<bool>::from_der(&hex!("01 02 ff ff")).is_err());
}
#[test]
fn debug_from_der_failures() {
use crate::Sequence;
// parsing any failed
eprintln!("--");
assert!(u16::from_der(&hex!("ff 00")).is_err());
// Indefinite length
eprintln!("--");
assert!(u16::from_der(&hex!("30 80 00 00")).is_err());
// DER constraints failed
eprintln!("--");
assert!(bool::from_der(&hex!("01 01 7f")).is_err());
// Incomplete sequence
eprintln!("--");
let _ = Sequence::from_der(&hex!("30 81 04 00 00"));
}
#[test]
fn debug_from_der_sequence() {
// parsing OK, recursive
let input = &hex!("30 08 02 03 01 00 01 02 01 01");
let (rem, result) = <Vec<u32>>::from_der(input).expect("parsing failed");
assert_eq!(&result, &[65537, 1]);
assert_eq!(rem, &[]);
}
#[test]
fn debug_from_der_sequence_fail() {
// tag is wrong
let input = &hex!("31 03 01 01 44");
let _ = <Vec<bool>>::from_der(input).expect_err("parsing should fail");
// sequence is ok but contraint fails on element
let input = &hex!("30 03 01 01 44");
let _ = <Vec<bool>>::from_der(input).expect_err("parsing should fail");
}
#[test]
fn debug_from_der_sequence_of() {
use crate::SequenceOf;
// parsing failure (wrong type)
let input = &hex!("30 03 01 01 00");
eprintln!("--");
let _ = <SequenceOf<u32>>::from_der(input).expect_err("parsing should fail");
eprintln!("--");
let _ = <Vec<u32>>::from_der(input).expect_err("parsing should fail");
}
#[test]
fn debug_from_der_set_fail() {
// set is ok but contraint fails on element
let input = &hex!("31 03 01 01 44");
let _ = <BTreeSet<bool>>::from_der(input).expect_err("parsing should fail");
}
#[test]
fn debug_from_der_set_of() {
use crate::SetOf;
use alloc::collections::BTreeSet;
// parsing failure (wrong type)
let input = &hex!("31 03 01 01 00");
eprintln!("--");
let _ = <SetOf<u32>>::from_der(input).expect_err("parsing should fail");
eprintln!("--");
let _ = <BTreeSet<u32>>::from_der(input).expect_err("parsing should fail");
eprintln!("--");
let _ = <HashSet<u32>>::from_der(input).expect_err("parsing should fail");
}
#[test]
fn debug_from_der_string_ok() {
let input = &hex!("0c 0a 53 6f 6d 65 2d 53 74 61 74 65");
let (rem, result) = Utf8String::from_der(input).expect("parsing failed");
assert_eq!(result.as_ref(), "Some-State");
assert_eq!(rem, &[]);
}
#[test]
fn debug_from_der_string_fail() {
// wrong charset
let input = &hex!("12 03 41 42 43");
let _ = NumericString::from_der(input).expect_err("parsing should fail");
}
}

409
vendor/asn1-rs/src/derive.rs vendored Normal file
View File

@@ -0,0 +1,409 @@
/// # BerSequence custom derive
///
/// `BerSequence` is a custom derive attribute, to derive a BER [`Sequence`](super::Sequence) parser automatically from the structure definition.
/// This attribute will automatically derive implementations for the following traits:
/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer)
/// - [`Tagged`](super::Tagged)
///
/// `DerSequence` implies `BerSequence`, and will conflict with this custom derive. Use `BerSequence` when you only want the
/// above traits derived.
///
/// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromBer`](super::FromBer) trait.
///
/// See [`derive`](crate::doc::derive) documentation for more examples and documentation.
///
/// ## Examples
///
/// To parse the following ASN.1 structure:
/// <pre>
/// S ::= SEQUENCE {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// Define a structure and add the `BerSequence` derive:
///
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(BerSequence)]
/// struct S {
/// a: u32,
/// b: u16,
/// c: u16
/// }
/// ```
///
/// ## Debugging
///
/// To help debugging the generated code, the `#[debug_derive]` attribute has been added.
///
/// When this attribute is specified, the generated code will be printed to `stderr` during compilation.
///
/// Example:
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(BerSequence)]
/// #[debug_derive]
/// struct S {
/// a: u32,
/// }
/// ```
pub use asn1_rs_derive::BerSequence;
/// # DerSequence custom derive
///
/// `DerSequence` is a custom derive attribute, to derive both BER and DER [`Sequence`](super::Sequence) parsers automatically from the structure definition.
/// This attribute will automatically derive implementations for the following traits:
/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer)
/// - [`Tagged`](super::Tagged)
/// - [`CheckDerConstraints`](super::CheckDerConstraints)
/// - [`FromDer`](super::FromDer)
///
/// `DerSequence` implies `BerSequence`, and will conflict with this custom derive.
///
/// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromDer`](super::FromDer) trait.
///
/// See [`derive`](crate::doc::derive) documentation for more examples and documentation.
///
/// ## Examples
///
/// To parse the following ASN.1 structure:
/// <pre>
/// S ::= SEQUENCE {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// Define a structure and add the `DerSequence` derive:
///
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(DerSequence)]
/// struct S {
/// a: u32,
/// b: u16,
/// c: u16
/// }
/// ```
///
/// ## Debugging
///
/// To help debugging the generated code, the `#[debug_derive]` attribute has been added.
///
/// When this attribute is specified, the generated code will be printed to `stderr` during compilation.
///
/// Example:
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(DerSequence)]
/// #[debug_derive]
/// struct S {
/// a: u32,
/// }
/// ```
pub use asn1_rs_derive::DerSequence;
/// # BerSet custom derive
///
/// `BerSet` is a custom derive attribute, to derive a BER [`Set`](super::Set) parser automatically from the structure definition.
/// This attribute will automatically derive implementations for the following traits:
/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer)
/// - [`Tagged`](super::Tagged)
///
/// `DerSet` implies `BerSet`, and will conflict with this custom derive. Use `BerSet` when you only want the
/// above traits derived.
///
/// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromBer`](super::FromBer) trait.
///
/// See [`derive`](crate::doc::derive) documentation for more examples and documentation.
///
/// ## Examples
///
/// To parse the following ASN.1 structure:
/// <pre>
/// S ::= SET {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// Define a structure and add the `BerSet` derive:
///
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(BerSet)]
/// struct S {
/// a: u32,
/// b: u16,
/// c: u16
/// }
/// ```
///
/// ## Debugging
///
/// To help debugging the generated code, the `#[debug_derive]` attribute has been added.
///
/// When this attribute is specified, the generated code will be printed to `stderr` during compilation.
///
/// Example:
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(BerSet)]
/// #[debug_derive]
/// struct S {
/// a: u32,
/// }
/// ```
pub use asn1_rs_derive::BerSet;
/// # DerSet custom derive
///
/// `DerSet` is a custom derive attribute, to derive both BER and DER [`Set`](super::Set) parsers automatically from the structure definition.
/// This attribute will automatically derive implementations for the following traits:
/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer)
/// - [`Tagged`](super::Tagged)
/// - [`CheckDerConstraints`](super::CheckDerConstraints)
/// - [`FromDer`](super::FromDer)
///
/// `DerSet` implies `BerSet`, and will conflict with this custom derive.
///
/// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromDer`](super::FromDer) trait.
///
/// See [`derive`](crate::doc::derive) documentation for more examples and documentation.
///
/// ## Examples
///
/// To parse the following ASN.1 structure:
/// <pre>
/// S ::= SEt {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// Define a structure and add the `DerSet` derive:
///
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(DerSet)]
/// struct S {
/// a: u32,
/// b: u16,
/// c: u16
/// }
/// ```
///
/// ## Debugging
///
/// To help debugging the generated code, the `#[debug_derive]` attribute has been added.
///
/// When this attribute is specified, the generated code will be printed to `stderr` during compilation.
///
/// Example:
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(DerSet)]
/// #[debug_derive]
/// struct S {
/// a: u32,
/// }
/// ```
pub use asn1_rs_derive::DerSet;
/// # BerAlias custom derive
///
/// `BerAlias` is a custom derive attribute, to derive a BER object parser automatically from the structure definition.
/// This attribute will automatically derive implementations for the following traits:
/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer)
/// - [`Tagged`](super::Tagged)
/// - [`CheckDerConstraints`](super::CheckDerConstraints)
/// - [`FromDer`](super::FromDer)
///
/// `DerAlias` implies `BerAlias`, and will conflict with this custom derive. Use `BerAlias` when you only want the
/// above traits derived.
///
/// When defining alias, only unnamed (tuple) structs with one field are supported.
///
/// Parser will be automatically derived from the struct field. This field type must implement the `TryFrom<Any>` trait.
///
/// See [`derive`](crate::doc::derive) documentation for more examples and documentation.
///
/// ## Examples
///
/// To parse the following ASN.1 object:
/// <pre>
/// MyInt ::= INTEGER(0..2^32)
/// </pre>
///
/// Define a structure and add the `BerAlias` derive:
///
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(BerAlias)]
/// struct S(pub u32);
/// ```
///
/// ## Debugging
///
/// To help debugging the generated code, the `#[debug_derive]` attribute has been added.
///
/// When this attribute is specified, the generated code will be printed to `stderr` during compilation.
///
/// Example:
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(BerAlias)]
/// #[debug_derive]
/// struct S(pub u32);
/// ```
pub use asn1_rs_derive::BerAlias;
/// # DerAlias custom derive
///
/// `DerAlias` is a custom derive attribute, to derive a DER object parser automatically from the structure definition.
/// This attribute will automatically derive implementations for the following traits:
/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer)
/// - [`Tagged`](super::Tagged)
///
/// `DerAlias` implies `BerAlias`, and will conflict with this custom derive.
///
/// When defining alias, only unnamed (tuple) structs with one field are supported.
///
/// Parser will be automatically derived from the struct field. This field type must implement the `TryFrom<Any>` and `FromDer` traits.
///
/// See [`derive`](crate::doc::derive) documentation for more examples and documentation.
///
/// ## Examples
///
/// To parse the following ASN.1 object:
/// <pre>
/// MyInt ::= INTEGER(0..2^32)
/// </pre>
///
/// Define a structure and add the `DerAlias` derive:
///
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(DerAlias)]
/// struct S(pub u32);
/// ```
///
/// ## Debugging
///
/// To help debugging the generated code, the `#[debug_derive]` attribute has been added.
///
/// When this attribute is specified, the generated code will be printed to `stderr` during compilation.
///
/// Example:
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(DerAlias)]
/// #[debug_derive]
/// struct S(pub u32);
/// ```
pub use asn1_rs_derive::DerAlias;
/// # ToStatic custom derive
///
/// `ToStatic` is a custom derive attribute, to derive the [`ToStatic`](ToStatic) trait automatically from the structure definition.
///
/// ## Example
///
/// ```rust
/// use asn1_rs::ToStatic;
/// use std::borrow::Cow;
///
/// #[derive(ToStatic)]
/// struct S<'a>(pub Cow<'a, str>);
/// ```
///
/// ## Debugging
///
/// To help debugging the generated code, the `#[debug_derive]` attribute has been added.
///
/// When this attribute is specified, the generated code will be printed to `stderr` during compilation.
///
/// Example:
/// ```rust
/// use asn1_rs::ToStatic;
/// use std::borrow::Cow;
///
/// #[derive(ToStatic)]
/// #[debug_derive]
/// struct S<'a>(pub Cow<'a, str>);
/// ```
pub use asn1_rs_derive::ToStatic;
/// # ToDerSequence custom derive
///
/// `ToDerSequence` is a custom derive attribute, to derive both DER [`Sequence`](super::Sequence) serialization automatically from the structure definition.
/// This attribute will automatically derive implementations for the following traits:
/// - [`ToDer`](super::ToDer)
///
/// Serialization will be automatically derived from struct fields. Every field type must implement the [`ToDer`](super::ToDer) trait.
///
/// See [`derive`](crate::doc::derive) documentation for more examples and documentation.
///
/// ## Examples
///
/// To serialize the following ASN.1 structure:
/// <pre>
/// S ::= SEQUENCE {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// Define a structure and add the `DerSequence` derive:
///
#[cfg_attr(feature = "std", doc = r#"```rust"#)]
#[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)]
/// use asn1_rs::*;
///
/// #[derive(DerSequence, ToDerSequence)]
/// struct S {
/// a: u32,
/// b: u16,
/// c: u16
/// }
///
/// let s = S { a: 1, b: 2, c: 3 };
/// let data = s.to_der_vec().expect("Serialization failed");
/// ```
///
/// ## Debugging
///
/// To help debugging the generated code, the `#[debug_derive]` attribute has been added.
///
/// When this attribute is specified, the generated code will be printed to `stderr` during compilation.
///
/// Example:
/// ```rust
/// use asn1_rs::*;
///
/// #[derive(DerSequence, ToDerSequence)]
/// #[debug_derive]
/// struct S {
/// a: u32,
/// }
/// ```
pub use asn1_rs_derive::ToDerSequence;

11
vendor/asn1-rs/src/doc/mod.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
//! Additional documentation: recipes, specific use cases and examples, etc.
#[doc = include_str!("../../doc/RECIPES.md")]
pub mod recipes {}
#[cfg(feature = "std")]
#[doc = include_str!("../../doc/DERIVE.md")]
pub mod derive {}
#[doc = include_str!("../../doc/DEBUG.md")]
pub mod debug {}

204
vendor/asn1-rs/src/error.rs vendored Normal file
View File

@@ -0,0 +1,204 @@
#![allow(unknown_lints)]
#![allow(non_local_definitions)] // false positive for displaydoc::Display: https://github.com/yaahc/displaydoc/issues/46
use crate::{Class, Tag};
use alloc::str;
use alloc::string;
#[cfg(not(feature = "std"))]
use alloc::string::String;
use displaydoc::Display;
use nom::error::{ErrorKind, FromExternalError, ParseError};
use nom::IResult;
#[cfg(feature = "std")]
use std::io;
use thiserror::Error;
#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Error)]
/// Error types for DER constraints
pub enum DerConstraint {
/// Indefinite length not allowed
IndefiniteLength,
/// Object must not be constructed
Constructed,
/// Object must be constructed
NotConstructed,
/// DateTime object is missing timezone
MissingTimeZone,
/// DateTime object is missing seconds
MissingSeconds,
/// Bitstring unused bits must be set to zero
UnusedBitsNotZero,
/// Boolean value must be 0x00 of 0xff
InvalidBoolean,
/// Integer must not be empty
IntegerEmpty,
/// Leading zeroes in Integer encoding
IntegerLeadingZeroes,
/// Leading 0xff in negative Integer encoding
IntegerLeadingFF,
}
/// The error type for operations of the [`FromBer`](crate::FromBer),
/// [`FromDer`](crate::FromDer), and associated traits.
#[derive(Clone, Debug, Display, PartialEq, Eq, Error)]
pub enum Error {
/// BER object does not have the expected type
BerTypeError,
/// BER object does not have the expected value
BerValueError,
/// Invalid Length
InvalidLength,
/// Invalid Value when parsing object with tag {tag:?} {msg:}
InvalidValue { tag: Tag, msg: String },
/// Invalid Tag
InvalidTag,
/// Unknown tag: {0:?}
UnknownTag(u32),
/// Unexpected Tag (expected: {expected:?}, actual: {actual:?})
UnexpectedTag { expected: Option<Tag>, actual: Tag },
/// Unexpected Class (expected: {expected:?}, actual: {actual:?})
UnexpectedClass {
expected: Option<Class>,
actual: Class,
},
/// Indefinite length not allowed
IndefiniteLengthUnexpected,
/// DER object was expected to be constructed (and found to be primitive)
ConstructExpected,
/// DER object was expected to be primitive (and found to be constructed)
ConstructUnexpected,
/// Integer too large to fit requested type
IntegerTooLarge,
/// BER integer is negative, while an unsigned integer was requested
IntegerNegative,
/// BER recursive parsing reached maximum depth
BerMaxDepth,
/// Invalid encoding or forbidden characters in string
StringInvalidCharset,
/// Invalid Date or Time
InvalidDateTime,
/// DER Failed constraint: {0:?}
DerConstraintFailed(DerConstraint),
/// Requesting borrowed data from a temporary object
LifetimeError,
/// Feature is not yet implemented
Unsupported,
/// incomplete data, missing: {0:?}
Incomplete(nom::Needed),
/// nom error: {0:?}
NomError(ErrorKind),
}
impl Error {
/// Build an error from the provided invalid value
#[inline]
pub const fn invalid_value(tag: Tag, msg: String) -> Self {
Self::InvalidValue { tag, msg }
}
/// Build an error from the provided unexpected class
#[inline]
pub const fn unexpected_class(expected: Option<Class>, actual: Class) -> Self {
Self::UnexpectedClass { expected, actual }
}
/// Build an error from the provided unexpected tag
#[inline]
pub const fn unexpected_tag(expected: Option<Tag>, actual: Tag) -> Self {
Self::UnexpectedTag { expected, actual }
}
}
impl<'a> ParseError<&'a [u8]> for Error {
fn from_error_kind(_input: &'a [u8], kind: ErrorKind) -> Self {
Error::NomError(kind)
}
fn append(_input: &'a [u8], kind: ErrorKind, _other: Self) -> Self {
Error::NomError(kind)
}
}
impl From<Error> for nom::Err<Error> {
fn from(e: Error) -> Self {
nom::Err::Error(e)
}
}
impl From<str::Utf8Error> for Error {
fn from(_: str::Utf8Error) -> Self {
Error::StringInvalidCharset
}
}
impl From<string::FromUtf8Error> for Error {
fn from(_: string::FromUtf8Error) -> Self {
Error::StringInvalidCharset
}
}
impl From<string::FromUtf16Error> for Error {
fn from(_: string::FromUtf16Error) -> Self {
Error::StringInvalidCharset
}
}
impl From<nom::Err<Error>> for Error {
fn from(e: nom::Err<Error>) -> Self {
match e {
nom::Err::Incomplete(n) => Self::Incomplete(n),
nom::Err::Error(e) | nom::Err::Failure(e) => e,
}
}
}
impl<I, E> FromExternalError<I, E> for Error {
fn from_external_error(_input: I, kind: ErrorKind, _e: E) -> Error {
Error::NomError(kind)
}
}
/// Flatten all `nom::Err` variants error into a single error type
pub fn from_nom_error<E, F>(e: nom::Err<E>) -> F
where
F: From<E> + From<Error>,
{
match e {
nom::Err::Error(e) | nom::Err::Failure(e) => F::from(e),
nom::Err::Incomplete(n) => F::from(Error::Incomplete(n)),
}
}
/// Holds the result of BER/DER serialization functions
pub type ParseResult<'a, T, E = Error> = IResult<&'a [u8], T, E>;
/// A specialized `Result` type for all operations from this crate.
pub type Result<T, E = Error> = core::result::Result<T, E>;
/// The error type for serialization operations of the [`ToDer`](crate::ToDer) trait.
#[cfg(feature = "std")]
#[derive(Debug, Error)]
pub enum SerializeError {
#[error("ASN.1 error: {0:?}")]
ASN1Error(#[from] Error),
#[error("Invalid Class {class:}")]
InvalidClass { class: u8 },
#[error("Invalid Length")]
InvalidLength,
#[error("I/O error: {0:?}")]
IOError(#[from] io::Error),
}
#[cfg(feature = "std")]
/// Holds the result of BER/DER encoding functions
pub type SerializeResult<T> = std::result::Result<T, SerializeError>;

504
vendor/asn1-rs/src/header.rs vendored Normal file
View File

@@ -0,0 +1,504 @@
use crate::ber::*;
use crate::der_constraint_fail_if;
use crate::error::*;
#[cfg(feature = "std")]
use crate::ToDer;
use crate::{BerParser, Class, DerParser, DynTagged, FromBer, FromDer, Length, Tag, ToStatic};
use alloc::borrow::Cow;
use core::convert::TryFrom;
use nom::bytes::streaming::take;
/// BER/DER object header (identifier and length)
#[derive(Clone, Debug)]
pub struct Header<'a> {
/// Object class: universal, application, context-specific, or private
pub(crate) class: Class,
/// Constructed attribute: true if constructed, else false
pub(crate) constructed: bool,
/// Tag number
pub(crate) tag: Tag,
/// Object length: value if definite, or indefinite
pub(crate) length: Length,
/// Optionally, the raw encoding of the tag
///
/// This is useful in some cases, where different representations of the same
/// BER tags have different meanings (BER only)
pub(crate) raw_tag: Option<Cow<'a, [u8]>>,
}
impl<'a> Header<'a> {
/// Build a new BER/DER header from the provided values
pub const fn new(class: Class, constructed: bool, tag: Tag, length: Length) -> Self {
Header {
tag,
constructed,
class,
length,
raw_tag: None,
}
}
/// Build a new BER/DER header from the provided tag, with default values for other fields
#[inline]
pub const fn new_simple(tag: Tag) -> Self {
let constructed = matches!(tag, Tag::Sequence | Tag::Set);
Self::new(Class::Universal, constructed, tag, Length::Definite(0))
}
/// Set the class of this `Header`
#[inline]
pub fn with_class(self, class: Class) -> Self {
Self { class, ..self }
}
/// Set the constructed flags of this `Header`
#[inline]
pub fn with_constructed(self, constructed: bool) -> Self {
Self {
constructed,
..self
}
}
/// Set the tag of this `Header`
#[inline]
pub fn with_tag(self, tag: Tag) -> Self {
Self { tag, ..self }
}
/// Set the length of this `Header`
#[inline]
pub fn with_length(self, length: Length) -> Self {
Self { length, ..self }
}
/// Update header to add reference to raw tag
#[inline]
pub fn with_raw_tag(self, raw_tag: Option<Cow<'a, [u8]>>) -> Self {
Header { raw_tag, ..self }
}
/// Return the class of this header.
#[inline]
pub const fn class(&self) -> Class {
self.class
}
/// Return true if this header has the 'constructed' flag.
#[inline]
pub const fn constructed(&self) -> bool {
self.constructed
}
/// Return the tag of this header.
#[inline]
pub const fn tag(&self) -> Tag {
self.tag
}
/// Return the length of this header.
#[inline]
pub const fn length(&self) -> Length {
self.length
}
/// Return the raw tag encoding, if it was stored in this object
#[inline]
pub fn raw_tag(&self) -> Option<&[u8]> {
self.raw_tag.as_ref().map(|cow| cow.as_ref())
}
/// Test if object is primitive
#[inline]
pub const fn is_primitive(&self) -> bool {
!self.constructed
}
/// Test if object is constructed
#[inline]
pub const fn is_constructed(&self) -> bool {
self.constructed
}
/// Return error if class is not the expected class
#[inline]
pub const fn assert_class(&self, class: Class) -> Result<()> {
self.class.assert_eq(class)
}
/// Return error if tag is not the expected tag
#[inline]
pub const fn assert_tag(&self, tag: Tag) -> Result<()> {
self.tag.assert_eq(tag)
}
/// Return error if object is not primitive
#[inline]
pub const fn assert_primitive(&self) -> Result<()> {
if self.is_primitive() {
Ok(())
} else {
Err(Error::ConstructUnexpected)
}
}
/// Return error if object is primitive
#[inline]
pub const fn assert_constructed(&self) -> Result<()> {
if !self.is_primitive() {
Ok(())
} else {
Err(Error::ConstructExpected)
}
}
/// Test if object class is Universal
#[inline]
pub const fn is_universal(&self) -> bool {
self.class as u8 == Class::Universal as u8
}
/// Test if object class is Application
#[inline]
pub const fn is_application(&self) -> bool {
self.class as u8 == Class::Application as u8
}
/// Test if object class is Context-specific
#[inline]
pub const fn is_contextspecific(&self) -> bool {
self.class as u8 == Class::ContextSpecific as u8
}
/// Test if object class is Private
#[inline]
pub const fn is_private(&self) -> bool {
self.class as u8 == Class::Private as u8
}
/// Return error if object length is definite
#[inline]
pub const fn assert_definite(&self) -> Result<()> {
if self.length.is_definite() {
Ok(())
} else {
Err(Error::DerConstraintFailed(DerConstraint::IndefiniteLength))
}
}
/// Get the content following a BER header
#[inline]
pub fn parse_ber_content<'i>(&'_ self, i: &'i [u8]) -> ParseResult<'i, &'i [u8]> {
// defaults to maximum depth 8
// depth is used only if BER, and length is indefinite
BerParser::get_object_content(i, self, 8)
}
/// Get the content following a DER header
#[inline]
pub fn parse_der_content<'i>(&'_ self, i: &'i [u8]) -> ParseResult<'i, &'i [u8]> {
self.assert_definite()?;
DerParser::get_object_content(i, self, 8)
}
}
impl From<Tag> for Header<'_> {
#[inline]
fn from(tag: Tag) -> Self {
let constructed = matches!(tag, Tag::Sequence | Tag::Set);
Self::new(Class::Universal, constructed, tag, Length::Definite(0))
}
}
impl ToStatic for Header<'_> {
type Owned = Header<'static>;
fn to_static(&self) -> Self::Owned {
let raw_tag: Option<Cow<'static, [u8]>> =
self.raw_tag.as_ref().map(|b| Cow::Owned(b.to_vec()));
Header {
tag: self.tag,
constructed: self.constructed,
class: self.class,
length: self.length,
raw_tag,
}
}
}
impl<'a> FromBer<'a> for Header<'a> {
fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> {
let (i1, el) = parse_identifier(bytes)?;
let class = match Class::try_from(el.0) {
Ok(c) => c,
Err(_) => unreachable!(), // Cannot fail, we have read exactly 2 bits
};
let (i2, len) = parse_ber_length_byte(i1)?;
let (i3, len) = match (len.0, len.1) {
(0, l1) => {
// Short form: MSB is 0, the rest encodes the length (which can be 0) (8.1.3.4)
(i2, Length::Definite(usize::from(l1)))
}
(_, 0) => {
// Indefinite form: MSB is 1, the rest is 0 (8.1.3.6)
// If encoding is primitive, definite form shall be used (8.1.3.2)
if el.1 == 0 {
return Err(nom::Err::Error(Error::ConstructExpected));
}
(i2, Length::Indefinite)
}
(_, l1) => {
// if len is 0xff -> error (8.1.3.5)
if l1 == 0b0111_1111 {
return Err(nom::Err::Error(Error::InvalidLength));
}
let (i3, llen) = take(l1)(i2)?;
match bytes_to_u64(llen) {
Ok(l) => {
let l =
usize::try_from(l).or(Err(nom::Err::Error(Error::InvalidLength)))?;
(i3, Length::Definite(l))
}
Err(_) => {
return Err(nom::Err::Error(Error::InvalidLength));
}
}
}
};
let constructed = el.1 != 0;
let hdr = Header::new(class, constructed, Tag(el.2), len).with_raw_tag(Some(el.3.into()));
Ok((i3, hdr))
}
}
impl<'a> FromDer<'a> for Header<'a> {
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> {
let (i1, el) = parse_identifier(bytes)?;
let class = match Class::try_from(el.0) {
Ok(c) => c,
Err(_) => unreachable!(), // Cannot fail, we have read exactly 2 bits
};
let (i2, len) = parse_ber_length_byte(i1)?;
let (i3, len) = match (len.0, len.1) {
(0, l1) => {
// Short form: MSB is 0, the rest encodes the length (which can be 0) (8.1.3.4)
(i2, Length::Definite(usize::from(l1)))
}
(_, 0) => {
// Indefinite form is not allowed in DER (10.1)
return Err(nom::Err::Error(Error::DerConstraintFailed(
DerConstraint::IndefiniteLength,
)));
}
(_, l1) => {
// if len is 0xff -> error (8.1.3.5)
if l1 == 0b0111_1111 {
return Err(nom::Err::Error(Error::InvalidLength));
}
// DER(9.1) if len is 0 (indefinite form), obj must be constructed
der_constraint_fail_if!(
&i[1..],
len.1 == 0 && el.1 != 1,
DerConstraint::NotConstructed
);
let (i3, llen) = take(l1)(i2)?;
match bytes_to_u64(llen) {
Ok(l) => {
// DER: should have been encoded in short form (< 127)
// XXX der_constraint_fail_if!(i, l < 127);
let l =
usize::try_from(l).or(Err(nom::Err::Error(Error::InvalidLength)))?;
(i3, Length::Definite(l))
}
Err(_) => {
return Err(nom::Err::Error(Error::InvalidLength));
}
}
}
};
let constructed = el.1 != 0;
let hdr = Header::new(class, constructed, Tag(el.2), len).with_raw_tag(Some(el.3.into()));
Ok((i3, hdr))
}
}
impl DynTagged for (Class, bool, Tag) {
fn tag(&self) -> Tag {
self.2
}
}
#[cfg(feature = "std")]
impl ToDer for (Class, bool, Tag) {
fn to_der_len(&self) -> Result<usize> {
let (_, _, tag) = self;
match tag.0 {
0..=30 => Ok(1),
t => {
let mut sz = 1;
let mut val = t;
loop {
if val <= 127 {
return Ok(sz + 1);
} else {
val >>= 7;
sz += 1;
}
}
}
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let (class, constructed, tag) = self;
let b0 = (*class as u8) << 6;
let b0 = b0 | if *constructed { 0b10_0000 } else { 0 };
if tag.0 > 30 {
let mut val = tag.0;
const BUF_SZ: usize = 8;
let mut buffer = [0u8; BUF_SZ];
let mut current_index = BUF_SZ - 1;
// first byte: class+constructed+0x1f
let b0 = b0 | 0b1_1111;
let mut sz = writer.write(&[b0])?;
// now write bytes from right (last) to left
// last encoded byte
buffer[current_index] = (val & 0x7f) as u8;
val >>= 7;
while val > 0 {
current_index -= 1;
if current_index == 0 {
return Err(SerializeError::InvalidLength);
}
buffer[current_index] = (val & 0x7f) as u8 | 0x80;
val >>= 7;
}
sz += writer.write(&buffer[current_index..])?;
Ok(sz)
} else {
let b0 = b0 | (tag.0 as u8);
let sz = writer.write(&[b0])?;
Ok(sz)
}
}
fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
Ok(0)
}
}
impl DynTagged for Header<'_> {
fn tag(&self) -> Tag {
self.tag
}
}
#[cfg(feature = "std")]
impl ToDer for Header<'_> {
fn to_der_len(&self) -> Result<usize> {
let tag_len = (self.class, self.constructed, self.tag).to_der_len()?;
let len_len = self.length.to_der_len()?;
Ok(tag_len + len_len)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let sz = (self.class, self.constructed, self.tag).write_der_header(writer)?;
let sz = sz + self.length.write_der_header(writer)?;
Ok(sz)
}
fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
Ok(0)
}
fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// use raw_tag if present
let sz = match &self.raw_tag {
Some(t) => writer.write(t)?,
None => (self.class, self.constructed, self.tag).write_der_header(writer)?,
};
let sz = sz + self.length.write_der_header(writer)?;
Ok(sz)
}
}
/// Compare two BER headers. `len` fields are compared only if both objects have it set (same for `raw_tag`)
impl<'a> PartialEq<Header<'a>> for Header<'a> {
fn eq(&self, other: &Header) -> bool {
self.class == other.class
&& self.tag == other.tag
&& self.constructed == other.constructed
&& {
if self.length.is_null() && other.length.is_null() {
self.length == other.length
} else {
true
}
}
&& {
// it tag is present for both, compare it
if self.raw_tag.as_ref().xor(other.raw_tag.as_ref()).is_none() {
self.raw_tag == other.raw_tag
} else {
true
}
}
}
}
impl Eq for Header<'_> {}
#[cfg(all(test, feature = "std"))]
mod tests {
use crate::*;
use hex_literal::hex;
/// Generic tests on methods, and coverage tests
#[test]
fn methods_header() {
// Getters
let input = &hex! {"02 01 00"};
let (rem, header) = Header::from_ber(input).expect("parsing header failed");
assert_eq!(header.class(), Class::Universal);
assert_eq!(header.tag(), Tag::Integer);
assert!(header.assert_primitive().is_ok());
assert!(header.assert_constructed().is_err());
assert!(header.is_universal());
assert!(!header.is_application());
assert!(!header.is_private());
assert_eq!(rem, &input[2..]);
// test PartialEq
let hdr2 = Header::new_simple(Tag::Integer);
assert_eq!(header, hdr2);
// builder methods
let hdr3 = hdr2
.with_class(Class::ContextSpecific)
.with_constructed(true)
.with_length(Length::Definite(1));
assert!(hdr3.constructed());
assert!(hdr3.is_constructed());
assert!(hdr3.assert_constructed().is_ok());
assert!(hdr3.is_contextspecific());
let xx = hdr3.to_der_vec().expect("serialize failed");
assert_eq!(&xx, &[0xa2, 0x01]);
// indefinite length
let hdr4 = hdr3.with_length(Length::Indefinite);
assert!(hdr4.assert_definite().is_err());
let xx = hdr4.to_der_vec().expect("serialize failed");
assert_eq!(&xx, &[0xa2, 0x80]);
// parse_*_content
let hdr = Header::new_simple(Tag(2)).with_length(Length::Definite(1));
let (_, r) = hdr.parse_ber_content(&input[2..]).unwrap();
assert_eq!(r, &input[2..]);
let (_, r) = hdr.parse_der_content(&input[2..]).unwrap();
assert_eq!(r, &input[2..]);
}
}

180
vendor/asn1-rs/src/length.rs vendored Normal file
View File

@@ -0,0 +1,180 @@
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);
}
}

218
vendor/asn1-rs/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,218 @@
//! [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE-MIT)
//! [![Apache License 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE-APACHE)
//! [![docs.rs](https://docs.rs/asn1-rs/badge.svg)](https://docs.rs/asn1-rs)
//! [![crates.io](https://img.shields.io/crates/v/asn1-rs.svg)](https://crates.io/crates/asn1-rs)
//! [![Download numbers](https://img.shields.io/crates/d/asn1-rs.svg)](https://crates.io/crates/asn1-rs)
//! [![Github CI](https://github.com/rusticata/asn1-rs/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/asn1-rs/actions)
//! [![Minimum rustc version](https://img.shields.io/badge/rustc-1.63.0+-lightgray.svg)](#rust-version-requirements)
//!
//! # BER/DER Parsers/Encoders
//!
//! A set of parsers/encoders for Basic Encoding Rules (BER [[X.690]]) and Distinguished Encoding Rules(DER
//! [[X.690]]) formats, implemented with the [nom] parser combinator framework.
//!
//! It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken
//! to ensure security and safety of this crate, including design (recursion limit, defensive
//! programming), tests, and fuzzing. It also aims to be panic-free.
//!
//! This crate is a rewrite of [der-parser](https://crates.io/crates/der-parser) to propose a more data-oriented API,
//! and add generalized support for serialization.
//!
//! Many ideas were borrowed from the [crypto/utils/der](https://github.com/RustCrypto/utils/tree/master/der) crate (like
//! the `Any`/`TryFrom`/`FromDer` mechanism), adapted and merged into a generalized BER/DER crate.
//! Credits (and many thanks) go to Tony Arcieri for writing the original crate.
//!
//! # BER/DER parsers
//!
//! BER stands for Basic Encoding Rules, and is defined in [[X.690]]. It defines a set of rules to
//! encode and decode ASN.1 [[X.680]] objects in binary.
//!
//! [[X.690]] also defines Distinguished Encoding Rules (DER), which is BER with added rules to
//! ensure canonical and unequivocal binary representation of objects.
//!
//! The choice of which one to use is usually guided by the speficication of the data format based
//! on BER or DER: for example, X.509 uses DER as encoding representation.
//!
//! The main traits for parsing are the [`FromBer`] and [`FromDer`] traits.
//! These traits provide methods to parse binary input, and return either the remaining (unparsed) bytes
//! and the parsed object, or an error.
//!
//! The parsers follow the interface from [nom], and the [`ParseResult`] object is a specialized version
//! of `nom::IResult`. This means that most `nom` combinators (`map`, `many0`, etc.) can be used in
//! combination to objects and methods from this crate. Reading the nom documentation may
//! help understanding how to write and combine parsers and use the output.
//!
//! **Minimum Supported Rust Version**: 1.63.0
//!
//! # Recipes
//!
//! See [doc::recipes] and [doc::derive] for more examples and recipes.
//!
//! See [doc::debug] for advice and tools to debug parsers.
//!
//! ## Examples
//!
//! Parse 2 BER integers:
//!
//! ```rust
//! use asn1_rs::{Integer, FromBer};
//!
//! let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01,
//! 0x02, 0x03, 0x01, 0x00, 0x00,
//! ];
//!
//! let (rem, obj1) = Integer::from_ber(&bytes).expect("parsing failed");
//! let (rem, obj2) = Integer::from_ber(&bytes).expect("parsing failed");
//!
//! assert_eq!(obj1, Integer::from_u32(65537));
//! ```
//!
//! In the above example, the generic [`Integer`] type is used. This type can contain integers of any
//! size, but do not provide a simple API to manipulate the numbers.
//!
//! In most cases, the integer either has a limit, or is expected to fit into a primitive type.
//! To get a simple value, just use the `from_ber`/`from_der` methods on the primitive types:
//!
//! ```rust
//! use asn1_rs::FromBer;
//!
//! let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01,
//! 0x02, 0x03, 0x01, 0x00, 0x00,
//! ];
//!
//! let (rem, obj1) = u32::from_ber(&bytes).expect("parsing failed");
//! let (rem, obj2) = u32::from_ber(&rem).expect("parsing failed");
//!
//! assert_eq!(obj1, 65537);
//! assert_eq!(obj2, 65536);
//! ```
//!
//! If the parsing succeeds, but the integer cannot fit into the expected type, the method will return
//! an `IntegerTooLarge` error.
//!
//! # BER/DER encoders
//!
//! BER/DER encoding is symmetrical to decoding, using the traits `ToBer` and [`ToDer`] traits.
//! These traits provide methods to write encoded content to objects with the `io::Write` trait,
//! or return an allocated `Vec<u8>` with the encoded data.
//! If the serialization fails, an error is returned.
//!
//! ## Examples
//!
//! Writing 2 BER integers:
//!
#![cfg_attr(feature = "std", doc = r#"```rust"#)]
#![cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)]
//! use asn1_rs::{Integer, ToDer};
//!
//! let mut writer = Vec::new();
//!
//! let obj1 = Integer::from_u32(65537);
//! let obj2 = Integer::from_u32(65536);
//!
//! let _ = obj1.write_der(&mut writer).expect("serialization failed");
//! let _ = obj2.write_der(&mut writer).expect("serialization failed");
//!
//! let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01,
//! 0x02, 0x03, 0x01, 0x00, 0x00,
//! ];
//! assert_eq!(&writer, bytes);
//! ```
//!
//! Similarly to `FromBer`/`FromDer`, serialization methods are also implemented for primitive types:
//!
#![cfg_attr(feature = "std", doc = r#"```rust"#)]
#![cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)]
//! use asn1_rs::ToDer;
//!
//! let mut writer = Vec::new();
//!
//! let _ = 65537.write_der(&mut writer).expect("serialization failed");
//! let _ = 65536.write_der(&mut writer).expect("serialization failed");
//!
//! let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01,
//! 0x02, 0x03, 0x01, 0x00, 0x00,
//! ];
//! assert_eq!(&writer, bytes);
//! ```
//!
//! If the parsing succeeds, but the integer cannot fit into the expected type, the method will return
//! an `IntegerTooLarge` error.
//!
//! ## Changes
//!
//! See `CHANGELOG.md`.
//!
//! # References
//!
//! - [[X.680]] Abstract Syntax Notation One (ASN.1): Specification of basic notation.
//! - [[X.690]] ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical
//! Encoding Rules (CER) and Distinguished Encoding Rules (DER).
//!
//! [X.680]: http://www.itu.int/rec/T-REC-X.680/en "Abstract Syntax Notation One (ASN.1):
//! Specification of basic notation."
//! [X.690]: https://www.itu.int/rec/T-REC-X.690/en "ASN.1 encoding rules: Specification of
//! Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules
//! (DER)."
//! [nom]: https://github.com/Geal/nom "Nom parser combinator framework"
#![deny(/*missing_docs,*/
unstable_features,
unused_import_braces,
unused_qualifications,
// unreachable_pub
)]
#![forbid(unsafe_code)]
#![warn(
/* missing_docs,
rust_2018_idioms,*/
missing_debug_implementations,
)]
// pragmas for doc
#![deny(rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(test(
no_crate_inject,
attr(deny(warnings/*, rust_2018_idioms*/), allow(dead_code, unused_variables))
))]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "std")]
extern crate core;
// #[cfg(feature = "alloc")]
extern crate alloc;
mod asn1_types;
mod ber;
mod class;
mod datetime;
mod debug;
mod derive;
mod error;
mod header;
mod length;
mod tag;
mod tostatic;
mod traits;
pub use asn1_types::*;
pub use class::*;
pub use datetime::*;
pub use derive::*;
pub use error::*;
pub use header::*;
pub use length::*;
pub use tag::*;
pub use traits::*;
pub use nom;
pub use nom::{Err, IResult, Needed};
#[doc(hidden)]
pub mod exports {
pub use alloc::borrow;
pub use asn1_rs_impl;
}
#[cfg(doc)]
pub mod doc;

76
vendor/asn1-rs/src/tag.rs vendored Normal file
View File

@@ -0,0 +1,76 @@
use crate::{Error, Result};
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
use rusticata_macros::newtype_enum;
/// BER/DER Tag as defined in X.680 section 8.4
///
/// X.690 doesn't specify the maximum tag size so we're assuming that people
/// aren't going to need anything more than a u32.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Tag(pub u32);
newtype_enum! {
impl display Tag {
EndOfContent = 0,
Boolean = 1,
Integer = 2,
BitString = 3,
OctetString = 4,
Null = 5,
Oid = 6,
ObjectDescriptor = 7,
External = 8,
RealType = 9,
Enumerated = 10,
EmbeddedPdv = 11,
Utf8String = 12,
RelativeOid = 13,
Sequence = 16,
Set = 17,
NumericString = 18,
PrintableString = 19,
T61String = 20,
TeletexString = 20,
VideotexString = 21,
Ia5String = 22,
UtcTime = 23,
GeneralizedTime = 24,
GraphicString = 25,
VisibleString = 26,
GeneralString = 27,
UniversalString = 28,
CharacterString = 29,
BmpString = 30,
}
}
impl Tag {
pub const fn assert_eq(&self, tag: Tag) -> Result<()> {
if self.0 == tag.0 {
Ok(())
} else {
Err(Error::UnexpectedTag {
expected: Some(tag),
actual: *self,
})
}
}
pub fn invalid_value(&self, msg: &str) -> Error {
Error::InvalidValue {
tag: *self,
msg: msg.to_string(),
}
}
}
impl From<u32> for Tag {
fn from(v: u32) -> Self {
Tag(v)
}
}

90
vendor/asn1-rs/src/tostatic.rs vendored Normal file
View File

@@ -0,0 +1,90 @@
use alloc::borrow::Cow;
use alloc::string::ToString;
/// Common trait for objects that can be transformed to a `'static` version of `self`
pub trait ToStatic {
type Owned: 'static;
fn to_static(&self) -> Self::Owned;
}
impl ToStatic for Cow<'_, str> {
type Owned = Cow<'static, str>;
fn to_static(&self) -> <Self as ToStatic>::Owned {
Cow::Owned(self.to_string())
}
}
impl ToStatic for Cow<'_, [u8]> {
type Owned = Cow<'static, [u8]>;
fn to_static(&self) -> <Self as ToStatic>::Owned {
Cow::Owned(self.to_vec())
}
}
macro_rules! impl_tostatic_primitive {
($t:ty) => {
impl ToStatic for $t {
type Owned = $t;
fn to_static(&self) -> <Self as ToStatic>::Owned {
self.clone()
}
}
};
($t:ty => $to:ty, $closure:expr) => {
impl ToStatic for $t {
type Owned = $to;
fn to_static(&self) -> <Self as ToStatic>::Owned {
let f: &dyn Fn(&Self) -> $to = &$closure;
f(self)
}
}
};
(I $($types: ty)+) => {
$(
impl_tostatic_primitive!($types);
)*
};
}
impl_tostatic_primitive!(bool);
impl_tostatic_primitive!(I i8 i16 i32 i64 i128 isize);
impl_tostatic_primitive!(I u8 u16 u32 u64 u128 usize);
impl<T> ToStatic for &'_ T
where
T: ToStatic,
{
type Owned = T::Owned;
fn to_static(&self) -> Self::Owned {
(*self).to_static()
}
}
impl<T> ToStatic for Option<T>
where
T: ToStatic,
{
type Owned = Option<T::Owned>;
fn to_static(&self) -> Self::Owned {
self.as_ref().map(ToStatic::to_static)
}
}
#[cfg(feature = "std")]
const _: () = {
impl_tostatic_primitive!(I String);
impl_tostatic_primitive!(str => String, |s| s.to_string());
impl<T> ToStatic for Box<T>
where
T: ToStatic,
{
type Owned = Box<T::Owned>;
fn to_static(&self) -> Self::Owned {
Box::new(self.as_ref().to_static())
}
}
};

354
vendor/asn1-rs/src/traits.rs vendored Normal file
View File

@@ -0,0 +1,354 @@
use crate::debug::{trace, trace_generic};
use crate::error::*;
use crate::{parse_der_any, Any, Class, Explicit, Implicit, Tag, TaggedParser};
use core::convert::{TryFrom, TryInto};
use core::fmt::{Debug, Display};
#[cfg(feature = "std")]
use std::io::Write;
/// Phantom type representing a BER parser
#[doc(hidden)]
#[derive(Debug)]
pub enum BerParser {}
/// Phantom type representing a DER parser
#[doc(hidden)]
#[derive(Debug)]
pub enum DerParser {}
#[doc(hidden)]
pub trait ASN1Parser {}
impl ASN1Parser for BerParser {}
impl ASN1Parser for DerParser {}
pub trait Tagged {
const TAG: Tag;
}
impl<T> Tagged for &'_ T
where
T: Tagged,
{
const TAG: Tag = T::TAG;
}
pub trait DynTagged {
fn tag(&self) -> Tag;
}
impl<T> DynTagged for T
where
T: Tagged,
{
fn tag(&self) -> Tag {
T::TAG
}
}
/// Base trait for BER object parsers
///
/// Library authors should usually not directly implement this trait, but should prefer implementing the
/// [`TryFrom<Any>`] trait,
/// which offers greater flexibility and provides an equivalent `FromBer` implementation for free.
///
/// # Examples
///
/// ```
/// use asn1_rs::{Any, Result, Tag};
/// use std::convert::TryFrom;
///
/// // The type to be decoded
/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
/// pub struct MyType(pub u32);
///
/// impl<'a> TryFrom<Any<'a>> for MyType {
/// type Error = asn1_rs::Error;
///
/// fn try_from(any: Any<'a>) -> Result<MyType> {
/// any.tag().assert_eq(Tag::Integer)?;
/// // for this fictive example, the type contains the number of characters
/// let n = any.data.len() as u32;
/// Ok(MyType(n))
/// }
/// }
///
/// // The above code provides a `FromBer` implementation for free.
///
/// // Example of parsing code:
/// use asn1_rs::FromBer;
///
/// let input = &[2, 1, 2];
/// // Objects can be parsed using `from_ber`, which returns the remaining bytes
/// // and the parsed object:
/// let (rem, my_type) = MyType::from_ber(input).expect("parsing failed");
/// ```
pub trait FromBer<'a, E = Error>: Sized {
/// Attempt to parse input bytes into a BER object
fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, E>;
}
impl<'a, T, E> FromBer<'a, E> for T
where
T: TryFrom<Any<'a>, Error = E>,
E: From<Error>,
{
fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, T, E> {
let (i, any) = Any::from_ber(bytes).map_err(nom::Err::convert)?;
let result = any.try_into().map_err(nom::Err::Error)?;
Ok((i, result))
}
}
/// Base trait for DER object parsers
///
/// Library authors should usually not directly implement this trait, but should prefer implementing the
/// [`TryFrom<Any>`] + [`CheckDerConstraints`] traits,
/// which offers greater flexibility and provides an equivalent `FromDer` implementation for free
/// (in fact, it provides both [`FromBer`] and `FromDer`).
///
/// Note: if you already implemented [`TryFrom<Any>`] and [`CheckDerConstraints`],
/// you can get a free [`FromDer`] implementation by implementing the
/// [`DerAutoDerive`] trait. This is not automatic, so it is also possible to manually
/// implement [`FromDer`] if preferred.
///
/// # Examples
///
/// ```
/// use asn1_rs::{Any, CheckDerConstraints, DerAutoDerive, Result, Tag};
/// use std::convert::TryFrom;
///
/// // The type to be decoded
/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
/// pub struct MyType(pub u32);
///
/// impl<'a> TryFrom<Any<'a>> for MyType {
/// type Error = asn1_rs::Error;
///
/// fn try_from(any: Any<'a>) -> Result<MyType> {
/// any.tag().assert_eq(Tag::Integer)?;
/// // for this fictive example, the type contains the number of characters
/// let n = any.data.len() as u32;
/// Ok(MyType(n))
/// }
/// }
///
/// impl CheckDerConstraints for MyType {
/// fn check_constraints(any: &Any) -> Result<()> {
/// any.header.assert_primitive()?;
/// Ok(())
/// }
/// }
///
/// impl DerAutoDerive for MyType {}
///
/// // The above code provides a `FromDer` implementation for free.
///
/// // Example of parsing code:
/// use asn1_rs::FromDer;
///
/// let input = &[2, 1, 2];
/// // Objects can be parsed using `from_der`, which returns the remaining bytes
/// // and the parsed object:
/// let (rem, my_type) = MyType::from_der(input).expect("parsing failed");
/// ```
pub trait FromDer<'a, E = Error>: Sized {
/// Attempt to parse input bytes into a DER object (enforcing constraints)
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E>;
}
/// Trait to automatically derive `FromDer`
///
/// This trait is only a marker to control if a DER parser should be automatically derived. It is
/// empty.
///
/// This trait is used in combination with others:
/// after implementing [`TryFrom<Any>`] and [`CheckDerConstraints`] for a type,
/// a free [`FromDer`] implementation is provided by implementing the
/// [`DerAutoDerive`] trait. This is the most common case.
///
/// However, this is not automatic so it is also possible to manually
/// implement [`FromDer`] if preferred.
/// Manual implementation is generally only needed for generic containers (for ex. `Vec<T>`),
/// because the default implementation adds a constraint on `T` to implement also `TryFrom<Any>`
/// and `CheckDerConstraints`. This is problematic when `T` only provides `FromDer`, and can be
/// solved by providing a manual implementation of [`FromDer`].
pub trait DerAutoDerive {}
impl<'a, T, E> FromDer<'a, E> for T
where
T: TryFrom<Any<'a>, Error = E>,
T: CheckDerConstraints,
T: DerAutoDerive,
E: From<Error> + Display + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, T, E> {
trace_generic(
core::any::type_name::<T>(),
"T::from_der",
|bytes| {
let (i, any) = trace(core::any::type_name::<T>(), parse_der_any, bytes)
.map_err(nom::Err::convert)?;
<T as CheckDerConstraints>::check_constraints(&any)
.map_err(|e| nom::Err::Error(e.into()))?;
let result = any.try_into().map_err(nom::Err::Error)?;
Ok((i, result))
},
bytes,
)
}
}
/// Verification of DER constraints
pub trait CheckDerConstraints {
fn check_constraints(any: &Any) -> Result<()>;
}
/// Common trait for all objects that can be encoded using the DER representation
///
/// # Examples
///
/// Objects from this crate can be encoded as DER:
///
/// ```
/// use asn1_rs::{Integer, ToDer};
///
/// let int = Integer::from(4u32);
/// let mut writer = Vec::new();
/// let sz = int.write_der(&mut writer).expect("serialization failed");
///
/// assert_eq!(&writer, &[0x02, 0x01, 0x04]);
/// # assert_eq!(sz, 3);
/// ```
///
/// Many of the primitive types can also directly be encoded as DER:
///
/// ```
/// use asn1_rs::ToDer;
///
/// let mut writer = Vec::new();
/// let sz = 4.write_der(&mut writer).expect("serialization failed");
///
/// assert_eq!(&writer, &[0x02, 0x01, 0x04]);
/// # assert_eq!(sz, 3);
/// ```
#[cfg(feature = "std")]
pub trait ToDer
where
Self: DynTagged,
{
/// Get the length of the object (including the header), when encoded
///
// Since we are using DER, length cannot be Indefinite, so we can use `usize`.
// XXX can this function fail?
fn to_der_len(&self) -> Result<usize>;
/// Write the DER encoded representation to a newly allocated `Vec<u8>`.
fn to_der_vec(&self) -> SerializeResult<Vec<u8>> {
let mut v = Vec::new();
let _ = self.write_der(&mut v)?;
Ok(v)
}
/// Similar to using `to_vec`, but uses provided values without changes.
/// This can generate an invalid encoding for a DER object.
fn to_der_vec_raw(&self) -> SerializeResult<Vec<u8>> {
let mut v = Vec::new();
let _ = self.write_der_raw(&mut v)?;
Ok(v)
}
/// Attempt to write the DER encoded representation (header and content) into this writer.
///
/// # Examples
///
/// ```
/// use asn1_rs::{Integer, ToDer};
///
/// let int = Integer::from(4u32);
/// let mut writer = Vec::new();
/// let sz = int.write_der(&mut writer).expect("serialization failed");
///
/// assert_eq!(&writer, &[0x02, 0x01, 0x04]);
/// # assert_eq!(sz, 3);
/// ```
fn write_der(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
let sz = self.write_der_header(writer)?;
let sz = sz + self.write_der_content(writer)?;
Ok(sz)
}
/// Attempt to write the DER header to this writer.
fn write_der_header(&self, writer: &mut dyn Write) -> SerializeResult<usize>;
/// Attempt to write the DER content (all except header) to this writer.
fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult<usize>;
/// Similar to using `to_der`, but uses provided values without changes.
/// This can generate an invalid encoding for a DER object.
fn write_der_raw(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
self.write_der(writer)
}
}
#[cfg(feature = "std")]
impl<'a, T> ToDer for &'a T
where
T: ToDer,
&'a T: DynTagged,
{
fn to_der_len(&self) -> Result<usize> {
(*self).to_der_len()
}
fn write_der_header(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
(*self).write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
(*self).write_der_content(writer)
}
}
/// Helper trait for creating tagged EXPLICIT values
///
/// # Examples
///
/// ```
/// use asn1_rs::{AsTaggedExplicit, Class, Error, TaggedParser};
///
/// // create a `[1] EXPLICIT INTEGER` value
/// let tagged: TaggedParser<_, _, Error> = 4u32.explicit(Class::ContextSpecific, 1);
/// ```
pub trait AsTaggedExplicit<'a, E = Error>: Sized {
fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E> {
TaggedParser::new_explicit(class, tag, self)
}
}
impl<'a, T, E> AsTaggedExplicit<'a, E> for T where T: Sized + 'a {}
/// Helper trait for creating tagged IMPLICIT values
///
/// # Examples
///
/// ```
/// use asn1_rs::{AsTaggedImplicit, Class, Error, TaggedParser};
///
/// // create a `[1] IMPLICIT INTEGER` value, not constructed
/// let tagged: TaggedParser<_, _, Error> = 4u32.implicit(Class::ContextSpecific, false, 1);
/// ```
pub trait AsTaggedImplicit<'a, E = Error>: Sized {
fn implicit(
self,
class: Class,
constructed: bool,
tag: u32,
) -> TaggedParser<'a, Implicit, Self, E> {
TaggedParser::new_implicit(class, constructed, tag, self)
}
}
impl<'a, T, E> AsTaggedImplicit<'a, E> for T where T: Sized + 'a {}
pub use crate::tostatic::*;