Make things a bit generic over Tag type (to allow SIMPLE-TLV tags)

This commit is contained in:
Nicolas Stalder
2021-02-20 00:57:58 +01:00
parent 19f4cf5d27
commit aea4ee6a4e
7 changed files with 157 additions and 54 deletions

View File

@@ -1,5 +1,5 @@
use core::convert::TryInto;
use crate::{Decodable, ErrorKind, Length, Result};
use crate::{Decodable, ErrorKind, Length, Result, TagLike};
/// SIMPLE-TLV decoder.
#[derive(Debug)]
@@ -36,15 +36,15 @@ impl<'a> Decoder<'a> {
}
/// Decode a TaggedValue with tag checked to be as expected, returning the value
pub fn decode_tagged_value<V: Decodable<'a>>(&mut self, tag: crate::Tag) -> Result<V> {
let tagged: crate::TaggedSlice = self.decode()?;
pub fn decode_tagged_value<T: Decodable<'a> + TagLike, V: Decodable<'a>>(&mut self, tag: T) -> Result<V> {
let tagged: crate::TaggedSlice<T> = self.decode()?;
tagged.tag().assert_eq(tag)?;
Self::new(tagged.as_bytes()).decode()
}
/// Decode a TaggedSlice with tag checked to be as expected, returning the value
pub fn decode_tagged_slice(&mut self, tag: crate::Tag) -> Result<&'a [u8]> {
let tagged: crate::TaggedSlice = self.decode()?;
pub fn decode_tagged_slice<T: Decodable<'a> + TagLike>(&mut self, tag: T) -> Result<&'a [u8]> {
let tagged: crate::TaggedSlice<T> = self.decode()?;
tagged.tag().assert_eq(tag)?;
Ok(tagged.as_bytes())
}

View File

@@ -1,35 +1,38 @@
//! SIMPLE-TLV headers.
use crate::{Decodable, Decoder, Encodable, Encoder, ErrorKind, Length, Result, Tag};
use crate::{Decodable, Decoder, Encodable, Encoder, ErrorKind, Length, Result, TagLike};
use core::convert::TryInto;
/// SIMPLE-TLV headers: tag + length component of TLV-encoded values
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) struct Header {
pub(crate) struct Header<T> {
/// Tag representing the type of the encoded value
pub tag: Tag,
pub tag: T,
/// Length of the encoded value
pub length: Length,
}
impl Header {
impl<T> Header<T> {
/// Create a new [`Header`] from a [`Tag`] and a specified length.
///
/// Returns [`Error`] if the length exceeds the limits of [`Length`]
pub fn new(tag: Tag, length: impl TryInto<Length>) -> Result<Self> {
pub fn new(tag: T, length: impl TryInto<Length>) -> Result<Self> {
let length = length.try_into().map_err(|_| ErrorKind::Overflow)?;
Ok(Self { tag, length })
}
}
impl Decodable<'_> for Header {
fn decode(decoder: &mut Decoder<'_>) -> Result<Header> {
let tag = Tag::decode(decoder)?;
impl<'a, T> Decodable<'a> for Header<T>
where
T: Decodable<'a> + TagLike,
{
fn decode<'b>(decoder: &'b mut Decoder<'a>) -> Result<Header<T>> {
let tag = T::decode(decoder)?;
let length = Length::decode(decoder).map_err(|e| {
if e.kind() == ErrorKind::Overlength {
ErrorKind::Length { tag }.into()
ErrorKind::Length { tag: tag.embedding() }.into()
} else {
e
}
@@ -39,7 +42,10 @@ impl Decodable<'_> for Header {
}
}
impl Encodable for Header {
impl<T> Encodable for Header<T>
where
T: Encodable
{
fn encoded_length(&self) -> Result<Length> {
self.tag.encoded_length()? + self.length.encoded_length()?
}

View File

@@ -37,6 +37,7 @@ mod encoder;
mod error;
mod header;
mod length;
mod simpletag;
mod slice;
mod tag;
mod tagged;
@@ -46,8 +47,9 @@ pub use decoder::Decoder;
pub use encoder::Encoder;
pub use error::{Error, ErrorKind, Result};
pub use length::Length;
pub use simpletag::SimpleTag;
pub use slice::Slice;
pub use tag::{Class, Tag};
pub use tag::{Class, Tag, TagLike};
pub use tagged::{TaggedSlice, TaggedValue};
pub use traits::{Container, Decodable, Encodable, Tagged};
#[cfg(feature = "heapless")]

72
src/simpletag.rs Normal file
View File

@@ -0,0 +1,72 @@
use core::convert::TryFrom;
use crate::{Decodable, Decoder, Encodable, Encoder, Error, ErrorKind, Length, Result, Tag, TagLike};
/// These are tags like in SIMPLE-TLV.
///
/// The tag field consists of a single byte encoding a tag number from 1 to 254. The values '00' and 'FF' are invalid.
///
/// The use case is that PIV (FIPS 201) data objects generally use BER-TLV, but, for historical reasons,
/// label entries with "simple" tags (in particular, tag numbers larger than 30 are still encoded
/// as single bytes.
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct SimpleTag(u8);
impl TryFrom<u8> for SimpleTag {
type Error = Error;
fn try_from(tag_number: u8) -> Result<Self> {
match tag_number {
byte if byte == 0 || byte == 0xFF => Err(ErrorKind::InvalidTag { byte }.into()),
valid_tag_number => Ok(Self(valid_tag_number)),
}
}
}
impl TagLike for SimpleTag {
fn embedding(self) -> Tag {
use crate::Class::*;
Tag { class: Universal, constructed: false, number: self.0 as u16 }
}
}
impl Decodable<'_> for SimpleTag {
fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
decoder.byte().and_then(Self::try_from)
}
}
impl Encodable for SimpleTag {
fn encoded_length(&self) -> Result<Length> {
Ok(1u8.into())
}
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.byte(self.0)
}
}
#[cfg(test)]
mod tests {
use core::convert::TryFrom;
use crate::{Encodable, SimpleTag, TaggedSlice};
#[test]
fn simple_tag() {
let mut buf = [0u8; 384];
let tag = SimpleTag::try_from(37).unwrap();
let slice = &[1u8,2,3];
let short = TaggedSlice::from(tag, slice).unwrap();
assert_eq!(
short.encode_to_slice(&mut buf).unwrap(),
&[37, 0x3, 1, 2, 3]
);
let slice = &[43u8; 256];
let long = TaggedSlice::from(tag, slice).unwrap();
let encoded = long.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..4], &[37, 0x82, 0x01, 0x00]);
assert_eq!(&encoded[4..], slice);
}
}

View File

@@ -83,28 +83,38 @@ impl TryFrom<u8> for Tag {
}
}
impl Tag {
/// This is the common trait that types to be used as tags
/// are supposed to implement.
pub trait TagLike: Copy + PartialEq + Sized {
/// To stick with one Error type, make sure the tag type can somehow
/// or other be coerced into a BerTag.
fn embedding(self) -> Tag;
/// Assert that this [`Tag`] matches the provided expected tag.
///
/// On mismatch, returns an [`Error`] with [`ErrorKind::UnexpectedTag`].
pub fn assert_eq(self, expected: Tag) -> Result<Tag> {
fn assert_eq(self, expected: Self) -> Result<Self> {
if self == expected {
Ok(self)
} else {
Err(ErrorKind::UnexpectedTag {
expected: Some(expected),
actual: self,
expected: Some(expected.embedding()),
actual: self.embedding(),
}
.into())
}
}
pub fn with_value<V>(self, value: V) -> TaggedValue<V> {
/// Ergonomic way to get a TaggedValue for a given tag and value
fn with_value<V>(self, value: V) -> TaggedValue<V, Self> {
TaggedValue::new(self, value)
}
// fn tagged(&self, tag: Tag) -> TaggedValue<&Self> {
// TaggedValue::new(tag, self)
// }
}
impl TagLike for Tag {
fn embedding(self) -> Tag {
self
}
}
impl fmt::Display for Tag {

View File

@@ -1,34 +1,37 @@
// //! Common handling for types backed by byte slices with enforcement of the
// //! format-level length limitation of 65,535 bytes.
use crate::{Decodable, Decoder, Encodable, Encoder, ErrorKind, header::Header, Length, Result, Slice, Tag};
use crate::{Decodable, Decoder, Encodable, Encoder, ErrorKind, header::Header, Length, Result, Slice, Tag, TagLike};
/// SIMPLE-TLV data object.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TaggedValue<V> {
tag: Tag,
pub struct TaggedValue<V, T=Tag> {
tag: T,
value: V,
}
/// Raw SIMPLE-TLV data object `TaggedValue<Slice<'_>>`.
pub type TaggedSlice<'a> = TaggedValue<Slice<'a>>;
pub type TaggedSlice<'a, T=Tag> = TaggedValue<Slice<'a>, T>;
impl<V> TaggedValue<V>
impl<V, T> TaggedValue<V, T>
where
T: Copy,
{
pub fn new(tag: Tag, value: V) -> Self {
pub fn new(tag: T, value: V) -> Self {
Self { tag, value }
}
pub fn tag(&self) -> Tag {
pub fn tag(&self) -> T {
self.tag
}
}
impl<'a, E> TaggedValue<&'a E>
impl<'a, E, T> TaggedValue<&'a E, T>
where
E: Encodable
E: Encodable,
T: Copy + Encodable,
{
fn header(&self) -> Result<Header> {
fn header(&self) -> Result<Header<T>> {
Ok(Header {
tag: self.tag(),
length: self.value.encoded_length()?,
@@ -36,9 +39,10 @@ where
}
}
impl<'a, E> Encodable for TaggedValue<&'a E>
impl<'a, E, T> Encodable for TaggedValue<&'a E, T>
where
E: Encodable
E: Encodable,
T: Copy + Encodable,
{
fn encoded_length(&self) -> Result<Length> {
self.header()?.encoded_length()? + self.value.encoded_length()?
@@ -49,10 +53,13 @@ where
}
}
impl<'a> TaggedSlice<'a> {
impl<'a, T> TaggedSlice<'a, T>
where
T: Copy
{
/// Create a new tagged slice, checking lengths.
pub fn from(tag: Tag, slice: &'a [u8]) -> Result<Self> {
pub fn from(tag: T, slice: &'a [u8]) -> Result<Self> {
Slice::new(slice)
.map(|slice| Self { tag, value: slice })
.map_err(|_| (ErrorKind::InvalidLength).into())
@@ -75,7 +82,7 @@ impl<'a> TaggedSlice<'a> {
/// Get the SIMPLE-TLV [`Header`] for this [`TaggedSlice`] value
#[allow(clippy::unnecessary_wraps)]
fn header(&self) -> Result<Header> {
fn header(&self) -> Result<Header<T>> {
Ok(Header {
tag: self.tag(),
length: self.length(),
@@ -85,9 +92,9 @@ impl<'a> TaggedSlice<'a> {
/// Decode nested values, creating a new [`Decoder`] for
/// the data contained in the sequence's body and passing it to the provided
/// [`FnOnce`].
pub fn decode_nested<F, T>(&self, f: F) -> Result<T>
pub fn decode_nested<F, R>(&self, f: F) -> Result<R>
where
F: FnOnce(&mut Decoder<'a>) -> Result<T>,
F: FnOnce(&mut Decoder<'a>) -> Result<R>,
{
let mut nested_decoder = Decoder::new(self.as_bytes());
let result = f(&mut nested_decoder)?;
@@ -95,17 +102,23 @@ impl<'a> TaggedSlice<'a> {
}
}
impl<'a> Decodable<'a> for TaggedSlice<'a> {
fn decode(decoder: &mut Decoder<'a>) -> Result<TaggedSlice<'a>> {
let header = Header::decode(decoder)?;
impl<'a, T> Decodable<'a> for TaggedSlice<'a, T>
where
T: Decodable<'a> + TagLike,
{
fn decode(decoder: &mut Decoder<'a>) -> Result<Self> {
let header = Header::<T>::decode(decoder)?;
let tag = header.tag;
let len = header.length.to_usize();
let value = decoder.bytes(len).map_err(|_| ErrorKind::Length { tag })?;
let value = decoder.bytes(len).map_err(|_| ErrorKind::Length { tag: tag.embedding() })?;
Self::from(tag, value)
}
}
impl<'a> Encodable for TaggedSlice<'a> {
impl<'a, T> Encodable for TaggedSlice<'a, T>
where
T: Copy + Encodable
{
fn encoded_length(&self) -> Result<Length> {
self.header()?.encoded_length()? + self.length()
}

View File

@@ -2,7 +2,7 @@
//! Trait definitions
use core::convert::{TryFrom, TryInto};
use crate::{Decoder, Encoder, Error, header::Header, Length, Result, Tag, TaggedSlice, TaggedValue};
use crate::{Decoder, Encoder, Error, header::Header, Length, Result, Tag, TaggedSlice, TaggedValue, TagLike};
#[cfg(feature = "alloc")]
use {
@@ -31,11 +31,11 @@ pub trait Decodable<'a>: Sized {
}
}
impl<'a, T> Decodable<'a> for T
impl<'a, X> Decodable<'a> for X
where
T: TryFrom<TaggedSlice<'a>, Error = Error>,
X: TryFrom<TaggedSlice<'a>, Error = Error>,
{
fn decode(decoder: &mut Decoder<'a>) -> Result<T> {
fn decode(decoder: &mut Decoder<'a>) -> Result<Self> {
TaggedSlice::decode(decoder)
.and_then(Self::try_from)
.or_else(|e| decoder.error(e.kind()))
@@ -138,13 +138,13 @@ pub trait EncodableHeapless: Encodable {
}
/// Types that can be tagged.
pub(crate) trait Taggable: Sized {
fn tagged(&self, tag: Tag) -> TaggedValue<&Self> {
pub(crate) trait Taggable<T: TagLike>: Sized {
fn tagged(&self, tag: T) -> TaggedValue<&Self, T> {
TaggedValue::new(tag, self)
}
}
impl<X> Taggable for X where X: Sized {}
impl<T, X> Taggable<T> for X where X: Sized, T: TagLike {}
// /// Types with an associated SIMPLE-TLV [`Tag`].
// pub trait Tagged {
@@ -288,7 +288,7 @@ impl_array!(
mod tests {
use core::convert::TryFrom;
use crate::{Decodable, Encodable, Error, Result, Tag, TaggedSlice};
use crate::{Decodable, Encodable, Error, Result, Tag, TaggedSlice, TagLike};
use super::{Taggable, Tagged, Container};
// The types [u8; 2], [u8; 3], [u8; 4] stand in here for any types for the fields