mirror of
https://github.com/trussed-dev/flexiber.git
synced 2026-06-20 04:16:10 -07:00
Initial commit, adjusted from simple-tlv
This commit is contained in:
+5
-5
@@ -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"]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user