mirror of
https://github.com/trussed-dev/flexiber.git
synced 2026-03-11 16:35:17 -07:00
Make things a bit generic over Tag type (to allow SIMPLE-TLV tags)
This commit is contained in:
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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()?
|
||||
}
|
||||
|
||||
@@ -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
72
src/simpletag.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
26
src/tag.rs
26
src/tag.rs
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user