From 19f4cf5d27e0bcb44e97150f1cb0df45805d3fee Mon Sep 17 00:00:00 2001 From: Nicolas Stalder Date: Fri, 19 Feb 2021 23:13:42 +0100 Subject: [PATCH] Initial commit, adjusted from simple-tlv --- Cargo.toml | 10 +- README.md | 9 +- bacon.toml | 4 + derive/Cargo.toml | 6 +- derive/src/decodable.rs | 59 +++++++++--- derive/src/encodable.rs | 50 ++++++---- derive/src/lib.rs | 88 +++++++++++------ src/decoder.rs | 5 +- src/encoder.rs | 34 +++---- src/error.rs | 15 ++- src/length.rs | 85 +++++++++-------- src/lib.rs | 26 ++--- src/tag.rs | 206 +++++++++++++++++++++++++++++++++++----- src/tagged.rs | 48 +++++++++- src/traits.rs | 58 +++++------ tests/derive.rs | 91 ++++++++++++------ 16 files changed, 554 insertions(+), 240 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 93a316a..39d7897 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,17 @@ [package] -name = "simple-tlv" +name = "flexiber" version = "0.1.0" authors = ["Nicolas Stalder ", "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"] diff --git a/README.md b/README.md index cb7e09e..03c30d2 100644 --- a/README.md +++ b/README.md @@ -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 -`simple-tlv` is licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT License](LICENSE-MIT) at your option. +`flexiber` is licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT License](LICENSE-MIT) at your option.
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. diff --git a/bacon.toml b/bacon.toml index aa1b440..3471c04 100644 --- a/bacon.toml +++ b/bacon.toml @@ -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 diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 28cb6b1..29949e3 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "simple-tlv_derive" +name = "flexiber_derive" version = "0.1.0" authors = ["Nicolas Stalder ", "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"] diff --git a/derive/src/decodable.rs b/derive/src/decodable.rs index d9d667f..187d98f 100644 --- a/derive/src/decodable.rs +++ b/derive/src/decodable.rs @@ -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) -> TokenStream { + fn finish(self, s: &Structure<'_>, tag: Option) -> 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> for @Self { - type Error = simple_tlv::Error; - fn try_from(tagged_slice: simple_tlv::TaggedSlice<'a>) -> simple_tlv::Result { + 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> for @Self { + type Error = flexiber::Error; + + fn try_from(tagged_slice: flexiber::TaggedSlice<'a>) -> flexiber::Result { 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 { + gen impl<'a> flexiber::Decodable<'a> for @Self { + fn decode(decoder: &mut flexiber::Decoder<'a>) -> flexiber::Result { use core::convert::{TryFrom, TryInto}; #decode_fields Ok(Self { #decode_result }) diff --git a/derive/src/encodable.rs b/derive/src/encodable.rs index 337dfe6..1209d32 100644 --- a/derive/src/encodable.rs +++ b/derive/src/encodable.rs @@ -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) -> TokenStream { + fn finish(self, s: &Structure<'_>, tag: Option) -> 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(&self, field_encoder: F) -> simple_tlv::Result + gen impl flexiber::Container for @Self { + fn fields(&self, field_encoder: F) -> flexiber::Result where - F: FnOnce(&[&dyn simple_tlv::Encodable]) -> simple_tlv::Result, + F: FnOnce(&[&dyn flexiber::Encodable]) -> flexiber::Result, { 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(&self, field_encoder: F) -> simple_tlv::Result + gen impl flexiber::Container for @Self { + fn fields(&self, field_encoder: F) -> flexiber::Result where - F: FnOnce(&[&dyn simple_tlv::Encodable]) -> simple_tlv::Result, + F: FnOnce(&[&dyn flexiber::Encodable]) -> flexiber::Result, { use core::convert::TryFrom; field_encoder(&[#encode_fields]) } } - gen impl simple_tlv::Encodable for @Self { - fn encoded_length(&self) -> simple_tlv::Result { + gen impl flexiber::Encodable for @Self { + fn encoded_length(&self) -> flexiber::Result { 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)) } } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 968dc5f..ad46a93 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -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, bool) { - let mut tag = None; +fn extract_attrs_optional_tag(name: &Ident, attrs: &[Attribute]) -> (Option, 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, 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, .. })) => { // 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, } } - (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 { diff --git a/src/decoder.rs b/src/decoder.rs index b34669e..a879180 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -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)] diff --git a/src/encoder.rs b/src/encoder.rs index ae10e94..b25c90b 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -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())); -// } -// } diff --git a/src/error.rs b/src/error.rs index 42b3b98..dca3de2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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"), } } } diff --git a/src/length.rs b/src/length.rs index 6a68782..a885ca0 100644 --- a/src/length.rs +++ b/src/length.rs @@ -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 for Length { impl Decodable<'_> for Length { fn decode(decoder: &mut Decoder<'_>) -> Result { 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 { 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()); } } diff --git a/src/lib.rs b/src/lib.rs index a4f64c9..89e7677 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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")] diff --git a/src/tag.rs b/src/tag.rs index a15bcdc..ed7f2ff 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -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 for Class { + type Error = Error; + fn try_from(value: u8) -> Result { + 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 { + let mut decoder = Decoder::new(encoding); + decoder.decode() + } +} impl TryFrom for Tag { type Error = Error; - fn try_from(tag_number: u8) -> Result { - 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 { + [encoded_value].as_ref().try_into() } } @@ -39,31 +107,119 @@ impl Tag { // } } -impl Decodable<'_> for Tag { - fn decode(decoder: &mut Decoder<'_>) -> Result { - decoder.byte().and_then(Self::try_from) - } -} - -impl Encodable for Tag { - fn encoded_length(&self) -> Result { - 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 { + 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 { + 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); } } diff --git a/src/tagged.rs b/src/tagged.rs index 1596215..e05c0e8 100644 --- a/src/tagged.rs +++ b/src/tagged.rs @@ -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); + } + } diff --git a/src/traits.rs b/src/traits.rs index 69f384f..fcbfe31 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -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 { - 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 { - 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 { - 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 ], diff --git a/tests/derive.rs b/tests/derive.rs index c0a25c1..1ddfb68 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -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);