Skip to content
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
1 change: 1 addition & 0 deletions der/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod ia5_string;
mod integer;
mod null;
mod octet_string;
mod octet_string2;
#[cfg(feature = "oid")]
mod oid;
mod optional;
Expand Down
121 changes: 121 additions & 0 deletions der/src/asn1/octet_string2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! ASN.1 `OCTET STRING` support.

use crate::{
Decode, DecodeValue, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader, Tag,
Writer, bytes_ref2::BytesRef, ord::OrdIsValueOrd,
};

#[cfg(feature = "alloc")]
use {
super::OctetString,
alloc::borrow::{Borrow, ToOwned},
};

/// ASN.1 `OCTET STRING` type: borrowed form.
///
/// Octet strings represent contiguous sequences of octets, a.k.a. bytes.
///
/// This is a zero-copy reference type which borrows from the input data.
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct OctetStringRef(BytesRef);

impl OctetStringRef {
/// Create a new ASN.1 `OCTET STRING` from a byte slice.
pub fn new(slice: &[u8]) -> Result<&Self, Error> {
let bytes = BytesRef::new(slice).map_err(|_| ErrorKind::Length { tag: Self::TAG })?;
Ok(Self::from_bytes_ref(bytes))
}

/// Reference constructor which keeps `BytesRef` out of the public API.
fn from_bytes_ref(bytes: &BytesRef) -> &Self {
// SAFETY: `Self` is a `repr(transparent)` newtype for `BytesRef`
#[allow(unsafe_code)]
unsafe {
&*(bytes as *const BytesRef as *const Self)
}
}

/// Borrow the inner byte slice.
pub fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}

/// Get the length of the inner byte slice.
pub fn len(&self) -> Length {
self.0.len()
}

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

/// Parse `T` from this `OCTET STRING`'s contents.
pub fn decode_into<'a, T: Decode<'a>>(&'a self) -> Result<T, T::Error> {
Decode::from_der(self.as_bytes())
}
}

impl<'a> DecodeValue<'a> for &'a OctetStringRef {
type Error = Error;

fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self, Error> {
<&BytesRef>::decode_value(reader, header).map(OctetStringRef::from_bytes_ref)
}
}

impl EncodeValue for OctetStringRef {
fn value_len(&self) -> Result<Length, Error> {
self.0.value_len()
}

fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> {
self.0.encode_value(writer)
}
}

impl FixedTag for OctetStringRef {
const TAG: Tag = Tag::OctetString;
}

impl OrdIsValueOrd for OctetStringRef {}

impl<'a> From<&'a OctetStringRef> for &'a [u8] {
fn from(octet_string: &'a OctetStringRef) -> &'a [u8] {
octet_string.as_bytes()
}
}

impl<'a> TryFrom<&'a [u8]> for &'a OctetStringRef {
type Error = Error;

fn try_from(byte_slice: &'a [u8]) -> Result<Self, Error> {
OctetStringRef::new(byte_slice)
}
}

#[cfg(feature = "alloc")]
impl Borrow<OctetStringRef> for OctetString {
fn borrow(&self) -> &OctetStringRef {
// TODO(tarcieri): avoid panic
OctetStringRef::new(self.as_bytes()).expect("should not be overlength")
}
}

#[cfg(feature = "alloc")]
impl From<&OctetStringRef> for OctetString {
fn from(string_ref: &OctetStringRef) -> Self {
// TODO(tarcieri): avoid panic
OctetString::new(string_ref.as_bytes()).expect("should not be overlength")
}
}

#[cfg(feature = "alloc")]
impl ToOwned for OctetStringRef {
type Owned = OctetString;

fn to_owned(&self) -> Self::Owned {
self.into()
}
}
95 changes: 95 additions & 0 deletions der/src/bytes_ref2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//! Common handling for types backed by byte slices with enforcement of a
//! library-level length limitation i.e. `Length::MAX`.

use crate::{DecodeValue, DerOrd, EncodeValue, Error, Header, Length, Reader, Result, Writer};
use core::cmp::Ordering;

/// Byte slice newtype which respects the `Length::MAX` limit.
#[derive(Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[repr(transparent)]
pub(crate) struct BytesRef([u8]);

impl BytesRef {
/// Create a new [`BytesRef`], ensuring that the provided `slice` value
/// is shorter than `Length::MAX`.
pub const fn new(slice: &[u8]) -> Result<&Self> {
match Length::new_usize(slice.len()) {
Ok(_) => Ok(Self::new_unchecked(slice)),
Err(err) => Err(err),
}
}

/// Perform a raw conversion of a byte slice to `Self` without first performing a length check.
const fn new_unchecked(slice: &[u8]) -> &Self {
// SAFETY: `Self` is a `repr(transparent)` newtype for `[u8]`
#[allow(unsafe_code)]
unsafe {
&*(slice as *const [u8] as *const Self)
}
}

/// Borrow the inner byte slice
pub const fn as_slice(&self) -> &[u8] {
&self.0
}

/// Get the [`Length`] of this [`BytesRef`]
pub fn len(&self) -> Length {
// TODO(tarcieri): non-panicking constructor
Length::new_usize(self.0.len()).expect("constructor should check length")
}

/// Is this [`BytesRef`] empty?
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}

/// Get a prefix of a [`crate::bytes_ref::BytesRef`] of the given length.
#[allow(dead_code)]
pub fn prefix(&self, length: Length) -> Result<&Self> {
let inner = self
.as_slice()
.get(..usize::try_from(length)?)
.ok_or_else(|| Error::incomplete(self.len()))?;

Ok(Self::new_unchecked(inner))
}
}

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

impl<'a> DecodeValue<'a> for &'a BytesRef {
type Error = Error;

fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
BytesRef::new(reader.read_slice(header.length)?)
}
}

impl EncodeValue for BytesRef {
fn value_len(&self) -> Result<Length> {
Ok(self.len())
}

fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
writer.write(self.as_ref())
}
}

impl DerOrd for BytesRef {
fn der_cmp(&self, other: &Self) -> Result<Ordering> {
Ok(self.as_slice().cmp(other.as_slice()))
}
}

impl<'a> TryFrom<&'a [u8]> for &'a BytesRef {
type Error = Error;

fn try_from(slice: &'a [u8]) -> Result<Self> {
BytesRef::new(slice)
}
}
3 changes: 2 additions & 1 deletion der/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![forbid(unsafe_code)]
#![deny(unsafe_code)] // only allowed for transmuting newtype references
#![warn(
// TODO: re-enable this lint and fix its warnings
// clippy::arithmetic_side_effects,
Expand Down Expand Up @@ -343,6 +343,7 @@ pub mod referenced;

pub(crate) mod arrayvec;
mod bytes_ref;
mod bytes_ref2;
mod datetime;
mod decode;
mod encode;
Expand Down