Initial commit, adjusted from simple-tlv

This commit is contained in:
Nicolas Stalder
2021-02-19 23:15:05 +01:00
parent 1cc9e2bcde
commit 19f4cf5d27
16 changed files with 554 additions and 240 deletions
+5 -5
View File
@@ -1,17 +1,17 @@
[package]
name = "simple-tlv"
name = "flexiber"
version = "0.1.0"
authors = ["Nicolas Stalder <n@stalder.io>", "RustCrypto Developers"]
license = "Apache-2.0 OR MIT"
edition = "2018"
description = "Encoding and decoding of SIMPLE-TLV as described in ISO 7816-4, without allocations."
repository = "https://github.com/nickray/simple-tlv"
description = "Encoding and decoding of BER-TLV as described in ISO 7816-4, without allocations."
repository = "https://github.com/nickray/flexiber"
categories = ["cryptography", "data-structures", "encoding", "no-std"]
keywords = ["crypto", "no_std", "serialization"]
readme = "README.md"
[dependencies]
simple-tlv_derive = { version = "0.1", optional = true, path = "derive" }
flexiber_derive = { version = "0.1", optional = true, path = "derive" }
[dependencies.heapless]
version = "0.6.0"
@@ -19,5 +19,5 @@ optional = true
[features]
alloc = []
derive = ["simple-tlv_derive"]
derive = ["flexiber_derive"]
std = ["alloc"]
+5 -4
View File
@@ -1,13 +1,14 @@
# simple-tlv
# flexiber
Encoding and decoding of SIMPLE-TLV as described in ISO 7816-4, without allocations.
Encoding and decoding of BER-TLV as described in ISO 7816-4, without allocations.
Follows the approach taken in [`der`][der].
Follows the approach taken in [`der`][der], and then in [`simple-tlv`][simple-tlv].
[der]: https://docs.rs/der
[simple-tlv]: https://docs.rs/simple-tlv
#### License
<sup>`simple-tlv` is licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT License](LICENSE-MIT) at your option.</sup>
<sup>`flexiber` is licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT License](LICENSE-MIT) at your option.</sup>
<br>
<sub>Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.</sub>
+4
View File
@@ -27,6 +27,10 @@ need_stdout = false
command = ["cargo", "test", "--color", "always"]
need_stdout = true
[jobs.test-derive]
command = ["cargo", "test", "--features", "derive", "--color", "always"]
need_stdout = true
[jobs.doc]
command = ["cargo", "doc", "--color", "always"]
need_stdout = true
+3 -3
View File
@@ -1,11 +1,11 @@
[package]
name = "simple-tlv_derive"
name = "flexiber_derive"
version = "0.1.0"
authors = ["Nicolas Stalder <n@stalder.io>", "RustCrypto Developers"]
license = "Apache-2.0 OR MIT"
edition = "2018"
description = "Procedural macros to derive `Decodable` and `Encodable` from `simple-tlv`."
repository = "https://github.com/nickray/simple-tlv/tree/main/derive"
description = "Procedural macros to derive `Decodable` and `Encodable` from `flexiber`."
repository = "https://github.com/nickray/flexiber/tree/main/derive"
categories = ["cryptography", "data-structures", "encoding", "no-std"]
keywords = ["crypto"]
+44 -15
View File
@@ -3,7 +3,7 @@ use quote::{quote, ToTokens};
use syn::{Attribute, DataStruct, Field, Ident};
use synstructure::Structure;
use crate::{extract_attrs_optional_tag, FieldAttrs};
use crate::{extract_attrs_optional_tag, FieldAttrs, Tag};
/// Derive Decodable on a struct
pub(crate) struct DeriveDecodableStruct {
@@ -17,7 +17,7 @@ pub(crate) struct DeriveDecodableStruct {
impl DeriveDecodableStruct {
pub fn derive(s: Structure<'_>, data: &DataStruct, name: &Ident, attrs: &[Attribute]) -> TokenStream {
let (tag, _) = extract_attrs_optional_tag(name, attrs);
let (tag, _slice) = extract_attrs_optional_tag(name, attrs);
let mut state = Self {
decode_fields: TokenStream::new(),
@@ -40,14 +40,33 @@ impl DeriveDecodableStruct {
/// Derive code for decoding a field of a message
fn derive_field_decoder(&mut self, field: &FieldAttrs) {
let field_name = &field.name;
let field_tag = field.tag;
let tag = field.tag;
let class = tag.class as u8;
let constructed = tag.constructed;
let tag_number = tag.number;
let field_decoder = if field.slice {
quote! { let #field_name =
decoder.decode_tagged_slice(::simple_tlv::Tag::try_from(#field_tag).unwrap())?.try_into()
.map_err(|_| simple_tlv::ErrorKind::Length { tag: simple_tlv::Tag::try_from(#field_tag).unwrap() })?;
quote! {
let tag = ::flexiber::Tag::from(
flexiber::Class::try_from(#class).unwrap(),
#constructed,
#tag_number
);
let #field_name =
decoder.decode_tagged_slice(tag)?.try_into().unwrap();
// .map_err(|_| flexiber::ErrorKind::Length { tag })?;
}
} else {
quote! { let #field_name = decoder.decode_tagged_value(::simple_tlv::Tag::try_from(#field_tag).unwrap())?; }
quote! {
let tag = ::flexiber::Tag::from(
flexiber::Class::try_from(#class).unwrap(),
#constructed,
#tag_number
);
let #field_name = decoder.decode_tagged_value(tag)?;
}
};
field_decoder.to_tokens(&mut self.decode_fields);
@@ -56,19 +75,29 @@ impl DeriveDecodableStruct {
}
/// Finish deriving a struct
fn finish(self, s: &Structure<'_>, tag: Option<u8>) -> TokenStream {
fn finish(self, s: &Structure<'_>, tag: Option<Tag>) -> TokenStream {
let decode_fields = self.decode_fields;
let decode_result = self.decode_result;
if let Some(tag) = tag {
s.gen_impl(quote! {
gen impl<'a> core::convert::TryFrom<simple_tlv::TaggedSlice<'a>> for @Self {
type Error = simple_tlv::Error;
fn try_from(tagged_slice: simple_tlv::TaggedSlice<'a>) -> simple_tlv::Result<Self> {
let class = tag.class as u8;
let constructed = tag.constructed;
let tag_number = tag.number;
s.gen_impl(quote! {
gen impl<'a> core::convert::TryFrom<flexiber::TaggedSlice<'a>> for @Self {
type Error = flexiber::Error;
fn try_from(tagged_slice: flexiber::TaggedSlice<'a>) -> flexiber::Result<Self> {
use core::convert::TryInto;
tagged_slice.tag().assert_eq(simple_tlv::Tag::try_from(#tag).unwrap())?;
let tag = ::flexiber::Tag::from(
flexiber::Class::try_from(#class).unwrap(),
#constructed,
#tag_number
);
tagged_slice.tag().assert_eq(tag)?;
tagged_slice.decode_nested(|decoder| {
#decode_fields
@@ -79,8 +108,8 @@ impl DeriveDecodableStruct {
})
} else {
s.gen_impl(quote! {
gen impl<'a> simple_tlv::Decodable<'a> for @Self {
fn decode(decoder: &mut simple_tlv::Decoder<'a>) -> simple_tlv::Result<Self> {
gen impl<'a> flexiber::Decodable<'a> for @Self {
fn decode(decoder: &mut flexiber::Decoder<'a>) -> flexiber::Result<Self> {
use core::convert::{TryFrom, TryInto};
#decode_fields
Ok(Self { #decode_result })
+29 -21
View File
@@ -3,7 +3,7 @@ use quote::{quote, ToTokens};
use syn::{Attribute, DataStruct, Field, Ident};
use synstructure::Structure;
use crate::{extract_attrs_optional_tag, FieldAttrs};
use crate::{extract_attrs_optional_tag, FieldAttrs, Tag};
/// Derive Encodable on a struct
pub(crate) struct DeriveEncodableStruct {
@@ -14,7 +14,7 @@ pub(crate) struct DeriveEncodableStruct {
impl DeriveEncodableStruct {
pub fn derive(s: Structure<'_>, data: &DataStruct, name: &Ident, attrs: &[Attribute]) -> TokenStream {
let (tag, _) = extract_attrs_optional_tag(name, attrs);
let (tag, _slice) = extract_attrs_optional_tag(name, attrs);
let mut state = Self {
encode_fields: TokenStream::new(),
@@ -36,35 +36,43 @@ impl DeriveEncodableStruct {
/// Derive code for encoding a field of a message
fn derive_field_encoder(&mut self, field: &FieldAttrs) {
let field_name = &field.name;
let field_tag = field.tag;
let tag = field.tag;
let class = tag.class as u8;
let constructed = tag.constructed;
let tag_number = tag.number;
let field_encoder = if field.slice {
quote! { &(::simple_tlv::TaggedSlice::from(simple_tlv::Tag::try_from(#field_tag).unwrap(), &self.#field_name)?), }
quote! { &(::flexiber::TaggedSlice::from(flexiber::Tag::from(flexiber::Class::try_from(#class).unwrap(), #constructed, #tag_number), &self.#field_name)?), }
} else {
quote! { &(::simple_tlv::Tag::try_from(#field_tag).unwrap().with_value(&self.#field_name)), }
quote! { &(::flexiber::Tag::from(flexiber::Class::try_from(#class).unwrap(), #constructed, #tag_number).with_value(&self.#field_name)), }
};
field_encoder.to_tokens(&mut self.encode_fields);
}
/// Finish deriving a struct
fn finish(self, s: &Structure<'_>, tag: Option<u8>) -> TokenStream {
fn finish(self, s: &Structure<'_>, tag: Option<Tag>) -> TokenStream {
let encode_fields = self.encode_fields;
if let Some(tag) = tag {
let class = tag.class as u8;
let constructed = tag.constructed;
let tag_number = tag.number;
s.gen_impl(quote! {
gen impl simple_tlv::Tagged for @Self {
fn tag() -> simple_tlv::Tag {
gen impl flexiber::Tagged for @Self {
fn tag() -> flexiber::Tag {
// TODO(nickray): FIXME FIXME
use core::convert::TryFrom;
simple_tlv::Tag::try_from(#tag).unwrap()
flexiber::Tag::from(flexiber::Class::try_from(#class).unwrap(), #constructed, #tag_number)
}
}
gen impl simple_tlv::Container for @Self {
fn fields<F, T>(&self, field_encoder: F) -> simple_tlv::Result<T>
gen impl flexiber::Container for @Self {
fn fields<F, T>(&self, field_encoder: F) -> flexiber::Result<T>
where
F: FnOnce(&[&dyn simple_tlv::Encodable]) -> simple_tlv::Result<T>,
F: FnOnce(&[&dyn flexiber::Encodable]) -> flexiber::Result<T>,
{
use core::convert::TryFrom;
field_encoder(&[#encode_fields])
@@ -73,25 +81,25 @@ impl DeriveEncodableStruct {
})
} else {
s.gen_impl(quote! {
gen impl simple_tlv::Container for @Self {
fn fields<F, T>(&self, field_encoder: F) -> simple_tlv::Result<T>
gen impl flexiber::Container for @Self {
fn fields<F, T>(&self, field_encoder: F) -> flexiber::Result<T>
where
F: FnOnce(&[&dyn simple_tlv::Encodable]) -> simple_tlv::Result<T>,
F: FnOnce(&[&dyn flexiber::Encodable]) -> flexiber::Result<T>,
{
use core::convert::TryFrom;
field_encoder(&[#encode_fields])
}
}
gen impl simple_tlv::Encodable for @Self {
fn encoded_length(&self) -> simple_tlv::Result<simple_tlv::Length> {
gen impl flexiber::Encodable for @Self {
fn encoded_length(&self) -> flexiber::Result<flexiber::Length> {
use core::convert::TryFrom;
use simple_tlv::Container;
self.fields(|encodables| simple_tlv::Length::try_from(encodables))
use flexiber::Container;
self.fields(|encodables| flexiber::Length::try_from(encodables))
}
fn encode(&self, encoder: &mut simple_tlv::Encoder<'_>) -> simple_tlv::Result<()> {
use simple_tlv::Container;
fn encode(&self, encoder: &mut flexiber::Encoder<'_>) -> flexiber::Result<()> {
use flexiber::Container;
self.fields(|fields| encoder.encode_untagged_collection(fields))
}
}
+61 -27
View File
@@ -1,4 +1,4 @@
//! Custom derive support for the `simple-tlv` crate
//! Custom derive support for the `flexiber` crate
//!
//! With `#[tlv(slice)]` set, `Encodable` should work for fields implementing `AsRef<[u8]>`,
//! and `Decodable` should work for fields implementing `TryFrom<[u8]>`, even if the field
@@ -24,11 +24,11 @@ decl_derive!(
/// Derive the [`Decodable`][1] trait on a struct.
///
/// See [toplevel documentation for the `simple-tlv_derive` crate][2] for more
/// See [toplevel documentation for the `flexiber_derive` crate][2] for more
/// information about how to use this macro.
///
/// [1]: https://docs.rs/simple-tlv/latest/simple_tlv/trait.Decodable.html
/// [2]: https://docs.rs/simple-tlv_derive/
/// [1]: https://docs.rs/flexiber/latest/flexiber/trait.Decodable.html
/// [2]: https://docs.rs/flexiber_derive/
derive_decodable
);
@@ -37,15 +37,15 @@ decl_derive!(
/// Derive the [`Encodable`][1] trait on a struct.
///
/// See [toplevel documentation for the `simple-tlv_derive` crate][2] for more
/// See [toplevel documentation for the `flexiber_derive` crate][2] for more
/// information about how to use this macro.
///
/// [1]: https://docs.rs/simple-tlv/latest/simple_tlv/trait.Decodable.html
/// [2]: https://docs.rs/simple-tlv_derive/
/// [1]: https://docs.rs/flexiber/latest/flexiber/trait.Decodable.html
/// [2]: https://docs.rs/flexiber_derive/
derive_encodable
);
/// Custom derive for `simple_tlv::Decodable`
/// Custom derive for `flexiber::Decodable`
fn derive_decodable(s: Structure<'_>) -> TokenStream {
let ast = s.ast();
@@ -56,7 +56,7 @@ fn derive_decodable(s: Structure<'_>) -> TokenStream {
}
}
/// Custom derive for `simple_tlv::Encodable`
/// Custom derive for `flexiber::Encodable`
fn derive_encodable(s: Structure<'_>) -> TokenStream {
let ast = s.ast();
@@ -67,14 +67,36 @@ fn derive_encodable(s: Structure<'_>) -> TokenStream {
}
}
#[derive(Clone, Copy, Debug, Default)]
struct Tag {
class: Class,
constructed: bool,
number: u16,
}
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
enum Class {
Universal = 0b00,
Application = 0b01,
Context = 0b10,
Private = 0b11,
}
impl Default for Class {
fn default() -> Self {
Class::Universal
}
}
/// Attributes of a field
#[derive(Debug)]
struct FieldAttrs {
/// Name of the field
pub name: Ident,
/// Value of the `#[tlv(tag = "...")]` attribute if provided
pub tag: u8,
/// Value of tag to use
pub tag: Tag,
/// Whether the `#[tlv(slice)]` attribute was set
pub slice: bool
@@ -95,8 +117,9 @@ impl FieldAttrs {
}
}
fn extract_attrs_optional_tag(name: &Ident, attrs: &[Attribute]) -> (Option<u8>, bool) {
let mut tag = None;
fn extract_attrs_optional_tag(name: &Ident, attrs: &[Attribute]) -> (Option<Tag>, bool) {
let mut tag = Tag::default();
let mut tag_number_is_set = false;
let mut slice = false;
for attr in attrs {
@@ -109,10 +132,23 @@ fn extract_attrs_optional_tag(name: &Ident, attrs: &[Attribute]) -> (Option<u8>,
for entry in nested {
match entry {
NestedMeta::Meta(Meta::Path(path)) => {
if !path.is_ident("slice") {
if path.is_ident("slice") {
slice = true;
} else if path.is_ident("universal") {
tag.class = Class::Universal;
} else if path.is_ident("application") {
tag.class = Class::Application;
} else if path.is_ident("context") {
tag.class = Class::Context;
} else if path.is_ident("private") {
tag.class = Class::Private;
} else if path.is_ident("constructed") {
tag.constructed = true;
} else if path.is_ident("primitive") {
tag.constructed = false;
} else {
panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
}
slice = true;
}
NestedMeta::Meta(Meta::NameValue(MetaNameValue {
path,
@@ -120,21 +156,15 @@ fn extract_attrs_optional_tag(name: &Ident, attrs: &[Attribute]) -> (Option<u8>,
..
})) => {
// Parse the `type = "..."` attribute
if !path.is_ident("tag") {
if !path.is_ident("number") {
panic!("unknown `tlv` attribute for field `{}`: {:?}", name, path);
}
if tag.is_some() {
panic!("duplicate SIMPLE-TLV `tag` attribute for field: {}", name);
}
let possibly_with_prefix = lit_str.value();
let without_prefix = possibly_with_prefix.trim_start_matches("0x");
let tag_value = u8::from_str_radix(without_prefix, 16).expect("tag values must be between one and 254");
if tag_value == 0 || tag_value == 255 {
panic!("SIMPLE-TLV tags must not be zero or 255");
}
tag = Some(tag_value);
let tag_number = u16::from_str_radix(without_prefix, 16).expect("tag values must be between one and 254");
tag.number = tag_number;
tag_number_is_set = true;
}
other => panic!(
"a malformed `tlv` attribute for field `{}`: {:?}",
@@ -150,10 +180,14 @@ fn extract_attrs_optional_tag(name: &Ident, attrs: &[Attribute]) -> (Option<u8>,
}
}
(tag, slice)
if tag_number_is_set {
(Some(tag), slice)
} else {
(None, slice)
}
}
fn extract_attrs(name: &Ident, attrs: &[Attribute]) -> (u8, bool) {
fn extract_attrs(name: &Ident, attrs: &[Attribute]) -> (Tag, bool) {
let (tag, slice) = extract_attrs_optional_tag(name, attrs);
if let Some(tag) = tag {
+2 -3
View File
@@ -135,14 +135,13 @@ impl<'a> From<&'a [u8]> for Decoder<'a> {
#[cfg(test)]
mod tests {
use core::convert::TryFrom;
use crate::{Decodable, Tag, TaggedSlice};
#[test]
fn zero_length() {
let buf: &[u8] = &[0x2A, 0x00];
let buf: &[u8] = &[0x05, 0x00];
let ts = TaggedSlice::from_bytes(buf).unwrap();
assert_eq!(ts, TaggedSlice::from(Tag::try_from(42).unwrap(), &[]).unwrap());
assert_eq!(ts, TaggedSlice::from(Tag::universal(0x5), &[]).unwrap());
}
}
// #[cfg(test)]
+18 -16
View File
@@ -153,26 +153,28 @@ impl<'a> Encoder<'a> {
#[cfg(test)]
mod tests {
use core::convert::TryFrom;
use crate::{Encodable, Tag, TaggedSlice};
#[test]
fn zero_length() {
let tv = TaggedSlice::from(Tag::try_from(42).unwrap(), &[]).unwrap();
let tv = TaggedSlice::from(Tag::universal(5), &[]).unwrap();
let mut buf = [0u8; 4];
assert_eq!(tv.encode_to_slice(&mut buf).unwrap(), &[0x2A, 0x00]);
assert_eq!(tv.encode_to_slice(&mut buf).unwrap(), &[0x5, 0x00]);
let tv = TaggedSlice::from(Tag::application(5).constructed(), &[]).unwrap();
let mut buf = [0u8; 4];
assert_eq!(tv.encode_to_slice(&mut buf).unwrap(), &[(0b01 << 6) | (1 << 5) | 5, 0x00]);
}
// use super::Encoder;
// use crate::{ErrorKind, Length};
// #[test]
// fn overlength_message() {
// let mut buffer = [];
// let mut encoder = Encoder::new(&mut buffer);
// let err = false.encode(&mut encoder).err().unwrap();
// assert_eq!(err.kind(), ErrorKind::Overlength);
// assert_eq!(err.position(), Some(Length::zero()));
// }
}
// use super::Encoder;
// use crate::{Encodable, ErrorKind, Length};
// #[test]
// fn overlength_message() {
// let mut buffer = [];
// let mut encoder = Encoder::new(&mut buffer);
// let err = false.encode(&mut encoder).err().unwrap();
// assert_eq!(err.kind(), ErrorKind::Overlength);
// assert_eq!(err.position(), Some(Length::zero()));
// }
// }
+12 -3
View File
@@ -103,6 +103,9 @@ pub enum ErrorKind {
/// Operation failed due to previous error
Failed,
/// Class has more than 2 bytes
InvalidClass { value: u8 },
/// Invalid tag
InvalidTag {
/// Raw byte value of the tag
@@ -178,6 +181,10 @@ pub enum ErrorKind {
// /// Tag of the unexpected value
// tag: Tag,
// },
/// Tag does not fit in 3 bytes
UnsupportedTagSize,
}
impl ErrorKind {
@@ -192,7 +199,11 @@ impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorKind::Failed => write!(f, "operation failed"),
ErrorKind::InvalidClass { value } => write!(f, "invalid class {}", value),
ErrorKind::InvalidLength => write!(f, "length greater than protocol maximum"),
ErrorKind::InvalidTag { byte } => {
write!(f, "invalid SIMPLE-TLV tag: 0x{:02x}", byte)
}
ErrorKind::Length { tag } => write!(f, "incorrect length for {}", tag),
// ErrorKind::Noncanonical => write!(f, "DER is not canonically encoded"),
// ErrorKind::Oid => write!(f, "malformed OID"),
@@ -220,14 +231,12 @@ impl fmt::Display for ErrorKind {
write!(f, "got {}", actual)
}
ErrorKind::InvalidTag { byte } => {
write!(f, "invalid SIMPLE-TLV tag: 0x{:02x}", byte)
}
// ErrorKind::UnknownTag { byte } => {
// write!(f, "unknown/unsupported ASN.1 DER tag: 0x{:02x}", byte)
// }
// ErrorKind::Utf8(e) => write!(f, "{}", e),
// ErrorKind::Value { tag } => write!(f, "malformed ASN.1 DER value for {}", tag),
ErrorKind::UnsupportedTagSize => write!(f, "tags occupying more than 3 octets not supported"),
}
}
}
+48 -37
View File
@@ -1,7 +1,7 @@
//! Length calculations for encoded ASN.1 DER values
use crate::{Decodable, Decoder, Encodable, Encoder, Error, ErrorKind, Result};
use core::{convert::{TryFrom, TryInto}, fmt, ops::Add};
use core::{convert::TryFrom, fmt, ops::Add};
/// SIMPLE-TLV-encoded length.
///
@@ -13,7 +13,7 @@ use core::{convert::{TryFrom, TryInto}, fmt, ops::Add};
/// - If the first byte is `0xFF`, then the length field consists of the subsequent two bytes interpreted as
/// big-endian integer, with any value from zero to 65,535.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
pub struct Length(u16);
pub struct Length(pub(crate) u16);
impl Length {
/// Return a length of `0`.
@@ -124,11 +124,25 @@ impl TryFrom<usize> for Length {
impl Decodable<'_> for Length {
fn decode(decoder: &mut Decoder<'_>) -> Result<Length> {
match decoder.byte()? {
0xFF => {
let be_len = decoder.bytes(2u8)?;
Ok(Length::from(u16::from_be_bytes(be_len.try_into().unwrap())))
len if len < 0x80 => Ok(len.into()),
// we do not support indefinite lengths
0x80 => Err(ErrorKind::InvalidLength.into()),
// one byte to follow
0x81 => {
let len = decoder.byte()?;
// allow non-minimum encodings
Ok(len.into())
}
0x82 => {
let len_hi = decoder.byte()? as u16;
let len = (len_hi << 8) | (decoder.byte()? as u16);
// allow non-minimum encodings
Ok(len.into())
}
_ => {
// We specialize to a maximum 3-byte length encoding of length
Err(ErrorKind::Overlength.into())
}
len => Ok(len.into()),
}
}
}
@@ -136,22 +150,29 @@ impl Decodable<'_> for Length {
impl Encodable for Length {
fn encoded_length(&self) -> Result<Length> {
match self.0 {
0..=0xFE => Ok(Length(1)),
_ => Ok(Length(3)),
0..=0x7F => Ok(Length(1)),
0x80..=0xFF => Ok(Length(2)),
0x100..=0xFFFF => Ok(Length(3)),
}
}
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
match self.0 {
0..=0xFE => encoder.byte(self.0 as u8),
_ => {
encoder.byte(0xFF)?;
encoder.bytes(&self.0.to_be_bytes())
0..=0x7F => encoder.byte(self.0 as u8),
0x80..=0xFF => {
encoder.byte(0x81)?;
encoder.byte(self.0 as u8)
}
0x100..=0xFFFF => {
encoder.byte(0x82)?;
encoder.byte((self.0 >> 8) as u8)?;
encoder.byte((self.0 & 0xFF) as u8)
}
}
}
}
impl fmt::Display for Length {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
@@ -161,37 +182,27 @@ impl fmt::Display for Length {
#[cfg(test)]
mod tests {
use super::Length;
use crate::{Decodable, Encodable, Error, ErrorKind};
use crate::{Decodable, Encodable};
#[test]
fn decode() {
assert_eq!(Length::zero(), Length::from_bytes(&[0x00]).unwrap());
assert_eq!(Length::from(0x7Fu8), Length::from_bytes(&[0x7F]).unwrap());
assert_eq!(Length::from(0x7Fu8), Length::from_bytes(&[0xFF, 0x00, 0x7F]).unwrap());
assert_eq!(Length::from(0xFEu8), Length::from_bytes(&[0xFE]).unwrap());
assert_eq!(Length::from(0xFEu8), Length::from_bytes(&[0xFF, 0x00, 0xFE]).unwrap());
// these are the current errors, do we want them?
assert_eq!(Length::from_bytes(&[0xFF]).unwrap_err(), Error::from(ErrorKind::Truncated));
assert_eq!(Length::from_bytes(&[0xFF, 0x12]).unwrap_err(), Error::from(ErrorKind::Truncated));
// this is a bit clumsy to express
assert!(Length::from_bytes(&[0xFF, 0x12, 0x34, 0x56]).is_err());
assert_eq!(
Length::from(0x80u8),
Length::from_bytes(&[0x81, 0x80]).unwrap()
);
assert_eq!(
Length::from(0xFFu8),
Length::from_bytes(&[0xFF, 0x00, 0xFF]).unwrap()
Length::from_bytes(&[0x81, 0xFF]).unwrap()
);
assert_eq!(
Length::from(0x100u16),
Length::from_bytes(&[0xFF, 0x01, 0x00]).unwrap()
);
assert_eq!(
Length::from(0xFFFFu16),
Length::from_bytes(&[0xFF, 0xFF, 0xFF]).unwrap()
Length::from_bytes(&[0x82, 0x01, 0x00]).unwrap()
);
}
@@ -210,23 +221,23 @@ mod tests {
);
assert_eq!(
&[0xFE],
Length::from(0xFEu8).encode_to_slice(&mut buffer).unwrap()
&[0x81, 0x80],
Length::from(0x80u8).encode_to_slice(&mut buffer).unwrap()
);
assert_eq!(
&[0xFF, 0x00, 0xFF],
&[0x81, 0xFF],
Length::from(0xFFu8).encode_to_slice(&mut buffer).unwrap()
);
assert_eq!(
&[0xFF, 0x01, 0x00],
&[0x82, 0x01, 0x00],
Length::from(0x100u16).encode_to_slice(&mut buffer).unwrap()
);
}
assert_eq!(
&[0xFF, 0xFF, 0xFF],
Length::from(0xFFFFu16).encode_to_slice(&mut buffer).unwrap()
);
#[test]
fn reject_indefinite_lengths() {
assert!(Length::from_bytes(&[0x80]).is_err());
}
}
+10 -16
View File
@@ -1,19 +1,11 @@
//! # simple-tlv
//! # flexiber
//!
//! Implementation of the SIMPLE-TLV serialization format from ISO 7816-4:2005.
//! Implementation of the BER-TLV serialization format from ISO 7816-4:2005.
//!
//! ### 5.2.1 SIMPLE-TLV data objects
//! Each SIMPLE-TLV data object shall consist of two or three consecutive fields: a mandatory tag field, a
//! mandatory length field and a conditional value field. A record (see 7.3.1) may be a SIMPLE-TLV data object.
//! - The tag field consists of a single byte encoding a tag number from 1 to 254. The values '00' and 'FF' are
//! invalid for tag fields. If a record is a SIMPLE-TLV data object, then the tag may be used as record identifier.
//! - The length field consists of one or three consecutive bytes.
//! - If the first byte is not set to 'FF', then the length field consists of a single byte encoding a number from
//! zero to 254 and denoted N.
//! - If the first byte is set to 'FF', then the length field continues on the subsequent two bytes with any
//! value encoding a number from zero to 65,535 and denoted N.
//! - If N is zero, there is no value field, i.e., the data object is empty. Otherwise (N > 0), the value field
//! consists of N consecutive bytes.
//! ITU-T X.690 (08/2015) defines the BER, CER and DER encoding rules for ASN.1
//!
//! The exact same document is [ISO/IET 8825-1][iso8825], which is freely available,
//! inconveniently packed as a single PDF in a ZIP file :)
//!
//! ## Credits
//! This library is a remix of `RustCrypto/utils/der`, with a view towards:
@@ -24,6 +16,8 @@
//! The core idea taken from `der` is to have `Encodable` require an `encoded_length` method.
//! By calling this recursively in a first pass, allocations required in other approaches are
//! avoided.
//!
//! [iso8825]: https://standards.iso.org/ittf/PubliclyAvailableStandards/c068345_ISO_IEC_8825-1_2015.zip
#![no_std]
#![forbid(unsafe_code)]
@@ -33,7 +27,7 @@
extern crate alloc;
#[cfg(feature = "derive")]
pub use simple_tlv_derive::{Decodable, Encodable};
pub use flexiber_derive::{Decodable, Encodable};
#[cfg(feature = "std")]
extern crate std;
@@ -53,7 +47,7 @@ pub use encoder::Encoder;
pub use error::{Error, ErrorKind, Result};
pub use length::Length;
pub use slice::Slice;
pub use tag::Tag;
pub use tag::{Class, Tag};
pub use tagged::{TaggedSlice, TaggedValue};
pub use traits::{Container, Decodable, Encodable, Tagged};
#[cfg(feature = "heapless")]
+181 -25
View File
@@ -1,17 +1,85 @@
use core::{convert::TryFrom, fmt};
use core::{convert::{TryFrom, TryInto}, fmt};
use crate::{Decodable, Decoder, Encodable, Encoder, Error, ErrorKind, Length, Result, TaggedValue};
const CLASS_OFFSET: usize = 6;
const CONSTRUCTED_OFFSET: usize = 5;
/// Indicator bit for constructed form encoding (i.e. vs primitive form)
const CONSTRUCTED_FLAG: u8 = 1u8 << CONSTRUCTED_OFFSET;
/// Indicator bit for constructed form encoding (i.e. vs primitive form)
const NOT_LAST_TAG_OCTET_FLAG: u8 = 1u8 << 7;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Class {
Universal = 0b00,
Application = 0b01,
Context = 0b10,
Private = 0b11,
}
impl TryFrom<u8> for Class {
type Error = Error;
fn try_from(value: u8) -> Result<Self> {
use Class::*;
Ok(match value {
0b00 => Universal,
0b01 => Application,
0b10 => Context,
0b11 => Private,
_ => return Err(ErrorKind::InvalidClass { value }.into()),
})
}
}
/// The tag field consists of a single byte encoding a tag number from 1 to 254. The values '00' and 'FF' are invalid.
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Tag(u8);
pub struct Tag {
pub class: Class,
pub constructed: bool,
pub number: u16,
}
impl Tag {
pub fn from(class: Class, constructed: bool, number: u16) -> Self {
Self { class, constructed, number }
}
pub fn universal(number: u16) -> Self {
Self { class: Class::Universal, constructed: false, number }
}
pub fn application(number: u16) -> Self {
Self { class: Class::Application, constructed: false, number }
}
pub fn context(number: u16) -> Self {
Self { class: Class::Context, constructed: false, number }
}
pub fn private(number: u16) -> Self {
Self { class: Class::Private, constructed: false, number }
}
pub fn constructed(self) -> Self {
let Self { class, constructed: _, number } = self;
Self { class, constructed: true, number }
}
}
impl TryFrom<&'_ [u8]> for Tag {
type Error = Error;
fn try_from(encoding: &[u8]) -> Result<Self> {
let mut decoder = Decoder::new(encoding);
decoder.decode()
}
}
impl TryFrom<u8> for Tag {
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)),
}
fn try_from(encoded_value: u8) -> Result<Self> {
[encoded_value].as_ref().try_into()
}
}
@@ -39,31 +107,119 @@ impl Tag {
// }
}
impl Decodable<'_> for Tag {
fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
decoder.byte().and_then(Self::try_from)
}
}
impl Encodable for Tag {
fn encoded_length(&self) -> Result<Length> {
Ok(1u8.into())
}
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.byte(self.0)
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// f.write_str(self.type_name())
write!(f, "Tag('{:02x}')", self.0)
// write!(f, "Tag('{:02x}')", self.0)
core::fmt::Debug::fmt(self, f)
}
}
impl fmt::Debug for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Tag('{:02x}')", self.0)
let mut buf = [0u8; 3];
let mut encoder = Encoder::new(&mut buf);
encoder.encode(self).unwrap();
write!(f, "Tag(class = {:?}, constructed = {}, number = {})", self.class, self.constructed, self.number)
}
}
impl Encodable for Tag {
fn encoded_length(&self) -> Result<Length> {
match self.number {
0..=0x1E => Ok(Length(1)),
0x1F..=0x7F => Ok(Length(2)),
0x80..=0x3FFF => Ok(Length(3)),
0x4000..=0xFFFF => Ok(Length(4)),
}
}
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
let first_byte = ((self.class as u8) << CLASS_OFFSET) | ((self.constructed as u8) << CONSTRUCTED_OFFSET);
match self.number {
0..=0x1E => encoder.byte(first_byte | (self.number as u8)),
0x1F..=0x7F => {
encoder.byte(first_byte | 0x1F)?;
encoder.byte(self.number as u8)
}
0x80..=0x3FFF => {
encoder.byte(first_byte | 0x1F)?;
encoder.byte(NOT_LAST_TAG_OCTET_FLAG | (self.number >> 7) as u8)?;
encoder.byte((self.number & 0x7F) as u8)
}
0x4000..=0xFFFF => {
todo!();
}
}
}
}
impl Decodable<'_> for Tag {
fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
let first_byte = decoder.byte()?;
let class = (first_byte >> 6).try_into()?;
let constructed = first_byte & CONSTRUCTED_FLAG != 0;
// remove class and primitive/constructed bits
let first_byte_masked = first_byte & ((1 << 5) - 1);
let number = match first_byte_masked {
number @ 0..=0x1E => {
number as u16
}
_ => {
let second_byte = decoder.byte()?;
if second_byte & NOT_LAST_TAG_OCTET_FLAG == 0 {
let number = second_byte;
number as u16
} else {
let number = second_byte & (!NOT_LAST_TAG_OCTET_FLAG);
let third_byte = decoder.byte()?;
if third_byte & NOT_LAST_TAG_OCTET_FLAG == 0 {
((number as u16) << 7) | (third_byte as u16)
} else {
todo!();
}
}
}
};
Ok(Self { class, constructed, number })
}
}
#[cfg(test)]
mod tests {
use crate::{Decodable, Encodable, Tag};
#[test]
fn reconstruct() {
let mut buf = [0u8; 32];
let tag = Tag::universal(30);
let encoded = tag.encode_to_slice(&mut buf).unwrap();
assert_eq!(encoded, &[0x1E]);
let tag2 = Tag::from_bytes(encoded).unwrap();
assert_eq!(tag, tag2);
let tag = Tag::universal(31);
let encoded = tag.encode_to_slice(&mut buf).unwrap();
assert_eq!(encoded, &[0x1F, 0x1F]);
let tag2 = Tag::from_bytes(encoded).unwrap();
assert_eq!(tag, tag2);
let tag = Tag::universal(0xAA);
let encoded = tag.encode_to_slice(&mut buf).unwrap();
assert_eq!(encoded, &[0x1F, 0x81, 0x2A]);
let tag2 = Tag::from_bytes(encoded).unwrap();
assert_eq!(tag, tag2);
let tag = Tag::universal(0x10).constructed();
let encoded = tag.encode_to_slice(&mut buf).unwrap();
// assert_eq!(encoded, &[0x1F, 0x81, 0x2A]);
assert_eq!(encoded, &[super::CONSTRUCTED_FLAG + 0x10]);
let tag2 = Tag::from_bytes(encoded).unwrap();
assert_eq!(tag, tag2);
}
}
+43 -5
View File
@@ -143,17 +143,55 @@ mod tests {
fn encode() {
let mut buf = [0u8; 1024];
let short = TaggedSlice::from(Tag::try_from(0x66).unwrap(), &[1, 2, 3]).unwrap();
let short = TaggedSlice::from(Tag::try_from(0x06).unwrap(), &[1, 2, 3]).unwrap();
assert_eq!(
short.encode_to_slice(&mut buf).unwrap(),
&[0x66, 0x3, 1, 2, 3]
&[0x06, 0x3, 1, 2, 3]
);
let slice = &[43u8; 256];
let long = TaggedSlice::from(Tag::try_from(0x66).unwrap(), slice).unwrap();
let long = TaggedSlice::from(Tag::universal(0x66), slice).unwrap();
let encoded = long.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..4], &[0x66, 0xFF, 0x01, 0x00]);
assert_eq!(&encoded[4..], slice);
assert_eq!(&encoded[..5], &[0x1F, 0x66, 0x82, 0x01, 0x00]);
assert_eq!(&encoded[5..], slice);
let long = TaggedSlice::from(Tag::universal(0x66).constructed(), slice).unwrap();
let encoded = long.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..5], &[0x3F, 0x66, 0x82, 0x01, 0x00]);
assert_eq!(&encoded[5..], slice);
let long = TaggedSlice::from(Tag::application(0x66), slice).unwrap();
let encoded = long.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..5], &[0x5F, 0x66, 0x82, 0x01, 0x00]);
assert_eq!(&encoded[5..], slice);
let long = TaggedSlice::from(Tag::application(0x66).constructed(), slice).unwrap();
let encoded = long.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..5], &[0x7F, 0x66, 0x82, 0x01, 0x00]);
assert_eq!(&encoded[5..], slice);
let long = TaggedSlice::from(Tag::context(0x66), slice).unwrap();
let encoded = long.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..5], &[0x9F, 0x66, 0x82, 0x01, 0x00]);
assert_eq!(&encoded[5..], slice);
let long = TaggedSlice::from(Tag::context(0x66).constructed(), slice).unwrap();
let encoded = long.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..5], &[0xBF, 0x66, 0x82, 0x01, 0x00]);
assert_eq!(&encoded[5..], slice);
let long = TaggedSlice::from(Tag::private(0x66), slice).unwrap();
let encoded = long.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..5], &[0xDF, 0x66, 0x82, 0x01, 0x00]);
assert_eq!(&encoded[5..], slice);
let long = TaggedSlice::from(Tag::private(0x66).constructed(), slice).unwrap();
let encoded = long.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..5], &[0xFF, 0x66, 0x82, 0x01, 0x00]);
assert_eq!(&encoded[5..], slice);
}
}
+29 -29
View File
@@ -301,11 +301,11 @@ mod tests {
// tag 0xAA
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct S {
// tag 0x11
// tag 0x01
x: [u8; 2],
// tag 0x22
// tag 0x02
y: [u8; 3],
// tag 0x33
// tag 0x03
z: [u8; 4],
}
@@ -314,11 +314,11 @@ mod tests {
type Error = Error;
fn try_from(tagged_slice: TaggedSlice<'a>) -> Result<S> {
tagged_slice.tag().assert_eq(Tag::try_from(0xAA).unwrap())?;
tagged_slice.tag().assert_eq(Tag::try_from(0x0A).unwrap())?;
tagged_slice.decode_nested(|decoder| {
let x = decoder.decode_tagged_value(Tag::try_from(0x11).unwrap())?;
let y = decoder.decode_tagged_value(Tag::try_from(0x22).unwrap())?;
let z = decoder.decode_tagged_value(Tag::try_from(0x33).unwrap())?;
let x = decoder.decode_tagged_value(Tag::try_from(0x01).unwrap())?;
let y = decoder.decode_tagged_value(Tag::try_from(0x02).unwrap())?;
let z = decoder.decode_tagged_value(Tag::try_from(0x03).unwrap())?;
Ok(Self { x, y, z })
})
@@ -328,7 +328,7 @@ mod tests {
// this is what needs to be done to get `Encodable`
impl Tagged for S {
fn tag() -> Tag {
Tag::try_from(0xAA).unwrap()
Tag::try_from(0x0A).unwrap()
}
}
@@ -339,10 +339,10 @@ mod tests {
{
// both approaches equivalent
field_encoder(&[
&(Tag::try_from(0x11).unwrap().with_value(&self.x.as_ref())),
&(Tag::try_from(0x01).unwrap().with_value(&self.x.as_ref())),
// &self.x.tagged(Tag::try_from(0x11).unwrap()),
&self.y.as_ref().tagged(Tag::try_from(0x22).unwrap()),
&self.z.as_ref().tagged(Tag::try_from(0x33).unwrap()),
&self.y.as_ref().tagged(Tag::try_from(0x02).unwrap()),
&self.z.as_ref().tagged(Tag::try_from(0x03).unwrap()),
])
}
@@ -356,10 +356,10 @@ mod tests {
let encoded = s.encode_to_slice(&mut buf).unwrap();
assert_eq!(encoded,
&[0xAA, 15,
0x11, 2, 1, 2,
0x22, 3, 3, 4, 5,
0x33, 4, 6, 7, 8, 9,
&[0x0A, 15,
0x01, 2, 1, 2,
0x02, 3, 3, 4, 5,
0x03, 4, 6, 7, 8, 9,
],
);
@@ -381,7 +381,7 @@ mod tests {
type Error = Error;
fn try_from(tagged_slice: TaggedSlice<'a>) -> Result<Self> {
tagged_slice.tag().assert_eq(Tag::try_from(0xBB).unwrap())?;
tagged_slice.tag().assert_eq(Tag::try_from(0x0B).unwrap())?;
tagged_slice.decode_nested(|decoder| {
let s = decoder.decode_tagged_value(Tag::try_from(0x01).unwrap())?;
let t = decoder.decode_tagged_value(Tag::try_from(0x02).unwrap())?;
@@ -393,7 +393,7 @@ mod tests {
impl Tagged for T {
fn tag() -> Tag {
Tag::try_from(0xBB).unwrap()
Tag::try_from(0x0B).unwrap()
}
}
@@ -420,12 +420,12 @@ mod tests {
let encoded = t.encode_to_slice(&mut buf).unwrap();
assert_eq!(encoded,
&[0xBB, 24,
&[0x0B, 24,
0x1, 17,
0xAA, 15,
0x11, 2, 1, 2,
0x22, 3, 3, 4, 5,
0x33, 4, 6, 7, 8, 9,
0x0A, 15,
0x01, 2, 1, 2,
0x02, 3, 3, 4, 5,
0x03, 4, 6, 7, 8, 9,
0x2, 3,
0xA, 0xB, 0xC
],
@@ -449,7 +449,7 @@ mod tests {
type Error = Error;
fn try_from(tagged_slice: TaggedSlice<'a>) -> Result<Self> {
tagged_slice.tag().assert_eq(Tag::try_from(0xCC).unwrap())?;
tagged_slice.tag().assert_eq(Tag::try_from(0x0C).unwrap())?;
tagged_slice.decode_nested(|decoder| {
let s = decoder.decode()?;
let t = decoder.decode_tagged_value(Tag::try_from(0x02).unwrap())?;
@@ -461,7 +461,7 @@ mod tests {
impl Tagged for T2 {
fn tag() -> Tag {
Tag::try_from(0xCC).unwrap()
Tag::try_from(0x0C).unwrap()
}
}
@@ -489,12 +489,12 @@ mod tests {
assert_eq!(encoded,
// &[0xBB, 24,
&[0xCC, 22,
&[0x0C, 22,
// 0x1, 17,
0xAA, 15,
0x11, 2, 1, 2,
0x22, 3, 3, 4, 5,
0x33, 4, 6, 7, 8, 9,
0x0A, 15,
0x01, 2, 1, 2,
0x02, 3, 3, 4, 5,
0x03, 4, 6, 7, 8, 9,
0x2, 3,
0xA, 0xB, 0xC
],
+60 -31
View File
@@ -2,32 +2,28 @@
#![cfg(feature = "derive")]
use simple_tlv::{Decodable, Encodable};
use flexiber::{Decodable, Encodable};
#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
#[tlv(tag = "0xAA")]
#[tlv(number = "0xAA")]
struct S {
#[tlv(slice, tag = "0x11")]
#[tlv(slice, number = "0x11")]
x: [u8; 2],
#[tlv(slice, tag = "0x22")]
#[tlv(slice, number = "0x22")]
y: [u8; 3],
#[tlv(slice, tag = "0x33")]
#[tlv(slice, number = "0x33")]
z: [u8; 4],
}
#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
#[tlv(tag = "0xBB")]
struct T {
#[tlv(tag = "0x44", slice)]
x: [u8; 1234],
}
#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
struct T2 {
#[tlv(tag = "0x44", slice)]
x: [u8; 1234],
#[tlv(tag = "0x55", slice)]
a: [u8; 5],
#[tlv(application, number = "0xAA")]
struct SApp {
#[tlv(slice, number = "0x11")]
x: [u8; 2],
#[tlv(slice, number = "0x22")]
y: [u8; 3],
#[tlv(slice, number = "0x33")]
z: [u8; 4],
}
#[test]
@@ -38,10 +34,10 @@ fn derived_reconstruct() {
let encoded = s.encode_to_slice(&mut buf).unwrap();
assert_eq!(encoded,
&[0xAA, 15,
0x11, 2, 1, 2,
0x22, 3, 3, 4, 5,
0x33, 4, 6, 7, 8, 9,
&[0x1F, 0x81, 0x2A, 17,
0x11, 2, 1, 2,
0x1F, 0x22, 3, 3, 4, 5,
0x1F, 0x33, 4, 6, 7, 8, 9,
],
);
@@ -49,6 +45,32 @@ fn derived_reconstruct() {
assert_eq!(s, s2);
}
#[test]
fn derived_reconstruct_application() {
let s = SApp { x: [1,2], y: [3,4,5], z: [6,7,8,9] };
let mut buf = [0u8; 1024];
let encoded = s.encode_to_slice(&mut buf).unwrap();
assert_eq!(encoded,
&[0x5F, 0x81, 0x2A, 17,
0x11, 2, 1, 2,
0x1F, 0x22, 3, 3, 4, 5,
0x1F, 0x33, 4, 6, 7, 8, 9,
],
);
let s2 = SApp::from_bytes(encoded).unwrap();
assert_eq!(s, s2);
}
#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
#[tlv(constructed, number = "0x10")]
struct T {
#[tlv(number = "0x44", slice)]
x: [u8; 1234],
}
#[test]
fn pretty_big() {
let mut x = [0u8; 1234];
@@ -64,17 +86,24 @@ fn pretty_big() {
let mut buf = [0u8; 1500];
let encoded = t.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..8], [
// 1234 + 4
0xBB, 0xFF, 0x04, 0xD6,
assert_eq!(&encoded[..9], [
// 1234 + 5
0x30, 0x82, 0x04, 0xD2 + 5,
// 1234
0x44, 0xFF, 0x04, 0xD2]);
assert_eq!(&encoded[8..], x);
0x1F, 0x44, 0x82, 0x04, 0xD2]);
assert_eq!(&encoded[9..], x);
let t2 = T::from_bytes(encoded).unwrap();
assert_eq!(t, t2);
}
#[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)]
struct T2 {
#[tlv(private, primitive, number = "0x44", slice)]
x: [u8; 1234],
#[tlv(application, constructed, number = "0x55", slice)]
a: [u8; 5],
}
#[test]
fn derive_untagged() {
@@ -88,11 +117,11 @@ fn derive_untagged() {
let mut buf = [0u8; 1500];
let encoded = t.encode_to_slice(&mut buf).unwrap();
assert_eq!(&encoded[..4], [
// 1234
0x44, 0xFF, 0x04, 0xD2]);
assert_eq!(&encoded[4..(encoded.len() - 7)], x);
assert_eq!(&encoded[(encoded.len() - 7)..], [0x55, 5, 17, 17, 17, 17, 17]);
assert_eq!(&encoded[..5], [
// 1234
223, 0x44, 0x82, 0x04, 0xD2]);
assert_eq!(&encoded[5..(encoded.len() - 8)], x);
assert_eq!(&encoded[(encoded.len() - 8)..], [0x7F, 0x55, 5, 17, 17, 17, 17, 17]);
let t2 = T2::from_bytes(encoded).unwrap();
assert_eq!(t, t2);