Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move to owned types #768

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion der/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ pub use self::{

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub use self::{any::Any, bit_string::BitString, octet_string::OctetString, set_of::SetOfVec};
pub use self::{
any::Any, bit_string::BitString, ia5_string::Ia5String, integer::bigint::UInt,
octet_string::OctetString, printable_string::PrintableString, set_of::SetOfVec,
teletex_string::TeletexString, utf8_string::Utf8String,
};

#[cfg(feature = "oid")]
#[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
Expand Down
81 changes: 74 additions & 7 deletions der/src/asn1/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
use core::cmp::Ordering;

#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use {crate::ByteVec, alloc::vec::Vec};

#[cfg(feature = "oid")]
use crate::asn1::ObjectIdentifier;
Expand Down Expand Up @@ -226,19 +226,80 @@ pub struct Any {
tag: Tag,

/// Inner value encoded as bytes.
value: Vec<u8>,
value: ByteVec,
}

#[cfg(feature = "alloc")]
impl Any {
/// Create a new [`Any`] from the provided [`Tag`] and DER bytes.
pub fn new(tag: Tag, bytes: impl Into<Vec<u8>>) -> Result<Self> {
let value = bytes.into();
let value = ByteVec::new(&bytes.into())?;

// Ensure the tag and value are a valid `AnyRef`.
AnyRef::new(tag, &value)?;
AnyRef::new(tag, value.as_slice())?;
Ok(Self { tag, value })
}

/// Infallible creation of an [`AnyRef`] from a [`ByteSlice`].
pub(crate) fn from_tag_and_value(tag: Tag, value: ByteSlice<'_>) -> Self {
Self {
tag,
value: value.into(),
}
}

/// Attempt to decode an ASN.1 `OBJECT IDENTIFIER`.
#[cfg(feature = "oid")]
#[cfg_attr(docsrs, doc(cfg(feature = "oid")))]
pub fn oid(&self) -> Result<ObjectIdentifier> {
AnyRef::from(self).try_into()
}

/// Attempt to decode an ASN.1 `UTCTime`.
pub fn utc_time(&self) -> Result<UtcTime> {
AnyRef::from(self).try_into()
}

/// Attempt to decode an ASN.1 `UTF8String`.
pub fn utf8_string<'a>(&'a self) -> Result<Utf8StringRef<'a>> {
AnyRef::from(self).try_into()
}

/// Attempt to decode an ASN.1 `IA5String`.
pub fn ia5_string<'a>(&'a self) -> Result<Ia5StringRef<'a>> {
AnyRef::from(self).try_into()
}

/// Attempt to decode an ASN.1 `PrintableString`.
pub fn printable_string<'a>(&'a self) -> Result<PrintableStringRef<'a>> {
AnyRef::from(self).try_into()
}

/// Attempt to decode an ASN.1 `TeletexString`.
pub fn teletex_string<'a>(&'a self) -> Result<TeletexStringRef<'a>> {
AnyRef::from(self).try_into()
}

/// Is this value an ASN.1 `NULL` value?
pub fn is_null(&self) -> bool {
AnyRef::from(self).is_null()
}

/// Attempt to decode this [`AnyRef`] type into the inner value.
pub fn decode_into<'a, T>(&'a self) -> Result<T>
where
T: DecodeValue<'a> + FixedTag,
{
self.tag.assert_eq(T::TAG)?;
let header = Header {
tag: self.tag,
length: self.value.len(),
};

let mut decoder = SliceReader::new(self.value.as_slice())?;
let result = T::decode_value(&mut decoder, header)?;
decoder.finish(result)
}
}

#[cfg(feature = "alloc")]
Expand All @@ -260,19 +321,19 @@ impl<'a> Decode<'a> for Any {
#[cfg(feature = "alloc")]
impl EncodeValue for Any {
fn value_len(&self) -> Result<Length> {
self.value.len().try_into()
Ok(self.value.len())
}

fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
writer.write(&self.value)
writer.write(self.value.as_slice())
}
}

#[cfg(feature = "alloc")]
impl<'a> From<&'a Any> for AnyRef<'a> {
fn from(any: &'a Any) -> AnyRef<'a> {
// Ensured to parse successfully in constructor
AnyRef::new(any.tag, &any.value).expect("invalid ANY")
AnyRef::new(any.tag, any.value.as_slice()).expect("invalid ANY")
}
}

Expand All @@ -282,3 +343,9 @@ impl Tagged for Any {
self.tag
}
}

impl ValueOrd for Any {
fn value_cmp(&self, other: &Self) -> Result<Ordering> {
self.value.der_cmp(&other.value)
}
}
138 changes: 136 additions & 2 deletions der/src/asn1/ia5_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ impl<'a> TryFrom<AnyRef<'a>> for Ia5StringRef<'a> {
}

impl<'a> From<Ia5StringRef<'a>> for AnyRef<'a> {
fn from(printable_string: Ia5StringRef<'a>) -> AnyRef<'a> {
AnyRef::from_tag_and_value(Tag::Ia5String, printable_string.inner.into())
fn from(international_string: Ia5StringRef<'a>) -> AnyRef<'a> {
AnyRef::from_tag_and_value(Tag::Ia5String, international_string.inner.into())
}
}

Expand All @@ -117,6 +117,140 @@ impl<'a> fmt::Debug for Ia5StringRef<'a> {
}
}

#[cfg(feature = "alloc")]
pub use self::alloc::*;

#[cfg(feature = "alloc")]
mod alloc {
use super::Ia5StringRef;
use crate::{
asn1::{Any, AnyRef},
ord::OrdIsValueOrd,
ByteSlice, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, Str,
Tag, Writer,
};
use core::{fmt, ops::Deref, str};

/// ASN.1 `IA5String` type.
///
/// Supports the [International Alphabet No. 5 (IA5)] character encoding, i.e.
/// the lower 128 characters of the ASCII alphabet. (Note: IA5 is now
/// technically known as the International Reference Alphabet or IRA as
/// specified in the ITU-T's T.50 recommendation).
///
/// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`].
///
/// This is a zero-copy reference type which borrows from the input data.
///
/// [International Alphabet No. 5 (IA5)]: https://en.wikipedia.org/wiki/T.50_%28standard%29
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
pub struct Ia5String {
/// Inner value
inner: Str,
}

impl Ia5String {
/// Create a new `IA5String`.
pub fn new<T>(input: &T) -> Result<Self>
where
T: AsRef<[u8]> + ?Sized,
{
let input = input.as_ref();

// Validate all characters are within IA5String's allowed set
if input.iter().any(|&c| c > 0x7F) {
return Err(Self::TAG.value_error());
}

Str::from_bytes(input)
.map(|inner| Self { inner })
.map_err(|_| Self::TAG.value_error())
}
}

impl Deref for Ia5String {
type Target = Str;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl AsRef<str> for Ia5String {
fn as_ref(&self) -> &str {
self.as_str()
}
}

impl AsRef<[u8]> for Ia5String {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}

impl<'a> DecodeValue<'a> for Ia5String {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
Self::new(ByteSlice::decode_value(reader, header)?.as_slice())
}
}

impl EncodeValue for Ia5String {
fn value_len(&self) -> Result<Length> {
self.inner.value_len()
}

fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
self.inner.encode_value(writer)
}
}

impl FixedTag for Ia5String {
const TAG: Tag = Tag::Ia5String;
}

impl OrdIsValueOrd for Ia5String {}

impl<'a> TryFrom<AnyRef<'a>> for Ia5String {
type Error = Error;

fn try_from(any: AnyRef<'a>) -> Result<Ia5String> {
any.decode_into()
}
}

impl<'a> From<&'a Ia5String> for AnyRef<'a> {
fn from(international_string: &'a Ia5String) -> AnyRef<'a> {
AnyRef::from_tag_and_value(Tag::Ia5String, (&international_string.inner).into())
}
}

impl<'a> From<Ia5StringRef<'a>> for Ia5String {
fn from(international_string: Ia5StringRef<'a>) -> Ia5String {
Ia5String {
inner: international_string.inner.into(),
}
}
}

impl fmt::Display for Ia5String {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}

impl fmt::Debug for Ia5String {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ia5String({:?})", self.as_str())
}
}

impl<'a> From<Ia5StringRef<'a>> for Any {
fn from(international_string: Ia5StringRef<'a>) -> Any {
Any::from_tag_and_value(Tag::Ia5String, international_string.inner.into())
}
}
}

#[cfg(test)]
mod tests {
use super::Ia5StringRef;
Expand Down
93 changes: 93 additions & 0 deletions der/src/asn1/integer/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,99 @@ impl<'a> FixedTag for UIntRef<'a> {
const TAG: Tag = Tag::Integer;
}

#[cfg(feature = "alloc")]
pub use self::alloc::*;

#[cfg(feature = "alloc")]
mod alloc {
use super::super::uint;
use crate::{
asn1::AnyRef, ByteVec, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header,
Length, Reader, Result, Tag, Writer,
};

/// "Big" unsigned ASN.1 `INTEGER` type.
///
/// Provides direct access to the underlying big endian bytes which comprise an
/// unsigned integer value.
///
/// Intended for use cases like very large integers that are used in
/// cryptographic applications (e.g. keys, signatures).
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd)]
pub struct UInt {
/// Inner value
inner: ByteVec,
}

impl UInt {
/// Create a new [`UIntRef`] from a byte slice.
pub fn new(bytes: &[u8]) -> Result<Self> {
let inner = ByteVec::new(uint::strip_leading_zeroes(bytes))
.map_err(|_| ErrorKind::Length { tag: Self::TAG })?;

Ok(Self { inner })
}

/// Borrow the inner byte slice which contains the least significant bytes
/// of a big endian integer value with all leading zeros stripped.
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_slice()
}

/// Get the length of this [`UInt`] in bytes.
pub fn len(&self) -> Length {
self.inner.len()
}

///// Is the inner byte slice empty?
//pub fn is_empty(&self) -> bool {
// self.inner.is_empty()
//}
}

impl<'a> DecodeValue<'a> for UInt {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
let buffer = ByteVec::decode_value(reader, header)?;
let bytes = buffer.as_slice();
let result = Self::new(uint::decode_to_slice(bytes)?)?;

// Ensure we compute the same encoded length as the original any value.
if result.value_len()? != header.length {
return Err(Self::TAG.non_canonical_error());
}

Ok(result)
}
}

impl EncodeValue for UInt {
fn value_len(&self) -> Result<Length> {
uint::encoded_len(self.inner.as_slice())
}

fn encode_value(&self, writer: &mut dyn Writer) -> Result<()> {
// Add leading `0x00` byte if required
if self.value_len()? > self.len() {
writer.write_byte(0)?;
}

writer.write(self.as_bytes())
}
}

impl TryFrom<AnyRef<'_>> for UInt {
type Error = Error;

fn try_from(any: AnyRef<'_>) -> Result<UInt> {
any.decode_into()
}
}

impl FixedTag for UInt {
const TAG: Tag = Tag::Integer;
}
}

#[cfg(test)]
mod tests {
use super::UIntRef;
Expand Down
Loading