diff options
Diffstat (limited to '')
92 files changed, 14447 insertions, 0 deletions
diff --git a/rust/vendor/asn1-rs-derive/.cargo-checksum.json b/rust/vendor/asn1-rs-derive/.cargo-checksum.json new file mode 100644 index 0000000..669dca2 --- /dev/null +++ b/rust/vendor/asn1-rs-derive/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"212bc31c983a73fda86be833521aa743e57ea332b4119d0c35d33c02b5e02ef9","src/alias.rs":"c7e13fb41a65ccacfc796c579d87dc5d1bfd6e77a1f17c712e938a6434602eeb","src/container.rs":"185278eeab89b7f01f98a1036157957672e55c30dd97db99e5035d989cd68097","src/lib.rs":"8b905ab337e91799cb302a52fade0d8f47eccd52691ab3cc4d2807f019318611","src/sequence.rs":"e8b29fcbd9005b33b6ff920952956f31865718a14dd06241c1e75864da6e3098","src/set.rs":"1465afc2a80f914df548a910c44d15efbfe0a5a00ef21c1efe42da33e8f5c6ad"},"package":"726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"}
\ No newline at end of file diff --git a/rust/vendor/asn1-rs-derive/Cargo.toml b/rust/vendor/asn1-rs-derive/Cargo.toml new file mode 100644 index 0000000..8dc4bad --- /dev/null +++ b/rust/vendor/asn1-rs-derive/Cargo.toml @@ -0,0 +1,36 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "asn1-rs-derive" +version = "0.4.0" +authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"] +description = "Derive macros for the `asn1-rs` crate" +homepage = "https://github.com/rusticata/asn1-rs" +license = "MIT/Apache-2.0" +repository = "https://github.com/rusticata/asn1-rs.git" + +[lib] +proc-macro = true + +[dependencies.proc-macro2] +version = "1.0" + +[dependencies.quote] +version = "1.0" + +[dependencies.syn] +version = "1.0" +features = ["full"] + +[dependencies.synstructure] +version = "0.12" diff --git a/rust/vendor/asn1-rs-derive/src/alias.rs b/rust/vendor/asn1-rs-derive/src/alias.rs new file mode 100644 index 0000000..82ca68e --- /dev/null +++ b/rust/vendor/asn1-rs-derive/src/alias.rs @@ -0,0 +1,61 @@ +use crate::container::*; +use proc_macro2::Span; +use quote::quote; +use syn::{Data, Ident}; + +pub fn derive_ber_alias(s: synstructure::Structure) -> proc_macro2::TokenStream { + let ast = s.ast(); + + let container = match &ast.data { + Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Alias), + _ => panic!("Unsupported type, cannot derive"), + }; + + let debug_derive = ast.attrs.iter().any(|attr| { + attr.path + .is_ident(&Ident::new("debug_derive", Span::call_site())) + }); + + let impl_tryfrom = container.gen_tryfrom(); + let impl_tagged = container.gen_tagged(); + let ts = s.gen_impl(quote! { + extern crate asn1_rs; + + #impl_tryfrom + #impl_tagged + }); + if debug_derive { + eprintln!("{}", ts); + } + ts +} + +pub fn derive_der_alias(s: synstructure::Structure) -> proc_macro2::TokenStream { + let ast = s.ast(); + + let container = match &ast.data { + Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Alias), + _ => panic!("Unsupported type, cannot derive"), + }; + + let debug_derive = ast.attrs.iter().any(|attr| { + attr.path + .is_ident(&Ident::new("debug_derive", Span::call_site())) + }); + let impl_tryfrom = container.gen_tryfrom(); + let impl_tagged = container.gen_tagged(); + let impl_checkconstraints = container.gen_checkconstraints(); + let impl_fromder = container.gen_fromder(); + let ts = s.gen_impl(quote! { + extern crate asn1_rs; + + #impl_tryfrom + #impl_tagged + #impl_checkconstraints + #impl_fromder + }); + if debug_derive { + eprintln!("{}", ts); + } + ts +} diff --git a/rust/vendor/asn1-rs-derive/src/container.rs b/rust/vendor/asn1-rs-derive/src/container.rs new file mode 100644 index 0000000..667992c --- /dev/null +++ b/rust/vendor/asn1-rs-derive/src/container.rs @@ -0,0 +1,488 @@ +use proc_macro2::{Literal, Span, TokenStream}; +use quote::{quote, ToTokens}; +use syn::{ + parse::ParseStream, parse_quote, spanned::Spanned, Attribute, DataStruct, DeriveInput, Field, + Fields, Ident, Lifetime, LitInt, Meta, Type, WherePredicate, +}; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ContainerType { + Alias, + Sequence, + Set, +} + +impl ToTokens for ContainerType { + fn to_tokens(&self, tokens: &mut TokenStream) { + let s = match self { + ContainerType::Alias => quote! {}, + ContainerType::Sequence => quote! { asn1_rs::Tag::Sequence }, + ContainerType::Set => quote! { asn1_rs::Tag::Set }, + }; + s.to_tokens(tokens) + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum Asn1Type { + Ber, + Der, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Asn1TagKind { + Explicit, + Implicit, +} + +impl ToTokens for Asn1TagKind { + fn to_tokens(&self, tokens: &mut TokenStream) { + let s = match self { + Asn1TagKind::Explicit => quote! { asn1_rs::Explicit }, + Asn1TagKind::Implicit => quote! { asn1_rs::Implicit }, + }; + s.to_tokens(tokens) + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Asn1TagClass { + Universal, + Application, + ContextSpecific, + Private, +} + +impl ToTokens for Asn1TagClass { + fn to_tokens(&self, tokens: &mut TokenStream) { + let s = match self { + Asn1TagClass::Application => quote! { asn1_rs::Class::APPLICATION }, + Asn1TagClass::ContextSpecific => quote! { asn1_rs::Class::CONTEXT_SPECIFIC }, + Asn1TagClass::Private => quote! { asn1_rs::Class::PRIVATE }, + Asn1TagClass::Universal => quote! { asn1_rs::Class::UNIVERSAL }, + }; + s.to_tokens(tokens) + } +} + +pub struct Container { + pub container_type: ContainerType, + pub fields: Vec<FieldInfo>, + pub where_predicates: Vec<WherePredicate>, + pub error: Option<Attribute>, + + is_any: bool, +} + +impl Container { + pub fn from_datastruct( + ds: &DataStruct, + ast: &DeriveInput, + container_type: ContainerType, + ) -> Self { + let mut is_any = false; + match (container_type, &ds.fields) { + (ContainerType::Alias, Fields::Unnamed(f)) => { + if f.unnamed.len() != 1 { + panic!("Alias: only tuple fields with one element are supported"); + } + match &f.unnamed[0].ty { + Type::Path(type_path) + if type_path + .clone() + .into_token_stream() + .to_string() + .starts_with("Any") => + { + is_any = true; + } + _ => (), + } + } + (ContainerType::Alias, _) => panic!("BER/DER alias must be used with tuple strucs"), + (_, Fields::Unnamed(_)) => panic!("BER/DER sequence cannot be used on tuple structs"), + _ => (), + } + + let fields = ds.fields.iter().map(FieldInfo::from).collect(); + + // get lifetimes from generics + let lfts: Vec<_> = ast.generics.lifetimes().collect(); + let mut where_predicates = Vec::new(); + if !lfts.is_empty() { + // input slice must outlive all lifetimes from Self + let lft = Lifetime::new("'ber", Span::call_site()); + let wh: WherePredicate = parse_quote! { #lft: #(#lfts)+* }; + where_predicates.push(wh); + }; + + // get custom attributes on container + let error = ast + .attrs + .iter() + .find(|attr| attr.path.is_ident(&Ident::new("error", Span::call_site()))) + .cloned(); + + Container { + container_type, + fields, + where_predicates, + error, + is_any, + } + } + + pub fn gen_tryfrom(&self) -> TokenStream { + let field_names = &self.fields.iter().map(|f| &f.name).collect::<Vec<_>>(); + let parse_content = + derive_ber_sequence_content(&self.fields, Asn1Type::Ber, self.error.is_some()); + let lifetime = Lifetime::new("'ber", Span::call_site()); + let wh = &self.where_predicates; + let error = if let Some(attr) = &self.error { + get_attribute_meta(attr).expect("Invalid error attribute format") + } else { + quote! { asn1_rs::Error } + }; + + let fn_content = if self.container_type == ContainerType::Alias { + // special case: is this an alias for Any + if self.is_any { + quote! { Ok(Self(any)) } + } else { + quote! { + let res = TryFrom::try_from(any)?; + Ok(Self(res)) + } + } + } else { + quote! { + use asn1_rs::nom::*; + any.tag().assert_eq(Self::TAG)?; + + // no need to parse sequence, we already have content + let i = any.data; + // + #parse_content + // + let _ = i; // XXX check if empty? + Ok(Self{#(#field_names),*}) + } + }; + // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing + // the `where` statement if there are none. + quote! { + use asn1_rs::{Any, FromBer}; + use core::convert::TryFrom; + + gen impl<#lifetime> TryFrom<Any<#lifetime>> for @Self where #(#wh)+* { + type Error = #error; + + fn try_from(any: Any<#lifetime>) -> asn1_rs::Result<Self, #error> { + #fn_content + } + } + } + } + + pub fn gen_tagged(&self) -> TokenStream { + let tag = if self.container_type == ContainerType::Alias { + // special case: is this an alias for Any + if self.is_any { + return quote! {}; + } + // find type of sub-item + let ty = &self.fields[0].type_; + quote! { <#ty as asn1_rs::Tagged>::TAG } + } else { + let container_type = self.container_type; + quote! { #container_type } + }; + quote! { + gen impl<'ber> asn1_rs::Tagged for @Self { + const TAG: asn1_rs::Tag = #tag; + } + } + } + + pub fn gen_checkconstraints(&self) -> TokenStream { + let lifetime = Lifetime::new("'ber", Span::call_site()); + let wh = &self.where_predicates; + // let parse_content = derive_ber_sequence_content(&field_names, Asn1Type::Der); + + let fn_content = if self.container_type == ContainerType::Alias { + // special case: is this an alias for Any + if self.is_any { + return quote! {}; + } + let ty = &self.fields[0].type_; + quote! { + any.tag().assert_eq(Self::TAG)?; + <#ty>::check_constraints(any) + } + } else { + let check_fields: Vec<_> = self + .fields + .iter() + .map(|field| { + let ty = &field.type_; + quote! { + let (rem, any) = Any::from_der(rem)?; + <#ty as CheckDerConstraints>::check_constraints(&any)?; + } + }) + .collect(); + quote! { + any.tag().assert_eq(Self::TAG)?; + let rem = &any.data; + #(#check_fields)* + Ok(()) + } + }; + + // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing + // the `where` statement if there are none. + quote! { + use asn1_rs::{CheckDerConstraints, Tagged}; + gen impl<#lifetime> CheckDerConstraints for @Self where #(#wh)+* { + fn check_constraints(any: &Any) -> asn1_rs::Result<()> { + #fn_content + } + } + } + } + + pub fn gen_fromder(&self) -> TokenStream { + let lifetime = Lifetime::new("'ber", Span::call_site()); + let wh = &self.where_predicates; + let field_names = &self.fields.iter().map(|f| &f.name).collect::<Vec<_>>(); + let parse_content = + derive_ber_sequence_content(&self.fields, Asn1Type::Der, self.error.is_some()); + let error = if let Some(attr) = &self.error { + get_attribute_meta(attr).expect("Invalid error attribute format") + } else { + quote! { asn1_rs::Error } + }; + + let fn_content = if self.container_type == ContainerType::Alias { + // special case: is this an alias for Any + if self.is_any { + quote! { + let (rem, any) = asn1_rs::Any::from_der(bytes).map_err(asn1_rs::nom::Err::convert)?; + Ok((rem,Self(any))) + } + } else { + quote! { + let (rem, any) = asn1_rs::Any::from_der(bytes).map_err(asn1_rs::nom::Err::convert)?; + any.header.assert_tag(Self::TAG).map_err(|e| asn1_rs::nom::Err::Error(e.into()))?; + let res = TryFrom::try_from(any)?; + Ok((rem,Self(res))) + } + } + } else { + quote! { + let (rem, any) = asn1_rs::Any::from_der(bytes).map_err(asn1_rs::nom::Err::convert)?; + any.header.assert_tag(Self::TAG).map_err(|e| asn1_rs::nom::Err::Error(e.into()))?; + let i = any.data; + // + #parse_content + // + // let _ = i; // XXX check if empty? + Ok((rem,Self{#(#field_names),*})) + } + }; + // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing + // the `where` statement if there are none. + quote! { + use asn1_rs::FromDer; + + gen impl<#lifetime> asn1_rs::FromDer<#lifetime, #error> for @Self where #(#wh)+* { + fn from_der(bytes: &#lifetime [u8]) -> asn1_rs::ParseResult<#lifetime, Self, #error> { + #fn_content + } + } + } + } +} + +#[derive(Debug)] +pub struct FieldInfo { + pub name: Ident, + pub type_: Type, + pub default: Option<TokenStream>, + pub optional: bool, + pub tag: Option<(Asn1TagKind, Asn1TagClass, u16)>, + pub map_err: Option<TokenStream>, +} + +impl From<&Field> for FieldInfo { + fn from(field: &Field) -> Self { + // parse attributes and keep supported ones + let mut optional = false; + let mut tag = None; + let mut map_err = None; + let mut default = None; + let name = field + .ident + .as_ref() + .map_or_else(|| Ident::new("_", Span::call_site()), |s| s.clone()); + for attr in &field.attrs { + let ident = match attr.path.get_ident() { + Some(ident) => ident.to_string(), + None => continue, + }; + match ident.as_str() { + "map_err" => { + let expr: syn::Expr = attr.parse_args().expect("could not parse map_err"); + map_err = Some(quote! { #expr }); + } + "default" => { + let expr: syn::Expr = attr.parse_args().expect("could not parse default"); + default = Some(quote! { #expr }); + optional = true; + } + "optional" => optional = true, + "tag_explicit" => { + if tag.is_some() { + panic!("tag cannot be set twice!"); + } + let (class, value) = attr.parse_args_with(parse_tag_args).unwrap(); + tag = Some((Asn1TagKind::Explicit, class, value)); + } + "tag_implicit" => { + if tag.is_some() { + panic!("tag cannot be set twice!"); + } + let (class, value) = attr.parse_args_with(parse_tag_args).unwrap(); + tag = Some((Asn1TagKind::Implicit, class, value)); + } + // ignore unknown attributes + _ => (), + } + } + FieldInfo { + name, + type_: field.ty.clone(), + default, + optional, + tag, + map_err, + } + } +} + +fn parse_tag_args(stream: ParseStream) -> Result<(Asn1TagClass, u16), syn::Error> { + let tag_class: Option<Ident> = stream.parse()?; + let tag_class = if let Some(ident) = tag_class { + let s = ident.to_string().to_uppercase(); + match s.as_str() { + "UNIVERSAL" => Asn1TagClass::Universal, + "CONTEXT-SPECIFIC" => Asn1TagClass::ContextSpecific, + "APPLICATION" => Asn1TagClass::Application, + "PRIVATE" => Asn1TagClass::Private, + _ => { + return Err(syn::Error::new(stream.span(), "Invalid tag class")); + } + } + } else { + Asn1TagClass::ContextSpecific + }; + let lit: LitInt = stream.parse()?; + let value = lit.base10_parse::<u16>()?; + Ok((tag_class, value)) +} + +fn derive_ber_sequence_content( + fields: &[FieldInfo], + asn1_type: Asn1Type, + custom_errors: bool, +) -> TokenStream { + let field_parsers: Vec<_> = fields + .iter() + .map(|f| get_field_parser(f, asn1_type, custom_errors)) + .collect(); + + quote! { + #(#field_parsers)* + } +} + +fn get_field_parser(f: &FieldInfo, asn1_type: Asn1Type, custom_errors: bool) -> TokenStream { + let from = match asn1_type { + Asn1Type::Ber => quote! {FromBer::from_ber}, + Asn1Type::Der => quote! {FromDer::from_der}, + }; + let name = &f.name; + let default = f + .default + .as_ref() + // use a type hint, otherwise compiler will not know what type provides .unwrap_or + .map(|x| quote! {let #name: Option<_> = #name; let #name = #name.unwrap_or(#x);}); + let map_err = if let Some(tt) = f.map_err.as_ref() { + if asn1_type == Asn1Type::Ber { + Some(quote! { .finish().map_err(#tt) }) + } else { + // Some(quote! { .map_err(|err| nom::Err::convert(#tt)) }) + Some(quote! { .map_err(|err| err.map(#tt)) }) + } + } else { + // add mapping functions only if custom errors are used + if custom_errors { + if asn1_type == Asn1Type::Ber { + Some(quote! { .finish() }) + } else { + Some(quote! { .map_err(nom::Err::convert) }) + } + } else { + None + } + }; + if let Some((tag_kind, class, n)) = f.tag { + let tag = Literal::u16_unsuffixed(n); + // test if tagged + optional + if f.optional { + return quote! { + let (i, #name) = { + if i.is_empty() { + (i, None) + } else { + let (_, header): (_, asn1_rs::Header) = #from(i)#map_err?; + if header.tag().0 == #tag { + let (i, t): (_, asn1_rs::TaggedValue::<_, _, #tag_kind, {#class}, #tag>) = #from(i)#map_err?; + (i, Some(t.into_inner())) + } else { + (i, None) + } + } + }; + #default + }; + } else { + // tagged, but not OPTIONAL + return quote! { + let (i, #name) = { + let (i, t): (_, asn1_rs::TaggedValue::<_, _, #tag_kind, {#class}, #tag>) = #from(i)#map_err?; + (i, t.into_inner()) + }; + #default + }; + } + } else { + // neither tagged nor optional + quote! { + let (i, #name) = #from(i)#map_err?; + #default + } + } +} + +fn get_attribute_meta(attr: &Attribute) -> Result<TokenStream, syn::Error> { + if let Ok(Meta::List(meta)) = attr.parse_meta() { + let content = &meta.nested; + Ok(quote! { #content }) + } else { + Err(syn::Error::new( + attr.span(), + "Invalid error attribute format", + )) + } +} diff --git a/rust/vendor/asn1-rs-derive/src/lib.rs b/rust/vendor/asn1-rs-derive/src/lib.rs new file mode 100644 index 0000000..c75cf37 --- /dev/null +++ b/rust/vendor/asn1-rs-derive/src/lib.rs @@ -0,0 +1,64 @@ +mod alias; +mod container; +mod sequence; +mod set; +use alias::*; +use sequence::*; +use set::*; + +synstructure::decl_derive!([BerAlias, attributes( + debug_derive, + default, + optional, + tag_explicit, + tag_implicit, + error, + map_err +)] => derive_ber_alias); +synstructure::decl_derive!([DerAlias, attributes( + debug_derive, + default, + optional, + tag_explicit, + tag_implicit, + error, + map_err +)] => derive_der_alias); + +synstructure::decl_derive!([BerSequence, attributes( + debug_derive, + default, + optional, + tag_explicit, + tag_implicit, + error, + map_err +)] => derive_ber_sequence); +synstructure::decl_derive!([DerSequence, attributes( + debug_derive, + default, + optional, + tag_explicit, + tag_implicit, + error, + map_err +)] => derive_der_sequence); + +synstructure::decl_derive!([BerSet, attributes( + debug_derive, + default, + optional, + tag_explicit, + tag_implicit, + error, + map_err +)] => derive_ber_set); +synstructure::decl_derive!([DerSet, attributes( + debug_derive, + default, + optional, + tag_explicit, + tag_implicit, + error, + map_err +)] => derive_der_set); diff --git a/rust/vendor/asn1-rs-derive/src/sequence.rs b/rust/vendor/asn1-rs-derive/src/sequence.rs new file mode 100644 index 0000000..8a0d249 --- /dev/null +++ b/rust/vendor/asn1-rs-derive/src/sequence.rs @@ -0,0 +1,61 @@ +use crate::container::*; +use proc_macro2::Span; +use quote::quote; +use syn::{Data, Ident}; + +pub fn derive_ber_sequence(s: synstructure::Structure) -> proc_macro2::TokenStream { + let ast = s.ast(); + + let container = match &ast.data { + Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Sequence), + _ => panic!("Unsupported type, cannot derive"), + }; + + let debug_derive = ast.attrs.iter().any(|attr| { + attr.path + .is_ident(&Ident::new("debug_derive", Span::call_site())) + }); + + let impl_tryfrom = container.gen_tryfrom(); + let impl_tagged = container.gen_tagged(); + let ts = s.gen_impl(quote! { + extern crate asn1_rs; + + #impl_tryfrom + #impl_tagged + }); + if debug_derive { + eprintln!("{}", ts); + } + ts +} + +pub fn derive_der_sequence(s: synstructure::Structure) -> proc_macro2::TokenStream { + let ast = s.ast(); + + let container = match &ast.data { + Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Sequence), + _ => panic!("Unsupported type, cannot derive"), + }; + + let debug_derive = ast.attrs.iter().any(|attr| { + attr.path + .is_ident(&Ident::new("debug_derive", Span::call_site())) + }); + let impl_tryfrom = container.gen_tryfrom(); + let impl_tagged = container.gen_tagged(); + let impl_checkconstraints = container.gen_checkconstraints(); + let impl_fromder = container.gen_fromder(); + let ts = s.gen_impl(quote! { + extern crate asn1_rs; + + #impl_tryfrom + #impl_tagged + #impl_checkconstraints + #impl_fromder + }); + if debug_derive { + eprintln!("{}", ts); + } + ts +} diff --git a/rust/vendor/asn1-rs-derive/src/set.rs b/rust/vendor/asn1-rs-derive/src/set.rs new file mode 100644 index 0000000..90ea04a --- /dev/null +++ b/rust/vendor/asn1-rs-derive/src/set.rs @@ -0,0 +1,61 @@ +use crate::container::*; +use proc_macro2::Span; +use quote::quote; +use syn::{Data, Ident}; + +pub fn derive_ber_set(s: synstructure::Structure) -> proc_macro2::TokenStream { + let ast = s.ast(); + + let container = match &ast.data { + Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Set), + _ => panic!("Unsupported type, cannot derive"), + }; + + let debug_derive = ast.attrs.iter().any(|attr| { + attr.path + .is_ident(&Ident::new("debug_derive", Span::call_site())) + }); + + let impl_tryfrom = container.gen_tryfrom(); + let impl_tagged = container.gen_tagged(); + let ts = s.gen_impl(quote! { + extern crate asn1_rs; + + #impl_tryfrom + #impl_tagged + }); + if debug_derive { + eprintln!("{}", ts); + } + ts +} + +pub fn derive_der_set(s: synstructure::Structure) -> proc_macro2::TokenStream { + let ast = s.ast(); + + let container = match &ast.data { + Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Set), + _ => panic!("Unsupported type, cannot derive"), + }; + + let debug_derive = ast.attrs.iter().any(|attr| { + attr.path + .is_ident(&Ident::new("debug_derive", Span::call_site())) + }); + let impl_tryfrom = container.gen_tryfrom(); + let impl_tagged = container.gen_tagged(); + let impl_checkconstraints = container.gen_checkconstraints(); + let impl_fromder = container.gen_fromder(); + let ts = s.gen_impl(quote! { + extern crate asn1_rs; + + #impl_tryfrom + #impl_tagged + #impl_checkconstraints + #impl_fromder + }); + if debug_derive { + eprintln!("{}", ts); + } + ts +} diff --git a/rust/vendor/asn1-rs-impl/.cargo-checksum.json b/rust/vendor/asn1-rs-impl/.cargo-checksum.json new file mode 100644 index 0000000..4a255f4 --- /dev/null +++ b/rust/vendor/asn1-rs-impl/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"1fda40b79fae27b66071324aeffa2deba87fd7bea40f36afb013bf90af50790d","src/lib.rs":"c052170f4c8f719ef07a722a1961c0e52423e28951ddbdf3700d7329bb79a277","tests/test_oid.rs":"0a4ed684b2c503315955b28d0587cf27ee7ed07e860f598960a99ee0415684d8"},"package":"2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"}
\ No newline at end of file diff --git a/rust/vendor/asn1-rs-impl/Cargo.toml b/rust/vendor/asn1-rs-impl/Cargo.toml new file mode 100644 index 0000000..261b72f --- /dev/null +++ b/rust/vendor/asn1-rs-impl/Cargo.toml @@ -0,0 +1,31 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "asn1-rs-impl" +version = "0.1.0" +authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"] +description = "Implementation details for the `asn1-rs` crate" +homepage = "https://github.com/rusticata/asn1-rs" +license = "MIT/Apache-2.0" +repository = "https://github.com/rusticata/asn1-rs.git" + +[lib] +proc-macro = true +[dependencies.proc-macro2] +version = "1" + +[dependencies.quote] +version = "1" + +[dependencies.syn] +version = "1" diff --git a/rust/vendor/asn1-rs-impl/src/lib.rs b/rust/vendor/asn1-rs-impl/src/lib.rs new file mode 100644 index 0000000..ea635cc --- /dev/null +++ b/rust/vendor/asn1-rs-impl/src/lib.rs @@ -0,0 +1,126 @@ +extern crate proc_macro; +use proc_macro::{Span, TokenStream}; +use syn::{parse_macro_input, Error, LitInt}; + +#[proc_macro] +pub fn encode_oid(input: TokenStream) -> TokenStream { + let token_stream = input.to_string(); + let (s, relative) = if token_stream.starts_with("rel ") { + (&token_stream[4..], true) + } else { + (token_stream.as_ref(), false) + }; + let items: Result<Vec<_>, _> = s.split('.').map(|x| x.trim().parse::<u128>()).collect(); + let mut items: &[_] = match items.as_ref() { + Ok(v) => v.as_ref(), + Err(_) => return create_error("Could not parse OID"), + }; + let mut v = Vec::new(); + if !relative { + if items.len() < 2 { + if items.len() == 1 && items[0] == 0 { + return "[0]".parse().unwrap(); + } + return create_error("Need at least two components for non-relative oid"); + } + if items[0] > 2 || items[1] > 39 { + return create_error("First components are too big"); + } + let first_byte = (items[0] * 40 + items[1]) as u8; + v.push(first_byte); + items = &items[2..]; + } + for &int in items { + let enc = encode_base128(int); + v.extend_from_slice(&enc); + } + // "fn answer() -> u32 { 42 }".parse().unwrap() + let mut s = String::with_capacity(2 + 6 * v.len()); + s.push('['); + for byte in v.iter() { + s.insert_str(s.len(), &format!("0x{:02x}, ", byte)); + } + s.push(']'); + s.parse().unwrap() +} + +#[inline] +fn create_error(msg: &str) -> TokenStream { + let s = format!("Invalid OID({})", msg); + Error::new(Span::call_site().into(), &s) + .to_compile_error() + .into() +} + +// encode int as base128 +fn encode_base128(int: u128) -> Vec<u8> { + let mut val = int; + let mut base128 = Vec::new(); + let lo = val & 0x7f; + base128.push(lo as u8); + val >>= 7; + loop { + if val == 0 { + base128.reverse(); + return base128; + } + let lo = val & 0x7f; + base128.push(lo as u8 | 0x80); + val >>= 7; + } +} + +#[proc_macro] +pub fn encode_int(input: TokenStream) -> TokenStream { + let lit = parse_macro_input!(input as LitInt); + + match impl_encode_int(lit) { + Ok(ts) => ts, + Err(e) => e.to_compile_error().into(), + } + + // let token_stream = input.to_string(); + // let items: Result<Vec<_>, _> = token_stream + // .split('.') + // .map(|x| x.trim().parse::<u64>()) + // .collect(); + // let err = Error::new(Span::call_site().into(), "invalid OID"); + // if let Ok(items) = items { + // let mut v = Vec::new(); + // if items.len() < 2 || items[0] > 2 || items[1] > 39 { + // return err.to_compile_error().into(); + // } + // let first_byte = (items[0] * 40 + items[1]) as u8; + // v.push(first_byte); + // for &int in &items[2..] { + // let enc = encode_base128(int); + // v.extend_from_slice(&enc); + // } + // // "fn answer() -> u32 { 42 }".parse().unwrap() + // let mut s = String::with_capacity(2 + 6 * v.len()); + // s.push('['); + // for byte in v.iter() { + // s.insert_str(s.len(), &format!("0x{:02x}, ", byte)); + // } + // s.push(']'); + // s.parse().unwrap() + // } else { + // eprintln!("could not parse OID '{}'", token_stream); + // err.to_compile_error().into() + // } +} + +fn impl_encode_int(lit: LitInt) -> Result<TokenStream, Error> { + let value = lit.base10_parse::<u64>()?; + + let bytes = value.to_be_bytes(); + let v: Vec<_> = bytes.iter().skip_while(|&c| *c == 0).collect(); + + let mut s = String::with_capacity(2 + 6 * v.len()); + s.push('['); + for byte in v.iter() { + s.insert_str(s.len(), &format!("0x{:02x}, ", byte)); + } + s.push(']'); + Ok(s.parse().unwrap()) +} diff --git a/rust/vendor/asn1-rs-impl/tests/test_oid.rs b/rust/vendor/asn1-rs-impl/tests/test_oid.rs new file mode 100644 index 0000000..b85d622 --- /dev/null +++ b/rust/vendor/asn1-rs-impl/tests/test_oid.rs @@ -0,0 +1,15 @@ +use asn1_rs_impl::{encode_int, encode_oid}; + +#[test] +fn test_encode_oid() { + // example from http://luca.ntop.org/Teaching/Appunti/asn1.html + let oid = encode_oid! {1.2.840.113549}; + assert_eq!(oid, [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d]); +} + +#[test] +fn test_encode_int() { + // + let int = encode_int!(1234); + assert_eq!(int, [0x04, 0xd2]); +} diff --git a/rust/vendor/asn1-rs/.cargo-checksum.json b/rust/vendor/asn1-rs/.cargo-checksum.json new file mode 100644 index 0000000..cebfcba --- /dev/null +++ b/rust/vendor/asn1-rs/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"4c39a6d04eb01d14dd6cebf32aaf25578755d70d7c0d3dc8cc82a556db971e15","Cargo.lock":"d7b378bf34e74292a21a11b161558dba385ba6fc34c56f5b19a3a86d8de82996","Cargo.toml":"c471f60e04f2dd7900ec6eaa38db83a634d675e48a1f92f41263f1ebe8e6bcf3","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"a5c61b93b6ee1d104af9920cf020ff3c7efe818e31fe562c72261847a728f513","README.md":"0f884f81e29080f9898679bc03ed2ce0d0e9e5748fb82b2b0c66110efe3e081d","doc/DERIVE.md":"5efcfbe54e5909501c1892aa388c29ba883b1c4cb444cac794f031a549fdd3d0","doc/RECIPES.md":"369afa9741f6b78e494de134c4b0243aead9c8d57ed07620d1b149726102f5b9","examples/dump-der.rs":"4ab76432ca0532c09c5255c9c3078850ceed65d3d759a8b9302d4c795a860bf9","src/asn1_types/any.rs":"7bb6d4587d41f8444d8267025963a73ba49068dc45d356d35346cfadca8c89c7","src/asn1_types/bitstring.rs":"58eb9cd0d53b0e18baf924f3a5ae9fae2df922370b451a5f3f44ae07f0652e6d","src/asn1_types/boolean.rs":"be3e6f9c484ee332a39d4c52aabd5257f165db43d66e6e10995f575b7ea05e4a","src/asn1_types/choice.rs":"ebe22979597c9e0696ab2a0ae547ddcaab0fc2a87925eaffea1751798bff3935","src/asn1_types/embedded_pdv.rs":"489c126ae4b3f11d0429a07079e22c8abd6ac6af2c5ee284e999911125a0860b","src/asn1_types/end_of_content.rs":"2e8fe47c63ce6cd7a922019a45a64e94e358f246a63cabc23085a5aae28c427d","src/asn1_types/enumerated.rs":"8aa62707ae2a48f07c8c77d4b969a13c1db9fe7e36d304aab824de41586fe9e7","src/asn1_types/generalizedtime.rs":"ada29a38a578028ee6b47143da6380a9ad593391fe3cac559efe56cbbf13c431","src/asn1_types/integer.rs":"454f0f2c30a19f7525e216c1c414d7c89886a4eff6bd1c43880579cb39f3452a","src/asn1_types/mod.rs":"1a712bbbf86d7323d4fd05fcfa12a62b0571d643c3724b40b316f641e48d5dfe","src/asn1_types/null.rs":"38b122b0d3cab051348c6f6204d8229b385537a00a1a36b6410356669b4d03a1","src/asn1_types/object_descriptor.rs":"f97a567de753fe80c19c0a1d7c787bde8a87146a0e41c9317d9c03d760859a3e","src/asn1_types/octetstring.rs":"f19273fc00c3c70d273c71863c60817982d2e1cf88856358a1d716fef6849b48","src/asn1_types/oid.rs":"905de319c952095bd8106bb5402b1c95a64156f163ea4db6e98a0a631d3c90c0","src/asn1_types/optional.rs":"5719f83ba427deb861b856cbae1ac4cc739c014c5bd572e1099273141639f41f","src/asn1_types/real.rs":"51bae0f0262a95dad8abe0448140509e3e761de3fd9cff8860bf2e4551050763","src/asn1_types/real/f32.rs":"991b229642f9b9f42b2b92d9381f49898b47447242149de4fdd325dc06a9b7be","src/asn1_types/real/f64.rs":"11da6c0f7bc892b5cbe23a1142f7eacf1639532e4a0b76075dc397ac882f5b8a","src/asn1_types/sequence.rs":"a3be696bfcbc21ee58c3a44eceefb8212fd24829bbb50fbb9d468d0ff9b15309","src/asn1_types/sequence/iterator.rs":"f4572a5c438437e259ae5ad2ed4226b432f6a92af8fa30b9aab761b04e171779","src/asn1_types/sequence/sequence_of.rs":"06beaa81a529ce94c192e683d5f1db8d914ae0c2843869e4d37e342fa092a272","src/asn1_types/sequence/vec.rs":"2a8fa0182e4c60ff09f01f8ad030478f5eddf2a1b3e1da2ac221c871b63cbd96","src/asn1_types/set.rs":"d999827742301ac9386ccca592fa3cfd1eaeb43c20e1eb434f95ab164113efcc","src/asn1_types/set/btreeset.rs":"0d902a7e65a03eb2db711816aa956aa8f1c34ad6c23df2cefb8d2f880a020b95","src/asn1_types/set/hashset.rs":"4114616395d317cd9eaf9787e1473580734c302b90c299ddf8967ee8ada95a86","src/asn1_types/set/iterator.rs":"feaa109ffef7019515ec0f1401e5424af4ff63ee5d226364df49d9787d765fce","src/asn1_types/set/set_of.rs":"e2d330bdad473a521c64997f5673a8e73dc76c90127e07bb6a1ec9fd067443c2","src/asn1_types/strings.rs":"3fc6a7a209d68d138da64ec4ce969439f4c0c327680cd814daf9a4a7dbfd352b","src/asn1_types/strings/bmpstring.rs":"4f7f1ff706a4765c3d3922144e2d668694b8c743c09f77cc215906cae81edaf2","src/asn1_types/strings/generalstring.rs":"19868123718db4572a6406bf300de66d9c75ded6d4953b51b19dd631d7b95d29","src/asn1_types/strings/graphicstring.rs":"626f9fba5cdbe693e7251445a6c776e298c4c4665bb0f894c966fecf910013a8","src/asn1_types/strings/ia5string.rs":"56f23fdde244cf751c07a0c4ad46fe68f162d116ba2b7a9fcebd77fd95892efb","src/asn1_types/strings/numericstring.rs":"ee981742dc547f458a2db569fda760cc47317fec12ffd2ae36505a3924fff059","src/asn1_types/strings/printablestring.rs":"a570a4f559c038dc4725483c6a9bee0d28ef8ade3054d6411c3f15e53346c22a","src/asn1_types/strings/str.rs":"86a886a89942f90b18033ec6e7d4922671e99e53724f2e0d057d06761a0ddea6","src/asn1_types/strings/string.rs":"2f5c81bf7a41af941a760b189f149e8290cb1551c3d7fc445795244d1479181b","src/asn1_types/strings/teletexstring.rs":"8d16c89f0454cf3b74585569842df21ab6465582eb54a387909c6c03beb3f0a7","src/asn1_types/strings/universalstring.rs":"c59a2213158f9ebd159bf62b0af2dab0d0f708ffd38565e88a4987c36756d31e","src/asn1_types/strings/utf8string.rs":"238ed3b2c69865f8a5fa2f9321c600b5ff2c747580862572e93c7fac5863164f","src/asn1_types/strings/videotexstring.rs":"7c2484628dbf261ca565f6f5ad3e7146a1752d5f7c08b068d319ee6fb5e95e5a","src/asn1_types/strings/visiblestring.rs":"7a10ce3618ad60f13792daeb1a3e2ee50b0d4e5632de5b20fefbc98fd0b07bd2","src/asn1_types/tagged.rs":"e8190e145c2c76bbc29c1903fcce92aca9690861c1a7de0de1e9b95b14d4c1a1","src/asn1_types/tagged/application.rs":"ac2851122f4a00c04c73b5573f15a3422aee7d55b10e91deded736040c7cd19c","src/asn1_types/tagged/builder.rs":"9698efdf2f573179a9c3aaed67053241a8eeb8ee7885444e906e6dd14ec552d4","src/asn1_types/tagged/explicit.rs":"ddd8db130372651a6e90c379bddc4ff1fee0b4507e79a5d237bee82856f96de8","src/asn1_types/tagged/helpers.rs":"9ea39600b1050c11f3eb7f064030febf9ef496e841b3da46a06acdcdb21aa968","src/asn1_types/tagged/implicit.rs":"13dfd74dd57b520b26b2a880c4dff84fa8bdabd6f888b758182c5565d0c109f4","src/asn1_types/tagged/optional.rs":"15081b9ed1b9a2336ca40849a6641b5638c9c6367701d5c0035ee9ca8df06539","src/asn1_types/tagged/parser.rs":"c607a82f1da4836b95f01a53438c15b75424c87d8ba385b1e7dbd874b4444ed2","src/asn1_types/tagged/private.rs":"e225b880dd235f95542c1b8a5e9083fb9779c74d092627d096194409402d8b76","src/asn1_types/utctime.rs":"d0e39aa9afdecde17d62e7a88f100c69425c1f416b2084f7f905f80390413c5c","src/ber/mod.rs":"def6d20e7d01288f09597c6a354e661fd433852c3b9628fe79cab0b0fe621dba","src/ber/parser.rs":"8f602b89f949920d527ada03ef8da4b350f7c36d693015c39d80143083cc3276","src/class.rs":"094ab4aa94dcc34059e98886270b4fae34d89283969006794eda02e59d547584","src/const_int.rs":"f82b27912091a73d568bc76971b4ecefa133e6f8e5437a9148abe1fe6ce22e15","src/datetime.rs":"91ef5de8cc3da54ee43db6214dfbe79d0b597b32b1d4aeca71345b62e5965905","src/derive.rs":"1069fe1eecf03f40b9d4a018b1b2e807f7c5302a05fe390cf6718370c2a65268","src/doc/mod.rs":"a8484ddb2414c02597e9666dcd09ee6d8459a7384c1e0e7848d8f2a86d45e105","src/error.rs":"0deb699ed0608b728314b8bb85cfa59249470a89dac1011a5bd2e84d81d46845","src/header.rs":"90c64189f1f3a47590a5e72c04596f7d0e890cd70566dc8f8fff00c2bd168dd2","src/length.rs":"6835e4cd8fd7b94c1166b8b569ca1aba4e2a6fc8f0c4fd6d28267b8b212fde31","src/lib.rs":"aee772089123a78ca21daf15863aa9663497cc06b2737f8aa64eff70db8e8977","src/tag.rs":"712985f35d6324ec1273050af19aea06ea8117498659c2493a575a2685903b28","src/traits.rs":"823a481487ce9a4282469dc57f382c06369703cf2fc6b0413f63339b1d651ca9","tests/ber.rs":"61e1801f7babdd5affdc4385b51ee2344045b15e9fe8bd00abc68615ce4a78f3","tests/compile_tests.rs":"80d5c86332fd6b7c1eb6d58bc24c1719f9519037c1b4b493ac4da6acc4e719f7","tests/cov.rs":"eb2ca229755b934a59a12e2a025b4fc3a514d9c5deb8e377e7a544cf6660f3fd","tests/der.rs":"aed728276d87749430c3b11677f583a7f5b45cf3c9dc66f97cef5328d441a99a","tests/krb5.rs":"f2012a389ca3e3b399167d485d7aee3e8e92fb1e93600502aa0e432174034d81","tests/to_der.rs":"d4a8ef5a4645c14adb88bafdc77e16592972dedb7edf350aa73428bdfff296fd","tests/x509.rs":"dae850913eccaecd874f628e7854940c5387469fe2fb62adb1db81bb312a1d9a"},"package":"7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0"}
\ No newline at end of file diff --git a/rust/vendor/asn1-rs/CHANGELOG.md b/rust/vendor/asn1-rs/CHANGELOG.md new file mode 100644 index 0000000..4846e10 --- /dev/null +++ b/rust/vendor/asn1-rs/CHANGELOG.md @@ -0,0 +1,102 @@ +# Change Log + +## [Unreleased][unreleased] + +### Changed/Fixed + +### Added + +### Thanks + +## 0.5.2 + +### Changed/Fixed + +- Fix decoding of integers: check if value will wrap if integer is signed +- Fix encoding of integers (add 0x00 prefix when required, and remove extra 0xff for negative integers) +- Fix a small math error in GeneralizedTime +- Introduce trait GetObjectContent, use `from_ber` when skipping BER content (closes #14) + +### Thanks + +- Nadja Reitzenstein, Christian Speich + +## 0.5.1 + +Minor fixes: + +- Fix constraints too strict on `TaggedValue::FromDer`, do not auto-derive +- Update oid-registry +- Fix `Any::as_relative_oid` to take a reference (and not consume input) + +derive: + +- Add special case handler for alias to Any +- Add support for DEFAULT attribute + +## 0.5.0 + +This release adds some new methods and custom derive attributes. +It also adds a lot of tests to improve code coverage. + +asn1-rs: + +- Add helper types for Application/Private tagged values +- Any: add methods `from_ber_and_then` (and `_der`) +- TaggedParser: add documentation for `from_ber_and_then` (and `_der`) +- Oid: add method `starts_with` +- Fix documentation of application and private tagged helpers +- Fix clippy warnings + +derive: + +- Add custom derive BerAlias and DerAlias + +coverage: + +- Add many tests to improve coverage + +## 0.4.2 + +Bugfix release: +- Remove explicit output lifetime in traits +- Fix wrong encoding `BmpString` when using `ToDer` +- Fix parsing of some EmbeddedPdv subtypes +- Fix encoded length for Enumerated +- Add missing `DerAutoDerive` impl for bool +- Add missing `DerAutoDerive` impl for f32/f64 +- Remove redundant check, `Any::from_der` checks than length is definite +- Length: fix potential bug when adding Length + Indefinite +- Fix inverted logic in `Header::assert_definite()` + +## 0.4.1 + +Minor fix: +- add missing file in distribution (fix docs.rs build) + +## 0.4.0 + +asn1-rs: + +- Add generic error parameter in traits and in types + - This was added for all types except a few (like `Vec<T>` or `BTreeSet<T>`) due to + Rust compiler limitations +- Add `DerAutoDerive` trait to control manual/automatic implementation of `FromDer` + - This allow controlling automatic trait implementation, and providing manual + implementations of both `FromDer` and `CheckDerConstraints` +- UtcTime: Introduce utc_adjusted_date() to map 2 chars years date to 20/21 centuries date (#9) + +derive: + +- Add attributes to simplify deriving EXPLICIT, IMPLICIT and OPTIONAL +- Add support for different tag classes (like APPLICATION or PRIVATE) +- Add support for custom errors and mapping errors +- Add support for deriving BER/DER SET +- DerDerive: derive both CheckDerConstraints and FromDer + +documentation: + +- Add doc modules for recipes and for custom derive attributes +- Add note on trailing bytes being ignored in sequence +- Improve documentation for notation with braces in TaggedValue +- Improve documentation diff --git a/rust/vendor/asn1-rs/Cargo.lock b/rust/vendor/asn1-rs/Cargo.lock new file mode 100644 index 0000000..65a13f3 --- /dev/null +++ b/rust/vendor/asn1-rs/Cargo.lock @@ -0,0 +1,475 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "asn1-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", +] + +[[package]] +name = "asn1-rs" +version = "0.5.2" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "bitvec", + "colored", + "cookie-factory", + "displaydoc", + "hex-literal", + "nom", + "num-bigint", + "num-traits", + "oid-registry", + "pem", + "rusticata-macros", + "thiserror", + "time", + "trybuild", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "basic-toml" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "cookie-factory" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" + +[[package]] +name = "displaydoc" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs 0.5.1", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", +] + +[[package]] +name = "trybuild" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3115bddce1b5f52dd4b5e0ec8298a66ce733e4cc6759247dc2d1c11508ec38" +dependencies = [ + "basic-toml", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/rust/vendor/asn1-rs/Cargo.toml b/rust/vendor/asn1-rs/Cargo.toml new file mode 100644 index 0000000..57a55b8 --- /dev/null +++ b/rust/vendor/asn1-rs/Cargo.toml @@ -0,0 +1,127 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.53" +name = "asn1-rs" +version = "0.5.2" +authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"] +include = [ + "CHANGELOG.md", + "LICENSE-*", + "README.md", + ".gitignore", + "Cargo.toml", + "doc/*.md", + "examples/*.rs", + "src/*.rs", + "src/asn1_types/*.rs", + "src/asn1_types/real/*.rs", + "src/asn1_types/sequence/*.rs", + "src/asn1_types/set/*.rs", + "src/asn1_types/strings/*.rs", + "src/asn1_types/tagged/*.rs", + "src/ber/*.rs", + "src/doc/*.rs", + "tests/*.rs", +] +description = "Parser/encoder for ASN.1 BER/DER data" +homepage = "https://github.com/rusticata/asn1-rs" +readme = "README.md" +keywords = [ + "BER", + "DER", + "ASN1", + "parser", + "nom", +] +categories = ["parser-implementations"] +license = "MIT/Apache-2.0" +repository = "https://github.com/rusticata/asn1-rs.git" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[dependencies.asn1-rs-derive] +version = "0.4" + +[dependencies.asn1-rs-impl] +version = "0.1" + +[dependencies.bitvec] +version = "1.0" +optional = true + +[dependencies.cookie-factory] +version = "0.3.0" +optional = true + +[dependencies.displaydoc] +version = "0.2.2" + +[dependencies.nom] +version = "7.0" +features = ["std"] +default_features = false + +[dependencies.num-bigint] +version = "0.4" +optional = true + +[dependencies.num-traits] +version = "0.2.14" + +[dependencies.rusticata-macros] +version = "4.0" + +[dependencies.thiserror] +version = "1.0.25" + +[dependencies.time] +version = "0.3" +features = [ + "macros", + "parsing", + "formatting", +] +optional = true + +[dev-dependencies.colored] +version = "2.0" + +[dev-dependencies.hex-literal] +version = "0.3.1" + +[dev-dependencies.oid-registry] +version = "0.6" +features = [ + "crypto", + "x509", +] + +[dev-dependencies.pem] +version = "1.0" + +[dev-dependencies.trybuild] +version = "1.0" + +[features] +bigint = ["num-bigint"] +bits = ["bitvec"] +datetime = ["time"] +default = ["std"] +serialize = ["cookie-factory"] +std = [] diff --git a/rust/vendor/asn1-rs/LICENSE-APACHE b/rust/vendor/asn1-rs/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/rust/vendor/asn1-rs/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/rust/vendor/asn1-rs/LICENSE-MIT b/rust/vendor/asn1-rs/LICENSE-MIT new file mode 100644 index 0000000..290e7b9 --- /dev/null +++ b/rust/vendor/asn1-rs/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 Pierre Chifflier + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/rust/vendor/asn1-rs/README.md b/rust/vendor/asn1-rs/README.md new file mode 100644 index 0000000..80fa4f6 --- /dev/null +++ b/rust/vendor/asn1-rs/README.md @@ -0,0 +1,179 @@ +<!-- cargo-sync-readme start --> + +[](./LICENSE-MIT) +[](./LICENSE-APACHE) +[](https://docs.rs/asn1-rs) +[](https://crates.io/crates/asn1-rs) +[](https://crates.io/crates/asn1-rs) +[](https://github.com/rusticata/asn1-rs/actions) +[](#rust-version-requirements) + +# BER/DER Parsers/Encoders + +A set of parsers/encoders for Basic Encoding Rules (BER [[X.690]]) and Distinguished Encoding Rules(DER +[[X.690]]) formats, implemented with the [nom] parser combinator framework. + +It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken +to ensure security and safety of this crate, including design (recursion limit, defensive +programming), tests, and fuzzing. It also aims to be panic-free. + +This crate is a rewrite of [der-parser](https://crates.io/crates/der-parser) to propose a more data-oriented API, +and add generalized support for serialization. + +Many ideas were borrowed from the [crypto/utils/der](https://github.com/RustCrypto/utils/tree/master/der) crate (like +the `Any`/`TryFrom`/`FromDer` mechanism), adapted and merged into a generalized BER/DER crate. +Credits (and many thanks) go to Tony Arcieri for writing the original crate. + +# BER/DER parsers + +BER stands for Basic Encoding Rules, and is defined in [[X.690]]. It defines a set of rules to +encode and decode ASN.1 [[X.680]] objects in binary. + +[[X.690]] also defines Distinguished Encoding Rules (DER), which is BER with added rules to +ensure canonical and unequivocal binary representation of objects. + +The choice of which one to use is usually guided by the speficication of the data format based +on BER or DER: for example, X.509 uses DER as encoding representation. + +The main traits for parsing are the [`FromBer`] and [`FromDer`] traits. +These traits provide methods to parse binary input, and return either the remaining (unparsed) bytes +and the parsed object, or an error. + +The parsers follow the interface from [nom], and the [`ParseResult`] object is a specialized version +of `nom::IResult`. This means that most `nom` combinators (`map`, `many0`, etc.) can be used in +combination to objects and methods from this crate. Reading the nom documentation may +help understanding how to write and combine parsers and use the output. + +**Minimum Supported Rust Version**: 1.53.0 + +Note: if the `bits` feature is enabled, MSRV is 1.56.0 (due to `bitvec` 1.0) + +# Recipes + +See [doc::recipes] and [doc::derive] for more examples and recipes. + +## Examples + +Parse 2 BER integers: + +```rust +use asn1_rs::{Integer, FromBer}; + +let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x03, 0x01, 0x00, 0x00, +]; + +let (rem, obj1) = Integer::from_ber(&bytes).expect("parsing failed"); +let (rem, obj2) = Integer::from_ber(&bytes).expect("parsing failed"); + +assert_eq!(obj1, Integer::from_u32(65537)); +``` + +In the above example, the generic [`Integer`] type is used. This type can contain integers of any +size, but do not provide a simple API to manipulate the numbers. + +In most cases, the integer either has a limit, or is expected to fit into a primitive type. +To get a simple value, just use the `from_ber`/`from_der` methods on the primitive types: + +```rust +use asn1_rs::FromBer; + +let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x03, 0x01, 0x00, 0x00, +]; + +let (rem, obj1) = u32::from_ber(&bytes).expect("parsing failed"); +let (rem, obj2) = u32::from_ber(&rem).expect("parsing failed"); + +assert_eq!(obj1, 65537); +assert_eq!(obj2, 65536); +``` + +If the parsing succeeds, but the integer cannot fit into the expected type, the method will return +an `IntegerTooLarge` error. + +# BER/DER encoders + +BER/DER encoding is symmetrical to decoding, using the traits `ToBer` and [`ToDer`] traits. +These traits provide methods to write encoded content to objects with the `io::Write` trait, +or return an allocated `Vec<u8>` with the encoded data. +If the serialization fails, an error is returned. + +## Examples + +Writing 2 BER integers: + +```rust +use asn1_rs::{Integer, ToDer}; + +let mut writer = Vec::new(); + +let obj1 = Integer::from_u32(65537); +let obj2 = Integer::from_u32(65536); + +let _ = obj1.write_der(&mut writer).expect("serialization failed"); +let _ = obj2.write_der(&mut writer).expect("serialization failed"); + +let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x03, 0x01, 0x00, 0x00, +]; +assert_eq!(&writer, bytes); +``` + +Similarly to `FromBer`/`FromDer`, serialization methods are also implemented for primitive types: + +```rust +use asn1_rs::ToDer; + +let mut writer = Vec::new(); + +let _ = 65537.write_der(&mut writer).expect("serialization failed"); +let _ = 65536.write_der(&mut writer).expect("serialization failed"); + +let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x03, 0x01, 0x00, 0x00, +]; +assert_eq!(&writer, bytes); +``` + +If the parsing succeeds, but the integer cannot fit into the expected type, the method will return +an `IntegerTooLarge` error. + +## Changes + +See `CHANGELOG.md`. + +# References + +- [[X.680]] Abstract Syntax Notation One (ASN.1): Specification of basic notation. +- [[X.690]] ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical + Encoding Rules (CER) and Distinguished Encoding Rules (DER). + +[X.680]: http://www.itu.int/rec/T-REC-X.680/en "Abstract Syntax Notation One (ASN.1): + Specification of basic notation." +[X.690]: https://www.itu.int/rec/T-REC-X.690/en "ASN.1 encoding rules: Specification of + Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules + (DER)." +[nom]: https://github.com/Geal/nom "Nom parser combinator framework" +<!-- cargo-sync-readme end --> + +## Changes + +See `CHANGELOG.md`, and `UPGRADING.md` for instructions for upgrading major versions. + +## License + +Licensed under either of + + * Apache License, Version 2.0 + ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license + ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +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/rust/vendor/asn1-rs/doc/DERIVE.md b/rust/vendor/asn1-rs/doc/DERIVE.md new file mode 100644 index 0000000..5322140 --- /dev/null +++ b/rust/vendor/asn1-rs/doc/DERIVE.md @@ -0,0 +1,323 @@ +# BER/DER Custom Derive Attributes + +## BER/DER Sequence parsers + +### `BER` + +To derive a BER `SEQUENCE` parser, add the [`BerSequence`] derive attribute to an existing struct. Parsers will be derived automatically for all fields, which must implement the [`FromBer`] trait. + +For ex: + +```rust +# use asn1_rs::*; +#[derive(Debug, PartialEq, BerSequence)] +pub struct S { + a: u32, + b: u16, + c: u16, +} + +# let parser = |input| -> Result<(), Error> { +let (rest, result) = S::from_ber(input)?; +# Ok(()) }; +``` + +After parsing b, any bytes that were leftover and not used to fill val will be returned in `rest`. + +When parsing a `SEQUENCE` into a struct, any trailing elements of the `SEQUENCE` that do +not have matching fields in val will not be included in `rest`, as these are considered +valid elements of the `SEQUENCE` and not trailing data. + +### `DER` + +To derive a `DER` parser, use the [`DerSequence`] custom attribute. + +*Note: the `DerSequence` attributes derive both `BER` and `DER` parsers.* + +## Tagged values + +### `EXPLICIT` + +There are several ways of parsing tagged values: either using types like [`TaggedExplicit`], or using custom annotations. + +Using `TaggedExplicit` works as usual. The only drawback is that the type is visible in the structure, so accessing the value must be done using `.as_ref()` or `.into_inner()`: + +```rust +# use asn1_rs::*; +#[derive(Debug, PartialEq, DerSequence)] +pub struct S2 { + a: u16, +} + +// test with EXPLICIT Vec +#[derive(Debug, PartialEq, DerSequence)] +pub struct S { + // a INTEGER + a: u32, + // b INTEGER + b: u16, + // c [0] EXPLICIT SEQUENCE OF S2 + c: TaggedExplicit<Vec<S2>, Error, 0>, +} + +# let parser = |input| -> Result<(), Error> { +let (rem, result) = S::from_ber(input)?; + +// Get a reference on c (type is &Vec<S2>) +let ref_c = result.c.as_ref(); +# Ok(()) }; +``` + +*Note: tags are context-specific by default. To specify other kind of tags (like `APPLICATION`) use [`TaggedValue`].* + +### `tag_explicit` + +To "hide" the tag from the parser, the `tag_explicit` attribute is provided. This attribute must specify the tag value (as an integer), and will automatically wrap reading the value with the specified tag. + +```rust +# use asn1_rs::*; +#[derive(Debug, PartialEq, DerSequence)] +pub struct S { + // a [0] EXPLICIT INTEGER + #[tag_explicit(0)] + a: u16, +} + +# let parser = |input| -> Result<(), Error> { +let (rem, result) = S::from_ber(input)?; +# Ok(()) }; +``` + +This method handles transparently the encapsulation and the read of the tagged value. + +*Note: tags are context-specific by default. To specify other kind of tags (like `APPLICATION`) add the tag class before the value in the `tag_explicit` attribute.* +For ex: `tag_explicit(APPLICATION 0)` or `tag_explicit(PRIVATE 2)`. + +### Tagged optional values + +The `optional` custom attribute can be used in addition of `tag_explicit` to specify that the value is `OPTIONAL`. + +The type of the annotated field member must be resolvable to `Option`. + +```rust +# use asn1_rs::*; +#[derive(Debug, PartialEq, DerSequence)] +pub struct S { + // a [0] EXPLICIT INTEGER OPTIONAL + #[tag_explicit(0)] + #[optional] + a: Option<u16>, + // b INTEGER + b: u16, +} + +# let parser = |input| -> Result<(), Error> { +let (rem, result) = S::from_ber(input)?; +# Ok(()) }; +``` + +### `IMPLICIT` + +Tagged `IMPLICIT` values are handled similarly as for `EXPLICIT`, and can be parsed either using the [`TaggedImplicit`] type, or using the `tag_implicit` custom attribute. + +For ex: +```rust +# use asn1_rs::*; +#[derive(Debug, PartialEq, DerSequence)] +pub struct S { + // a [0] IMPLICIT INTEGER OPTIONAL + #[tag_implicit(0)] + #[optional] + a: Option<u16>, + // b INTEGER + b: u16, +} + +# let parser = |input| -> Result<(), Error> { +let (rem, result) = S::from_ber(input)?; +# Ok(()) }; +``` + +## `OPTIONAL` values (not tagged) + +The `optional` custom attribute can be specified to indicate the value is `OPTIONAL`. + +```rust +# use asn1_rs::*; +#[derive(Debug, PartialEq, DerSequence)] +pub struct S { + // a INTEGER + a: u16, + // b INTEGER OPTIONAL + #[optional] + b: Option<u16>, +} + +# let parser = |input| -> Result<(), Error> { +let (rem, result) = S::from_ber(input)?; +# Ok(()) }; +``` + +**Important**: there are several limitations to this attribute. + +In particular, the parser is eager: when an `OPTIONAL` value of some type is followed by another value (not `OPTIONAL`) of the same type, this can create problem. +If only one value is present, the parser will affect it to the first field, and then raise an error because the second is absent. + +Note that this does not concern tagged optional values (unless they have the same tag). + +## `DEFAULT` + +The `default` custom attribute can be specified to indicate the value has a `DEFAULT` attribute. The value can also be marked as +`OPTIONAL`, but this can be omitted. + +Since the value can always be obtained, the type should not be `Option<T>`, but should use `T` directly. + +```rust +# use asn1_rs::*; +#[derive(Debug, PartialEq, DerSequence)] +#[debug_derive] +pub struct S { + // a INTEGER + a: u16, + // b INTEGER DEFAULT 0 + #[default(0_u16)] + b: u16, +} + +# let parser = |input| -> Result<(), Error> { +let (rem, result) = S::from_ber(input)?; +# Ok(()) }; +``` + +Limitations are the same as for `OPTIONAL` attribute. + +## Debugging + +To help debugging the generated code, the `#[debug_derive]` attribute has been added. + +When this attribute is specified, the generated code will be printed to `stderr` during compilation. + +Example: +```rust +use asn1_rs::*; + +#[derive(BerSequence)] +#[debug_derive] +struct S { + a: u32, +} +``` + +## BER/DER Set parsers + +Parsing BER/DER `SET` objects is very similar to `SEQUENCE`. Use the [`BerSet`] and [`DerSet`] custom derive attributes on the structure, and everything else is exactly the same as for sequences (see above for documentation). + +Example: +```rust +# use asn1_rs::*; +use std::collections::BTreeSet; + +// `Ord` is needed because we will parse as a `BTreeSet` later +#[derive(Debug, DerSet, PartialEq, Eq, PartialOrd, Ord)] +pub struct S2 { + a: u16, +} + +// test with EXPLICIT Vec +#[derive(Debug, PartialEq, DerSet)] +pub struct S { + // a INTEGER + a: u32, + // b INTEGER + b: u16, + // c [0] EXPLICIT SET OF S2 + c: TaggedExplicit<BTreeSet<S2>, Error, 0>, +} + +# let parser = |input| -> Result<(), Error> { +let (rem, result) = S::from_ber(input)?; + +// Get a reference on c (type is &BTreeSet<S2>) +let ref_c = result.c.as_ref(); +# Ok(()) }; +``` + +# Advanced + +## Custom errors + +Derived parsers can use the `error` attribute to specify the error type of the parser. + +The custom error type must implement `From<Error>`, so the derived parsers will transparently convert errors using the [`Into`] trait. + + +Example: +```rust +# use asn1_rs::*; +# +#[derive(Debug, PartialEq)] +pub enum MyError { + NotYetImplemented, +} + +impl From<asn1_rs::Error> for MyError { + fn from(_: asn1_rs::Error) -> Self { + MyError::NotYetImplemented + } +} + +#[derive(DerSequence)] +#[error(MyError)] +pub struct T2 { + pub a: u32, +} +``` + +## Mapping errors + +Sometimes, it is necessary to map the returned error to another type, for example when a subparser returns a different error type than the parser's, and the [`Into`] trait cannot be implemented. This is often used in combination with the `error` attribute, but can also be used alone. + +The `map_err` attribute can be used to specify a function or closure to map errors. The function signature is `fn (e1: E1) -> E2`. + +Example: +```rust +# use asn1_rs::*; +# +#[derive(Debug, PartialEq)] +pub enum MyError { + NotYetImplemented, +} + +impl From<asn1_rs::Error> for MyError { + fn from(_: asn1_rs::Error) -> Self { + MyError::NotYetImplemented + } +} + +#[derive(DerSequence)] +#[error(MyError)] +pub struct T2 { + pub a: u32, +} + +// subparser returns an error of type MyError, +// which is mapped to `Error` +#[derive(DerSequence)] +pub struct T4 { + #[map_err(|_| Error::BerTypeError)] + pub a: T2, +} +``` + +*Note*: when deriving BER and DER parsers, errors paths are different (`TryFrom` returns the error type, while [`FromDer`] returns a [`ParseResult`]). Some code will be inserted by the `map_err` attribute to handle this transparently and keep the same function signature. + +[`FromBer`]: crate::FromBer +[`FromDer`]: crate::FromDer +[`BerSequence`]: crate::BerSequence +[`DerSequence`]: crate::DerSequence +[`BerSet`]: crate::BerSet +[`DerSet`]: crate::DerSet +[`ParseResult`]: crate::ParseResult +[`TaggedExplicit`]: crate::TaggedExplicit +[`TaggedImplicit`]: crate::TaggedImplicit +[`TaggedValue`]: crate::TaggedValue diff --git a/rust/vendor/asn1-rs/doc/RECIPES.md b/rust/vendor/asn1-rs/doc/RECIPES.md new file mode 100644 index 0000000..b053c6d --- /dev/null +++ b/rust/vendor/asn1-rs/doc/RECIPES.md @@ -0,0 +1,238 @@ +# Documentation: BER/DER parsing recipes + +## Builtin types + +Most builtin types can be parsed by calling the `from_der` or `from_der` functions (see `FromBer` and `FromDer` traits for documentation). + +For ex: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = <u32>::from_der(input)?; +# Ok(()) }; +``` + +Note: this crates makes extensive use of types annotation and turbofish operator, for example `<Type>::from_der()` or `TaggedExplicit::<u32, Error, 0>::from_der()`. + +See table B-3 in <https://doc.rust-lang.org/book/appendix-02-operators.html> for reference on syntax. + +## `SEQUENCE` and `SET` + +The `SEQUENCE` and `SET` types are handled very similarly, so recipes will be given for `SEQUENCE`, but can be adapted to `SET` by replacing words. + +### Parsing `SEQUENCE` + +Usually, the sequence envelope does not need to be stored, so it just needs to be parsed to get the sequence content and parse it. +The methods [`from_ber_and_then`](crate::Sequence::from_ber_and_then()) and [`from_der_and_then`](crate::Sequence::from_der_and_then()) provide helpers for that: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = Sequence::from_ber_and_then(input, |i| { + // first item is INTEGER + let (rem, a) = u32::from_der(input)?; + // second item is OCTET STRING + let (rem, b) = <&[u8]>::from_der(input)?; + Ok((rem, (a, b))) +})?; +// result has type (u32, &[u8]) +assert_eq!(result.0, 0); +assert_eq!(result.1, b"\x00\x01"); +# Ok(()) }; +``` + +### Automatically deriving sequence parsers + +The [`BerSequence`](crate::BerSequence) and [`DerSequence`](crate::DerSequence) +custom derive provide attributes to automatically derive a parser for a sequence. + +For ex: + +```rust +# use asn1_rs::*; +#[derive(DerSequence)] +pub struct S { + a: u32, + b: u16, + c: u16, +} + +# let parser = |input| -> Result<(), Error> { +let (rem, result) = S::from_der(input)?; +# Ok(()) }; +``` + +This will work for any field type that implements [`FromBer`](crate::FromBer) or [`FromDer`](crate::FromDer), respectively. + +See [`derive`](mod@derive) documentation for more examples and documentation. + +### Parsing `SEQUENCE OF` + +`SEQUENCE OF T` can be parsed using either type `SequenceOf<T>` or `Vec<T>`: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = SequenceOf::<u32>::from_der(input)?; +# Ok(()) }; +``` + +or + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = <Vec<u32>>::from_der(input)?; +# Ok(()) }; +``` + +`SET OF T` can be parsed using either `SetOf<T>`, `BTreeSet<T>` or `HashSet<T>`. + +## `EXPLICIT` tagged values + +### Parsing `EXPLICIT`, expecting a known tag + +If you expect only a specific tag, use `TaggedExplicit`. + +For ex, to parse a `[3] EXPLICIT INTEGER`: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = TaggedExplicit::<u32, Error, 0>::from_der(input)?; +// result has type TaggedValue. Use `.as_ref()` or `.into_inner()` +// to access content +let tag = result.tag(); +let class = result.class(); +assert_eq!(result.as_ref(), &0); +# Ok(()) }; +``` + +### Specifying the class + +`TaggedExplicit` does not check the class, and accepts any class. It expects you to check the class after reading the value. + + +To specify the class in the parser, use `TaggedValue`: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +// Note: the strange notation (using braces) is required by the compiler to use +// a constant instead of the numeric value. +let (rem, result) = TaggedValue::<u32, Error, Explicit, {Class::CONTEXT_SPECIFIC}, 0>::from_der(input)?; +# Ok(()) }; +``` + +Note that `TaggedExplicit` is a type alias to `TaggedValue`, so the objects are the same. + +### Accepting any `EXPLICIT` tag + +To parse a value, accepting any class or tag, use `TaggedParser`. + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = TaggedParser::<Explicit, u32>::from_der(input)?; +// result has type TaggedParser. Use `.as_ref()` or `.into_inner()` +// to access content +let tag = result.tag(); +let class = result.class(); +assert_eq!(result.as_ref(), &0); +# Ok(()) }; +``` + +### Optional tagged values + +To parse optional tagged values, `Option<TaggedExplicit<...>>` can be used: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = Option::<TaggedExplicit::<u32, Error, 0>>::from_der(input)?; +# Ok(()) }; +``` + +The type `OptTaggedExplicit` is also provided as an alias: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = OptTaggedExplicit::<u32, Error, 0>::from_der(input)?; +# Ok(()) }; +``` + +## `IMPLICIT` tagged values + +### Parsing `IMPLICIT`, expecting a known tag + +If you expect only a specific tag, use `TaggedImplicit`. + +For ex, to parse a `[3] EXPLICIT INTEGER`: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = TaggedExplicit::<u32, Error, 0>::from_der(input)?; +// result has type TaggedValue. Use `.as_ref()` or `.into_inner()` +// to access content +let tag = result.tag(); +let class = result.class(); +assert_eq!(result.as_ref(), &0); +# Ok(()) }; +``` + +### Specifying the class + +`TaggedImplicit` does not check the class, and accepts any class. It expects you to check the class after reading the value. + + +To specify the class in the parser, use `TaggedValue`: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +// Note: the strange notation (using braces) is required by the compiler to use +// a constant instead of the numeric value. +let (rem, result) = TaggedValue::<u32, Error, Implicit, { Class::CONTEXT_SPECIFIC }, 1>::from_der(input)?; +# Ok(()) }; +``` + +Note that `TaggedImplicit` is a type alias to `TaggedValue`, so the objects are the same. + +### Accepting any `IMPLICIT` tag + +To parse a value, accepting any class or tag, use `TaggedParser`. + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = TaggedParser::<Implicit, u32>::from_der(input)?; +// result has type TaggedParser. Use `.as_ref()` or `.into_inner()` +// to access content +let tag = result.tag(); +let class = result.class(); +assert_eq!(result.as_ref(), &0); +# Ok(()) }; +``` + +### Optional tagged values + +To parse optional tagged values, `Option<TaggedImplicit<...>>` can be used: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = Option::<TaggedImplicit::<u32, Error, 0>>::from_der(input)?; +# Ok(()) }; +``` + +The type `OptTaggedImplicit` is also provided as an alias: + +```rust +# use asn1_rs::*; +# let parser = |input| -> Result<(), Error> { +let (rem, result) = OptTaggedImplicit::<u32, Error, 0>::from_der(input)?; +# Ok(()) }; +``` diff --git a/rust/vendor/asn1-rs/examples/dump-der.rs b/rust/vendor/asn1-rs/examples/dump-der.rs new file mode 100644 index 0000000..29ee897 --- /dev/null +++ b/rust/vendor/asn1-rs/examples/dump-der.rs @@ -0,0 +1,243 @@ +use asn1_rs::{Any, Class, FromDer, Length, Result, Tag}; +use colored::*; +use nom::HexDisplay; +use oid_registry::{format_oid, Oid as DerOid, OidRegistry}; +use std::cmp::min; +use std::error::Error; +use std::marker::PhantomData; +use std::{env, fs}; + +struct Context<'a> { + oid_registry: OidRegistry<'a>, + hex_max: usize, + t: PhantomData<&'a ()>, +} + +impl<'a> Default for Context<'a> { + fn default() -> Self { + let oid_registry = OidRegistry::default().with_all_crypto().with_x509(); + Context { + oid_registry, + hex_max: 64, + t: PhantomData, + } + } +} + +#[macro_export] +macro_rules! indent_println { + ( $depth: expr, $fmt:expr ) => { + println!(concat!("{:indent$}",$fmt), "", indent = 2*$depth) + }; + ( $depth: expr, $fmt:expr, $( $x:expr ),* ) => { + println!(concat!("{:indent$}",$fmt), "", $($x),*, indent = 2*$depth) + }; +} + +#[allow(dead_code)] +pub fn print_hex_dump(bytes: &[u8], max_len: usize) { + let m = min(bytes.len(), max_len); + print!("{}", &bytes[..m].to_hex(16)); + if bytes.len() > max_len { + println!("... <continued>"); + } +} + +fn main() -> std::result::Result<(), Box<dyn Error>> { + let ctx = Context::default(); + for filename in env::args().skip(1) { + eprintln!("File: {}", filename); + let content = fs::read(&filename)?; + // check for PEM file + if filename.ends_with(".pem") || content.starts_with(b"----") { + let pems = pem::parse_many(&content).expect("Parsing PEM failed"); + if pems.is_empty() { + eprintln!("{}", "No PEM section decoded".bright_red()); + continue; + } + for (idx, pem) in pems.iter().enumerate() { + eprintln!("Pem entry {} [{}]", idx, pem.tag.bright_blue()); + print_der(&pem.contents, 1, &ctx); + } + } else { + print_der(&content, 1, &ctx); + } + } + + Ok(()) +} + +fn print_der(i: &[u8], depth: usize, ctx: &Context) { + match Any::from_der(i) { + Ok((rem, any)) => { + print_der_any(any, depth, ctx); + if !rem.is_empty() { + let warning = format!("WARNING: {} extra bytes after object", rem.len()); + indent_println!(depth, "{}", warning.bright_red()); + print_hex_dump(rem, ctx.hex_max); + } + } + Err(e) => { + eprintln!("Error while parsing at depth {}: {:?}", depth, e); + } + } +} + +fn print_der_result_any(r: Result<Any>, depth: usize, ctx: &Context) { + match r { + Ok(any) => print_der_any(any, depth, ctx), + Err(e) => { + eprintln!("Error while parsing at depth {}: {:?}", depth, e); + } + } +} + +fn print_der_any(any: Any, depth: usize, ctx: &Context) { + let class = match any.header.class() { + Class::Universal => "UNIVERSAL".to_string().white(), + c => c.to_string().cyan(), + }; + let hdr = format!( + "[c:{} t:{}({}) l:{}]", + class, + any.header.tag().0, + any.header.tag().to_string().white(), + str_of_length(any.header.length()) + ); + indent_println!(depth, "{}", hdr); + match any.header.class() { + Class::Universal => (), + Class::ContextSpecific | Class::Application => { + // attempt to decode inner object (if EXPLICIT) + match Any::from_der(any.data) { + Ok((rem2, inner)) => { + indent_println!( + depth + 1, + "{} (rem.len={})", + format!("EXPLICIT [{}]", any.header.tag().0).green(), + // any.header.tag.0, + rem2.len() + ); + print_der_any(inner, depth + 2, ctx); + } + Err(_) => { + // assume tagged IMPLICIT + indent_println!( + depth + 1, + "{}", + "could not decode (IMPLICIT tagging?)".bright_red() + ); + } + } + return; + } + _ => { + indent_println!( + depth + 1, + "tagged: [{}] {}", + any.header.tag().0, + "*NOT SUPPORTED*".red() + ); + return; + } + } + match any.header.tag() { + Tag::BitString => { + let b = any.bitstring().unwrap(); + indent_println!(depth + 1, "BITSTRING"); + print_hex_dump(b.as_ref(), ctx.hex_max); + } + Tag::Boolean => { + let b = any.bool().unwrap(); + indent_println!(depth + 1, "BOOLEAN: {}", b.to_string().green()); + } + Tag::EmbeddedPdv => { + let e = any.embedded_pdv().unwrap(); + indent_println!(depth + 1, "EMBEDDED PDV: {:?}", e); + print_hex_dump(e.data_value, ctx.hex_max); + } + Tag::Enumerated => { + let i = any.enumerated().unwrap(); + indent_println!(depth + 1, "ENUMERATED: {}", i.0); + } + Tag::GeneralizedTime => { + let s = any.generalizedtime().unwrap(); + indent_println!(depth + 1, "GeneralizedTime: {}", s); + } + Tag::GeneralString => { + let s = any.generalstring().unwrap(); + indent_println!(depth + 1, "GeneralString: {}", s.as_ref()); + } + Tag::Ia5String => { + let s = any.ia5string().unwrap(); + indent_println!(depth + 1, "IA5String: {}", s.as_ref()); + } + Tag::Integer => { + let i = any.integer().unwrap(); + match i.as_i128() { + Ok(i) => { + indent_println!(depth + 1, "{}", i); + } + Err(_) => { + print_hex_dump(i.as_ref(), ctx.hex_max); + } + } + } + Tag::Null => (), + Tag::OctetString => { + let b = any.octetstring().unwrap(); + indent_println!(depth + 1, "OCTETSTRING"); + print_hex_dump(b.as_ref(), ctx.hex_max); + } + Tag::Oid => { + let oid = any.oid().unwrap(); + let der_oid = DerOid::new(oid.as_bytes().into()); + indent_println!( + depth + 1, + "OID: {}", + format_oid(&der_oid, &ctx.oid_registry).cyan() + ); + } + Tag::PrintableString => { + let s = any.printablestring().unwrap(); + indent_println!(depth + 1, "PrintableString: {}", s.as_ref()); + } + Tag::RelativeOid => { + let oid = any.oid().unwrap(); + let der_oid = DerOid::new(oid.as_bytes().into()); + indent_println!( + depth + 1, + "RELATIVE-OID: {}", + format_oid(&der_oid, &ctx.oid_registry).cyan() + ); + } + Tag::Set => { + let seq = any.set().unwrap(); + for item in seq.der_iter::<Any, asn1_rs::Error>() { + print_der_result_any(item, depth + 1, ctx); + } + } + Tag::Sequence => { + let seq = any.sequence().unwrap(); + for item in seq.der_iter::<Any, asn1_rs::Error>() { + print_der_result_any(item, depth + 1, ctx); + } + } + Tag::UtcTime => { + let s = any.utctime().unwrap(); + indent_println!(depth + 1, "UtcTime: {}", s); + } + Tag::Utf8String => { + let s = any.utf8string().unwrap(); + indent_println!(depth + 1, "UTF-8: {}", s.as_ref()); + } + _ => unimplemented!("unsupported tag {}", any.header.tag()), + } +} + +fn str_of_length(l: Length) -> String { + match l { + Length::Definite(l) => l.to_string(), + Length::Indefinite => "Indefinite".to_string(), + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/any.rs b/rust/vendor/asn1-rs/src/asn1_types/any.rs new file mode 100644 index 0000000..8628d7d --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/any.rs @@ -0,0 +1,429 @@ +use crate::ber::*; +use crate::*; +use alloc::borrow::Cow; +use alloc::string::String; +use core::convert::{TryFrom, TryInto}; + +/// The `Any` object is not strictly an ASN.1 type, but holds a generic description of any object +/// that could be encoded. +/// +/// It contains a header, and either a reference to or owned data for the object content. +/// +/// Note: this type is only provided in **borrowed** version (*i.e.* it cannot own the inner data). +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Any<'a> { + /// The object header + pub header: Header<'a>, + /// The object contents + pub data: &'a [u8], +} + +impl<'a> Any<'a> { + /// Create a new `Any` from BER/DER header and content + #[inline] + pub const fn new(header: Header<'a>, data: &'a [u8]) -> Self { + Any { header, data } + } + + /// Create a new `Any` from a tag, and BER/DER content + #[inline] + pub const fn from_tag_and_data(tag: Tag, data: &'a [u8]) -> Self { + let constructed = matches!(tag, Tag::Sequence | Tag::Set); + Any { + header: Header { + tag, + constructed, + class: Class::Universal, + length: Length::Definite(data.len()), + raw_tag: None, + }, + data, + } + } + + /// Return the `Class` of this object + #[inline] + pub const fn class(&self) -> Class { + self.header.class + } + + /// Update the class of the current object + #[inline] + pub fn with_class(self, class: Class) -> Self { + Any { + header: self.header.with_class(class), + ..self + } + } + + /// Return the `Tag` of this object + #[inline] + pub const fn tag(&self) -> Tag { + self.header.tag + } + + /// Update the tag of the current object + #[inline] + pub fn with_tag(self, tag: Tag) -> Self { + Any { + header: self.header.with_tag(tag), + data: self.data, + } + } + + /// Get the bytes representation of the *content* + #[inline] + pub fn as_bytes(&'a self) -> &'a [u8] { + self.data + } + + #[inline] + pub fn parse_ber<T>(&'a self) -> ParseResult<'a, T> + where + T: FromBer<'a>, + { + T::from_ber(self.data) + } + + /// Parse a BER value and apply the provided parsing function to content + /// + /// After parsing, the sequence object and header are discarded. + pub fn from_ber_and_then<F, T, E>( + class: Class, + tag: u32, + bytes: &'a [u8], + op: F, + ) -> ParseResult<'a, T, E> + where + F: FnOnce(&'a [u8]) -> ParseResult<T, E>, + E: From<Error>, + { + let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?; + any.tag() + .assert_eq(Tag(tag)) + .map_err(|e| nom::Err::Error(e.into()))?; + any.class() + .assert_eq(class) + .map_err(|e| nom::Err::Error(e.into()))?; + let (_, res) = op(any.data)?; + Ok((rem, res)) + } + + /// Parse a DER value and apply the provided parsing function to content + /// + /// After parsing, the sequence object and header are discarded. + pub fn from_der_and_then<F, T, E>( + class: Class, + tag: u32, + bytes: &'a [u8], + op: F, + ) -> ParseResult<'a, T, E> + where + F: FnOnce(&'a [u8]) -> ParseResult<T, E>, + E: From<Error>, + { + let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; + any.tag() + .assert_eq(Tag(tag)) + .map_err(|e| nom::Err::Error(e.into()))?; + any.class() + .assert_eq(class) + .map_err(|e| nom::Err::Error(e.into()))?; + let (_, res) = op(any.data)?; + Ok((rem, res)) + } + + #[inline] + pub fn parse_der<T>(&'a self) -> ParseResult<'a, T> + where + T: FromDer<'a>, + { + T::from_der(self.data) + } + + /// Get the content following a BER header + #[inline] + pub fn parse_ber_content<'i>(i: &'i [u8], header: &'_ Header) -> ParseResult<'i, &'i [u8]> { + header.parse_ber_content(i) + } + + /// Get the content following a DER header + #[inline] + pub fn parse_der_content<'i>(i: &'i [u8], header: &'_ Header) -> ParseResult<'i, &'i [u8]> { + header.assert_definite()?; + DerParser::get_object_content(i, header, 8) + } +} + +macro_rules! impl_any_into { + (IMPL $sname:expr, $fn_name:ident => $ty:ty, $asn1:expr) => { + #[doc = "Attempt to convert object to `"] + #[doc = $sname] + #[doc = "` (ASN.1 type: `"] + #[doc = $asn1] + #[doc = "`)."] + pub fn $fn_name(self) -> Result<$ty> { + self.try_into() + } + }; + ($fn_name:ident => $ty:ty, $asn1:expr) => { + impl_any_into! { + IMPL stringify!($ty), $fn_name => $ty, $asn1 + } + }; +} + +macro_rules! impl_any_as { + (IMPL $sname:expr, $fn_name:ident => $ty:ty, $asn1:expr) => { + #[doc = "Attempt to create ASN.1 type `"] + #[doc = $asn1] + #[doc = "` from this object."] + #[inline] + pub fn $fn_name(&self) -> Result<$ty> { + TryFrom::try_from(self) + } + }; + ($fn_name:ident => $ty:ty, $asn1:expr) => { + impl_any_as! { + IMPL stringify!($ty), $fn_name => $ty, $asn1 + } + }; +} + +impl<'a> Any<'a> { + impl_any_into!(bitstring => BitString<'a>, "BIT STRING"); + impl_any_into!(bmpstring => BmpString<'a>, "BmpString"); + impl_any_into!(bool => bool, "BOOLEAN"); + impl_any_into!(boolean => Boolean, "BOOLEAN"); + impl_any_into!(embedded_pdv => EmbeddedPdv<'a>, "EMBEDDED PDV"); + impl_any_into!(enumerated => Enumerated, "ENUMERATED"); + impl_any_into!(generalizedtime => GeneralizedTime, "GeneralizedTime"); + impl_any_into!(generalstring => GeneralString<'a>, "GeneralString"); + impl_any_into!(graphicstring => GraphicString<'a>, "GraphicString"); + impl_any_into!(i8 => i8, "INTEGER"); + impl_any_into!(i16 => i16, "INTEGER"); + impl_any_into!(i32 => i32, "INTEGER"); + impl_any_into!(i64 => i64, "INTEGER"); + impl_any_into!(i128 => i128, "INTEGER"); + impl_any_into!(ia5string => Ia5String<'a>, "IA5String"); + impl_any_into!(integer => Integer<'a>, "INTEGER"); + impl_any_into!(null => Null, "NULL"); + impl_any_into!(numericstring => NumericString<'a>, "NumericString"); + impl_any_into!(objectdescriptor => ObjectDescriptor<'a>, "ObjectDescriptor"); + impl_any_into!(octetstring => OctetString<'a>, "OCTET STRING"); + impl_any_into!(oid => Oid<'a>, "OBJECT IDENTIFIER"); + /// Attempt to convert object to `Oid` (ASN.1 type: `RELATIVE-OID`). + pub fn relative_oid(self) -> Result<Oid<'a>> { + self.header.assert_tag(Tag::RelativeOid)?; + let asn1 = Cow::Borrowed(self.data); + Ok(Oid::new_relative(asn1)) + } + impl_any_into!(printablestring => PrintableString<'a>, "PrintableString"); + // XXX REAL + impl_any_into!(sequence => Sequence<'a>, "SEQUENCE"); + impl_any_into!(set => Set<'a>, "SET"); + impl_any_into!(str => &'a str, "UTF8String"); + impl_any_into!(string => String, "UTF8String"); + impl_any_into!(teletexstring => TeletexString<'a>, "TeletexString"); + impl_any_into!(u8 => u8, "INTEGER"); + impl_any_into!(u16 => u16, "INTEGER"); + impl_any_into!(u32 => u32, "INTEGER"); + impl_any_into!(u64 => u64, "INTEGER"); + impl_any_into!(u128 => u128, "INTEGER"); + impl_any_into!(universalstring => UniversalString<'a>, "UniversalString"); + impl_any_into!(utctime => UtcTime, "UTCTime"); + impl_any_into!(utf8string => Utf8String<'a>, "UTF8String"); + impl_any_into!(videotexstring => VideotexString<'a>, "VideotexString"); + impl_any_into!(visiblestring => VisibleString<'a>, "VisibleString"); + + impl_any_as!(as_bitstring => BitString, "BITSTRING"); + impl_any_as!(as_bool => bool, "BOOLEAN"); + impl_any_as!(as_boolean => Boolean, "BOOLEAN"); + impl_any_as!(as_embedded_pdv => EmbeddedPdv, "EMBEDDED PDV"); + impl_any_as!(as_endofcontent => EndOfContent, "END OF CONTENT (not a real ASN.1 type)"); + impl_any_as!(as_enumerated => Enumerated, "ENUMERATED"); + impl_any_as!(as_generalizedtime => GeneralizedTime, "GeneralizedTime"); + impl_any_as!(as_generalstring => GeneralizedTime, "GeneralString"); + impl_any_as!(as_graphicstring => GraphicString, "GraphicString"); + impl_any_as!(as_i8 => i8, "INTEGER"); + impl_any_as!(as_i16 => i16, "INTEGER"); + impl_any_as!(as_i32 => i32, "INTEGER"); + impl_any_as!(as_i64 => i64, "INTEGER"); + impl_any_as!(as_i128 => i128, "INTEGER"); + impl_any_as!(as_ia5string => Ia5String, "IA5String"); + impl_any_as!(as_integer => Integer, "INTEGER"); + impl_any_as!(as_null => Null, "NULL"); + impl_any_as!(as_numericstring => NumericString, "NumericString"); + impl_any_as!(as_objectdescriptor => ObjectDescriptor, "OBJECT IDENTIFIER"); + impl_any_as!(as_octetstring => OctetString, "OCTET STRING"); + impl_any_as!(as_oid => Oid, "OBJECT IDENTIFIER"); + /// Attempt to create ASN.1 type `RELATIVE-OID` from this object. + pub fn as_relative_oid(&self) -> Result<Oid<'a>> { + self.header.assert_tag(Tag::RelativeOid)?; + let asn1 = Cow::Borrowed(self.data); + Ok(Oid::new_relative(asn1)) + } + impl_any_as!(as_printablestring => PrintableString, "PrintableString"); + impl_any_as!(as_sequence => Sequence, "SEQUENCE"); + impl_any_as!(as_set => Set, "SET"); + impl_any_as!(as_str => &str, "UTF8String"); + impl_any_as!(as_string => String, "UTF8String"); + impl_any_as!(as_teletexstring => TeletexString, "TeletexString"); + impl_any_as!(as_u8 => u8, "INTEGER"); + impl_any_as!(as_u16 => u16, "INTEGER"); + impl_any_as!(as_u32 => u32, "INTEGER"); + impl_any_as!(as_u64 => u64, "INTEGER"); + impl_any_as!(as_u128 => u128, "INTEGER"); + impl_any_as!(as_universalstring => UniversalString, "UniversalString"); + impl_any_as!(as_utctime => UtcTime, "UTCTime"); + impl_any_as!(as_utf8string => Utf8String, "UTF8String"); + impl_any_as!(as_videotexstring => VideotexString, "VideotexString"); + impl_any_as!(as_visiblestring => VisibleString, "VisibleString"); + + /// Attempt to create an `Option<T>` from this object. + pub fn as_optional<'b, T>(&'b self) -> Result<Option<T>> + where + T: TryFrom<&'b Any<'a>, Error = Error>, + 'a: 'b, + { + match TryFrom::try_from(self) { + Ok(t) => Ok(Some(t)), + Err(Error::UnexpectedTag { .. }) => Ok(None), + Err(e) => Err(e), + } + } + + /// Attempt to create a tagged value (EXPLICIT) from this object. + pub fn as_tagged_explicit<T, E, const CLASS: u8, const TAG: u32>( + &self, + ) -> Result<TaggedValue<T, E, Explicit, CLASS, TAG>, E> + where + T: FromBer<'a, E>, + E: From<Error>, + { + TryFrom::try_from(self) + } + + /// Attempt to create a tagged value (IMPLICIT) from this object. + pub fn as_tagged_implicit<T, E, const CLASS: u8, const TAG: u32>( + &self, + ) -> Result<TaggedValue<T, E, Implicit, CLASS, TAG>, E> + where + T: TryFrom<Any<'a>, Error = E>, + T: Tagged, + E: From<Error>, + { + TryFrom::try_from(self) + } +} + +impl<'a> FromBer<'a> for Any<'a> { + fn from_ber(bytes: &'a [u8]) -> ParseResult<Self> { + let (i, header) = Header::from_ber(bytes)?; + let (i, data) = BerParser::get_object_content(i, &header, MAX_RECURSION)?; + Ok((i, Any { header, data })) + } +} + +impl<'a> FromDer<'a> for Any<'a> { + fn from_der(bytes: &'a [u8]) -> ParseResult<Self> { + let (i, header) = Header::from_der(bytes)?; + // X.690 section 10.1: The definite form of length encoding shall be used + header.length.assert_definite()?; + let (i, data) = DerParser::get_object_content(i, &header, MAX_RECURSION)?; + Ok((i, Any { header, data })) + } +} + +impl CheckDerConstraints for Any<'_> { + fn check_constraints(any: &Any) -> Result<()> { + any.header.length().assert_definite()?; + // if len < 128, must use short form (10.1: minimum number of octets) + Ok(()) + } +} + +impl DerAutoDerive for Any<'_> {} + +impl DynTagged for Any<'_> { + fn tag(&self) -> Tag { + self.tag() + } +} + +// impl<'a> ToStatic for Any<'a> { +// type Owned = Any<'static>; + +// fn to_static(&self) -> Self::Owned { +// Any { +// header: self.header.to_static(), +// data: Cow::Owned(self.data.to_vec()), +// } +// } +// } + +#[cfg(feature = "std")] +impl ToDer for Any<'_> { + fn to_der_len(&self) -> Result<usize> { + let hdr_len = self.header.to_der_len()?; + Ok(hdr_len + self.data.len()) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + // create fake header to have correct length + let header = Header::new( + self.header.class, + self.header.constructed, + self.header.tag, + Length::Definite(self.data.len()), + ); + let sz = header.write_der_header(writer)?; + Ok(sz) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(self.data).map_err(Into::into) + } + + /// Similar to using `to_der`, but uses header without computing length value + fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let sz = self.header.write_der_header(writer)?; + let sz = sz + writer.write(self.data)?; + Ok(sz) + } +} + +#[cfg(test)] +mod tests { + use crate::*; + use hex_literal::hex; + + #[test] + fn methods_any() { + let header = Header::new_simple(Tag::Integer); + let any = Any::new(header, &[]) + .with_class(Class::ContextSpecific) + .with_tag(Tag(0)); + assert_eq!(any.as_bytes(), &[]); + + let input = &hex! {"80 03 02 01 01"}; + let (_, any) = Any::from_ber(input).expect("parsing failed"); + + let (_, r) = any.parse_ber::<Integer>().expect("parse_ber failed"); + assert_eq!(r.as_u32(), Ok(1)); + let (_, r) = any.parse_der::<Integer>().expect("parse_der failed"); + assert_eq!(r.as_u32(), Ok(1)); + + let header = &any.header; + let (_, content) = Any::parse_ber_content(&input[2..], header).unwrap(); + assert_eq!(content.len(), 3); + let (_, content) = Any::parse_der_content(&input[2..], header).unwrap(); + assert_eq!(content.len(), 3); + + let (_, any) = Any::from_der(&input[2..]).unwrap(); + Any::check_constraints(&any).unwrap(); + assert_eq!(<Any as DynTagged>::tag(&any), any.tag()); + let int = any.integer().unwrap(); + assert_eq!(int.as_u16(), Ok(1)); + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/bitstring.rs b/rust/vendor/asn1-rs/src/asn1_types/bitstring.rs new file mode 100644 index 0000000..fad03cf --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/bitstring.rs @@ -0,0 +1,157 @@ +use crate::*; +use alloc::borrow::Cow; +#[cfg(feature = "bits")] +use bitvec::{order::Msb0, slice::BitSlice}; +use core::convert::TryFrom; + +/// ASN.1 `BITSTRING` type +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BitString<'a> { + pub unused_bits: u8, + pub data: Cow<'a, [u8]>, +} + +impl<'a> BitString<'a> { + // Length must be >= 1 (first byte is number of ignored bits) + pub const fn new(unused_bits: u8, s: &'a [u8]) -> Self { + BitString { + unused_bits, + data: Cow::Borrowed(s), + } + } + + /// Test if bit `bitnum` is set + pub fn is_set(&self, bitnum: usize) -> bool { + let byte_pos = bitnum / 8; + if byte_pos >= self.data.len() { + return false; + } + let b = 7 - (bitnum % 8); + (self.data[byte_pos] & (1 << b)) != 0 + } + + /// Constructs a shared `&BitSlice` reference over the object data. + #[cfg(feature = "bits")] + pub fn as_bitslice(&self) -> Option<&BitSlice<u8, Msb0>> { + BitSlice::<_, Msb0>::try_from_slice(&self.data).ok() + } +} + +impl<'a> AsRef<[u8]> for BitString<'a> { + fn as_ref(&self) -> &[u8] { + &self.data + } +} + +impl<'a> TryFrom<Any<'a>> for BitString<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<BitString<'a>> { + TryFrom::try_from(&any) + } +} + +// non-consuming version +impl<'a, 'b> TryFrom<&'b Any<'a>> for BitString<'a> { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<BitString<'a>> { + any.tag().assert_eq(Self::TAG)?; + if any.data.is_empty() { + return Err(Error::InvalidLength); + } + let s = any.data; + let (unused_bits, data) = (s[0], Cow::Borrowed(&s[1..])); + Ok(BitString { unused_bits, data }) + } +} + +impl<'a> CheckDerConstraints for BitString<'a> { + fn check_constraints(any: &Any) -> Result<()> { + // X.690 section 10.2 + any.header.assert_primitive()?; + // Check that padding bits are all 0 (X.690 section 11.2.1) + match any.data.len() { + 0 => Err(Error::InvalidLength), + 1 => { + // X.690 section 11.2.2 Note 2 + if any.data[0] == 0 { + Ok(()) + } else { + Err(Error::InvalidLength) + } + } + len => { + let unused_bits = any.data[0]; + let last_byte = any.data[len - 1]; + if last_byte.trailing_zeros() < unused_bits as u32 { + return Err(Error::DerConstraintFailed(DerConstraint::UnusedBitsNotZero)); + } + + Ok(()) + } + } + } +} + +impl DerAutoDerive for BitString<'_> {} + +impl<'a> Tagged for BitString<'a> { + const TAG: Tag = Tag::BitString; +} + +#[cfg(feature = "std")] +impl ToDer for BitString<'_> { + fn to_der_len(&self) -> Result<usize> { + let sz = self.data.len(); + if sz < 127 { + // 1 (class+tag) + 1 (length) + 1 (unused bits) + len + Ok(3 + sz) + } else { + // 1 (class+tag) + n (length) + 1 (unused bits) + len + let n = Length::Definite(sz + 1).to_der_len()?; + Ok(2 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(1 + self.data.len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let sz = writer.write(&[self.unused_bits])?; + let sz = sz + writer.write(&self.data)?; + Ok(sz) + } +} + +#[cfg(test)] +mod tests { + use super::BitString; + + #[test] + fn test_bitstring_is_set() { + let obj = BitString::new(0, &[0x0f, 0x00, 0x40]); + assert!(!obj.is_set(0)); + assert!(obj.is_set(7)); + assert!(!obj.is_set(9)); + assert!(obj.is_set(17)); + } + + #[cfg(feature = "bits")] + #[test] + fn test_bitstring_to_bitvec() { + let obj = BitString::new(0, &[0x0f, 0x00, 0x40]); + let bv = obj.as_bitslice().expect("could not get bitslice"); + assert_eq!(bv.get(0).as_deref(), Some(&false)); + assert_eq!(bv.get(7).as_deref(), Some(&true)); + assert_eq!(bv.get(9).as_deref(), Some(&false)); + assert_eq!(bv.get(17).as_deref(), Some(&true)); + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/boolean.rs b/rust/vendor/asn1-rs/src/asn1_types/boolean.rs new file mode 100644 index 0000000..ed620e4 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/boolean.rs @@ -0,0 +1,147 @@ +use crate::*; +use core::convert::TryFrom; + +/// ASN.1 `BOOLEAN` type +/// +/// BER objects consider any non-zero value as `true`, and `0` as `false`. +/// +/// DER objects must use value `0x0` (`false`) or `0xff` (`true`). +#[derive(Debug, PartialEq, Eq)] +pub struct Boolean { + pub value: u8, +} + +impl Boolean { + /// `BOOLEAN` object for value `false` + pub const FALSE: Boolean = Boolean::new(0); + /// `BOOLEAN` object for value `true` + pub const TRUE: Boolean = Boolean::new(0xff); + + /// Create a new `Boolean` from the provided logical value. + #[inline] + pub const fn new(value: u8) -> Self { + Boolean { value } + } + + /// Return the `bool` value from this object. + #[inline] + pub const fn bool(&self) -> bool { + self.value != 0 + } +} + +impl<'a> TryFrom<Any<'a>> for Boolean { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Boolean> { + TryFrom::try_from(&any) + } +} + +// non-consuming version +impl<'a, 'b> TryFrom<&'b Any<'a>> for Boolean { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Boolean> { + any.tag().assert_eq(Self::TAG)?; + // X.690 section 8.2.1: + // The encoding of a boolean value shall be primitive. The contents octets shall consist of a single octet + if any.header.length != Length::Definite(1) { + return Err(Error::InvalidLength); + } + let value = any.data[0]; + Ok(Boolean { value }) + } +} + +impl CheckDerConstraints for Boolean { + fn check_constraints(any: &Any) -> Result<()> { + let c = any.data[0]; + // X.690 section 11.1 + if !(c == 0 || c == 0xff) { + return Err(Error::DerConstraintFailed(DerConstraint::InvalidBoolean)); + } + Ok(()) + } +} + +impl DerAutoDerive for Boolean {} + +impl Tagged for Boolean { + const TAG: Tag = Tag::Boolean; +} + +#[cfg(feature = "std")] +impl ToDer for Boolean { + fn to_der_len(&self) -> Result<usize> { + // 3 = 1 (tag) + 1 (length) + 1 (value) + Ok(3) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let b = if self.value != 0 { 0xff } else { 0x00 }; + writer.write(&[b]).map_err(Into::into) + } + + /// Similar to using `to_der`, but uses header without computing length value + fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let sz = writer.write(&[Self::TAG.0 as u8, 0x01, self.value])?; + Ok(sz) + } +} + +impl<'a> TryFrom<Any<'a>> for bool { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<bool> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for bool { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<bool> { + any.tag().assert_eq(Self::TAG)?; + let b = Boolean::try_from(any)?; + Ok(b.bool()) + } +} + +impl CheckDerConstraints for bool { + fn check_constraints(any: &Any) -> Result<()> { + let c = any.data[0]; + // X.690 section 11.1 + if !(c == 0 || c == 0xff) { + return Err(Error::DerConstraintFailed(DerConstraint::InvalidBoolean)); + } + Ok(()) + } +} + +impl DerAutoDerive for bool {} + +impl Tagged for bool { + const TAG: Tag = Tag::Boolean; +} + +#[cfg(feature = "std")] +impl ToDer for bool { + fn to_der_len(&self) -> Result<usize> { + // 3 = 1 (tag) + 1 (length) + 1 (value) + Ok(3) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let b = if *self { 0xff } else { 0x00 }; + writer.write(&[b]).map_err(Into::into) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/choice.rs b/rust/vendor/asn1-rs/src/asn1_types/choice.rs new file mode 100644 index 0000000..4bc01cd --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/choice.rs @@ -0,0 +1,21 @@ +use crate::{FromBer, FromDer, Tag, Tagged}; + +pub trait Choice { + /// Is the provided [`Tag`] decodable as a variant of this `CHOICE`? + fn can_decode(tag: Tag) -> bool; +} + +/// This blanket impl allows any [`Tagged`] type to function as a [`Choice`] +/// with a single alternative. +impl<T> Choice for T +where + T: Tagged, +{ + fn can_decode(tag: Tag) -> bool { + T::TAG == tag + } +} + +pub trait BerChoice<'a>: Choice + FromBer<'a> {} + +pub trait DerChoice<'a>: Choice + FromDer<'a> {} diff --git a/rust/vendor/asn1-rs/src/asn1_types/embedded_pdv.rs b/rust/vendor/asn1-rs/src/asn1_types/embedded_pdv.rs new file mode 100644 index 0000000..9452856 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/embedded_pdv.rs @@ -0,0 +1,125 @@ +use crate::*; +use core::convert::TryFrom; + +#[derive(Debug, PartialEq, Eq)] +pub struct EmbeddedPdv<'a> { + pub identification: PdvIdentification<'a>, + pub data_value_descriptor: Option<ObjectDescriptor<'a>>, + pub data_value: &'a [u8], +} + +#[derive(Debug, PartialEq, Eq)] +pub enum PdvIdentification<'a> { + Syntaxes { + s_abstract: Oid<'a>, + s_transfer: Oid<'a>, + }, + Syntax(Oid<'a>), + PresentationContextId(Integer<'a>), + ContextNegotiation { + presentation_context_id: Integer<'a>, + presentation_syntax: Oid<'a>, + }, + TransferSyntax(Oid<'a>), + Fixed, +} + +impl<'a> TryFrom<Any<'a>> for EmbeddedPdv<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for EmbeddedPdv<'a> { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Self> { + let data = any.data; + // AUTOMATIC TAGS means all values will be tagged (IMPLICIT) + // [0] -> identification + let (rem, seq0) = + TaggedParser::<Explicit, Any>::parse_ber(Class::ContextSpecific, Tag(0), data)?; + let inner = seq0.inner; + let identification = match inner.tag() { + Tag(0) => { + // syntaxes SEQUENCE { + // abstract OBJECT IDENTIFIER, + // transfer OBJECT IDENTIFIER + // }, + // AUTOMATIC tags -> implicit! Hopefully, Oid does not check tag value! + let (rem, s_abstract) = Oid::from_ber(inner.data)?; + let (_, s_transfer) = Oid::from_ber(rem)?; + PdvIdentification::Syntaxes { + s_abstract, + s_transfer, + } + } + Tag(1) => { + // syntax OBJECT IDENTIFIER + let oid = Oid::new(inner.data.into()); + PdvIdentification::Syntax(oid) + } + Tag(2) => { + // presentation-context-id INTEGER + let i = Integer::new(inner.data); + PdvIdentification::PresentationContextId(i) + } + Tag(3) => { + // context-negotiation SEQUENCE { + // presentation-context-id INTEGER, + // transfer-syntax OBJECT IDENTIFIER + // }, + // AUTOMATIC tags -> implicit! + let (rem, any) = Any::from_ber(inner.data)?; + let presentation_context_id = Integer::new(any.data); + let (_, presentation_syntax) = Oid::from_ber(rem)?; + PdvIdentification::ContextNegotiation { + presentation_context_id, + presentation_syntax, + } + } + Tag(4) => { + // transfer-syntax OBJECT IDENTIFIER + let oid = Oid::new(inner.data.into()); + PdvIdentification::TransferSyntax(oid) + } + Tag(5) => { + // fixed NULL + PdvIdentification::Fixed + } + _ => { + return Err(inner + .tag() + .invalid_value("Invalid identification tag in EMBEDDED PDV")) + } + }; + // [1] -> data-value-descriptor ObjectDescriptor OPTIONAL + // *BUT* WITH COMPONENTS data-value-descriptor ABSENT + // XXX this should be parse_ber? + // let (rem, data_value_descriptor) = + // TaggedOptional::from(1).parse_der(rem, |_, inner| ObjectDescriptor::from_ber(inner))?; + let (rem, data_value_descriptor) = (rem, None); + // [2] -> data-value OCTET STRING + let (_, data_value) = + TaggedParser::<Implicit, &[u8]>::parse_ber(Class::ContextSpecific, Tag(2), rem)?; + let data_value = data_value.inner; + let obj = EmbeddedPdv { + identification, + data_value_descriptor, + data_value, + }; + Ok(obj) + } +} + +impl CheckDerConstraints for EmbeddedPdv<'_> { + fn check_constraints(any: &Any) -> Result<()> { + any.header.length().assert_definite()?; + any.header.assert_constructed()?; + Ok(()) + } +} + +impl DerAutoDerive for EmbeddedPdv<'_> {} diff --git a/rust/vendor/asn1-rs/src/asn1_types/end_of_content.rs b/rust/vendor/asn1-rs/src/asn1_types/end_of_content.rs new file mode 100644 index 0000000..4a9d291 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/end_of_content.rs @@ -0,0 +1,55 @@ +use crate::{Any, Error, Result, Tag, Tagged}; +use core::convert::TryFrom; + +/// End-of-contents octets +/// +/// `EndOfContent` is not a BER type, but represents a marked to indicate the end of contents +/// of an object, when the length is `Indefinite` (see X.690 section 8.1.5). +/// +/// This type cannot exist in DER, and so provides no `FromDer`/`ToDer` implementation. +#[derive(Debug)] +pub struct EndOfContent {} + +impl EndOfContent { + pub const fn new() -> Self { + EndOfContent {} + } +} + +impl<'a> TryFrom<Any<'a>> for EndOfContent { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<EndOfContent> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for EndOfContent { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<EndOfContent> { + any.tag().assert_eq(Self::TAG)?; + if !any.header.length.is_null() { + return Err(Error::InvalidLength); + } + Ok(EndOfContent {}) + } +} + +impl Tagged for EndOfContent { + const TAG: Tag = Tag::EndOfContent; +} + +// impl ToDer for EndOfContent { +// fn to_der_len(&self) -> Result<usize> { +// Ok(2) +// } + +// fn write_der_header(&self, writer: &mut dyn std::io::Write) -> crate::SerializeResult<usize> { +// writer.write(&[Self::TAG.0 as u8, 0x00]).map_err(Into::into) +// } + +// fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> crate::SerializeResult<usize> { +// Ok(0) +// } +// } diff --git a/rust/vendor/asn1-rs/src/asn1_types/enumerated.rs b/rust/vendor/asn1-rs/src/asn1_types/enumerated.rs new file mode 100644 index 0000000..5f843a8 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/enumerated.rs @@ -0,0 +1,72 @@ +use crate::ber::bytes_to_u64; +use crate::*; +use core::convert::TryFrom; + +/// ASN.1 `ENUMERATED` type +/// +/// # Limitations +/// +/// Supported values are limited to 0 .. 2^32 +#[derive(Debug, PartialEq, Eq)] +pub struct Enumerated(pub u32); + +impl Enumerated { + pub const fn new(value: u32) -> Self { + Enumerated(value) + } +} + +impl<'a> TryFrom<Any<'a>> for Enumerated { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Enumerated> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for Enumerated { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Enumerated> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_primitive()?; + let res_u64 = bytes_to_u64(any.data)?; + if res_u64 > (<u32>::MAX as u64) { + return Err(Error::IntegerTooLarge); + } + let value = res_u64 as u32; + Ok(Enumerated(value)) + } +} + +impl CheckDerConstraints for Enumerated { + fn check_constraints(any: &Any) -> Result<()> { + any.header.length.assert_definite()?; + Ok(()) + } +} + +impl DerAutoDerive for Enumerated {} + +impl Tagged for Enumerated { + const TAG: Tag = Tag::Enumerated; +} + +#[cfg(feature = "std")] +impl ToDer for Enumerated { + fn to_der_len(&self) -> Result<usize> { + Integer::from(self.0).to_der_len() + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let i = Integer::from(self.0); + let len = i.data.len(); + let header = Header::new(Class::Universal, false, Self::TAG, Length::Definite(len)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let int = Integer::from(self.0); + int.write_der_content(writer).map_err(Into::into) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/generalizedtime.rs b/rust/vendor/asn1-rs/src/asn1_types/generalizedtime.rs new file mode 100644 index 0000000..6e039d8 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/generalizedtime.rs @@ -0,0 +1,303 @@ +use crate::datetime::decode_decimal; +use crate::*; +use alloc::format; +use alloc::string::String; +use core::convert::TryFrom; +use core::fmt; +#[cfg(feature = "datetime")] +use time::OffsetDateTime; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct GeneralizedTime(pub ASN1DateTime); + +impl GeneralizedTime { + pub const fn new(datetime: ASN1DateTime) -> Self { + GeneralizedTime(datetime) + } + + pub fn from_bytes(bytes: &[u8]) -> Result<Self> { + // X.680 section 42 defines a GeneralizedTime as a VisibleString restricted to: + // + // a) a string representing the calendar date, as specified in ISO 8601, with a four-digit representation of the + // year, a two-digit representation of the month and a two-digit representation of the day, without use of + // separators, followed by a string representing the time of day, as specified in ISO 8601, without separators + // other than decimal comma or decimal period (as provided for in ISO 8601), and with no terminating Z (as + // provided for in ISO 8601); or + // b) the characters in a) above followed by an upper-case letter Z ; or + // c) he characters in a) above followed by a string representing a local time differential, as specified in + // ISO 8601, without separators. + let (year, month, day, hour, minute, rem) = match bytes { + [year1, year2, year3, year4, mon1, mon2, day1, day2, hour1, hour2, min1, min2, rem @ ..] => + { + let year_hi = decode_decimal(Self::TAG, *year1, *year2)?; + let year_lo = decode_decimal(Self::TAG, *year3, *year4)?; + let year = (year_hi as u32) * 100 + (year_lo as u32); + let month = decode_decimal(Self::TAG, *mon1, *mon2)?; + let day = decode_decimal(Self::TAG, *day1, *day2)?; + let hour = decode_decimal(Self::TAG, *hour1, *hour2)?; + let minute = decode_decimal(Self::TAG, *min1, *min2)?; + (year, month, day, hour, minute, rem) + } + _ => return Err(Self::TAG.invalid_value("malformed time string (not yymmddhhmm)")), + }; + if rem.is_empty() { + return Err(Self::TAG.invalid_value("malformed time string")); + } + // check for seconds + let (second, rem) = match rem { + [sec1, sec2, rem @ ..] => { + let second = decode_decimal(Self::TAG, *sec1, *sec2)?; + (second, rem) + } + _ => (0, rem), + }; + if month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59 { + // eprintln!("GeneralizedTime: time checks failed"); + // eprintln!(" month:{}", month); + // eprintln!(" day:{}", day); + // eprintln!(" hour:{}", hour); + // eprintln!(" minute:{}", minute); + // eprintln!(" second:{}", second); + return Err(Self::TAG.invalid_value("time components with invalid values")); + } + if rem.is_empty() { + // case a): no fractional seconds part, and no terminating Z + return Ok(GeneralizedTime(ASN1DateTime::new( + year, + month, + day, + hour, + minute, + second, + None, + ASN1TimeZone::Undefined, + ))); + } + // check for fractional seconds + let (millisecond, rem) = match rem { + [b'.' | b',', rem @ ..] => { + let mut fsecond = 0; + let mut rem = rem; + let mut digits = 0; + for idx in 0..=4 { + if rem.is_empty() { + if idx == 0 { + // dot or comma, but no following digit + return Err(Self::TAG.invalid_value( + "malformed time string (dot or comma but no digits)", + )); + } + digits = idx; + break; + } + if idx == 4 { + return Err( + Self::TAG.invalid_value("malformed time string (invalid milliseconds)") + ); + } + match rem[0] { + b'0'..=b'9' => { + // cannot overflow, max 4 digits will be read + fsecond = fsecond * 10 + (rem[0] - b'0') as u16; + } + b'Z' | b'+' | b'-' => { + digits = idx; + break; + } + _ => { + return Err(Self::TAG.invalid_value( + "malformed time string (invalid milliseconds/timezone)", + )) + } + } + rem = &rem[1..]; + } + // fix fractional seconds depending on the number of digits + // for ex, date "xxxx.3" means 3000 milliseconds, not 3 + let fsecond = match digits { + 1 => fsecond * 100, + 2 => fsecond * 10, + _ => fsecond, + }; + (Some(fsecond), rem) + } + _ => (None, rem), + }; + // check timezone + if rem.is_empty() { + // case a): fractional seconds part, and no terminating Z + return Ok(GeneralizedTime(ASN1DateTime::new( + year, + month, + day, + hour, + minute, + second, + millisecond, + ASN1TimeZone::Undefined, + ))); + } + let tz = match rem { + [b'Z'] => ASN1TimeZone::Z, + [b'+', h1, h2, m1, m2] => { + let hh = decode_decimal(Self::TAG, *h1, *h2)?; + let mm = decode_decimal(Self::TAG, *m1, *m2)?; + ASN1TimeZone::Offset(hh as i8, mm as i8) + } + [b'-', h1, h2, m1, m2] => { + let hh = decode_decimal(Self::TAG, *h1, *h2)?; + let mm = decode_decimal(Self::TAG, *m1, *m2)?; + ASN1TimeZone::Offset(-(hh as i8), mm as i8) + } + _ => return Err(Self::TAG.invalid_value("malformed time string: no time zone")), + }; + Ok(GeneralizedTime(ASN1DateTime::new( + year, + month, + day, + hour, + minute, + second, + millisecond, + tz, + ))) + } + + /// Return a ISO 8601 combined date and time with time zone. + #[cfg(feature = "datetime")] + #[cfg_attr(docsrs, doc(cfg(feature = "datetime")))] + pub fn utc_datetime(&self) -> Result<OffsetDateTime> { + self.0.to_datetime() + } +} + +impl<'a> TryFrom<Any<'a>> for GeneralizedTime { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<GeneralizedTime> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for GeneralizedTime { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<GeneralizedTime> { + any.tag().assert_eq(Self::TAG)?; + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_visible(b: &u8) -> bool { + 0x20 <= *b && *b <= 0x7f + } + if !any.data.iter().all(is_visible) { + return Err(Error::StringInvalidCharset); + } + + GeneralizedTime::from_bytes(any.data) + } +} + +impl fmt::Display for GeneralizedTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let dt = &self.0; + let fsec = match self.0.millisecond { + Some(v) => format!(".{}", v), + None => String::new(), + }; + match dt.tz { + ASN1TimeZone::Undefined => write!( + f, + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec + ), + ASN1TimeZone::Z => write!( + f, + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}Z", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec + ), + ASN1TimeZone::Offset(hh, mm) => { + let (s, hh) = if hh > 0 { ('+', hh) } else { ('-', -hh) }; + write!( + f, + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}{}{:02}{:02}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec, s, hh, mm + ) + } + } + } +} + +impl CheckDerConstraints for GeneralizedTime { + fn check_constraints(any: &Any) -> Result<()> { + // X.690 section 11.7.1: The encoding shall terminate with a "Z" + if any.data.last() != Some(&b'Z') { + return Err(Error::DerConstraintFailed(DerConstraint::MissingTimeZone)); + } + // X.690 section 11.7.2: The seconds element shall always be present. + // XXX + // X.690 section 11.7.4: The decimal point element, if present, shall be the point option "." + if any.data.iter().any(|&b| b == b',') { + return Err(Error::DerConstraintFailed(DerConstraint::MissingSeconds)); + } + Ok(()) + } +} + +impl DerAutoDerive for GeneralizedTime {} + +impl Tagged for GeneralizedTime { + const TAG: Tag = Tag::GeneralizedTime; +} + +#[cfg(feature = "std")] +impl ToDer for GeneralizedTime { + fn to_der_len(&self) -> Result<usize> { + // data: + // - 8 bytes for YYYYMMDD + // - 6 for hhmmss in DER (X.690 section 11.7.2) + // - (variable) the fractional part, without trailing zeros, with a point "." + // - 1 for the character Z in DER (X.690 section 11.7.1) + // data length: 15 + fractional part + // + // thus, length will always be on 1 byte (short length) and + // class+structure+tag also on 1 + // + // total: = 1 (class+constructed+tag) + 1 (length) + 15 + fractional + let num_digits = match self.0.millisecond { + None => 0, + Some(v) => 1 + v.to_string().len(), + }; + Ok(2 + 15 + num_digits) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + // see above for length value + let num_digits = match self.0.millisecond { + None => 0, + Some(v) => 1 + v.to_string().len() as u8, + }; + writer + .write(&[Self::TAG.0 as u8, 15 + num_digits]) + .map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let fractional = match self.0.millisecond { + None => "".to_string(), + Some(v) => format!(".{}", v), + }; + let num_digits = fractional.len(); + write!( + writer, + "{:04}{:02}{:02}{:02}{:02}{:02}{}Z", + self.0.year, + self.0.month, + self.0.day, + self.0.hour, + self.0.minute, + self.0.second, + fractional, + )?; + // write_fmt returns (), see above for length value + Ok(15 + num_digits) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/integer.rs b/rust/vendor/asn1-rs/src/asn1_types/integer.rs new file mode 100644 index 0000000..59f846a --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/integer.rs @@ -0,0 +1,712 @@ +use crate::*; +use alloc::borrow::Cow; +use alloc::vec; +use core::convert::{TryFrom, TryInto}; + +#[cfg(feature = "bigint")] +#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] +pub use num_bigint::{BigInt, BigUint, Sign}; + +/// Decode an unsigned integer into a big endian byte slice with all leading +/// zeroes removed (if positive) and extra 0xff remove (if negative) +fn trim_slice<'a>(any: &'a Any<'_>) -> Result<&'a [u8]> { + let bytes = any.data; + + if bytes.is_empty() || (bytes[0] != 0x00 && bytes[0] != 0xff) { + return Ok(bytes); + } + + match bytes.iter().position(|&b| b != 0) { + // first byte is not 0 + Some(0) => (), + // all bytes are 0 + None => return Ok(&bytes[bytes.len() - 1..]), + Some(first) => return Ok(&bytes[first..]), + } + + // same for negative integers : skip byte 0->n if byte 0->n = 0xff AND byte n+1 >= 0x80 + match bytes.windows(2).position(|s| match s { + &[a, b] => !(a == 0xff && b >= 0x80), + _ => true, + }) { + // first byte is not 0xff + Some(0) => (), + // all bytes are 0xff + None => return Ok(&bytes[bytes.len() - 1..]), + Some(first) => return Ok(&bytes[first..]), + } + + Ok(bytes) +} + +/// Decode an unsigned integer into a byte array of the requested size +/// containing a big endian integer. +fn decode_array_uint<const N: usize>(any: &Any<'_>) -> Result<[u8; N]> { + if is_highest_bit_set(any.data) { + return Err(Error::IntegerNegative); + } + let input = trim_slice(any)?; + + if input.len() > N { + return Err(Error::IntegerTooLarge); + } + + // Input has leading zeroes removed, so we need to add them back + let mut output = [0u8; N]; + assert!(input.len() <= N); + output[N.saturating_sub(input.len())..].copy_from_slice(input); + Ok(output) +} + +/// Decode an unsigned integer of the specified size. +/// +/// Returns a byte array of the requested size containing a big endian integer. +fn decode_array_int<const N: usize>(any: &Any<'_>) -> Result<[u8; N]> { + if any.data.len() > N { + return Err(Error::IntegerTooLarge); + } + + // any.tag().assert_eq(Tag::Integer)?; + let mut output = [0xFFu8; N]; + let offset = N.saturating_sub(any.as_bytes().len()); + output[offset..].copy_from_slice(any.as_bytes()); + Ok(output) +} + +/// Is the highest bit of the first byte in the slice 1? (if present) +#[inline] +fn is_highest_bit_set(bytes: &[u8]) -> bool { + bytes + .first() + .map(|byte| byte & 0b10000000 != 0) + .unwrap_or(false) +} + +macro_rules! impl_int { + ($uint:ty => $int:ty) => { + impl<'a> TryFrom<Any<'a>> for $int { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + TryFrom::try_from(&any) + } + } + + impl<'a, 'b> TryFrom<&'b Any<'a>> for $int { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Self> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_primitive()?; + let uint = if is_highest_bit_set(any.as_bytes()) { + <$uint>::from_be_bytes(decode_array_int(&any)?) + } else { + // read as uint, but check if the value will fit in a signed integer + let u = <$uint>::from_be_bytes(decode_array_uint(&any)?); + if u > <$int>::MAX as $uint { + return Err(Error::IntegerTooLarge); + } + u + }; + Ok(uint as $int) + } + } + + impl CheckDerConstraints for $int { + fn check_constraints(any: &Any) -> Result<()> { + check_der_int_constraints(any) + } + } + + impl DerAutoDerive for $int {} + + impl Tagged for $int { + const TAG: Tag = Tag::Integer; + } + + #[cfg(feature = "std")] + impl ToDer for $int { + fn to_der_len(&self) -> Result<usize> { + let int = Integer::from(*self); + int.to_der_len() + } + + fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let int = Integer::from(*self); + int.write_der(writer) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let int = Integer::from(*self); + int.write_der_header(writer) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let int = Integer::from(*self); + int.write_der_content(writer) + } + } + }; +} + +macro_rules! impl_uint { + ($ty:ty) => { + impl<'a> TryFrom<Any<'a>> for $ty { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + TryFrom::try_from(&any) + } + } + impl<'a, 'b> TryFrom<&'b Any<'a>> for $ty { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Self> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_primitive()?; + let result = Self::from_be_bytes(decode_array_uint(any)?); + Ok(result) + } + } + impl CheckDerConstraints for $ty { + fn check_constraints(any: &Any) -> Result<()> { + check_der_int_constraints(any) + } + } + + impl DerAutoDerive for $ty {} + + impl Tagged for $ty { + const TAG: Tag = Tag::Integer; + } + + #[cfg(feature = "std")] + impl ToDer for $ty { + fn to_der_len(&self) -> Result<usize> { + let int = Integer::from(*self); + int.to_der_len() + } + + fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let int = Integer::from(*self); + int.write_der(writer) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let int = Integer::from(*self); + int.write_der_header(writer) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let int = Integer::from(*self); + int.write_der_content(writer) + } + } + }; +} + +impl_uint!(u8); +impl_uint!(u16); +impl_uint!(u32); +impl_uint!(u64); +impl_uint!(u128); +impl_int!(u8 => i8); +impl_int!(u16 => i16); +impl_int!(u32 => i32); +impl_int!(u64 => i64); +impl_int!(u128 => i128); + +/// ASN.1 `INTEGER` type +/// +/// Generic representation for integer types. +/// BER/DER integers can be of any size, so it is not possible to store them as simple integers (they +/// are stored as raw bytes). +/// +/// The internal representation can be obtained using `.as_ref()`. +/// +/// # Note +/// +/// Methods from/to BER and DER encodings are also implemented for primitive types +/// (`u8`, `u16` to `u128`, and `i8` to `i128`). +/// In most cases, it is easier to use these types directly. +/// +/// # Examples +/// +/// Creating an `Integer` +/// +/// ``` +/// use asn1_rs::Integer; +/// +/// // unsigned +/// let i = Integer::from(4); +/// assert_eq!(i.as_ref(), &[4]); +/// // signed +/// let j = Integer::from(-2); +/// assert_eq!(j.as_ref(), &[0xfe]); +/// ``` +/// +/// Converting an `Integer` to a primitive type (using the `TryInto` trait) +/// +/// ``` +/// use asn1_rs::{Error, Integer}; +/// use std::convert::TryInto; +/// +/// let i = Integer::new(&[0x12, 0x34, 0x56, 0x78]); +/// // converts to an u32 +/// let n: u32 = i.try_into().unwrap(); +/// +/// // Same, but converting to an u16: will fail, value cannot fit into an u16 +/// let i = Integer::new(&[0x12, 0x34, 0x56, 0x78]); +/// assert_eq!(i.try_into() as Result<u16, _>, Err(Error::IntegerTooLarge)); +/// ``` +/// +/// Encoding an `Integer` to DER +/// +/// ``` +/// use asn1_rs::{Integer, ToDer}; +/// +/// let i = Integer::from(4); +/// let v = i.to_der_vec().unwrap(); +/// assert_eq!(&v, &[2, 1, 4]); +/// +/// // same, with primitive types +/// let v = 4.to_der_vec().unwrap(); +/// assert_eq!(&v, &[2, 1, 4]); +/// ``` +#[derive(Debug, Eq, PartialEq)] +pub struct Integer<'a> { + pub(crate) data: Cow<'a, [u8]>, +} + +impl<'a> Integer<'a> { + /// Creates a new `Integer` containing the given value (borrowed). + #[inline] + pub const fn new(s: &'a [u8]) -> Self { + Integer { + data: Cow::Borrowed(s), + } + } + + /// Creates a borrowed `Any` for this object + #[inline] + pub fn any(&'a self) -> Any<'a> { + Any::from_tag_and_data(Self::TAG, &self.data) + } + + /// Returns a `BigInt` built from this `Integer` value. + #[cfg(feature = "bigint")] + #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] + pub fn as_bigint(&self) -> BigInt { + BigInt::from_signed_bytes_be(&self.data) + } + + /// Returns a `BigUint` built from this `Integer` value. + #[cfg(feature = "bigint")] + #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] + pub fn as_biguint(&self) -> Result<BigUint> { + if is_highest_bit_set(&self.data) { + Err(Error::IntegerNegative) + } else { + Ok(BigUint::from_bytes_be(&self.data)) + } + } + + /// Build an `Integer` from a constant array of bytes representation of an integer. + pub fn from_const_array<const N: usize>(b: [u8; N]) -> Self { + // if high bit set -> add leading 0 to ensure unsigned + if is_highest_bit_set(&b) { + let mut bytes = vec![0]; + bytes.extend_from_slice(&b); + + Integer { + data: Cow::Owned(bytes), + } + } + // otherwise -> remove 0 unless next has high bit set + else { + let mut idx = 0; + + while idx < b.len() - 1 { + if b[idx] == 0 && b[idx + 1] < 0x80 { + idx += 1; + continue; + } + break; + } + + Integer { + data: Cow::Owned(b[idx..].to_vec()), + } + } + } + + fn from_const_array_negative<const N: usize>(b: [u8; N]) -> Self { + let mut idx = 0; + + // Skip leading FF unless next has high bit clear + while idx < b.len() - 1 { + if b[idx] == 0xFF && b[idx + 1] >= 0x80 { + idx += 1; + continue; + } + break; + } + + if idx == b.len() { + Integer { + data: Cow::Borrowed(&[0]), + } + } else { + Integer { + data: Cow::Owned(b[idx..].to_vec()), + } + } + } +} + +macro_rules! impl_from_to { + ($ty:ty, $sty:expr, $from:ident, $to:ident) => { + impl From<$ty> for Integer<'_> { + fn from(i: $ty) -> Self { + Self::$from(i) + } + } + + impl TryFrom<Integer<'_>> for $ty { + type Error = Error; + + fn try_from(value: Integer<'_>) -> Result<Self> { + value.$to() + } + } + + impl Integer<'_> { + #[doc = "Attempts to convert an `Integer` to a `"] + #[doc = $sty] + #[doc = "`."] + #[doc = ""] + #[doc = "This function returns an `IntegerTooLarge` error if the integer will not fit into the output type."] + pub fn $to(&self) -> Result<$ty> { + self.any().try_into() + } + } + }; + (IMPL SIGNED $ty:ty, $sty:expr, $from:ident, $to:ident) => { + impl_from_to!($ty, $sty, $from, $to); + + impl Integer<'_> { + #[doc = "Converts a `"] + #[doc = $sty] + #[doc = "` to an `Integer`"] + #[doc = ""] + #[doc = "Note: this function allocates data."] + pub fn $from(i: $ty) -> Self { + let b = i.to_be_bytes(); + if i >= 0 { + Self::from_const_array(b) + } else { + Self::from_const_array_negative(b) + } + } + } + }; + (IMPL UNSIGNED $ty:ty, $sty:expr, $from:ident, $to:ident) => { + impl_from_to!($ty, $sty, $from, $to); + + impl Integer<'_> { + #[doc = "Converts a `"] + #[doc = $sty] + #[doc = "` to an `Integer`"] + #[doc = ""] + #[doc = "Note: this function allocates data."] + pub fn $from(i: $ty) -> Self { + Self::from_const_array(i.to_be_bytes()) + } + } + }; + (SIGNED $ty:ty, $from:ident, $to:ident) => { + impl_from_to!(IMPL SIGNED $ty, stringify!($ty), $from, $to); + }; + (UNSIGNED $ty:ty, $from:ident, $to:ident) => { + impl_from_to!(IMPL UNSIGNED $ty, stringify!($ty), $from, $to); + }; +} + +impl_from_to!(SIGNED i8, from_i8, as_i8); +impl_from_to!(SIGNED i16, from_i16, as_i16); +impl_from_to!(SIGNED i32, from_i32, as_i32); +impl_from_to!(SIGNED i64, from_i64, as_i64); +impl_from_to!(SIGNED i128, from_i128, as_i128); + +impl_from_to!(UNSIGNED u8, from_u8, as_u8); +impl_from_to!(UNSIGNED u16, from_u16, as_u16); +impl_from_to!(UNSIGNED u32, from_u32, as_u32); +impl_from_to!(UNSIGNED u64, from_u64, as_u64); +impl_from_to!(UNSIGNED u128, from_u128, as_u128); + +impl<'a> AsRef<[u8]> for Integer<'a> { + fn as_ref(&self) -> &[u8] { + &self.data + } +} + +impl<'a> TryFrom<Any<'a>> for Integer<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Integer<'a>> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for Integer<'a> { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Integer<'a>> { + any.tag().assert_eq(Self::TAG)?; + Ok(Integer { + data: Cow::Borrowed(any.data), + }) + } +} + +impl<'a> CheckDerConstraints for Integer<'a> { + fn check_constraints(any: &Any) -> Result<()> { + check_der_int_constraints(any) + } +} + +fn check_der_int_constraints(any: &Any) -> Result<()> { + any.header.assert_primitive()?; + any.header.length.assert_definite()?; + match any.as_bytes() { + [] => Err(Error::DerConstraintFailed(DerConstraint::IntegerEmpty)), + [0] => Ok(()), + // leading zeroes + [0, byte, ..] if *byte < 0x80 => Err(Error::DerConstraintFailed( + DerConstraint::IntegerLeadingZeroes, + )), + // negative integer with non-minimal encoding + [0xff, byte, ..] if *byte >= 0x80 => { + Err(Error::DerConstraintFailed(DerConstraint::IntegerLeadingFF)) + } + _ => Ok(()), + } +} + +impl DerAutoDerive for Integer<'_> {} + +impl<'a> Tagged for Integer<'a> { + const TAG: Tag = Tag::Integer; +} + +#[cfg(feature = "std")] +impl ToDer for Integer<'_> { + fn to_der_len(&self) -> Result<usize> { + let sz = self.data.len(); + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // hmm, a very long integer. anyway: + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(self.data.len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(&self.data).map_err(Into::into) + } +} + +/// Helper macro to declare integers at compile-time +/// +/// [`Integer`] stores the encoded representation of the integer, so declaring +/// an integer requires to either use a runtime function or provide the encoded value. +/// This macro simplifies this task by encoding the value. +/// It can be used the following ways: +/// +/// - `int!(1234)`: Create a const expression for the corresponding `Integer<'static>` +/// - `int!(raw 1234)`: Return the DER encoded form as a byte array (hex-encoded, big-endian +/// representation from the integer, with leading zeroes removed). +/// +/// # Examples +/// +/// ```rust +/// use asn1_rs::{int, Integer}; +/// +/// const INT0: Integer = int!(1234); +/// ``` +#[macro_export] +macro_rules! int { + (raw $item:expr) => { + $crate::exports::asn1_rs_impl::encode_int!($item) + }; + (rel $item:expr) => { + $crate::exports::asn1_rs_impl::encode_int!(rel $item) + }; + ($item:expr) => { + $crate::Integer::new( + &$crate::int!(raw $item), + ) + }; +} + +#[cfg(test)] +mod tests { + use crate::{Any, FromDer, Header, Tag, ToDer}; + use std::convert::TryInto; + + // Vectors from Section 5.7 of: + // https://luca.ntop.org/Teaching/Appunti/asn1.html + pub(crate) const I0_BYTES: &[u8] = &[0x02, 0x01, 0x00]; + pub(crate) const I127_BYTES: &[u8] = &[0x02, 0x01, 0x7F]; + pub(crate) const I128_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0x80]; + pub(crate) const I256_BYTES: &[u8] = &[0x02, 0x02, 0x01, 0x00]; + pub(crate) const INEG128_BYTES: &[u8] = &[0x02, 0x01, 0x80]; + pub(crate) const INEG129_BYTES: &[u8] = &[0x02, 0x02, 0xFF, 0x7F]; + + // Additional vectors + pub(crate) const I255_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0xFF]; + pub(crate) const I32767_BYTES: &[u8] = &[0x02, 0x02, 0x7F, 0xFF]; + pub(crate) const I65535_BYTES: &[u8] = &[0x02, 0x03, 0x00, 0xFF, 0xFF]; + pub(crate) const INEG32768_BYTES: &[u8] = &[0x02, 0x02, 0x80, 0x00]; + + #[test] + fn decode_i8() { + assert_eq!(0, i8::from_der(I0_BYTES).unwrap().1); + assert_eq!(127, i8::from_der(I127_BYTES).unwrap().1); + assert_eq!(-128, i8::from_der(INEG128_BYTES).unwrap().1); + } + + #[test] + fn encode_i8() { + assert_eq!(0i8.to_der_vec().unwrap(), I0_BYTES); + assert_eq!(127i8.to_der_vec().unwrap(), I127_BYTES); + assert_eq!((-128i8).to_der_vec().unwrap(), INEG128_BYTES); + } + + #[test] + fn decode_i16() { + assert_eq!(0, i16::from_der(I0_BYTES).unwrap().1); + assert_eq!(127, i16::from_der(I127_BYTES).unwrap().1); + assert_eq!(128, i16::from_der(I128_BYTES).unwrap().1); + assert_eq!(255, i16::from_der(I255_BYTES).unwrap().1); + assert_eq!(256, i16::from_der(I256_BYTES).unwrap().1); + assert_eq!(32767, i16::from_der(I32767_BYTES).unwrap().1); + assert_eq!(-128, i16::from_der(INEG128_BYTES).unwrap().1); + assert_eq!(-129, i16::from_der(INEG129_BYTES).unwrap().1); + assert_eq!(-32768, i16::from_der(INEG32768_BYTES).unwrap().1); + } + + #[test] + fn encode_i16() { + assert_eq!(0i16.to_der_vec().unwrap(), I0_BYTES); + assert_eq!(127i16.to_der_vec().unwrap(), I127_BYTES); + assert_eq!(128i16.to_der_vec().unwrap(), I128_BYTES); + assert_eq!(255i16.to_der_vec().unwrap(), I255_BYTES); + assert_eq!(256i16.to_der_vec().unwrap(), I256_BYTES); + assert_eq!(32767i16.to_der_vec().unwrap(), I32767_BYTES); + assert_eq!((-128i16).to_der_vec().unwrap(), INEG128_BYTES); + assert_eq!((-129i16).to_der_vec().unwrap(), INEG129_BYTES); + assert_eq!((-32768i16).to_der_vec().unwrap(), INEG32768_BYTES); + } + + #[test] + fn decode_u8() { + assert_eq!(0, u8::from_der(I0_BYTES).unwrap().1); + assert_eq!(127, u8::from_der(I127_BYTES).unwrap().1); + assert_eq!(255, u8::from_der(I255_BYTES).unwrap().1); + } + + #[test] + fn encode_u8() { + assert_eq!(0u8.to_der_vec().unwrap(), I0_BYTES); + assert_eq!(127u8.to_der_vec().unwrap(), I127_BYTES); + assert_eq!(255u8.to_der_vec().unwrap(), I255_BYTES); + } + + #[test] + fn decode_u16() { + assert_eq!(0, u16::from_der(I0_BYTES).unwrap().1); + assert_eq!(127, u16::from_der(I127_BYTES).unwrap().1); + assert_eq!(255, u16::from_der(I255_BYTES).unwrap().1); + assert_eq!(256, u16::from_der(I256_BYTES).unwrap().1); + assert_eq!(32767, u16::from_der(I32767_BYTES).unwrap().1); + assert_eq!(65535, u16::from_der(I65535_BYTES).unwrap().1); + } + + #[test] + fn encode_u16() { + assert_eq!(0u16.to_der_vec().unwrap(), I0_BYTES); + assert_eq!(127u16.to_der_vec().unwrap(), I127_BYTES); + assert_eq!(255u16.to_der_vec().unwrap(), I255_BYTES); + assert_eq!(256u16.to_der_vec().unwrap(), I256_BYTES); + assert_eq!(32767u16.to_der_vec().unwrap(), I32767_BYTES); + assert_eq!(65535u16.to_der_vec().unwrap(), I65535_BYTES); + } + + /// Integers must be encoded with a minimum number of octets + #[test] + fn reject_non_canonical() { + assert!(i8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err()); + assert!(i16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err()); + assert!(u8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err()); + assert!(u16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err()); + } + + #[test] + fn declare_int() { + let int = super::int!(1234); + assert_eq!(int.try_into(), Ok(1234)); + } + + #[test] + fn trim_slice() { + use super::trim_slice; + let h = Header::new_simple(Tag(0)); + // no zero nor ff - nothing to remove + let input: &[u8] = &[0x7f, 0xff, 0x00, 0x02]; + assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input))); + // + // 0x00 + // + // empty - nothing to remove + let input: &[u8] = &[]; + assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input))); + // one zero - nothing to remove + let input: &[u8] = &[0]; + assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input))); + // all zeroes - keep only one + let input: &[u8] = &[0, 0, 0]; + assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input))); + // some zeroes - keep only the non-zero part + let input: &[u8] = &[0, 0, 1]; + assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input))); + // + // 0xff + // + // one ff - nothing to remove + let input: &[u8] = &[0xff]; + assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input))); + // all ff - keep only one + let input: &[u8] = &[0xff, 0xff, 0xff]; + assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input))); + // some ff - keep only the non-zero part + let input: &[u8] = &[0xff, 0xff, 1]; + assert_eq!(Ok(&input[1..]), trim_slice(&Any::new(h.clone(), input))); + // some ff and a MSB 1 - keep only the non-zero part + let input: &[u8] = &[0xff, 0xff, 0x80, 1]; + assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input))); + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/mod.rs b/rust/vendor/asn1-rs/src/asn1_types/mod.rs new file mode 100644 index 0000000..33e50d6 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/mod.rs @@ -0,0 +1,26 @@ +mod any; +mod bitstring; +mod boolean; +mod choice; +mod embedded_pdv; +mod end_of_content; +mod enumerated; +mod generalizedtime; +mod integer; +mod null; +mod object_descriptor; +mod octetstring; +mod oid; +mod optional; +mod real; +mod sequence; +mod set; +mod strings; +mod tagged; +mod utctime; + +pub use { + any::*, bitstring::*, boolean::*, choice::*, embedded_pdv::*, end_of_content::*, enumerated::*, + generalizedtime::*, integer::*, null::*, object_descriptor::*, octetstring::*, oid::*, + optional::*, real::*, sequence::*, set::*, strings::*, tagged::*, utctime::*, +}; diff --git a/rust/vendor/asn1-rs/src/asn1_types/null.rs b/rust/vendor/asn1-rs/src/asn1_types/null.rs new file mode 100644 index 0000000..ba9f0f5 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/null.rs @@ -0,0 +1,99 @@ +use crate::*; +use core::convert::TryFrom; + +/// ASN.1 `NULL` type +#[derive(Debug, PartialEq, Eq)] +pub struct Null {} + +impl Null { + pub const fn new() -> Self { + Null {} + } +} + +impl<'a> TryFrom<Any<'a>> for Null { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Null> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for Null { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Null> { + any.tag().assert_eq(Self::TAG)?; + if !any.header.length.is_null() { + return Err(Error::InvalidLength); + } + Ok(Null {}) + } +} + +impl CheckDerConstraints for Null { + fn check_constraints(_any: &Any) -> Result<()> { + Ok(()) + } +} + +impl DerAutoDerive for Null {} + +impl Tagged for Null { + const TAG: Tag = Tag::Null; +} + +#[cfg(feature = "std")] +impl ToDer for Null { + fn to_der_len(&self) -> Result<usize> { + Ok(2) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(&[0x05, 0x00]).map_err(Into::into) + } + + fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + Ok(0) + } +} + +impl<'a> TryFrom<Any<'a>> for () { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<()> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_primitive()?; + if !any.header.length.is_null() { + return Err(Error::InvalidLength); + } + Ok(()) + } +} + +impl CheckDerConstraints for () { + fn check_constraints(_any: &Any) -> Result<()> { + Ok(()) + } +} + +impl DerAutoDerive for () {} + +impl Tagged for () { + const TAG: Tag = Tag::Null; +} + +#[cfg(feature = "std")] +impl ToDer for () { + fn to_der_len(&self) -> Result<usize> { + Ok(2) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(&[0x05, 0x00]).map_err(Into::into) + } + + fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + Ok(0) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/object_descriptor.rs b/rust/vendor/asn1-rs/src/asn1_types/object_descriptor.rs new file mode 100644 index 0000000..db78870 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/object_descriptor.rs @@ -0,0 +1,17 @@ +use crate::{asn1_string, TestValidCharset}; +use crate::{Error, Result}; +use alloc::string::String; + +// X.680 section 44.3 +// ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString + +asn1_string!(ObjectDescriptor); + +impl<'a> TestValidCharset for ObjectDescriptor<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + if !i.iter().all(u8::is_ascii) { + return Err(Error::StringInvalidCharset); + } + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/octetstring.rs b/rust/vendor/asn1-rs/src/asn1_types/octetstring.rs new file mode 100644 index 0000000..b4b71e5 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/octetstring.rs @@ -0,0 +1,157 @@ +use crate::*; +use alloc::borrow::Cow; +use core::convert::TryFrom; + +/// ASN.1 `OCTETSTRING` type +#[derive(Debug, PartialEq, Eq)] +pub struct OctetString<'a> { + data: Cow<'a, [u8]>, +} + +impl<'a> OctetString<'a> { + pub const fn new(s: &'a [u8]) -> Self { + OctetString { + data: Cow::Borrowed(s), + } + } + + /// Get the bytes representation of the *content* + pub fn as_cow(&'a self) -> &Cow<'a, [u8]> { + &self.data + } + + /// Get the bytes representation of the *content* + pub fn into_cow(self) -> Cow<'a, [u8]> { + self.data + } +} + +impl<'a> AsRef<[u8]> for OctetString<'a> { + fn as_ref(&self) -> &[u8] { + &self.data + } +} + +impl<'a> From<&'a [u8]> for OctetString<'a> { + fn from(b: &'a [u8]) -> Self { + OctetString { + data: Cow::Borrowed(b), + } + } +} + +impl<'a> TryFrom<Any<'a>> for OctetString<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<OctetString<'a>> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for OctetString<'a> { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<OctetString<'a>> { + any.tag().assert_eq(Self::TAG)?; + Ok(OctetString { + data: Cow::Borrowed(any.data), + }) + } +} + +impl<'a> CheckDerConstraints for OctetString<'a> { + fn check_constraints(any: &Any) -> Result<()> { + // X.690 section 10.2 + any.header.assert_primitive()?; + Ok(()) + } +} + +impl DerAutoDerive for OctetString<'_> {} + +impl<'a> Tagged for OctetString<'a> { + const TAG: Tag = Tag::OctetString; +} + +#[cfg(feature = "std")] +impl ToDer for OctetString<'_> { + fn to_der_len(&self) -> Result<usize> { + let sz = self.data.len(); + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(self.data.len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(&self.data).map_err(Into::into) + } +} + +impl<'a> TryFrom<Any<'a>> for &'a [u8] { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<&'a [u8]> { + any.tag().assert_eq(Self::TAG)?; + let s = OctetString::try_from(any)?; + match s.data { + Cow::Borrowed(s) => Ok(s), + Cow::Owned(_) => Err(Error::LifetimeError), + } + } +} + +impl<'a> CheckDerConstraints for &'a [u8] { + fn check_constraints(any: &Any) -> Result<()> { + // X.690 section 10.2 + any.header.assert_primitive()?; + Ok(()) + } +} + +impl DerAutoDerive for &'_ [u8] {} + +impl<'a> Tagged for &'a [u8] { + const TAG: Tag = Tag::OctetString; +} + +#[cfg(feature = "std")] +impl ToDer for &'_ [u8] { + fn to_der_len(&self) -> Result<usize> { + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(self.len()), + ); + Ok(header.to_der_len()? + self.len()) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(self.len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(self).map_err(Into::into) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/oid.rs b/rust/vendor/asn1-rs/src/asn1_types/oid.rs new file mode 100644 index 0000000..a6629b4 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/oid.rs @@ -0,0 +1,517 @@ +use crate::*; +use alloc::borrow::Cow; +#[cfg(not(feature = "std"))] +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::{ + convert::TryFrom, fmt, iter::FusedIterator, marker::PhantomData, ops::Shl, str::FromStr, +}; + +#[cfg(feature = "bigint")] +use num_bigint::BigUint; +use num_traits::Num; + +/// An error for OID parsing functions. +#[derive(Debug)] +pub enum OidParseError { + TooShort, + /// Signalizes that the first or second component is too large. + /// The first must be within the range 0 to 6 (inclusive). + /// The second component must be less than 40. + FirstComponentsTooLarge, + ParseIntError, +} + +/// Object ID (OID) representation which can be relative or non-relative. +/// An example for an OID in string representation is `"1.2.840.113549.1.1.5"`. +/// +/// For non-relative OIDs restrictions apply to the first two components. +/// +/// This library contains a procedural macro `oid` which can be used to +/// create oids. For example `oid!(1.2.44.233)` or `oid!(rel 44.233)` +/// for relative oids. See the [module documentation](index.html) for more information. +#[derive(Hash, PartialEq, Eq, Clone)] + +pub struct Oid<'a> { + asn1: Cow<'a, [u8]>, + relative: bool, +} + +impl<'a> TryFrom<Any<'a>> for Oid<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for Oid<'a> { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Self> { + // check that any.data.last().unwrap() >> 7 == 0u8 + let asn1 = Cow::Borrowed(any.data); + Ok(Oid::new(asn1)) + } +} + +impl<'a> CheckDerConstraints for Oid<'a> { + fn check_constraints(any: &Any) -> Result<()> { + any.header.assert_primitive()?; + any.header.length.assert_definite()?; + Ok(()) + } +} + +impl DerAutoDerive for Oid<'_> {} + +impl<'a> Tagged for Oid<'a> { + const TAG: Tag = Tag::Oid; +} + +#[cfg(feature = "std")] +impl ToDer for Oid<'_> { + fn to_der_len(&self) -> Result<usize> { + // OID/REL-OID tag will not change header size, so we don't care here + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(self.asn1.len()), + ); + Ok(header.to_der_len()? + self.asn1.len()) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let tag = if self.relative { + Tag::RelativeOid + } else { + Tag::Oid + }; + let header = Header::new( + Class::Universal, + false, + tag, + Length::Definite(self.asn1.len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(&self.asn1).map_err(Into::into) + } +} + +fn encode_relative(ids: &'_ [u64]) -> impl Iterator<Item = u8> + '_ { + ids.iter().flat_map(|id| { + let bit_count = 64 - id.leading_zeros(); + let octets_needed = ((bit_count + 6) / 7).max(1); + (0..octets_needed).map(move |i| { + let flag = if i == octets_needed - 1 { 0 } else { 1 << 7 }; + ((id >> (7 * (octets_needed - 1 - i))) & 0b111_1111) as u8 | flag + }) + }) +} + +impl<'a> Oid<'a> { + /// Create an OID from the ASN.1 DER encoded form. See the [module documentation](index.html) + /// for other ways to create oids. + pub const fn new(asn1: Cow<'a, [u8]>) -> Oid { + Oid { + asn1, + relative: false, + } + } + + /// Create a relative OID from the ASN.1 DER encoded form. See the [module documentation](index.html) + /// for other ways to create relative oids. + pub const fn new_relative(asn1: Cow<'a, [u8]>) -> Oid { + Oid { + asn1, + relative: true, + } + } + + /// Build an OID from an array of object identifier components. + /// This method allocates memory on the heap. + pub fn from(s: &[u64]) -> core::result::Result<Oid<'static>, OidParseError> { + if s.len() < 2 { + if s.len() == 1 && s[0] == 0 { + return Ok(Oid { + asn1: Cow::Borrowed(&[0]), + relative: false, + }); + } + return Err(OidParseError::TooShort); + } + if s[0] >= 7 || s[1] >= 40 { + return Err(OidParseError::FirstComponentsTooLarge); + } + let asn1_encoded: Vec<u8> = [(s[0] * 40 + s[1]) as u8] + .iter() + .copied() + .chain(encode_relative(&s[2..])) + .collect(); + Ok(Oid { + asn1: Cow::from(asn1_encoded), + relative: false, + }) + } + + /// Build a relative OID from an array of object identifier components. + pub fn from_relative(s: &[u64]) -> core::result::Result<Oid<'static>, OidParseError> { + if s.is_empty() { + return Err(OidParseError::TooShort); + } + let asn1_encoded: Vec<u8> = encode_relative(s).collect(); + Ok(Oid { + asn1: Cow::from(asn1_encoded), + relative: true, + }) + } + + /// Create a deep copy of the oid. + /// + /// This method allocates data on the heap. The returned oid + /// can be used without keeping the ASN.1 representation around. + /// + /// Cloning the returned oid does again allocate data. + pub fn to_owned(&self) -> Oid<'static> { + Oid { + asn1: Cow::from(self.asn1.to_vec()), + relative: self.relative, + } + } + + /// Get the encoded oid without the header. + #[inline] + pub fn as_bytes(&self) -> &[u8] { + self.asn1.as_ref() + } + + /// Get the encoded oid without the header. + #[deprecated(since = "0.2.0", note = "Use `as_bytes` instead")] + #[inline] + pub fn bytes(&self) -> &[u8] { + self.as_bytes() + } + + /// Get the bytes representation of the encoded oid + pub fn into_cow(self) -> Cow<'a, [u8]> { + self.asn1 + } + + /// Convert the OID to a string representation. + /// The string contains the IDs separated by dots, for ex: "1.2.840.113549.1.1.5" + #[cfg(feature = "bigint")] + pub fn to_id_string(&self) -> String { + let ints: Vec<String> = self.iter_bigint().map(|i| i.to_string()).collect(); + ints.join(".") + } + + #[cfg(not(feature = "bigint"))] + /// Convert the OID to a string representation. + /// + /// If every arc fits into a u64 a string like "1.2.840.113549.1.1.5" + /// is returned, otherwise a hex representation. + /// + /// See also the "bigint" feature of this crate. + pub fn to_id_string(&self) -> String { + if let Some(arcs) = self.iter() { + let ints: Vec<String> = arcs.map(|i| i.to_string()).collect(); + ints.join(".") + } else { + let mut ret = String::with_capacity(self.asn1.len() * 3); + for (i, o) in self.asn1.iter().enumerate() { + ret.push_str(&format!("{:02x}", o)); + if i + 1 != self.asn1.len() { + ret.push(' '); + } + } + ret + } + } + + /// Return an iterator over the sub-identifiers (arcs). + #[cfg(feature = "bigint")] + pub fn iter_bigint( + &'_ self, + ) -> impl Iterator<Item = BigUint> + FusedIterator + ExactSizeIterator + '_ { + SubIdentifierIterator { + oid: self, + pos: 0, + first: false, + n: PhantomData, + } + } + + /// Return an iterator over the sub-identifiers (arcs). + /// Returns `None` if at least one arc does not fit into `u64`. + pub fn iter( + &'_ self, + ) -> Option<impl Iterator<Item = u64> + FusedIterator + ExactSizeIterator + '_> { + // Check that every arc fits into u64 + let bytes = if self.relative { + &self.asn1 + } else if self.asn1.is_empty() { + &[] + } else { + &self.asn1[1..] + }; + let max_bits = bytes + .iter() + .fold((0usize, 0usize), |(max, cur), c| { + let is_end = (c >> 7) == 0u8; + if is_end { + (max.max(cur + 7), 0) + } else { + (max, cur + 7) + } + }) + .0; + if max_bits > 64 { + return None; + } + + Some(SubIdentifierIterator { + oid: self, + pos: 0, + first: false, + n: PhantomData, + }) + } + + pub fn from_ber_relative(bytes: &'a [u8]) -> ParseResult<'a, Self> { + let (rem, any) = Any::from_ber(bytes)?; + any.header.assert_primitive()?; + any.header.assert_tag(Tag::RelativeOid)?; + let asn1 = Cow::Borrowed(any.data); + Ok((rem, Oid::new_relative(asn1))) + } + + pub fn from_der_relative(bytes: &'a [u8]) -> ParseResult<'a, Self> { + let (rem, any) = Any::from_der(bytes)?; + any.header.assert_tag(Tag::RelativeOid)?; + Self::check_constraints(&any)?; + let asn1 = Cow::Borrowed(any.data); + Ok((rem, Oid::new_relative(asn1))) + } + + /// Returns true if `needle` is a prefix of the OID. + pub fn starts_with(&self, needle: &Oid) -> bool { + self.asn1.len() >= needle.asn1.len() && self.asn1.starts_with(needle.as_bytes()) + } +} + +trait Repr: Num + Shl<usize, Output = Self> + From<u8> {} +impl<N> Repr for N where N: Num + Shl<usize, Output = N> + From<u8> {} + +struct SubIdentifierIterator<'a, N: Repr> { + oid: &'a Oid<'a>, + pos: usize, + first: bool, + n: PhantomData<&'a N>, +} + +impl<'a, N: Repr> Iterator for SubIdentifierIterator<'a, N> { + type Item = N; + + fn next(&mut self) -> Option<Self::Item> { + use num_traits::identities::Zero; + + if self.pos == self.oid.asn1.len() { + return None; + } + if !self.oid.relative { + if !self.first { + debug_assert!(self.pos == 0); + self.first = true; + return Some((self.oid.asn1[0] / 40).into()); + } else if self.pos == 0 { + self.pos += 1; + if self.oid.asn1[0] == 0 && self.oid.asn1.len() == 1 { + return None; + } + return Some((self.oid.asn1[0] % 40).into()); + } + } + // decode objet sub-identifier according to the asn.1 standard + let mut res = <N as Zero>::zero(); + for o in self.oid.asn1[self.pos..].iter() { + self.pos += 1; + res = (res << 7) + (o & 0b111_1111).into(); + let flag = o >> 7; + if flag == 0u8 { + break; + } + } + Some(res) + } +} + +impl<'a, N: Repr> FusedIterator for SubIdentifierIterator<'a, N> {} + +impl<'a, N: Repr> ExactSizeIterator for SubIdentifierIterator<'a, N> { + fn len(&self) -> usize { + if self.oid.relative { + self.oid.asn1.iter().filter(|o| (*o >> 7) == 0u8).count() + } else if self.oid.asn1.len() == 0 { + 0 + } else if self.oid.asn1.len() == 1 { + if self.oid.asn1[0] == 0 { + 1 + } else { + 2 + } + } else { + 2 + self.oid.asn1[2..] + .iter() + .filter(|o| (*o >> 7) == 0u8) + .count() + } + } + + #[cfg(feature = "exact_size_is_empty")] + fn is_empty(&self) -> bool { + self.oid.asn1.is_empty() + } +} + +impl<'a> fmt::Display for Oid<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.relative { + f.write_str("rel. ")?; + } + f.write_str(&self.to_id_string()) + } +} + +impl<'a> fmt::Debug for Oid<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("OID(")?; + <Oid as fmt::Display>::fmt(self, f)?; + f.write_str(")") + } +} + +impl<'a> FromStr for Oid<'a> { + type Err = OidParseError; + + fn from_str(s: &str) -> core::result::Result<Self, Self::Err> { + let v: core::result::Result<Vec<_>, _> = s.split('.').map(|c| c.parse::<u64>()).collect(); + v.map_err(|_| OidParseError::ParseIntError) + .and_then(|v| Oid::from(&v)) + } +} + +/// Helper macro to declare integers at compile-time +/// +/// Since the DER encoded oids are not very readable we provide a +/// procedural macro `oid!`. The macro can be used the following ways: +/// +/// - `oid!(1.4.42.23)`: Create a const expression for the corresponding `Oid<'static>` +/// - `oid!(rel 42.23)`: Create a const expression for the corresponding relative `Oid<'static>` +/// - `oid!(raw 1.4.42.23)`/`oid!(raw rel 42.23)`: Obtain the DER encoded form as a byte array. +/// +/// # Comparing oids +/// +/// Comparing a parsed oid to a static oid is probably the most common +/// thing done with oids in your code. The `oid!` macro can be used in expression positions for +/// this purpose. For example +/// ``` +/// use asn1_rs::{oid, Oid}; +/// +/// # let some_oid: Oid<'static> = oid!(1.2.456); +/// const SOME_STATIC_OID: Oid<'static> = oid!(1.2.456); +/// assert_eq!(some_oid, SOME_STATIC_OID) +/// ``` +/// To get a relative Oid use `oid!(rel 1.2)`. +/// +/// Because of limitations for procedural macros ([rust issue](https://github.com/rust-lang/rust/issues/54727)) +/// and constants used in patterns ([rust issue](https://github.com/rust-lang/rust/issues/31434)) +/// the `oid` macro can not directly be used in patterns, also not through constants. +/// You can do this, though: +/// ``` +/// # use asn1_rs::{oid, Oid}; +/// # let some_oid: Oid<'static> = oid!(1.2.456); +/// const SOME_OID: Oid<'static> = oid!(1.2.456); +/// if some_oid == SOME_OID || some_oid == oid!(1.2.456) { +/// println!("match"); +/// } +/// +/// // Alternatively, compare the DER encoded form directly: +/// const SOME_OID_RAW: &[u8] = &oid!(raw 1.2.456); +/// match some_oid.as_bytes() { +/// SOME_OID_RAW => println!("match"), +/// _ => panic!("no match"), +/// } +/// ``` +/// *Attention*, be aware that the latter version might not handle the case of a relative oid correctly. An +/// extra check might be necessary. +#[macro_export] +macro_rules! oid { + (raw $items:expr) => { + $crate::exports::asn1_rs_impl::encode_oid!($items) + }; + (rel $items:expr) => { + $crate::Oid::new_relative($crate::exports::borrow::Cow::Borrowed( + &$crate::exports::asn1_rs_impl::encode_oid!(rel $items), + )) + }; + ($items:expr) => { + $crate::Oid::new($crate::exports::borrow::Cow::Borrowed( + &$crate::oid!(raw $items), + )) + }; +} + +#[cfg(test)] +mod tests { + use crate::{FromDer, Oid, ToDer}; + use hex_literal::hex; + + #[test] + fn declare_oid() { + let oid = super::oid! {1.2.840.113549.1}; + assert_eq!(oid.to_string(), "1.2.840.113549.1"); + } + + const OID_RSA_ENCRYPTION: &[u8] = &oid! {raw 1.2.840.113549.1.1.1}; + const OID_EC_PUBLIC_KEY: &[u8] = &oid! {raw 1.2.840.10045.2.1}; + #[allow(clippy::match_like_matches_macro)] + fn compare_oid(oid: &Oid) -> bool { + match oid.as_bytes() { + OID_RSA_ENCRYPTION => true, + OID_EC_PUBLIC_KEY => true, + _ => false, + } + } + + #[test] + fn test_compare_oid() { + let oid = Oid::from(&[1, 2, 840, 113_549, 1, 1, 1]).unwrap(); + assert_eq!(oid, oid! {1.2.840.113549.1.1.1}); + let oid = Oid::from(&[1, 2, 840, 113_549, 1, 1, 1]).unwrap(); + assert!(compare_oid(&oid)); + } + + #[test] + fn oid_to_der() { + let oid = super::oid! {1.2.840.113549.1}; + assert_eq!(oid.to_der_len(), Ok(9)); + let v = oid.to_der_vec().expect("could not serialize"); + assert_eq!(&v, &hex! {"06 07 2a 86 48 86 f7 0d 01"}); + let (_, oid2) = Oid::from_der(&v).expect("could not re-parse"); + assert_eq!(&oid, &oid2); + } + + #[test] + fn oid_starts_with() { + const OID_RSA_ENCRYPTION: Oid = oid! {1.2.840.113549.1.1.1}; + const OID_EC_PUBLIC_KEY: Oid = oid! {1.2.840.10045.2.1}; + let oid = super::oid! {1.2.840.113549.1}; + assert!(OID_RSA_ENCRYPTION.starts_with(&oid)); + assert!(!OID_EC_PUBLIC_KEY.starts_with(&oid)); + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/optional.rs b/rust/vendor/asn1-rs/src/asn1_types/optional.rs new file mode 100644 index 0000000..a8027ab --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/optional.rs @@ -0,0 +1,87 @@ +use crate::*; + +// note: we cannot implement `TryFrom<Any<'a>> with generic errors for Option<T>`, +// because this conflicts with generic `T` implementation in +// `src/traits.rs`, since `T` always satisfies `T: Into<Option<T>>` +// +// for the same reason, we cannot use a generic error type here +impl<'a, T> FromBer<'a> for Option<T> +where + T: FromBer<'a>, +{ + fn from_ber(bytes: &'a [u8]) -> ParseResult<Self> { + if bytes.is_empty() { + return Ok((bytes, None)); + } + match T::from_ber(bytes) { + Ok((rem, t)) => Ok((rem, Some(t))), + Err(nom::Err::Error(Error::UnexpectedTag { .. })) => Ok((bytes, None)), + Err(e) => Err(e), + } + } +} + +impl<'a, T> FromDer<'a> for Option<T> +where + T: FromDer<'a>, +{ + fn from_der(bytes: &'a [u8]) -> ParseResult<Self> { + if bytes.is_empty() { + return Ok((bytes, None)); + } + match T::from_der(bytes) { + Ok((rem, t)) => Ok((rem, Some(t))), + Err(nom::Err::Error(Error::UnexpectedTag { .. })) => Ok((bytes, None)), + Err(e) => Err(e), + } + } +} + +impl<T> CheckDerConstraints for Option<T> +where + T: CheckDerConstraints, +{ + fn check_constraints(any: &Any) -> Result<()> { + T::check_constraints(any) + } +} + +impl<T> DynTagged for Option<T> +where + T: DynTagged, +{ + fn tag(&self) -> Tag { + if self.is_some() { + self.tag() + } else { + Tag(0) + } + } +} + +#[cfg(feature = "std")] +impl<T> ToDer for Option<T> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + match self { + None => Ok(0), + Some(t) => t.to_der_len(), + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + match self { + None => Ok(0), + Some(t) => t.write_der_header(writer), + } + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + match self { + None => Ok(0), + Some(t) => t.write_der_content(writer), + } + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/real.rs b/rust/vendor/asn1-rs/src/asn1_types/real.rs new file mode 100644 index 0000000..db72a0d --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/real.rs @@ -0,0 +1,462 @@ +use crate::*; +use alloc::format; +use core::convert::TryFrom; +use nom::Needed; + +mod f32; +mod f64; +pub use self::f32::*; +pub use self::f64::*; + +/// ASN.1 `REAL` type +/// +/// # Limitations +/// +/// When encoding binary values, only base 2 is supported +#[derive(Debug, PartialEq)] +pub enum Real { + /// Non-special values + Binary { + mantissa: f64, + base: u32, + exponent: i32, + enc_base: u8, + }, + /// Infinity (∞). + Infinity, + /// Negative infinity (−∞). + NegInfinity, + /// Zero + Zero, +} + +impl Real { + /// Create a new `REAL` from the `f64` value. + pub fn new(f: f64) -> Self { + if f.is_infinite() { + if f.is_sign_positive() { + Self::Infinity + } else { + Self::NegInfinity + } + } else if f.abs() == 0.0 { + Self::Zero + } else { + let mut e = 0; + let mut f = f; + while f.fract() != 0.0 { + f *= 10.0_f64; + e -= 1; + } + Real::Binary { + mantissa: f, + base: 10, + exponent: e, + enc_base: 10, + } + .normalize_base10() + } + } + + pub const fn with_enc_base(self, enc_base: u8) -> Self { + match self { + Real::Binary { + mantissa, + base, + exponent, + .. + } => Real::Binary { + mantissa, + base, + exponent, + enc_base, + }, + e => e, + } + } + + fn normalize_base10(self) -> Self { + match self { + Real::Binary { + mantissa, + base: 10, + exponent, + enc_base: _enc_base, + } => { + let mut m = mantissa; + let mut e = exponent; + while m.abs() > f64::EPSILON && m.rem_euclid(10.0).abs() < f64::EPSILON { + m /= 10.0; + e += 1; + } + Real::Binary { + mantissa: m, + base: 10, + exponent: e, + enc_base: _enc_base, + } + } + _ => self, + } + } + + /// Create a new binary `REAL` + #[inline] + pub const fn binary(mantissa: f64, base: u32, exponent: i32) -> Self { + Self::Binary { + mantissa, + base, + exponent, + enc_base: 2, + } + } + + /// Returns `true` if this value is positive infinity or negative infinity, and + /// `false` otherwise. + #[inline] + pub fn is_infinite(&self) -> bool { + matches!(self, Real::Infinity | Real::NegInfinity) + } + + /// Returns `true` if this number is not infinite. + #[inline] + pub fn is_finite(&self) -> bool { + matches!(self, Real::Zero | Real::Binary { .. }) + } + + /// Returns the 'f64' value of this `REAL`. + /// + /// Returned value is a float, and may be infinite. + pub fn f64(&self) -> f64 { + match self { + Real::Binary { + mantissa, + base, + exponent, + .. + } => { + let f = mantissa; + let exp = (*base as f64).powi(*exponent); + f * exp + } + Real::Zero => 0.0_f64, + Real::Infinity => f64::INFINITY, + Real::NegInfinity => f64::NEG_INFINITY, + } + } + + /// Returns the 'f32' value of this `REAL`. + /// + /// This functions casts the result of [`Real::f64`] to a `f32`, and loses precision. + pub fn f32(&self) -> f32 { + self.f64() as f32 + } +} + +impl<'a> TryFrom<Any<'a>> for Real { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_primitive()?; + let data = &any.data; + if data.is_empty() { + return Ok(Real::Zero); + } + // code inspired from pyasn1 + let first = data[0]; + let rem = &data[1..]; + if first & 0x80 != 0 { + // binary encoding (X.690 section 8.5.6) + let rem = rem; + // format of exponent + let (n, rem) = match first & 0x03 { + 4 => { + let (b, rem) = rem + .split_first() + .ok_or_else(|| Error::Incomplete(Needed::new(1)))?; + (*b as usize, rem) + } + b => (b as usize + 1, rem), + }; + if n >= rem.len() { + return Err(any.tag().invalid_value("Invalid float value(exponent)")); + } + // n cannot be 0 (see the +1 above) + let (eo, rem) = rem.split_at(n); + // so 'eo' cannot be empty + let mut e = if eo[0] & 0x80 != 0 { -1 } else { 0 }; + // safety check: 'eo' length must be <= container type for 'e' + if eo.len() > 4 { + return Err(any.tag().invalid_value("Exponent too large (REAL)")); + } + for b in eo { + e = (e << 8) | (*b as i32); + } + // base bits + let b = (first >> 4) & 0x03; + let _enc_base = match b { + 0 => 2, + 1 => 8, + 2 => 16, + _ => return Err(any.tag().invalid_value("Illegal REAL encoding base")), + }; + let e = match b { + // base 2 + 0 => e, + // base 8 + 1 => e * 3, + // base 16 + 2 => e * 4, + _ => return Err(any.tag().invalid_value("Illegal REAL base")), + }; + if rem.len() > 8 { + return Err(any.tag().invalid_value("Mantissa too large (REAL)")); + } + let mut p = 0; + for b in rem { + p = (p << 8) | (*b as i64); + } + // sign bit + let p = if first & 0x40 != 0 { -p } else { p }; + // scale bits + let sf = (first >> 2) & 0x03; + let p = match sf { + 0 => p as f64, + sf => { + // 2^sf: cannot overflow, sf is between 0 and 3 + let scale = 2_f64.powi(sf as _); + (p as f64) * scale + } + }; + Ok(Real::Binary { + mantissa: p, + base: 2, + exponent: e, + enc_base: _enc_base, + }) + } else if first & 0x40 != 0 { + // special real value (X.690 section 8.5.8) + // there shall be only one contents octet, + if any.header.length != Length::Definite(1) { + return Err(Error::InvalidLength); + } + // with values as follows + match first { + 0x40 => Ok(Real::Infinity), + 0x41 => Ok(Real::NegInfinity), + _ => Err(any.tag().invalid_value("Invalid float special value")), + } + } else { + // decimal encoding (X.690 section 8.5.7) + let s = alloc::str::from_utf8(rem)?; + match first & 0x03 { + 0x1 => { + // NR1 + match s.parse::<u32>() { + Err(_) => Err(any.tag().invalid_value("Invalid float string encoding")), + Ok(v) => Ok(Real::new(v.into())), + } + } + 0x2 /* NR2 */ | 0x3 /* NR3 */=> { + match s.parse::<f64>() { + Err(_) => Err(any.tag().invalid_value("Invalid float string encoding")), + Ok(v) => Ok(Real::new(v)), + } + } + c => { + Err(any.tag().invalid_value(&format!("Invalid NR ({})", c))) + } + } + } + } +} + +impl CheckDerConstraints for Real { + fn check_constraints(any: &Any) -> Result<()> { + any.header.assert_primitive()?; + any.header.length.assert_definite()?; + // XXX more checks + Ok(()) + } +} + +impl DerAutoDerive for Real {} + +impl Tagged for Real { + const TAG: Tag = Tag::RealType; +} + +#[cfg(feature = "std")] +impl ToDer for Real { + fn to_der_len(&self) -> Result<usize> { + match self { + Real::Zero => Ok(0), + Real::Infinity | Real::NegInfinity => Ok(1), + Real::Binary { .. } => { + let mut sink = std::io::sink(); + let n = self + .write_der_content(&mut sink) + .map_err(|_| Self::TAG.invalid_value("Serialization of REAL failed"))?; + Ok(n) + } + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(self.to_der_len()?), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + match self { + Real::Zero => Ok(0), + Real::Infinity => writer.write(&[0x40]).map_err(Into::into), + Real::NegInfinity => writer.write(&[0x41]).map_err(Into::into), + Real::Binary { + mantissa, + base, + exponent, + enc_base: _enc_base, + } => { + if *base == 10 { + // using character form + let sign = if *exponent == 0 { "+" } else { "" }; + let s = format!("\x03{}E{}{}", mantissa, sign, exponent); + return writer.write(s.as_bytes()).map_err(Into::into); + } + if *base != 2 { + return Err(Self::TAG.invalid_value("Invalid base for REAL").into()); + } + let mut first: u8 = 0x80; + // choose encoding base + let enc_base = *_enc_base; + let (ms, mut m, enc_base, mut e) = + drop_floating_point(*mantissa, enc_base, *exponent); + assert!(m != 0); + if ms < 0 { + first |= 0x40 + }; + // exponent & mantissa normalization + match enc_base { + 2 => { + while m & 0x1 == 0 { + m >>= 1; + e += 1; + } + } + 8 => { + while m & 0x7 == 0 { + m >>= 3; + e += 1; + } + first |= 0x10; + } + _ /* 16 */ => { + while m & 0xf == 0 { + m >>= 4; + e += 1; + } + first |= 0x20; + } + } + // scale factor + // XXX in DER, sf is always 0 (11.3.1) + let mut sf = 0; + while m & 0x1 == 0 && sf < 4 { + m >>= 1; + sf += 1; + } + first |= sf << 2; + // exponent length and bytes + let len_e = match e.abs() { + 0..=0xff => 1, + 0x100..=0xffff => 2, + 0x1_0000..=0xff_ffff => 3, + // e is an `i32` so it can't be longer than 4 bytes + // use 4, so `first` is ORed with 3 + _ => 4, + }; + first |= (len_e - 1) & 0x3; + // write first byte + let mut n = writer.write(&[first])?; + // write exponent + // special case: number of bytes from exponent is > 3 and cannot fit in 2 bits + #[allow(clippy::identity_op)] + if len_e == 4 { + let b = len_e & 0xff; + n += writer.write(&[b])?; + } + // we only need to write e.len() bytes + let bytes = e.to_be_bytes(); + n += writer.write(&bytes[(4 - len_e) as usize..])?; + // write mantissa + let bytes = m.to_be_bytes(); + let mut idx = 0; + for &b in bytes.iter() { + if b != 0 { + break; + } + idx += 1; + } + n += writer.write(&bytes[idx..])?; + Ok(n) + } + } + } +} + +impl From<f32> for Real { + fn from(f: f32) -> Self { + Real::new(f.into()) + } +} + +impl From<f64> for Real { + fn from(f: f64) -> Self { + Real::new(f) + } +} + +impl From<Real> for f32 { + fn from(r: Real) -> Self { + r.f32() + } +} + +impl From<Real> for f64 { + fn from(r: Real) -> Self { + r.f64() + } +} + +#[cfg(feature = "std")] +fn drop_floating_point(m: f64, b: u8, e: i32) -> (i8, u64, u8, i32) { + let ms = if m.is_sign_positive() { 1 } else { -1 }; + let es = if e.is_positive() { 1 } else { -1 }; + let mut m = m.abs(); + let mut e = e; + // + if b == 8 { + m *= 2_f64.powi((e.abs() / 3) * es); + e = (e.abs() / 3) * es; + } else if b == 16 { + m *= 2_f64.powi((e.abs() / 4) * es); + e = (e.abs() / 4) * es; + } + // + while m.abs() > f64::EPSILON { + if m.fract() != 0.0 { + m *= b as f64; + e -= 1; + } else { + break; + } + } + (ms, m as u64, b, e) +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/real/f32.rs b/rust/vendor/asn1-rs/src/asn1_types/real/f32.rs new file mode 100644 index 0000000..cb41dbf --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/real/f32.rs @@ -0,0 +1,27 @@ +use crate::{Any, CheckDerConstraints, DerAutoDerive, Error, Real, Result, Tag, Tagged}; +use core::convert::{TryFrom, TryInto}; + +impl<'a> TryFrom<Any<'a>> for f32 { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<f32> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_primitive()?; + let real: Real = any.try_into()?; + Ok(real.f32()) + } +} + +impl CheckDerConstraints for f32 { + fn check_constraints(any: &Any) -> Result<()> { + any.header.assert_primitive()?; + any.header.length.assert_definite()?; + Ok(()) + } +} + +impl DerAutoDerive for f32 {} + +impl Tagged for f32 { + const TAG: Tag = Tag::RealType; +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/real/f64.rs b/rust/vendor/asn1-rs/src/asn1_types/real/f64.rs new file mode 100644 index 0000000..4986d9b --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/real/f64.rs @@ -0,0 +1,27 @@ +use crate::{Any, CheckDerConstraints, DerAutoDerive, Error, Real, Result, Tag, Tagged}; +use core::convert::{TryFrom, TryInto}; + +impl<'a> TryFrom<Any<'a>> for f64 { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<f64> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_primitive()?; + let real: Real = any.try_into()?; + Ok(real.f64()) + } +} + +impl CheckDerConstraints for f64 { + fn check_constraints(any: &Any) -> Result<()> { + any.header.assert_primitive()?; + any.header.length.assert_definite()?; + Ok(()) + } +} + +impl DerAutoDerive for f64 {} + +impl Tagged for f64 { + const TAG: Tag = Tag::RealType; +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/sequence.rs b/rust/vendor/asn1-rs/src/asn1_types/sequence.rs new file mode 100644 index 0000000..3a65ec0 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/sequence.rs @@ -0,0 +1,398 @@ +use crate::*; +use alloc::borrow::Cow; +use alloc::vec::Vec; +use core::convert::TryFrom; + +mod iterator; +mod sequence_of; +mod vec; + +pub use iterator::*; +pub use sequence_of::*; +pub use vec::*; + +/// The `SEQUENCE` object is an ordered list of heteregeneous types. +/// +/// Sequences can usually be of 2 types: +/// - a list of different objects (`SEQUENCE`, usually parsed as a `struct`) +/// - a list of similar objects (`SEQUENCE OF`, usually parsed as a `Vec<T>`) +/// +/// The current object covers the former. For the latter, see the [`SequenceOf`] documentation. +/// +/// The `Sequence` object contains the (*unparsed*) encoded representation of its content. It provides +/// methods to parse and iterate contained objects, or convert the sequence to other types. +/// +/// # Building a Sequence +/// +/// To build a DER sequence: +/// - if the sequence is composed of objects of the same type, the [`Sequence::from_iter_to_der`] method can be used +/// - otherwise, the [`ToDer`] trait can be used to create content incrementally +/// +/// ``` +/// use asn1_rs::{Integer, Sequence, SerializeResult, ToDer}; +/// +/// fn build_seq<'a>() -> SerializeResult<Sequence<'a>> { +/// let mut v = Vec::new(); +/// // add an Integer object (construct type): +/// let i = Integer::from_u32(4); +/// let _ = i.write_der(&mut v)?; +/// // some primitive objects also implement `ToDer`. A string will be mapped as `Utf8String`: +/// let _ = "abcd".write_der(&mut v)?; +/// // return the sequence built from the DER content +/// Ok(Sequence::new(v.into())) +/// } +/// +/// let seq = build_seq().unwrap(); +/// +/// ``` +/// +/// # Examples +/// +/// ``` +/// use asn1_rs::{Error, Sequence}; +/// +/// // build sequence +/// let it = [2, 3, 4].iter(); +/// let seq = Sequence::from_iter_to_der(it).unwrap(); +/// +/// // `seq` now contains the serialized DER representation of the array +/// +/// // iterate objects +/// let mut sum = 0; +/// for item in seq.der_iter::<u32, Error>() { +/// // item has type `Result<u32>`, since parsing the serialized bytes could fail +/// sum += item.expect("parsing list item failed"); +/// } +/// assert_eq!(sum, 9); +/// +/// ``` +/// +/// Note: the above example encodes a `SEQUENCE OF INTEGER` object, the [`SequenceOf`] object could +/// be used to provide a simpler API. +/// +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Sequence<'a> { + /// Serialized DER representation of the sequence content + pub content: Cow<'a, [u8]>, +} + +impl<'a> Sequence<'a> { + /// Build a sequence, given the provided content + pub const fn new(content: Cow<'a, [u8]>) -> Self { + Sequence { content } + } + + /// Consume the sequence and return the content + #[inline] + pub fn into_content(self) -> Cow<'a, [u8]> { + self.content + } + + /// Apply the parsing function to the sequence content, consuming the sequence + /// + /// Note: this function expects the caller to take ownership of content. + /// In some cases, handling the lifetime of objects is not easy (when keeping only references on + /// data). Other methods are provided (depending on the use case): + /// - [`Sequence::parse`] takes a reference on the sequence data, but does not consume it, + /// - [`Sequence::from_der_and_then`] does the parsing of the sequence and applying the function + /// in one step, ensuring there are only references (and dropping the temporary sequence). + pub fn and_then<U, F, E>(self, op: F) -> ParseResult<'a, U, E> + where + F: FnOnce(Cow<'a, [u8]>) -> ParseResult<U, E>, + { + op(self.content) + } + + /// Same as [`Sequence::from_der_and_then`], but using BER encoding (no constraints). + pub fn from_ber_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E> + where + F: FnOnce(&'a [u8]) -> ParseResult<U, E>, + E: From<Error>, + { + let (rem, seq) = Sequence::from_ber(bytes).map_err(Err::convert)?; + let data = match seq.content { + Cow::Borrowed(b) => b, + // Since 'any' is built from 'bytes', it is borrowed by construction + Cow::Owned(_) => unreachable!(), + }; + let (_, res) = op(data)?; + Ok((rem, res)) + } + + /// Parse a DER sequence and apply the provided parsing function to content + /// + /// After parsing, the sequence object and header are discarded. + /// + /// ``` + /// use asn1_rs::{FromDer, ParseResult, Sequence}; + /// + /// // Parse a SEQUENCE { + /// // a INTEGER (0..255), + /// // b INTEGER (0..4294967296) + /// // } + /// // and return only `(a,b) + /// fn parser(i: &[u8]) -> ParseResult<(u8, u32)> { + /// Sequence::from_der_and_then(i, |i| { + /// let (i, a) = u8::from_der(i)?; + /// let (i, b) = u32::from_der(i)?; + /// Ok((i, (a, b))) + /// } + /// ) + /// } + /// ``` + pub fn from_der_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E> + where + F: FnOnce(&'a [u8]) -> ParseResult<U, E>, + E: From<Error>, + { + let (rem, seq) = Sequence::from_der(bytes).map_err(Err::convert)?; + let data = match seq.content { + Cow::Borrowed(b) => b, + // Since 'any' is built from 'bytes', it is borrowed by construction + Cow::Owned(_) => unreachable!(), + }; + let (_, res) = op(data)?; + Ok((rem, res)) + } + + /// Apply the parsing function to the sequence content (non-consuming version) + pub fn parse<F, T, E>(&'a self, mut f: F) -> ParseResult<'a, T, E> + where + F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>, + { + let input: &[u8] = &self.content; + f(input) + } + + /// Apply the parsing function to the sequence content (consuming version) + /// + /// Note: to parse and apply a parsing function in one step, use the + /// [`Sequence::from_der_and_then`] method. + /// + /// # Limitations + /// + /// This function fails if the sequence contains `Owned` data, because the parsing function + /// takes a reference on data (which is dropped). + pub fn parse_into<F, T, E>(self, mut f: F) -> ParseResult<'a, T, E> + where + F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>, + E: From<Error>, + { + match self.content { + Cow::Borrowed(b) => f(b), + _ => Err(nom::Err::Error(Error::LifetimeError.into())), + } + } + + /// Return an iterator over the sequence content, attempting to decode objects as BER + /// + /// This method can be used when all objects from the sequence have the same type. + pub fn ber_iter<T, E>(&'a self) -> SequenceIterator<'a, T, BerParser, E> + where + T: FromBer<'a, E>, + { + SequenceIterator::new(&self.content) + } + + /// Return an iterator over the sequence content, attempting to decode objects as DER + /// + /// This method can be used when all objects from the sequence have the same type. + pub fn der_iter<T, E>(&'a self) -> SequenceIterator<'a, T, DerParser, E> + where + T: FromDer<'a, E>, + { + SequenceIterator::new(&self.content) + } + + /// Attempt to parse the sequence as a `SEQUENCE OF` items (BER), and return the parsed items as a `Vec`. + pub fn ber_sequence_of<T, E>(&'a self) -> Result<Vec<T>, E> + where + T: FromBer<'a, E>, + E: From<Error>, + { + self.ber_iter().collect() + } + + /// Attempt to parse the sequence as a `SEQUENCE OF` items (DER), and return the parsed items as a `Vec`. + pub fn der_sequence_of<T, E>(&'a self) -> Result<Vec<T>, E> + where + T: FromDer<'a, E>, + E: From<Error>, + { + self.der_iter().collect() + } + + /// Attempt to parse the sequence as a `SEQUENCE OF` items (BER) (consuming input), + /// and return the parsed items as a `Vec`. + /// + /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects. + pub fn into_ber_sequence_of<T, U, E>(self) -> Result<Vec<T>, E> + where + for<'b> T: FromBer<'b, E>, + E: From<Error>, + T: ToStatic<Owned = T>, + { + match self.content { + Cow::Borrowed(bytes) => SequenceIterator::<T, BerParser, E>::new(bytes).collect(), + Cow::Owned(data) => { + let v1 = SequenceIterator::<T, BerParser, E>::new(&data) + .collect::<Result<Vec<T>, E>>()?; + let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>(); + Ok(v2) + } + } + } + + /// Attempt to parse the sequence as a `SEQUENCE OF` items (DER) (consuming input), + /// and return the parsed items as a `Vec`. + /// + /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects. + pub fn into_der_sequence_of<T, U, E>(self) -> Result<Vec<T>, E> + where + for<'b> T: FromDer<'b, E>, + E: From<Error>, + T: ToStatic<Owned = T>, + { + match self.content { + Cow::Borrowed(bytes) => SequenceIterator::<T, DerParser, E>::new(bytes).collect(), + Cow::Owned(data) => { + let v1 = SequenceIterator::<T, DerParser, E>::new(&data) + .collect::<Result<Vec<T>, E>>()?; + let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>(); + Ok(v2) + } + } + } + + pub fn into_der_sequence_of_ref<T, E>(self) -> Result<Vec<T>, E> + where + T: FromDer<'a, E>, + E: From<Error>, + { + match self.content { + Cow::Borrowed(bytes) => SequenceIterator::<T, DerParser, E>::new(bytes).collect(), + Cow::Owned(_) => Err(Error::LifetimeError.into()), + } + } +} + +impl<'a> ToStatic for Sequence<'a> { + type Owned = Sequence<'static>; + + fn to_static(&self) -> Self::Owned { + Sequence { + content: Cow::Owned(self.content.to_vec()), + } + } +} + +impl<T, U> ToStatic for Vec<T> +where + T: ToStatic<Owned = U>, + U: 'static, +{ + type Owned = Vec<U>; + + fn to_static(&self) -> Self::Owned { + self.iter().map(|t| t.to_static()).collect() + } +} + +impl<'a> AsRef<[u8]> for Sequence<'a> { + fn as_ref(&self) -> &[u8] { + &self.content + } +} + +impl<'a> TryFrom<Any<'a>> for Sequence<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Sequence<'a>> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for Sequence<'a> { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Sequence<'a>> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + Ok(Sequence { + content: Cow::Borrowed(any.data), + }) + } +} + +impl<'a> CheckDerConstraints for Sequence<'a> { + fn check_constraints(_any: &Any) -> Result<()> { + // TODO: iterate on ANY objects and check constraints? -> this will not be exhaustive + // test, for ex INTEGER encoding will not be checked + Ok(()) + } +} + +impl<'a> DerAutoDerive for Sequence<'a> {} + +impl<'a> Tagged for Sequence<'a> { + const TAG: Tag = Tag::Sequence; +} + +#[cfg(feature = "std")] +impl ToDer for Sequence<'_> { + fn to_der_len(&self) -> Result<usize> { + let sz = self.content.len(); + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + true, + Self::TAG, + Length::Definite(self.content.len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(&self.content).map_err(Into::into) + } +} + +#[cfg(feature = "std")] +impl<'a> Sequence<'a> { + /// Attempt to create a `Sequence` from an iterator over serializable objects (to DER) + /// + /// # Examples + /// + /// ``` + /// use asn1_rs::Sequence; + /// + /// // build sequence + /// let it = [2, 3, 4].iter(); + /// let seq = Sequence::from_iter_to_der(it).unwrap(); + /// ``` + pub fn from_iter_to_der<T, IT>(it: IT) -> SerializeResult<Self> + where + IT: Iterator<Item = T>, + T: ToDer, + T: Tagged, + { + let mut v = Vec::new(); + for item in it { + let item_v = <T as ToDer>::to_der_vec(&item)?; + v.extend_from_slice(&item_v); + } + Ok(Sequence { + content: Cow::Owned(v), + }) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/sequence/iterator.rs b/rust/vendor/asn1-rs/src/asn1_types/sequence/iterator.rs new file mode 100644 index 0000000..0ff5fc9 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/sequence/iterator.rs @@ -0,0 +1,106 @@ +use crate::{ASN1Parser, BerParser, DerParser, Error, FromBer, FromDer}; +use core::marker::PhantomData; + +/// An Iterator over binary data, parsing elements of type `T` +/// +/// This helps parsing `SEQUENCE OF` items of type `T`. The type of parser +/// (BER/DER) is specified using the generic parameter `F` of this struct. +/// +/// Note: the iterator must start on the sequence *contents*, not the sequence itself. +/// +/// # Examples +/// +/// ```rust +/// use asn1_rs::{DerParser, Integer, SequenceIterator}; +/// +/// let data = &[0x30, 0x6, 0x2, 0x1, 0x1, 0x2, 0x1, 0x2]; +/// for (idx, item) in SequenceIterator::<Integer, DerParser>::new(&data[2..]).enumerate() { +/// let item = item.unwrap(); // parsing could have failed +/// let i = item.as_u32().unwrap(); // integer can be negative, or too large to fit into u32 +/// assert_eq!(i as usize, idx + 1); +/// } +/// ``` +#[derive(Debug)] +pub struct SequenceIterator<'a, T, F, E = Error> +where + F: ASN1Parser, +{ + data: &'a [u8], + has_error: bool, + _t: PhantomData<T>, + _f: PhantomData<F>, + _e: PhantomData<E>, +} + +impl<'a, T, F, E> SequenceIterator<'a, T, F, E> +where + F: ASN1Parser, +{ + pub fn new(data: &'a [u8]) -> Self { + SequenceIterator { + data, + has_error: false, + _t: PhantomData, + _f: PhantomData, + _e: PhantomData, + } + } +} + +impl<'a, T, E> Iterator for SequenceIterator<'a, T, BerParser, E> +where + T: FromBer<'a, E>, + E: From<Error>, +{ + type Item = Result<T, E>; + + fn next(&mut self) -> Option<Self::Item> { + if self.has_error || self.data.is_empty() { + return None; + } + match T::from_ber(self.data) { + Ok((rem, obj)) => { + self.data = rem; + Some(Ok(obj)) + } + Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => { + self.has_error = true; + Some(Err(e)) + } + + Err(nom::Err::Incomplete(n)) => { + self.has_error = true; + Some(Err(Error::Incomplete(n).into())) + } + } + } +} + +impl<'a, T, E> Iterator for SequenceIterator<'a, T, DerParser, E> +where + T: FromDer<'a, E>, + E: From<Error>, +{ + type Item = Result<T, E>; + + fn next(&mut self) -> Option<Self::Item> { + if self.has_error || self.data.is_empty() { + return None; + } + match T::from_der(self.data) { + Ok((rem, obj)) => { + self.data = rem; + Some(Ok(obj)) + } + Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => { + self.has_error = true; + Some(Err(e)) + } + + Err(nom::Err::Incomplete(n)) => { + self.has_error = true; + Some(Err(Error::Incomplete(n).into())) + } + } + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/sequence/sequence_of.rs b/rust/vendor/asn1-rs/src/asn1_types/sequence/sequence_of.rs new file mode 100644 index 0000000..239e72f --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/sequence/sequence_of.rs @@ -0,0 +1,150 @@ +use crate::*; +use alloc::vec::Vec; +use core::convert::TryFrom; +use core::iter::FromIterator; + +/// The `SEQUENCE OF` object is an ordered list of homogeneous types. +/// +/// # Examples +/// +/// ``` +/// use asn1_rs::SequenceOf; +/// use std::iter::FromIterator; +/// +/// // build set +/// let it = [2, 3, 4].iter(); +/// let seq = SequenceOf::from_iter(it); +/// +/// // `seq` now contains the serialized DER representation of the array +/// +/// // iterate objects +/// let mut sum = 0; +/// for item in seq.iter() { +/// // item has type `Result<u32>`, since parsing the serialized bytes could fail +/// sum += *item; +/// } +/// assert_eq!(sum, 9); +/// +/// ``` +#[derive(Debug)] +pub struct SequenceOf<T> { + pub(crate) items: Vec<T>, +} + +impl<T> SequenceOf<T> { + /// Builds a `SEQUENCE OF` from the provided content + #[inline] + pub const fn new(items: Vec<T>) -> Self { + SequenceOf { items } + } + + /// Returns the length of this `SEQUENCE` (the number of items). + #[inline] + pub fn len(&self) -> usize { + self.items.len() + } + + /// Returns `true` if this `SEQUENCE` is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } + + /// Returns an iterator over the items of the `SEQUENCE`. + #[inline] + pub fn iter(&self) -> impl Iterator<Item = &T> { + self.items.iter() + } +} + +impl<T> AsRef<[T]> for SequenceOf<T> { + fn as_ref(&self) -> &[T] { + &self.items + } +} + +impl<'a, T> IntoIterator for &'a SequenceOf<T> { + type Item = &'a T; + type IntoIter = core::slice::Iter<'a, T>; + + fn into_iter(self) -> core::slice::Iter<'a, T> { + self.items.iter() + } +} + +impl<'a, T> IntoIterator for &'a mut SequenceOf<T> { + type Item = &'a mut T; + type IntoIter = core::slice::IterMut<'a, T>; + + fn into_iter(self) -> core::slice::IterMut<'a, T> { + self.items.iter_mut() + } +} + +impl<T> From<SequenceOf<T>> for Vec<T> { + fn from(set: SequenceOf<T>) -> Self { + set.items + } +} + +impl<T> FromIterator<T> for SequenceOf<T> { + fn from_iter<IT: IntoIterator<Item = T>>(iter: IT) -> Self { + let items = iter.into_iter().collect(); + SequenceOf::new(items) + } +} + +impl<'a, T> TryFrom<Any<'a>> for SequenceOf<T> +where + T: FromBer<'a>, +{ + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + any.tag().assert_eq(Self::TAG)?; + if !any.header.is_constructed() { + return Err(Error::ConstructExpected); + } + let items = SequenceIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?; + Ok(SequenceOf::new(items)) + } +} + +impl<T> CheckDerConstraints for SequenceOf<T> +where + T: CheckDerConstraints, +{ + fn check_constraints(any: &Any) -> Result<()> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + for item in SequenceIterator::<Any, DerParser>::new(any.data) { + let item = item?; + T::check_constraints(&item)?; + } + Ok(()) + } +} + +impl<T> DerAutoDerive for SequenceOf<T> {} + +impl<T> Tagged for SequenceOf<T> { + const TAG: Tag = Tag::Sequence; +} + +#[cfg(feature = "std")] +impl<T> ToDer for SequenceOf<T> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + self.items.to_der_len() + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + self.items.write_der_header(writer) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + self.items.write_der_content(writer) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/sequence/vec.rs b/rust/vendor/asn1-rs/src/asn1_types/sequence/vec.rs new file mode 100644 index 0000000..d8beead --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/sequence/vec.rs @@ -0,0 +1,138 @@ +use crate::*; +use alloc::vec::Vec; +use core::convert::TryFrom; + +// // XXX this compiles but requires bound TryFrom :/ +// impl<'a, 'b, T> TryFrom<&'b Any<'a>> for Vec<T> +// where +// T: TryFrom<&'b Any<'a>>, +// for<'e> <T as TryFrom<&'b Any<'a>>>::Error: From<Error>, +// T: FromBer<'a, <T as TryFrom<&'b Any<'a>>>::Error>, +// // T: FromBer<'a, E>, +// // E: From<Error>, +// { +// type Error = <T as TryFrom<&'b Any<'a>>>::Error; + +// fn try_from(any: &'b Any<'a>) -> Result<Vec<T>, Self::Error> { +// any.tag().assert_eq(Self::TAG)?; +// any.header.assert_constructed()?; +// let v = SequenceIterator::<T, BerParser, Self::Error>::new(any.data) +// .collect::<Result<Vec<T>, Self::Error>>()?; +// Ok(v) +// } +// } + +// // XXX this compiles but requires bound TryFrom :/ +// impl<'a, 'b, T> TryFrom<&'b Any<'a>> for Vec<T> +// where +// T: TryFrom<&'b Any<'a>>, +// <T as TryFrom<&'b Any<'a>>>::Error: From<Error>, +// T: FromBer<'a, <T as TryFrom<&'b Any<'a>>>::Error>, +// // T: FromBer<'a, E>, +// // E: From<Error>, +// { +// type Error = <T as TryFrom<&'b Any<'a>>>::Error; + +// fn try_from(any: &'b Any<'a>) -> Result<Vec<T>, Self::Error> { +// any.tag().assert_eq(Self::TAG)?; +// any.header.assert_constructed()?; +// let v = SequenceIterator::<T, BerParser, Self::Error>::new(any.data) +// .collect::<Result<Vec<T>, Self::Error>>()?; +// Ok(v) +// } +// } + +impl<'a, T> TryFrom<Any<'a>> for Vec<T> +where + T: FromBer<'a>, +{ + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + let items = SetIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?; + Ok(items) + } +} + +impl<T> CheckDerConstraints for Vec<T> +where + T: CheckDerConstraints, +{ + fn check_constraints(any: &Any) -> Result<()> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + for item in SequenceIterator::<Any, DerParser>::new(any.data) { + let item = item?; + <T as CheckDerConstraints>::check_constraints(&item)?; + } + Ok(()) + } +} + +impl<T> Tagged for Vec<T> { + const TAG: Tag = Tag::Sequence; +} + +// impl<'a, T> FromBer<'a> for Vec<T> +// where +// T: FromBer<'a>, +// { +// fn from_ber(bytes: &'a [u8]) -> ParseResult<Self> { +// let (rem, any) = Any::from_ber(bytes)?; +// any.header.assert_tag(Self::TAG)?; +// let v = SequenceIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?; +// Ok((rem, v)) +// } +// } + +/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints` +impl<'a, T, E> FromDer<'a, E> for Vec<T> +where + T: FromDer<'a, E>, + E: From<Error>, +{ + fn from_der(bytes: &'a [u8]) -> ParseResult<Self, E> { + let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; + any.header + .assert_tag(Self::TAG) + .map_err(|e| nom::Err::Error(e.into()))?; + let v = SequenceIterator::<T, DerParser, E>::new(any.data) + .collect::<Result<Vec<T>, E>>() + .map_err(nom::Err::Error)?; + Ok((rem, v)) + } +} + +#[cfg(feature = "std")] +impl<T> ToDer for Vec<T> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + let mut len = 0; + for t in self.iter() { + len += t.to_der_len()?; + } + let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); + Ok(header.to_der_len()? + len) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut len = 0; + for t in self.iter() { + len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?; + } + let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut sz = 0; + for t in self.iter() { + sz += t.write_der(writer)?; + } + Ok(sz) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/set.rs b/rust/vendor/asn1-rs/src/asn1_types/set.rs new file mode 100644 index 0000000..ef693d2 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/set.rs @@ -0,0 +1,387 @@ +use crate::*; +use alloc::borrow::Cow; +use alloc::vec::Vec; +use core::convert::TryFrom; + +mod btreeset; +mod hashset; +mod iterator; +mod set_of; + +pub use btreeset::*; +#[cfg(feature = "std")] +pub use hashset::*; +pub use iterator::*; +pub use set_of::*; + +/// The `SET` object is an unordered list of heteregeneous types. +/// +/// Sets can usually be of 2 types: +/// - a list of different objects (`SET`, usually parsed as a `struct`) +/// - a list of similar objects (`SET OF`, usually parsed as a `BTreeSet<T>` or `HashSet<T>`) +/// +/// The current object covers the former. For the latter, see the [`SetOf`] documentation. +/// +/// The `Set` object contains the (*unparsed*) encoded representation of its content. It provides +/// methods to parse and iterate contained objects, or convert the sequence to other types. +/// +/// # Building a Set +/// +/// To build a DER set: +/// - if the set is composed of objects of the same type, the [`Set::from_iter_to_der`] method can be used +/// - otherwise, the [`ToDer`] trait can be used to create content incrementally +/// +/// ``` +/// use asn1_rs::{Integer, Set, SerializeResult, ToDer}; +/// +/// fn build_set<'a>() -> SerializeResult<Set<'a>> { +/// let mut v = Vec::new(); +/// // add an Integer object (construct type): +/// let i = Integer::from_u32(4); +/// let _ = i.write_der(&mut v)?; +/// // some primitive objects also implement `ToDer`. A string will be mapped as `Utf8String`: +/// let _ = "abcd".write_der(&mut v)?; +/// // return the set built from the DER content +/// Ok(Set::new(v.into())) +/// } +/// +/// let seq = build_set().unwrap(); +/// +/// ``` +/// +/// # Examples +/// +/// ``` +/// use asn1_rs::{Error, Set}; +/// +/// // build set +/// let it = [2, 3, 4].iter(); +/// let set = Set::from_iter_to_der(it).unwrap(); +/// +/// // `set` now contains the serialized DER representation of the array +/// +/// // iterate objects +/// let mut sum = 0; +/// for item in set.der_iter::<u32, Error>() { +/// // item has type `Result<u32>`, since parsing the serialized bytes could fail +/// sum += item.expect("parsing list item failed"); +/// } +/// assert_eq!(sum, 9); +/// +/// ``` +/// +/// Note: the above example encodes a `SET OF INTEGER` object, the [`SetOf`] object could +/// be used to provide a simpler API. +/// +#[derive(Clone, Debug)] +pub struct Set<'a> { + /// Serialized DER representation of the set content + pub content: Cow<'a, [u8]>, +} + +impl<'a> Set<'a> { + /// Build a set, given the provided content + pub const fn new(content: Cow<'a, [u8]>) -> Self { + Set { content } + } + + /// Consume the set and return the content + #[inline] + pub fn into_content(self) -> Cow<'a, [u8]> { + self.content + } + + /// Apply the parsing function to the set content, consuming the set + /// + /// Note: this function expects the caller to take ownership of content. + /// In some cases, handling the lifetime of objects is not easy (when keeping only references on + /// data). Other methods are provided (depending on the use case): + /// - [`Set::parse`] takes a reference on the set data, but does not consume it, + /// - [`Set::from_der_and_then`] does the parsing of the set and applying the function + /// in one step, ensuring there are only references (and dropping the temporary set). + pub fn and_then<U, F, E>(self, op: F) -> ParseResult<'a, U, E> + where + F: FnOnce(Cow<'a, [u8]>) -> ParseResult<U, E>, + { + op(self.content) + } + + /// Same as [`Set::from_der_and_then`], but using BER encoding (no constraints). + pub fn from_ber_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E> + where + F: FnOnce(&'a [u8]) -> ParseResult<U, E>, + E: From<Error>, + { + let (rem, seq) = Set::from_ber(bytes).map_err(Err::convert)?; + let data = match seq.content { + Cow::Borrowed(b) => b, + // Since 'any' is built from 'bytes', it is borrowed by construction + Cow::Owned(_) => unreachable!(), + }; + let (_, res) = op(data)?; + Ok((rem, res)) + } + + /// Parse a DER set and apply the provided parsing function to content + /// + /// After parsing, the set object and header are discarded. + /// + /// ``` + /// use asn1_rs::{FromDer, ParseResult, Set}; + /// + /// // Parse a SET { + /// // a INTEGER (0..255), + /// // b INTEGER (0..4294967296) + /// // } + /// // and return only `(a,b) + /// fn parser(i: &[u8]) -> ParseResult<(u8, u32)> { + /// Set::from_der_and_then(i, |i| { + /// let (i, a) = u8::from_der(i)?; + /// let (i, b) = u32::from_der(i)?; + /// Ok((i, (a, b))) + /// } + /// ) + /// } + /// ``` + pub fn from_der_and_then<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E> + where + F: FnOnce(&'a [u8]) -> ParseResult<U, E>, + E: From<Error>, + { + let (rem, seq) = Set::from_der(bytes).map_err(Err::convert)?; + let data = match seq.content { + Cow::Borrowed(b) => b, + // Since 'any' is built from 'bytes', it is borrowed by construction + Cow::Owned(_) => unreachable!(), + }; + let (_, res) = op(data)?; + Ok((rem, res)) + } + + /// Apply the parsing function to the set content (non-consuming version) + pub fn parse<F, T, E>(&'a self, mut f: F) -> ParseResult<'a, T, E> + where + F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>, + { + let input: &[u8] = &self.content; + f(input) + } + + /// Apply the parsing function to the set content (consuming version) + /// + /// Note: to parse and apply a parsing function in one step, use the + /// [`Set::from_der_and_then`] method. + /// + /// # Limitations + /// + /// This function fails if the set contains `Owned` data, because the parsing function + /// takes a reference on data (which is dropped). + pub fn parse_into<F, T, E>(self, mut f: F) -> ParseResult<'a, T, E> + where + F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>, + E: From<Error>, + { + match self.content { + Cow::Borrowed(b) => f(b), + _ => Err(nom::Err::Error(Error::LifetimeError.into())), + } + } + + /// Return an iterator over the set content, attempting to decode objects as BER + /// + /// This method can be used when all objects from the set have the same type. + pub fn ber_iter<T, E>(&'a self) -> SetIterator<'a, T, BerParser, E> + where + T: FromBer<'a, E>, + { + SetIterator::new(&self.content) + } + + /// Return an iterator over the set content, attempting to decode objects as DER + /// + /// This method can be used when all objects from the set have the same type. + pub fn der_iter<T, E>(&'a self) -> SetIterator<'a, T, DerParser, E> + where + T: FromDer<'a, E>, + { + SetIterator::new(&self.content) + } + + /// Attempt to parse the set as a `SET OF` items (BER), and return the parsed items as a `Vec`. + pub fn ber_set_of<T, E>(&'a self) -> Result<Vec<T>, E> + where + T: FromBer<'a, E>, + E: From<Error>, + { + self.ber_iter().collect() + } + + /// Attempt to parse the set as a `SET OF` items (DER), and return the parsed items as a `Vec`. + pub fn der_set_of<T, E>(&'a self) -> Result<Vec<T>, E> + where + T: FromDer<'a, E>, + E: From<Error>, + { + self.der_iter().collect() + } + + /// Attempt to parse the set as a `SET OF` items (BER) (consuming input), + /// and return the parsed items as a `Vec`. + /// + /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects. + pub fn into_ber_set_of<T, E>(self) -> Result<Vec<T>, E> + where + for<'b> T: FromBer<'b, E>, + E: From<Error>, + T: ToStatic<Owned = T>, + { + match self.content { + Cow::Borrowed(bytes) => SetIterator::<T, BerParser, E>::new(bytes).collect(), + Cow::Owned(data) => { + let v1 = + SetIterator::<T, BerParser, E>::new(&data).collect::<Result<Vec<T>, E>>()?; + let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>(); + Ok(v2) + } + } + } + + /// Attempt to parse the set as a `SET OF` items (DER) (consuming input), + /// and return the parsed items as a `Vec`. + /// + /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects. + pub fn into_der_set_of<T, E>(self) -> Result<Vec<T>, E> + where + for<'b> T: FromDer<'b, E>, + E: From<Error>, + T: ToStatic<Owned = T>, + { + match self.content { + Cow::Borrowed(bytes) => SetIterator::<T, DerParser, E>::new(bytes).collect(), + Cow::Owned(data) => { + let v1 = + SetIterator::<T, DerParser, E>::new(&data).collect::<Result<Vec<T>, E>>()?; + let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>(); + Ok(v2) + } + } + } + + pub fn into_der_set_of_ref<T, E>(self) -> Result<Vec<T>, E> + where + T: FromDer<'a, E>, + E: From<Error>, + { + match self.content { + Cow::Borrowed(bytes) => SetIterator::<T, DerParser, E>::new(bytes).collect(), + Cow::Owned(_) => Err(Error::LifetimeError.into()), + } + } +} + +impl<'a> ToStatic for Set<'a> { + type Owned = Set<'static>; + + fn to_static(&self) -> Self::Owned { + Set { + content: Cow::Owned(self.content.to_vec()), + } + } +} + +impl<'a> AsRef<[u8]> for Set<'a> { + fn as_ref(&self) -> &[u8] { + &self.content + } +} + +impl<'a> TryFrom<Any<'a>> for Set<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Set<'a>> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for Set<'a> { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<Set<'a>> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + Ok(Set { + content: Cow::Borrowed(any.data), + }) + } +} + +impl<'a> CheckDerConstraints for Set<'a> { + fn check_constraints(_any: &Any) -> Result<()> { + Ok(()) + } +} + +impl<'a> DerAutoDerive for Set<'a> {} + +impl<'a> Tagged for Set<'a> { + const TAG: Tag = Tag::Set; +} + +#[cfg(feature = "std")] +impl ToDer for Set<'_> { + fn to_der_len(&self) -> Result<usize> { + let sz = self.content.len(); + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + true, + Self::TAG, + Length::Definite(self.content.len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(&self.content).map_err(Into::into) + } +} + +#[cfg(feature = "std")] +impl<'a> Set<'a> { + /// Attempt to create a `Set` from an iterator over serializable objects (to DER) + /// + /// # Examples + /// + /// ``` + /// use asn1_rs::Set; + /// + /// // build set + /// let it = [2, 3, 4].iter(); + /// let seq = Set::from_iter_to_der(it).unwrap(); + /// ``` + pub fn from_iter_to_der<T, IT>(it: IT) -> SerializeResult<Self> + where + IT: Iterator<Item = T>, + T: ToDer, + T: Tagged, + { + let mut v = Vec::new(); + for item in it { + let item_v = <T as ToDer>::to_der_vec(&item)?; + v.extend_from_slice(&item_v); + } + Ok(Set { + content: Cow::Owned(v), + }) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/set/btreeset.rs b/rust/vendor/asn1-rs/src/asn1_types/set/btreeset.rs new file mode 100644 index 0000000..a70f9f9 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/set/btreeset.rs @@ -0,0 +1,124 @@ +use crate::*; +use alloc::collections::BTreeSet; +use core::convert::TryFrom; + +impl<T> Tagged for BTreeSet<T> { + const TAG: Tag = Tag::Set; +} + +impl<'a, T> TryFrom<Any<'a>> for BTreeSet<T> +where + T: FromBer<'a>, + T: Ord, +{ + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + let items = SetIterator::<T, BerParser>::new(any.data).collect::<Result<BTreeSet<T>>>()?; + Ok(items) + } +} + +impl<T> CheckDerConstraints for BTreeSet<T> +where + T: CheckDerConstraints, +{ + fn check_constraints(any: &Any) -> Result<()> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + for item in SetIterator::<Any, DerParser>::new(any.data) { + let item = item?; + T::check_constraints(&item)?; + } + Ok(()) + } +} + +/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints` +impl<'a, T, E> FromDer<'a, E> for BTreeSet<T> +where + T: FromDer<'a, E>, + T: Ord, + E: From<Error>, +{ + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; + any.tag() + .assert_eq(Self::TAG) + .map_err(|e| nom::Err::Error(e.into()))?; + any.header + .assert_constructed() + .map_err(|e| nom::Err::Error(e.into()))?; + let items = SetIterator::<T, DerParser, E>::new(any.data) + .collect::<Result<BTreeSet<T>, E>>() + .map_err(nom::Err::Error)?; + Ok((rem, items)) + } +} + +#[cfg(feature = "std")] +impl<T> ToDer for BTreeSet<T> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + let mut len = 0; + for t in self.iter() { + len += t.to_der_len()?; + } + let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); + Ok(header.to_der_len()? + len) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut len = 0; + for t in self.iter() { + len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?; + } + let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut sz = 0; + for t in self.iter() { + sz += t.write_der(writer)?; + } + Ok(sz) + } +} + +#[cfg(test)] +mod tests { + use crate::*; + use core::convert::TryFrom; + use hex_literal::hex; + use std::collections::BTreeSet; + + #[test] + fn ber_btreeset() { + let input = &hex! {"31 06 02 01 00 02 01 01"}; + let (_, any) = Any::from_ber(input).expect("parsing hashset failed"); + <BTreeSet<u32>>::check_constraints(&any).unwrap(); + + let h = <BTreeSet<u32>>::try_from(any).unwrap(); + + assert_eq!(h.len(), 2); + } + + #[test] + fn der_btreeset() { + let input = &hex! {"31 06 02 01 00 02 01 01"}; + let r: IResult<_, _, Error> = BTreeSet::<u32>::from_der(input); + let (_, h) = r.expect("parsing hashset failed"); + + assert_eq!(h.len(), 2); + + assert_eq!(h.to_der_len(), Ok(8)); + let v = h.to_der_vec().expect("could not serialize"); + let (_, h2) = SetOf::<u32>::from_der(&v).unwrap(); + assert!(h.iter().eq(h2.iter())); + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/set/hashset.rs b/rust/vendor/asn1-rs/src/asn1_types/set/hashset.rs new file mode 100644 index 0000000..d505301 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/set/hashset.rs @@ -0,0 +1,125 @@ +#![cfg(feature = "std")] +use crate::*; +use std::collections::HashSet; +use std::convert::TryFrom; +use std::hash::Hash; + +impl<T> Tagged for HashSet<T> { + const TAG: Tag = Tag::Set; +} + +impl<'a, T> TryFrom<Any<'a>> for HashSet<T> +where + T: FromBer<'a>, + T: Hash + Eq, +{ + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + let items = SetIterator::<T, BerParser>::new(any.data).collect::<Result<HashSet<T>>>()?; + Ok(items) + } +} + +impl<T> CheckDerConstraints for HashSet<T> +where + T: CheckDerConstraints, +{ + fn check_constraints(any: &Any) -> Result<()> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + for item in SetIterator::<Any, DerParser>::new(any.data) { + let item = item?; + T::check_constraints(&item)?; + } + Ok(()) + } +} + +/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints` +impl<'a, T, E> FromDer<'a, E> for HashSet<T> +where + T: FromDer<'a, E>, + T: Hash + Eq, + E: From<Error>, +{ + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; + any.tag() + .assert_eq(Self::TAG) + .map_err(|e| nom::Err::Error(e.into()))?; + any.header + .assert_constructed() + .map_err(|e| nom::Err::Error(e.into()))?; + let items = SetIterator::<T, DerParser, E>::new(any.data) + .collect::<Result<HashSet<T>, E>>() + .map_err(nom::Err::Error)?; + Ok((rem, items)) + } +} + +impl<T> ToDer for HashSet<T> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + let mut len = 0; + for t in self.iter() { + len += t.to_der_len()?; + } + let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); + Ok(header.to_der_len()? + len) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut len = 0; + for t in self.iter() { + len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?; + } + let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut sz = 0; + for t in self.iter() { + sz += t.write_der(writer)?; + } + Ok(sz) + } +} + +#[cfg(test)] +mod tests { + use crate::*; + use core::convert::TryFrom; + use hex_literal::hex; + use std::collections::HashSet; + + #[test] + fn ber_hashset() { + let input = &hex! {"31 06 02 01 00 02 01 01"}; + let (_, any) = Any::from_ber(input).expect("parsing hashset failed"); + <HashSet<u32>>::check_constraints(&any).unwrap(); + + let h = <HashSet<u32>>::try_from(any).unwrap(); + + assert_eq!(h.len(), 2); + } + + #[test] + fn der_hashset() { + let input = &hex! {"31 06 02 01 00 02 01 01"}; + let r: IResult<_, _, Error> = HashSet::<u32>::from_der(input); + let (_, h) = r.expect("parsing hashset failed"); + + assert_eq!(h.len(), 2); + + assert_eq!(h.to_der_len(), Ok(8)); + let v = h.to_der_vec().expect("could not serialize"); + let (_, h2) = SetOf::<u32>::from_der(&v).unwrap(); + assert!(h.iter().eq(h2.iter())); + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/set/iterator.rs b/rust/vendor/asn1-rs/src/asn1_types/set/iterator.rs new file mode 100644 index 0000000..eb46075 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/set/iterator.rs @@ -0,0 +1,22 @@ +pub use crate::{Error, SequenceIterator}; + +/// An Iterator over binary data, parsing elements of type `T` +/// +/// This helps parsing `SET OF` items of type `T`. The type of parser +/// (BER/DER) is specified using the generic parameter `F` of this struct. +/// +/// Note: the iterator must start on the set *contents*, not the set itself. +/// +/// # Examples +/// +/// ```rust +/// use asn1_rs::{DerParser, Integer, SetIterator}; +/// +/// let data = &[0x30, 0x6, 0x2, 0x1, 0x1, 0x2, 0x1, 0x2]; +/// for (idx, item) in SetIterator::<Integer, DerParser>::new(&data[2..]).enumerate() { +/// let item = item.unwrap(); // parsing could have failed +/// let i = item.as_u32().unwrap(); // integer can be negative, or too large to fit into u32 +/// assert_eq!(i as usize, idx + 1); +/// } +/// ``` +pub type SetIterator<'a, T, F, E = Error> = SequenceIterator<'a, T, F, E>; diff --git a/rust/vendor/asn1-rs/src/asn1_types/set/set_of.rs b/rust/vendor/asn1-rs/src/asn1_types/set/set_of.rs new file mode 100644 index 0000000..af6c0a7 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/set/set_of.rs @@ -0,0 +1,150 @@ +use crate::*; +use alloc::vec::Vec; +use core::convert::TryFrom; +use core::iter::FromIterator; + +/// The `SET OF` object is an unordered list of homogeneous types. +/// +/// # Examples +/// +/// ``` +/// use asn1_rs::SetOf; +/// use std::iter::FromIterator; +/// +/// // build set +/// let it = [2, 3, 4].iter(); +/// let set = SetOf::from_iter(it); +/// +/// // `set` now contains the serialized DER representation of the array +/// +/// // iterate objects +/// let mut sum = 0; +/// for item in set.iter() { +/// // item has type `Result<u32>`, since parsing the serialized bytes could fail +/// sum += *item; +/// } +/// assert_eq!(sum, 9); +/// +/// ``` +#[derive(Debug)] +pub struct SetOf<T> { + items: Vec<T>, +} + +impl<T> SetOf<T> { + /// Builds a `SET OF` from the provided content + #[inline] + pub const fn new(items: Vec<T>) -> Self { + SetOf { items } + } + + /// Returns the length of this `SET` (the number of items). + #[inline] + pub fn len(&self) -> usize { + self.items.len() + } + + /// Returns `true` if this `SET` is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } + + /// Returns an iterator over the items of the `SET`. + #[inline] + pub fn iter(&self) -> impl Iterator<Item = &T> { + self.items.iter() + } +} + +impl<T> AsRef<[T]> for SetOf<T> { + fn as_ref(&self) -> &[T] { + &self.items + } +} + +impl<'a, T> IntoIterator for &'a SetOf<T> { + type Item = &'a T; + type IntoIter = core::slice::Iter<'a, T>; + + fn into_iter(self) -> core::slice::Iter<'a, T> { + self.items.iter() + } +} + +impl<'a, T> IntoIterator for &'a mut SetOf<T> { + type Item = &'a mut T; + type IntoIter = core::slice::IterMut<'a, T>; + + fn into_iter(self) -> core::slice::IterMut<'a, T> { + self.items.iter_mut() + } +} + +impl<T> From<SetOf<T>> for Vec<T> { + fn from(set: SetOf<T>) -> Self { + set.items + } +} + +impl<T> FromIterator<T> for SetOf<T> { + fn from_iter<IT: IntoIterator<Item = T>>(iter: IT) -> Self { + let items = iter.into_iter().collect(); + SetOf::new(items) + } +} + +impl<'a, T> TryFrom<Any<'a>> for SetOf<T> +where + T: FromBer<'a>, +{ + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self> { + any.tag().assert_eq(Self::TAG)?; + if !any.header.is_constructed() { + return Err(Error::ConstructExpected); + } + let items = SetIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?; + Ok(SetOf::new(items)) + } +} + +impl<T> CheckDerConstraints for SetOf<T> +where + T: CheckDerConstraints, +{ + fn check_constraints(any: &Any) -> Result<()> { + any.tag().assert_eq(Self::TAG)?; + any.header.assert_constructed()?; + for item in SetIterator::<Any, DerParser>::new(any.data) { + let item = item?; + T::check_constraints(&item)?; + } + Ok(()) + } +} + +impl<T> DerAutoDerive for SetOf<T> {} + +impl<T> Tagged for SetOf<T> { + const TAG: Tag = Tag::Set; +} + +#[cfg(feature = "std")] +impl<T> ToDer for SetOf<T> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + self.items.to_der_len() + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + self.items.write_der_header(writer) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + self.items.write_der_content(writer) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings.rs b/rust/vendor/asn1-rs/src/asn1_types/strings.rs new file mode 100644 index 0000000..66a4c19 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings.rs @@ -0,0 +1,171 @@ +mod bmpstring; +mod generalstring; +mod graphicstring; +mod ia5string; +mod numericstring; +mod printablestring; +mod str; +mod string; +mod teletexstring; +mod universalstring; +mod utf8string; +mod videotexstring; +mod visiblestring; + +pub use self::str::*; +pub use bmpstring::*; +pub use generalstring::*; +pub use graphicstring::*; +pub use ia5string::*; +pub use numericstring::*; +pub use printablestring::*; +pub use string::*; +pub use teletexstring::*; +pub use universalstring::*; +pub use utf8string::*; +pub use videotexstring::*; +pub use visiblestring::*; + +/// Base trait for BER string objects and character set validation +/// +/// This trait is implemented by several types, and is used to determine if some bytes +/// would be valid for the given type. +/// +/// # Example +/// +/// ```rust +/// use asn1_rs::{PrintableString, TestValidCharset, VisibleString}; +/// +/// let bytes: &[u8] = b"abcd*4"; +/// let res = PrintableString::test_valid_charset(bytes); +/// assert!(res.is_err()); +/// let res = VisibleString::test_valid_charset(bytes); +/// assert!(res.is_ok()); +/// ``` +pub trait TestValidCharset { + /// Check character set for this object type. + fn test_valid_charset(i: &[u8]) -> crate::Result<()>; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! asn1_string { + (IMPL $name:ident, $sname:expr) => { + #[doc="ASN.1 restricted character string type (`"] + #[doc = $sname] + #[doc = "`)"] + #[derive(Debug, PartialEq, Eq)] + pub struct $name<'a> { + pub(crate) data: alloc::borrow::Cow<'a, str>, + } + + impl<'a> $name<'a> { + pub const fn new(s: &'a str) -> Self { + $name { + data: alloc::borrow::Cow::Borrowed(s), + } + } + + pub fn string(&self) -> String { + use alloc::string::ToString; + self.data.to_string() + } + } + + impl<'a> AsRef<str> for $name<'a> { + fn as_ref(&self) -> &str { + &self.data + } + } + + impl<'a> From<&'a str> for $name<'a> { + fn from(s: &'a str) -> Self { + Self::new(s) + } + } + + impl From<String> for $name<'_> { + fn from(s: String) -> Self { + Self { + data: alloc::borrow::Cow::Owned(s), + } + } + } + + impl<'a> core::convert::TryFrom<$crate::Any<'a>> for $name<'a> { + type Error = $crate::Error; + + fn try_from(any: $crate::Any<'a>) -> $crate::Result<$name<'a>> { + use core::convert::TryFrom; + TryFrom::try_from(&any) + } + } + + impl<'a, 'b> core::convert::TryFrom<&'b $crate::Any<'a>> for $name<'a> { + type Error = $crate::Error; + + fn try_from(any: &'b $crate::Any<'a>) -> $crate::Result<$name<'a>> { + use $crate::traits::Tagged; + use alloc::borrow::Cow; + any.tag().assert_eq(Self::TAG)?; + <$name>::test_valid_charset(any.data)?; + + let s = alloc::str::from_utf8(any.data)?; + let data = Cow::Borrowed(s); + Ok($name { data }) + } + } + + impl<'a> $crate::CheckDerConstraints for $name<'a> { + fn check_constraints(any: &$crate::Any) -> $crate::Result<()> { + any.header.assert_primitive()?; + Ok(()) + } + } + + impl $crate::DerAutoDerive for $name<'_> {} + + impl<'a> $crate::Tagged for $name<'a> { + const TAG: $crate::Tag = $crate::Tag::$name; + } + + #[cfg(feature = "std")] + impl $crate::ToDer for $name<'_> { + fn to_der_len(&self) -> Result<usize> { + let sz = self.data.as_bytes().len(); + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = $crate::Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header( + &self, + writer: &mut dyn std::io::Write, + ) -> $crate::SerializeResult<usize> { + use $crate::Tagged; + let header = $crate::Header::new( + $crate::Class::Universal, + false, + Self::TAG, + $crate::Length::Definite(self.data.len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content( + &self, + writer: &mut dyn std::io::Write, + ) -> $crate::SerializeResult<usize> { + writer.write(self.data.as_bytes()).map_err(Into::into) + } + } + }; + ($name:ident) => { + asn1_string!(IMPL $name, stringify!($name)); + }; +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/bmpstring.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/bmpstring.rs new file mode 100644 index 0000000..490c68c --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/bmpstring.rs @@ -0,0 +1,132 @@ +// do not use the `asn1_string` macro, since types are not the same +// X.680 section 37.15 + +use crate::*; +use alloc::borrow::Cow; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +/// ASN.1 `BMPSTRING` type +/// +/// Note: parsing a `BmpString` allocates memory since the UTF-16 to UTF-8 conversion requires a memory allocation. +/// (see `String::from_utf16` method). +#[derive(Debug, PartialEq, Eq)] +pub struct BmpString<'a> { + pub(crate) data: Cow<'a, str>, +} + +impl<'a> BmpString<'a> { + pub const fn new(s: &'a str) -> Self { + BmpString { + data: Cow::Borrowed(s), + } + } + + pub fn string(&self) -> String { + self.data.to_string() + } +} + +impl<'a> AsRef<str> for BmpString<'a> { + fn as_ref(&self) -> &str { + &self.data + } +} + +impl<'a> From<&'a str> for BmpString<'a> { + fn from(s: &'a str) -> Self { + Self::new(s) + } +} + +impl From<String> for BmpString<'_> { + fn from(s: String) -> Self { + Self { + data: alloc::borrow::Cow::Owned(s), + } + } +} + +impl<'a> core::convert::TryFrom<Any<'a>> for BmpString<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<BmpString<'a>> { + any.tag().assert_eq(Self::TAG)?; + + // read slice as big-endian UTF-16 string + let v = &any + .data + .chunks(2) + .map(|s| match s { + [a, b] => ((*a as u16) << 8) | (*b as u16), + [a] => *a as u16, + _ => unreachable!(), + }) + .collect::<Vec<_>>(); + + let s = String::from_utf16(v)?; + let data = Cow::Owned(s); + + Ok(BmpString { data }) + } +} + +impl<'a> CheckDerConstraints for BmpString<'a> { + fn check_constraints(any: &Any) -> Result<()> { + any.header.assert_primitive()?; + Ok(()) + } +} + +impl DerAutoDerive for BmpString<'_> {} + +impl<'a> Tagged for BmpString<'a> { + const TAG: Tag = Tag::BmpString; +} + +impl<'a> TestValidCharset for BmpString<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + if i.len() % 2 != 0 { + return Err(Error::StringInvalidCharset); + } + let iter = i.chunks(2).map(|s| ((s[0] as u16) << 8) | (s[1] as u16)); + for c in char::decode_utf16(iter) { + if c.is_err() { + return Err(Error::StringInvalidCharset); + } + } + Ok(()) + } +} + +#[cfg(feature = "std")] +impl ToDer for BmpString<'_> { + fn to_der_len(&self) -> Result<usize> { + // compute the UTF-16 length + let sz = self.data.encode_utf16().count() * 2; + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + // compute the UTF-16 length + let l = self.data.encode_utf16().count() * 2; + let header = Header::new(Class::Universal, false, Self::TAG, Length::Definite(l)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut v = Vec::new(); + for u in self.data.encode_utf16() { + v.push((u >> 8) as u8); + v.push((u & 0xff) as u8); + } + writer.write(&v).map_err(Into::into) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/generalstring.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/generalstring.rs new file mode 100644 index 0000000..f455d6a --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/generalstring.rs @@ -0,0 +1,14 @@ +use crate::{asn1_string, TestValidCharset}; +use crate::{Error, Result}; +use alloc::string::String; + +asn1_string!(GeneralString); + +impl<'a> TestValidCharset for GeneralString<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + if !i.iter().all(u8::is_ascii) { + return Err(Error::StringInvalidCharset); + } + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/graphicstring.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/graphicstring.rs new file mode 100644 index 0000000..3d84040 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/graphicstring.rs @@ -0,0 +1,14 @@ +use crate::{asn1_string, TestValidCharset}; +use crate::{Error, Result}; +use alloc::string::String; + +asn1_string!(GraphicString); + +impl<'a> TestValidCharset for GraphicString<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + if !i.iter().all(u8::is_ascii) { + return Err(Error::StringInvalidCharset); + } + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/ia5string.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/ia5string.rs new file mode 100644 index 0000000..4b37465 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/ia5string.rs @@ -0,0 +1,14 @@ +use crate::{asn1_string, TestValidCharset}; +use crate::{Error, Result}; +use alloc::string::String; + +asn1_string!(Ia5String); + +impl<'a> TestValidCharset for Ia5String<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + if !i.iter().all(u8::is_ascii) { + return Err(Error::StringInvalidCharset); + } + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/numericstring.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/numericstring.rs new file mode 100644 index 0000000..dbe4ff1 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/numericstring.rs @@ -0,0 +1,18 @@ +use crate::{asn1_string, TestValidCharset}; +use crate::{Error, Result}; +use alloc::string::String; + +asn1_string!(NumericString); + +impl<'a> TestValidCharset for NumericString<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_numeric(b: &u8) -> bool { + matches!(*b, b'0'..=b'9' | b' ') + } + if !i.iter().all(is_numeric) { + return Err(Error::StringInvalidCharset); + } + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/printablestring.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/printablestring.rs new file mode 100644 index 0000000..5cd51fa --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/printablestring.rs @@ -0,0 +1,35 @@ +use crate::{asn1_string, TestValidCharset}; +use crate::{Error, Result}; +use alloc::string::String; + +asn1_string!(PrintableString); + +impl<'a> TestValidCharset for PrintableString<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + // Argument must be a reference, because of the .iter().all(F) call below + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_printable(b: &u8) -> bool { + matches!(*b, + b'a'..=b'z' + | b'A'..=b'Z' + | b'0'..=b'9' + | b' ' + | b'\'' + | b'(' + | b')' + | b'+' + | b',' + | b'-' + | b'.' + | b'/' + | b':' + | b'=' + | b'?') + } + + if !i.iter().all(is_printable) { + return Err(Error::StringInvalidCharset); + } + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/str.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/str.rs new file mode 100644 index 0000000..a90e8e3 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/str.rs @@ -0,0 +1,67 @@ +use crate::*; +use alloc::borrow::Cow; +use core::convert::TryFrom; + +impl<'a> TryFrom<Any<'a>> for &'a str { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<&'a str> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for &'a str { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<&'a str> { + any.tag().assert_eq(Self::TAG)?; + let s = Utf8String::try_from(any)?; + match s.data { + Cow::Borrowed(s) => Ok(s), + Cow::Owned(_) => Err(Error::LifetimeError), + } + } +} + +impl<'a> CheckDerConstraints for &'a str { + fn check_constraints(any: &Any) -> Result<()> { + // X.690 section 10.2 + any.header.assert_primitive()?; + Ok(()) + } +} + +impl DerAutoDerive for &'_ str {} + +impl<'a> Tagged for &'a str { + const TAG: Tag = Tag::Utf8String; +} + +#[cfg(feature = "std")] +impl<'a> ToDer for &'a str { + fn to_der_len(&self) -> Result<usize> { + let sz = self.as_bytes().len(); + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(self.as_bytes().len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(self.as_bytes()).map_err(Into::into) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/string.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/string.rs new file mode 100644 index 0000000..81f6ba8 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/string.rs @@ -0,0 +1,64 @@ +use crate::*; +use alloc::string::String; +use core::convert::TryFrom; + +impl<'a> TryFrom<Any<'a>> for String { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<String> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for String { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<String> { + any.tag().assert_eq(Self::TAG)?; + let s = Utf8String::try_from(any)?; + Ok(s.data.into_owned()) + } +} + +impl CheckDerConstraints for String { + fn check_constraints(any: &Any) -> Result<()> { + // X.690 section 10.2 + any.header.assert_primitive()?; + Ok(()) + } +} + +impl DerAutoDerive for String {} + +impl Tagged for String { + const TAG: Tag = Tag::Utf8String; +} + +#[cfg(feature = "std")] +impl ToDer for String { + fn to_der_len(&self) -> Result<usize> { + let sz = self.as_bytes().len(); + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(self.len()), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + writer.write(self.as_ref()).map_err(Into::into) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/teletexstring.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/teletexstring.rs new file mode 100644 index 0000000..2f58804 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/teletexstring.rs @@ -0,0 +1,18 @@ +use crate::{asn1_string, TestValidCharset}; +use crate::{Error, Result}; +use alloc::string::String; + +asn1_string!(TeletexString); + +impl<'a> TestValidCharset for TeletexString<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_visible(b: &u8) -> bool { + 0x20 <= *b && *b <= 0x7f + } + if !i.iter().all(is_visible) { + return Err(Error::StringInvalidCharset); + } + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/universalstring.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/universalstring.rs new file mode 100644 index 0000000..9006362 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/universalstring.rs @@ -0,0 +1,137 @@ +// do not use the `asn1_string` macro, since types are not the same +// X.680 section 37.6 and X.690 section 8.21.7 + +use crate::*; +use alloc::borrow::Cow; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::convert::TryFrom; +use core::iter::FromIterator; + +/// ASN.1 `UniversalString` type +/// +/// Note: parsing a `UniversalString` allocates memory since the UCS-4 to UTF-8 conversion requires a memory allocation. +#[derive(Debug, PartialEq, Eq)] +pub struct UniversalString<'a> { + pub(crate) data: Cow<'a, str>, +} + +impl<'a> UniversalString<'a> { + pub const fn new(s: &'a str) -> Self { + UniversalString { + data: Cow::Borrowed(s), + } + } + + pub fn string(&self) -> String { + self.data.to_string() + } +} + +impl<'a> AsRef<str> for UniversalString<'a> { + fn as_ref(&self) -> &str { + &self.data + } +} + +impl<'a> From<&'a str> for UniversalString<'a> { + fn from(s: &'a str) -> Self { + Self::new(s) + } +} + +impl From<String> for UniversalString<'_> { + fn from(s: String) -> Self { + Self { + data: alloc::borrow::Cow::Owned(s), + } + } +} + +impl<'a> TryFrom<Any<'a>> for UniversalString<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<UniversalString<'a>> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for UniversalString<'a> { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<UniversalString<'a>> { + any.tag().assert_eq(Self::TAG)?; + + if any.data.len() % 4 != 0 { + return Err(Error::StringInvalidCharset); + } + + // read slice as big-endian UCS-4 string + let v = &any + .data + .chunks(4) + .map(|s| match s { + [a, b, c, d] => { + let u32_val = ((*a as u32) << 24) + | ((*b as u32) << 16) + | ((*c as u32) << 8) + | (*d as u32); + char::from_u32(u32_val) + } + _ => unreachable!(), + }) + .collect::<Option<Vec<_>>>() + .ok_or(Error::StringInvalidCharset)?; + + let s = String::from_iter(v); + let data = Cow::Owned(s); + + Ok(UniversalString { data }) + } +} + +impl<'a> CheckDerConstraints for UniversalString<'a> { + fn check_constraints(any: &Any) -> Result<()> { + any.header.assert_primitive()?; + Ok(()) + } +} + +impl DerAutoDerive for UniversalString<'_> {} + +impl<'a> Tagged for UniversalString<'a> { + const TAG: Tag = Tag::UniversalString; +} + +#[cfg(feature = "std")] +impl ToDer for UniversalString<'_> { + fn to_der_len(&self) -> Result<usize> { + // UCS-4: 4 bytes per character + let sz = self.data.as_bytes().len() * 4; + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let header = Header::new( + Class::Universal, + false, + Self::TAG, + Length::Definite(self.data.as_bytes().len() * 4), + ); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + self.data + .chars() + .try_for_each(|c| writer.write(&(c as u32).to_be_bytes()[..]).map(|_| ()))?; + Ok(self.data.as_bytes().len() * 4) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/utf8string.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/utf8string.rs new file mode 100644 index 0000000..0bf87d4 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/utf8string.rs @@ -0,0 +1,13 @@ +use crate::asn1_string; +use crate::Result; +use crate::TestValidCharset; +use alloc::string::String; + +asn1_string!(Utf8String); + +impl<'a> TestValidCharset for Utf8String<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + let _ = core::str::from_utf8(i)?; + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/videotexstring.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/videotexstring.rs new file mode 100644 index 0000000..51c5a46 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/videotexstring.rs @@ -0,0 +1,19 @@ +use crate::{asn1_string, TestValidCharset}; +use crate::{Error, Result}; +use alloc::string::String; + +asn1_string!(VideotexString); + +impl<'a> TestValidCharset for VideotexString<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_visible(b: &u8) -> bool { + // XXX + 0x20 <= *b && *b <= 0x7f + } + if !i.iter().all(is_visible) { + return Err(Error::StringInvalidCharset); + } + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/strings/visiblestring.rs b/rust/vendor/asn1-rs/src/asn1_types/strings/visiblestring.rs new file mode 100644 index 0000000..2b141dc --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/strings/visiblestring.rs @@ -0,0 +1,18 @@ +use crate::{asn1_string, TestValidCharset}; +use crate::{Error, Result}; +use alloc::string::String; + +asn1_string!(VisibleString); + +impl<'a> TestValidCharset for VisibleString<'a> { + fn test_valid_charset(i: &[u8]) -> Result<()> { + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_visible(b: &u8) -> bool { + 0x20 <= *b && *b <= 0x7f + } + if !i.iter().all(is_visible) { + return Err(Error::StringInvalidCharset); + } + Ok(()) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/tagged.rs b/rust/vendor/asn1-rs/src/asn1_types/tagged.rs new file mode 100644 index 0000000..c1652d4 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/tagged.rs @@ -0,0 +1,128 @@ +use crate::{Class, Error, Tag, Tagged}; +use core::marker::PhantomData; + +mod application; +mod builder; +mod explicit; +mod helpers; +mod implicit; +mod optional; +mod parser; +mod private; + +pub use application::*; +pub use builder::*; +pub use explicit::*; +pub use helpers::*; +pub use implicit::*; +pub use optional::*; +pub use parser::*; +pub use private::*; + +pub(crate) const CONTEXT_SPECIFIC: u8 = Class::ContextSpecific as u8; + +/// A type parameter for `IMPLICIT` tagged values. +#[derive(Debug, PartialEq, Eq)] +pub enum Implicit {} + +/// A type parameter for `EXPLICIT` tagged values. +#[derive(Debug, PartialEq, Eq)] +pub enum Explicit {} + +/// A type parameter for tagged values either [`Explicit`] or [`Implicit`]. +pub trait TagKind {} + +impl TagKind for Implicit {} +impl TagKind for Explicit {} + +/// Helper object for creating `FromBer`/`FromDer` types for TAGGED OPTIONAL types +/// +/// When parsing `ContextSpecific` (the most common class), see [`TaggedExplicit`] and +/// [`TaggedImplicit`] alias types. +/// +/// # Notes +/// +/// `CLASS` must be between 0 and 4. See [`Class`] for possible values for the `CLASS` parameter. +/// Constants from this class can be used, but they must be wrapped in braces due to +/// [Rust syntax for generics](https://doc.rust-lang.org/reference/items/generics.html) +/// (see example below). +/// +/// # Examples +/// +/// To parse a `[APPLICATION 0] EXPLICIT INTEGER` object: +/// +/// ```rust +/// use asn1_rs::{Class, Error, Explicit, FromBer, Integer, TaggedValue}; +/// +/// let bytes = &[0x60, 0x03, 0x2, 0x1, 0x2]; +/// +/// // If tagged object is present (and has expected tag), parsing succeeds: +/// let (_, tagged) = +/// TaggedValue::<Integer, Error, Explicit, {Class::APPLICATION}, 0>::from_ber(bytes) +/// .unwrap(); +/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2))); +/// ``` +#[derive(Debug, PartialEq, Eq)] +pub struct TaggedValue<T, E, TagKind, const CLASS: u8, const TAG: u32> { + pub(crate) inner: T, + + tag_kind: PhantomData<TagKind>, + _e: PhantomData<E>, +} + +impl<T, E, TagKind, const CLASS: u8, const TAG: u32> TaggedValue<T, E, TagKind, CLASS, TAG> { + /// Consumes the `TaggedParser`, returning the wrapped value. + #[inline] + pub fn into_inner(self) -> T { + self.inner + } + + /// Return the (outer) tag of this object + pub const fn tag(&self) -> Tag { + Self::TAG + } + + /// Return the (outer) class of this object + #[inline] + pub const fn class(&self) -> u8 { + CLASS + } +} + +impl<T, E, const CLASS: u8, const TAG: u32> TaggedValue<T, E, Explicit, CLASS, TAG> { + /// Constructs a new `EXPLICIT TaggedParser` with the provided value + #[inline] + pub const fn explicit(inner: T) -> Self { + TaggedValue { + inner, + tag_kind: PhantomData, + _e: PhantomData, + } + } +} + +impl<T, E, const CLASS: u8, const TAG: u32> TaggedValue<T, E, Implicit, CLASS, TAG> { + /// Constructs a new `IMPLICIT TaggedParser` with the provided value + #[inline] + pub const fn implicit(inner: T) -> Self { + TaggedValue { + inner, + tag_kind: PhantomData, + _e: PhantomData, + } + } +} + +impl<T, E, TagKind, const CLASS: u8, const TAG: u32> AsRef<T> + for TaggedValue<T, E, TagKind, CLASS, TAG> +{ + fn as_ref(&self) -> &T { + &self.inner + } +} + +impl<T, E, TagKind, const CLASS: u8, const TAG: u32> Tagged + for TaggedValue<T, E, TagKind, CLASS, TAG> +{ + const TAG: Tag = Tag(TAG); +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/tagged/application.rs b/rust/vendor/asn1-rs/src/asn1_types/tagged/application.rs new file mode 100644 index 0000000..ddd2b07 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/tagged/application.rs @@ -0,0 +1,42 @@ +use crate::{Class, Explicit, Implicit, TaggedValue}; + +/// A helper object to parse `[APPLICATION n] EXPLICIT T` +/// +/// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to +/// parse explicit application-tagged values. +/// +/// # Examples +/// +/// To parse a `[APPLICATION 0] EXPLICIT INTEGER` object: +/// +/// ```rust +/// use asn1_rs::{ApplicationExplicit, Error, FromBer, Integer, TaggedValue}; +/// +/// let bytes = &[0x60, 0x03, 0x2, 0x1, 0x2]; +/// +/// // If tagged object is present (and has expected tag), parsing succeeds: +/// let (_, tagged) = ApplicationExplicit::<Integer, Error, 0>::from_ber(bytes).unwrap(); +/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2))); +/// ``` +pub type ApplicationExplicit<T, E, const TAG: u32> = + TaggedValue<T, E, Explicit, { Class::APPLICATION }, TAG>; + +/// A helper object to parse `[APPLICATION n] IMPLICIT T` +/// +/// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to +/// parse explicit application-tagged values. +/// +/// # Examples +/// +/// To parse a `[APPLICATION 0] IMPLICIT INTEGER` object: +/// +/// ```rust +/// use asn1_rs::{ApplicationImplicit, Error, FromBer, Integer, TaggedValue}; +/// +/// let bytes = &[0x60, 0x1, 0x2]; +/// +/// let (_, tagged) = ApplicationImplicit::<Integer, Error, 0>::from_ber(bytes).unwrap(); +/// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2_u8))); +/// ``` +pub type ApplicationImplicit<T, E, const TAG: u32> = + TaggedValue<T, E, Implicit, { Class::APPLICATION }, TAG>; diff --git a/rust/vendor/asn1-rs/src/asn1_types/tagged/builder.rs b/rust/vendor/asn1-rs/src/asn1_types/tagged/builder.rs new file mode 100644 index 0000000..093b9d9 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/tagged/builder.rs @@ -0,0 +1,104 @@ +use super::{Error, Explicit, Implicit, TaggedParser}; +use crate::{Class, FromBer, FromDer, ParseResult, Tag}; +use core::marker::PhantomData; + +/// A builder for parsing tagged values (`IMPLICIT` or `EXPLICIT`) +/// +/// # Examples +/// +/// ``` +/// use asn1_rs::{Class, Tag, TaggedParserBuilder}; +/// +/// let parser = TaggedParserBuilder::explicit() +/// .with_class(Class::ContextSpecific) +/// .with_tag(Tag(0)) +/// .der_parser::<u32>(); +/// +/// let input = &[0xa0, 0x03, 0x02, 0x01, 0x02]; +/// let (rem, tagged) = parser(input).expect("parsing failed"); +/// +/// assert!(rem.is_empty()); +/// assert_eq!(tagged.tag(), Tag(0)); +/// assert_eq!(tagged.as_ref(), &2); +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct TaggedParserBuilder<TagKind, E = Error> { + class: Class, + tag: Tag, + tag_kind: PhantomData<TagKind>, + _e: PhantomData<E>, +} + +impl<TagKind, E> TaggedParserBuilder<TagKind, E> { + /// Create a default `TaggedParserBuilder` builder + /// + /// `TagKind` must be specified as either [`Explicit`] or [`Implicit`] + /// + /// ``` + /// use asn1_rs::{Explicit, TaggedParserBuilder}; + /// + /// let builder = TaggedParserBuilder::<Explicit>::new(); + /// ``` + pub const fn new() -> Self { + TaggedParserBuilder { + class: Class::Universal, + tag: Tag(0), + tag_kind: PhantomData, + _e: PhantomData, + } + } + + /// Set the expected `Class` for the builder + pub const fn with_class(self, class: Class) -> Self { + Self { class, ..self } + } + + /// Set the expected `Tag` for the builder + pub const fn with_tag(self, tag: Tag) -> Self { + Self { tag, ..self } + } +} + +impl<E> TaggedParserBuilder<Explicit, E> { + /// Create a `TagParser` builder for `EXPLICIT` tagged values + pub const fn explicit() -> Self { + TaggedParserBuilder::new() + } +} + +impl<E> TaggedParserBuilder<Implicit, E> { + /// Create a `TagParser` builder for `IMPLICIT` tagged values + pub const fn implicit() -> Self { + TaggedParserBuilder::new() + } +} + +impl<TagKind, E> TaggedParserBuilder<TagKind, E> { + /// Create the BER parser from the builder parameters + /// + /// This method will consume the builder and return a parser (to be used as a function). + pub fn ber_parser<'a, T>( + self, + ) -> impl Fn(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, TagKind, T, E>, E> + where + TaggedParser<'a, TagKind, T, E>: FromBer<'a, E>, + E: From<Error>, + { + move |bytes: &[u8]| TaggedParser::<TagKind, T, E>::parse_ber(self.class, self.tag, bytes) + } +} + +impl<TagKind, E> TaggedParserBuilder<TagKind, E> { + /// Create the DER parser from the builder parameters + /// + /// This method will consume the builder and return a parser (to be used as a function). + pub fn der_parser<'a, T>( + self, + ) -> impl Fn(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, TagKind, T, E>, E> + where + TaggedParser<'a, TagKind, T, E>: FromDer<'a, E>, + E: From<Error>, + { + move |bytes: &[u8]| TaggedParser::<TagKind, T, E>::parse_der(self.class, self.tag, bytes) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/tagged/explicit.rs b/rust/vendor/asn1-rs/src/asn1_types/tagged/explicit.rs new file mode 100644 index 0000000..7fdbcce --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/tagged/explicit.rs @@ -0,0 +1,262 @@ +use crate::*; +use core::convert::TryFrom; +use core::marker::PhantomData; + +impl<'a, T, E, const CLASS: u8, const TAG: u32> TryFrom<Any<'a>> + for TaggedValue<T, E, Explicit, CLASS, TAG> +where + T: FromBer<'a, E>, + E: From<Error>, +{ + type Error = E; + + fn try_from(any: Any<'a>) -> Result<Self, E> { + Self::try_from(&any) + } +} + +impl<'a, 'b, T, E, const CLASS: u8, const TAG: u32> TryFrom<&'b Any<'a>> + for TaggedValue<T, E, Explicit, CLASS, TAG> +where + T: FromBer<'a, E>, + E: From<Error>, +{ + type Error = E; + + fn try_from(any: &'b Any<'a>) -> Result<Self, E> { + any.tag().assert_eq(Tag(TAG))?; + any.header.assert_constructed()?; + if any.class() as u8 != CLASS { + let class = Class::try_from(CLASS).ok(); + return Err(Error::unexpected_class(class, any.class()).into()); + } + let (_, inner) = match T::from_ber(any.data) { + Ok((rem, res)) => (rem, res), + Err(Err::Error(e)) | Err(Err::Failure(e)) => return Err(e), + Err(Err::Incomplete(n)) => return Err(Error::Incomplete(n).into()), + }; + Ok(TaggedValue::explicit(inner)) + } +} + +impl<'a, T, E, const CLASS: u8, const TAG: u32> FromDer<'a, E> + for TaggedValue<T, E, Explicit, CLASS, TAG> +where + T: FromDer<'a, E>, + E: From<Error>, +{ + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; + any.tag() + .assert_eq(Tag(TAG)) + .map_err(|e| Err::Error(e.into()))?; + any.header + .assert_constructed() + .map_err(|e| Err::Error(e.into()))?; + if any.class() as u8 != CLASS { + let class = Class::try_from(CLASS).ok(); + return Err(Err::Error( + Error::unexpected_class(class, any.class()).into(), + )); + } + let (_, inner) = T::from_der(any.data)?; + Ok((rem, TaggedValue::explicit(inner))) + } +} + +impl<T, E, const CLASS: u8, const TAG: u32> CheckDerConstraints + for TaggedValue<T, E, Explicit, CLASS, TAG> +where + T: CheckDerConstraints, +{ + fn check_constraints(any: &Any) -> Result<()> { + any.header.length.assert_definite()?; + let (_, inner) = Any::from_ber(any.data)?; + T::check_constraints(&inner)?; + Ok(()) + } +} + +#[cfg(feature = "std")] +impl<T, E, const CLASS: u8, const TAG: u32> ToDer for TaggedValue<T, E, Explicit, CLASS, TAG> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + let sz = self.inner.to_der_len()?; + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let inner_len = self.inner.to_der_len()?; + let class = + Class::try_from(CLASS).map_err(|_| SerializeError::InvalidClass { class: CLASS })?; + let header = Header::new(class, true, self.tag(), Length::Definite(inner_len)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + self.inner.write_der(writer) + } +} + +/// A helper object to parse `[ n ] EXPLICIT T` +/// +/// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged +/// optional values. +/// +/// This helper expects context-specific tags. +/// See [`TaggedValue`] or [`TaggedParser`] for more generic implementations if needed. +/// +/// # Examples +/// +/// To parse a `[0] EXPLICIT INTEGER` object: +/// +/// ```rust +/// use asn1_rs::{Error, FromBer, Integer, TaggedExplicit, TaggedValue}; +/// +/// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2]; +/// +/// // If tagged object is present (and has expected tag), parsing succeeds: +/// let (_, tagged) = TaggedExplicit::<Integer, Error, 0>::from_ber(bytes).unwrap(); +/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2))); +/// ``` +pub type TaggedExplicit<T, E, const TAG: u32> = TaggedValue<T, E, Explicit, CONTEXT_SPECIFIC, TAG>; + +// implementations for TaggedParser + +impl<'a, T, E> TaggedParser<'a, Explicit, T, E> { + pub const fn new_explicit(class: Class, tag: u32, inner: T) -> Self { + Self { + header: Header::new(class, true, Tag(tag), Length::Definite(0)), + inner, + tag_kind: PhantomData, + _e: PhantomData, + } + } + + /// Parse a BER tagged value and apply the provided parsing function to content + /// + /// After parsing, the sequence object and header are discarded. + /// + /// Note: this function is provided for `Explicit`, but there is not difference between + /// explicit or implicit tags. The `op` function is responsible of handling the content. + #[inline] + pub fn from_ber_and_then<F>( + class: Class, + tag: u32, + bytes: &'a [u8], + op: F, + ) -> ParseResult<'a, T, E> + where + F: FnOnce(&'a [u8]) -> ParseResult<T, E>, + E: From<Error>, + { + Any::from_ber_and_then(class, tag, bytes, op) + } + + /// Parse a DER tagged value and apply the provided parsing function to content + /// + /// After parsing, the sequence object and header are discarded. + /// + /// Note: this function is provided for `Explicit`, but there is not difference between + /// explicit or implicit tags. The `op` function is responsible of handling the content. + #[inline] + pub fn from_der_and_then<F>( + class: Class, + tag: u32, + bytes: &'a [u8], + op: F, + ) -> ParseResult<'a, T, E> + where + F: FnOnce(&'a [u8]) -> ParseResult<T, E>, + E: From<Error>, + { + Any::from_der_and_then(class, tag, bytes, op) + } +} + +impl<'a, T, E> FromBer<'a, E> for TaggedParser<'a, Explicit, T, E> +where + T: FromBer<'a, E>, + E: From<Error>, +{ + fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?; + let header = any.header; + let (_, inner) = T::from_ber(any.data)?; + let tagged = TaggedParser { + header, + inner, + tag_kind: PhantomData, + _e: PhantomData, + }; + Ok((rem, tagged)) + } +} + +impl<'a, T, E> FromDer<'a, E> for TaggedParser<'a, Explicit, T, E> +where + T: FromDer<'a, E>, + E: From<Error>, +{ + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; + let header = any.header; + let (_, inner) = T::from_der(any.data)?; + let tagged = TaggedParser { + header, + inner, + tag_kind: PhantomData, + _e: PhantomData, + }; + Ok((rem, tagged)) + } +} + +impl<'a, T> CheckDerConstraints for TaggedParser<'a, Explicit, T> +where + T: CheckDerConstraints, +{ + fn check_constraints(any: &Any) -> Result<()> { + any.header.length.assert_definite()?; + let (_, inner_any) = Any::from_der(any.data)?; + T::check_constraints(&inner_any)?; + Ok(()) + } +} + +#[cfg(feature = "std")] +impl<'a, T> ToDer for TaggedParser<'a, Explicit, T> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + let sz = self.inner.to_der_len()?; + if sz < 127 { + // 1 (class+tag) + 1 (length) + len + Ok(2 + sz) + } else { + // 1 (class+tag) + n (length) + len + let n = Length::Definite(sz).to_der_len()?; + Ok(1 + n + sz) + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let inner_len = self.inner.to_der_len()?; + let header = Header::new(self.class(), true, self.tag(), Length::Definite(inner_len)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + self.inner.write_der(writer) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/tagged/helpers.rs b/rust/vendor/asn1-rs/src/asn1_types/tagged/helpers.rs new file mode 100644 index 0000000..dfb1018 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/tagged/helpers.rs @@ -0,0 +1,103 @@ +use super::{Explicit, Implicit, TaggedParser}; +use crate::{Any, Error, FromDer, Header, ParseResult, Tag, Tagged}; +use nom::error::ParseError; +use nom::{Err, IResult}; + +// helper functions for parsing tagged objects + +pub fn parse_der_tagged_explicit<'a, IntoTag, T, E>( + tag: IntoTag, +) -> impl FnMut(&'a [u8]) -> ParseResult<TaggedParser<'a, Explicit, T, E>, E> +where + IntoTag: Into<Tag>, + TaggedParser<'a, Explicit, T, E>: FromDer<'a, E>, + E: From<Error>, +{ + let tag = tag.into(); + move |i| { + let (rem, tagged) = TaggedParser::from_der(i)?; + tagged.assert_tag(tag).map_err(|e| Err::Error(e.into()))?; + Ok((rem, tagged)) + } +} + +pub fn parse_der_tagged_explicit_g<'a, IntoTag, T, F, E>( + tag: IntoTag, + f: F, +) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T, E> +where + F: Fn(&'a [u8], Header<'a>) -> IResult<&'a [u8], T, E>, + E: ParseError<&'a [u8]> + From<Error>, + IntoTag: Into<Tag>, +{ + let tag = tag.into(); + parse_der_container(tag, move |any: Any<'a>| { + any.header + .assert_tag(tag) + .map_err(|e| nom::Err::convert(e.into()))?; + f(any.data, any.header) + }) +} + +pub fn parse_der_tagged_implicit<'a, IntoTag, T, E>( + tag: IntoTag, +) -> impl FnMut(&'a [u8]) -> ParseResult<TaggedParser<'a, Implicit, T, E>, E> +where + IntoTag: Into<Tag>, + // T: TryFrom<Any<'a>, Error = Error> + Tagged, + TaggedParser<'a, Implicit, T, E>: FromDer<'a, E>, + E: From<Error>, +{ + let tag = tag.into(); + move |i| { + let (rem, tagged) = TaggedParser::from_der(i)?; + tagged + .assert_tag(tag) + .map_err(|e| nom::Err::convert(e.into()))?; + Ok((rem, tagged)) + } +} + +pub fn parse_der_tagged_implicit_g<'a, IntoTag, T, F, E>( + tag: IntoTag, + f: F, +) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T, E> +where + F: Fn(&'a [u8], Tag, Header<'a>) -> IResult<&'a [u8], T, E>, + E: ParseError<&'a [u8]> + From<Error>, + IntoTag: Into<Tag>, + T: Tagged, +{ + let tag = tag.into(); + parse_der_container(tag, move |any: Any<'a>| { + // verify tag of external header + any.header + .assert_tag(tag) + .map_err(|e| nom::Err::convert(e.into()))?; + // build a fake header with the expected tag + let Any { header, data } = any; + let header = Header { + tag: T::TAG, + ..header.clone() + }; + f(data, tag, header) + }) +} + +fn parse_der_container<'a, T, F, E>( + tag: Tag, + f: F, +) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T, E> +where + F: Fn(Any<'a>) -> IResult<&'a [u8], T, E>, + E: ParseError<&'a [u8]> + From<Error>, +{ + move |i: &[u8]| { + let (rem, any) = Any::from_der(i).map_err(nom::Err::convert)?; + any.header + .assert_tag(tag) + .map_err(|e| nom::Err::convert(e.into()))?; + let (_, output) = f(any)?; + Ok((rem, output)) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/tagged/implicit.rs b/rust/vendor/asn1-rs/src/asn1_types/tagged/implicit.rs new file mode 100644 index 0000000..53a921c --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/tagged/implicit.rs @@ -0,0 +1,287 @@ +use crate::*; +use core::convert::TryFrom; +use core::marker::PhantomData; + +impl<'a, T, E, const CLASS: u8, const TAG: u32> TryFrom<Any<'a>> + for TaggedValue<T, E, Implicit, CLASS, TAG> +where + T: TryFrom<Any<'a>, Error = E>, + T: Tagged, + E: From<Error>, +{ + type Error = E; + + fn try_from(any: Any<'a>) -> Result<Self, E> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b, E, T, const CLASS: u8, const TAG: u32> TryFrom<&'b Any<'a>> + for TaggedValue<T, E, Implicit, CLASS, TAG> +where + T: TryFrom<Any<'a>, Error = E>, + T: Tagged, + E: From<Error>, +{ + type Error = E; + + fn try_from(any: &'b Any<'a>) -> Result<Self, E> { + any.tag().assert_eq(Tag(TAG))?; + // XXX if input is empty, this function is not called + + if any.class() as u8 != CLASS { + let class = Class::try_from(CLASS).ok(); + return Err(Error::unexpected_class(class, any.class()).into()); + } + let any = Any { + header: Header { + tag: T::TAG, + ..any.header.clone() + }, + data: any.data, + }; + match T::try_from(any) { + Ok(inner) => Ok(TaggedValue::implicit(inner)), + Err(e) => Err(e), + } + } +} + +impl<'a, T, E, const CLASS: u8, const TAG: u32> FromDer<'a, E> + for TaggedValue<T, E, Implicit, CLASS, TAG> +where + T: TryFrom<Any<'a>, Error = E>, + T: Tagged, + E: From<Error>, +{ + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; + any.tag() + .assert_eq(Tag(TAG)) + .map_err(|e| Err::Error(e.into()))?; + if any.class() as u8 != CLASS { + let class = Class::try_from(CLASS).ok(); + return Err(Err::Error( + Error::unexpected_class(class, any.class()).into(), + )); + } + let any = Any { + header: Header { + tag: T::TAG, + ..any.header.clone() + }, + data: any.data, + }; + match T::try_from(any) { + Ok(inner) => Ok((rem, TaggedValue::implicit(inner))), + Err(e) => Err(nom::Err::Error(e)), + } + } +} + +impl<T, E, const CLASS: u8, const TAG: u32> CheckDerConstraints + for TaggedValue<T, E, Implicit, CLASS, TAG> +where + T: CheckDerConstraints, + T: Tagged, +{ + fn check_constraints(any: &Any) -> Result<()> { + any.header.length.assert_definite()?; + let header = any.header.clone().with_tag(T::TAG); + let inner = Any::new(header, any.data); + T::check_constraints(&inner)?; + Ok(()) + } +} + +#[cfg(feature = "std")] +impl<T, E, const CLASS: u8, const TAG: u32> ToDer for TaggedValue<T, E, Implicit, CLASS, TAG> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + self.inner.to_der_len() + } + + fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let class = + Class::try_from(CLASS).map_err(|_| SerializeError::InvalidClass { class: CLASS })?; + let mut v = Vec::new(); + let inner_len = self.inner.write_der_content(&mut v)?; + // XXX X.690 section 8.14.3: if implicing tagging was used [...]: + // XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise + let constructed = matches!(TAG, 16 | 17); + let header = Header::new(class, constructed, self.tag(), Length::Definite(inner_len)); + let sz = header.write_der_header(writer)?; + let sz = sz + writer.write(&v)?; + Ok(sz) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut sink = std::io::sink(); + let class = + Class::try_from(CLASS).map_err(|_| SerializeError::InvalidClass { class: CLASS })?; + let inner_len = self.inner.write_der_content(&mut sink)?; + // XXX X.690 section 8.14.3: if implicing tagging was used [...]: + // XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise + let constructed = matches!(TAG, 16 | 17); + let header = Header::new(class, constructed, self.tag(), Length::Definite(inner_len)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + self.inner.write_der(writer) + } +} + +/// A helper object to parse `[ n ] IMPLICIT T` +/// +/// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged +/// optional values. +/// +/// This helper expects context-specific tags. +/// See [`TaggedValue`] or [`TaggedParser`] for more generic implementations if needed. +/// +/// # Examples +/// +/// To parse a `[0] IMPLICIT INTEGER OPTIONAL` object: +/// +/// ```rust +/// use asn1_rs::{Error, FromBer, Integer, TaggedImplicit, TaggedValue}; +/// +/// let bytes = &[0xa0, 0x1, 0x2]; +/// +/// let (_, tagged) = TaggedImplicit::<Integer, Error, 0>::from_ber(bytes).unwrap(); +/// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2))); +/// ``` +pub type TaggedImplicit<T, E, const TAG: u32> = TaggedValue<T, E, Implicit, CONTEXT_SPECIFIC, TAG>; + +impl<'a, T, E> FromBer<'a, E> for TaggedParser<'a, Implicit, T, E> +where + T: TryFrom<Any<'a>, Error = E>, + T: Tagged, + E: From<Error>, +{ + fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?; + let Any { header, data } = any; + let any = Any { + header: Header { + tag: T::TAG, + ..header.clone() + }, + data, + }; + match T::try_from(any) { + Ok(t) => { + let tagged_value = TaggedParser { + header, + inner: t, + tag_kind: PhantomData, + _e: PhantomData, + }; + Ok((rem, tagged_value)) + } + Err(e) => Err(nom::Err::Error(e)), + } + } +} + +// implementations for TaggedParser + +impl<'a, T, E> TaggedParser<'a, Implicit, T, E> { + pub const fn new_implicit(class: Class, constructed: bool, tag: u32, inner: T) -> Self { + Self { + header: Header::new(class, constructed, Tag(tag), Length::Definite(0)), + inner, + tag_kind: PhantomData, + _e: PhantomData, + } + } +} + +impl<'a, T, E> FromDer<'a, E> for TaggedParser<'a, Implicit, T, E> +where + T: TryFrom<Any<'a>, Error = E>, + T: CheckDerConstraints, + T: Tagged, + E: From<Error>, +{ + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; + let Any { header, data } = any; + let any = Any { + header: Header { + tag: T::TAG, + ..header.clone() + }, + data, + }; + T::check_constraints(&any).map_err(|e| nom::Err::Error(e.into()))?; + match T::try_from(any) { + Ok(t) => { + let tagged_value = TaggedParser { + header, + inner: t, + tag_kind: PhantomData, + _e: PhantomData, + }; + Ok((rem, tagged_value)) + } + Err(e) => Err(nom::Err::Error(e)), + } + } +} + +impl<'a, T> CheckDerConstraints for TaggedParser<'a, Implicit, T> +where + T: CheckDerConstraints, + T: Tagged, +{ + fn check_constraints(any: &Any) -> Result<()> { + any.header.length.assert_definite()?; + let any = Any { + header: Header { + tag: T::TAG, + ..any.header.clone() + }, + data: any.data, + }; + T::check_constraints(&any)?; + Ok(()) + } +} + +#[cfg(feature = "std")] +impl<'a, T> ToDer for TaggedParser<'a, Implicit, T> +where + T: ToDer, +{ + fn to_der_len(&self) -> Result<usize> { + self.inner.to_der_len() + } + + fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut v = Vec::new(); + let inner_len = self.inner.write_der_content(&mut v)?; + // XXX X.690 section 8.14.3: if implicing tagging was used [...]: + // XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise + let header = Header::new(self.class(), false, self.tag(), Length::Definite(inner_len)); + let sz = header.write_der_header(writer)?; + let sz = sz + writer.write(&v)?; + Ok(sz) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let mut sink = std::io::sink(); + let inner_len = self.inner.write_der_content(&mut sink)?; + // XXX X.690 section 8.14.3: if implicing tagging was used [...]: + // XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise + let header = Header::new(self.class(), false, self.tag(), Length::Definite(inner_len)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + self.inner.write_der_content(writer) + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/tagged/optional.rs b/rust/vendor/asn1-rs/src/asn1_types/tagged/optional.rs new file mode 100644 index 0000000..00504df --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/tagged/optional.rs @@ -0,0 +1,240 @@ +use super::{explicit::TaggedExplicit, implicit::TaggedImplicit}; +use crate::*; + +/// Helper object to parse TAGGED OPTIONAL types (explicit or implicit) +/// +/// This object can be used similarly to a builder pattern, to specify the expected class and +/// tag of the object to parse, and the content parsing function. +/// +/// The content parsing function takes two arguments: the outer header, and the data. +/// +/// It can be used for both EXPLICIT or IMPLICIT tagged objects by using parsing functions that +/// expect a header (or not) in the contents. +/// +/// The [`OptTaggedParser::from`] method is a shortcut to build an object with `ContextSpecific` +/// class and the given tag. The [`OptTaggedParser::new`] method is more generic. +/// +/// See also [`OptTaggedExplicit`] and [`OptTaggedImplicit`] for alternatives that implement [`FromBer`]/ +/// [`FromDer`]. +/// +/// # Examples +/// +/// To parse a `[APPLICATION 0] EXPLICIT INTEGER OPTIONAL` object: +/// +/// ```rust +/// use asn1_rs::{Class, FromDer, Integer, Tag, OptTaggedParser}; +/// +/// let bytes = &[0x60, 0x03, 0x2, 0x1, 0x2]; +/// +/// let (_, tagged) = OptTaggedParser::new(Class::Application, Tag(0)) +/// .parse_der(bytes, |_, data| Integer::from_der(data)) +/// .unwrap(); +/// +/// assert_eq!(tagged, Some(Integer::from(2))); +/// ``` +/// +/// To parse a `[0] IMPLICIT INTEGER OPTIONAL` object: +/// +/// ```rust +/// use asn1_rs::{Error, Integer, OptTaggedParser}; +/// +/// let bytes = &[0xa0, 0x1, 0x2]; +/// +/// let (_, tagged) = OptTaggedParser::from(0) +/// .parse_der::<_, Error, _>(bytes, |_, data| Ok((&[], Integer::new(data)))) +/// .unwrap(); +/// +/// assert_eq!(tagged, Some(Integer::from(2))); +/// ``` +#[derive(Debug)] +pub struct OptTaggedParser { + /// The expected class for the object to parse + pub class: Class, + /// The expected tag for the object to parse + pub tag: Tag, +} + +impl OptTaggedParser { + /// Build a new `OptTaggedParser` object. + /// + /// If using `Class::ContextSpecific`, using [`OptTaggedParser::from`] with either a `Tag` or `u32` is + /// a shorter way to build this object. + pub const fn new(class: Class, tag: Tag) -> Self { + OptTaggedParser { class, tag } + } + + pub const fn universal(tag: u32) -> Self { + Self::new(Class::Universal, Tag(tag)) + } + + pub const fn tagged(tag: u32) -> Self { + Self::new(Class::ContextSpecific, Tag(tag)) + } + + pub const fn application(tag: u32) -> Self { + Self::new(Class::Application, Tag(tag)) + } + + pub const fn private(tag: u32) -> Self { + Self::new(Class::Private, Tag(tag)) + } + + /// Parse input as BER, and apply the provided function to parse object. + /// + /// Returns the remaining bytes, and `Some(T)` if expected tag was found, else `None`. + /// + /// This function returns an error if tag was found but has a different class, or if parsing fails. + /// + /// # Examples + /// + /// To parse a `[0] EXPLICIT INTEGER OPTIONAL` object: + /// + /// ```rust + /// use asn1_rs::{FromBer, Integer, OptTaggedParser}; + /// + /// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2]; + /// + /// let (_, tagged) = OptTaggedParser::from(0) + /// .parse_ber(bytes, |_, data| Integer::from_ber(data)) + /// .unwrap(); + /// + /// assert_eq!(tagged, Some(Integer::from(2))); + /// ``` + pub fn parse_ber<'a, T, E, F>(&self, bytes: &'a [u8], f: F) -> ParseResult<'a, Option<T>, E> + where + F: Fn(Header, &'a [u8]) -> ParseResult<'a, T, E>, + E: From<Error>, + { + if bytes.is_empty() { + return Ok((bytes, None)); + } + let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?; + if any.tag() != self.tag { + return Ok((bytes, None)); + } + if any.class() != self.class { + return Err(Err::Error( + Error::unexpected_class(Some(self.class), any.class()).into(), + )); + } + let Any { header, data } = any; + let (_, res) = f(header, data)?; + Ok((rem, Some(res))) + } + + /// Parse input as DER, and apply the provided function to parse object. + /// + /// Returns the remaining bytes, and `Some(T)` if expected tag was found, else `None`. + /// + /// This function returns an error if tag was found but has a different class, or if parsing fails. + /// + /// # Examples + /// + /// To parse a `[0] EXPLICIT INTEGER OPTIONAL` object: + /// + /// ```rust + /// use asn1_rs::{FromDer, Integer, OptTaggedParser}; + /// + /// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2]; + /// + /// let (_, tagged) = OptTaggedParser::from(0) + /// .parse_der(bytes, |_, data| Integer::from_der(data)) + /// .unwrap(); + /// + /// assert_eq!(tagged, Some(Integer::from(2))); + /// ``` + pub fn parse_der<'a, T, E, F>(&self, bytes: &'a [u8], f: F) -> ParseResult<'a, Option<T>, E> + where + F: Fn(Header, &'a [u8]) -> ParseResult<'a, T, E>, + E: From<Error>, + { + if bytes.is_empty() { + return Ok((bytes, None)); + } + let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; + if any.tag() != self.tag { + return Ok((bytes, None)); + } + if any.class() != self.class { + return Err(Err::Error( + Error::unexpected_class(Some(self.class), any.class()).into(), + )); + } + let Any { header, data } = any; + let (_, res) = f(header, data)?; + Ok((rem, Some(res))) + } +} + +impl From<Tag> for OptTaggedParser { + /// Build a `TaggedOptional` object with class `ContextSpecific` and given tag + #[inline] + fn from(tag: Tag) -> Self { + OptTaggedParser::new(Class::ContextSpecific, tag) + } +} + +impl From<u32> for OptTaggedParser { + /// Build a `TaggedOptional` object with class `ContextSpecific` and given tag + #[inline] + fn from(tag: u32) -> Self { + OptTaggedParser::new(Class::ContextSpecific, Tag(tag)) + } +} + +/// A helper object to parse `[ n ] EXPLICIT T OPTIONAL` +/// +/// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged +/// optional values. +/// +/// This helper expects context-specific tags. +/// Use `Option<` [`TaggedValue`] `>` for a more generic implementation. +/// +/// # Examples +/// +/// To parse a `[0] EXPLICIT INTEGER OPTIONAL` object: +/// +/// ```rust +/// use asn1_rs::{Error, FromBer, Integer, OptTaggedExplicit, TaggedValue}; +/// +/// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2]; +/// +/// // If tagged object is present (and has expected tag), parsing succeeds: +/// let (_, tagged) = OptTaggedExplicit::<Integer, Error, 0>::from_ber(bytes).unwrap(); +/// assert_eq!(tagged, Some(TaggedValue::explicit(Integer::from(2)))); +/// +/// // If tagged object is not present or has different tag, parsing +/// // also succeeds (returning None): +/// let (_, tagged) = OptTaggedExplicit::<Integer, Error, 0>::from_ber(&[]).unwrap(); +/// assert_eq!(tagged, None); +/// let (_, tagged) = OptTaggedExplicit::<Integer, Error, 1>::from_ber(bytes).unwrap(); +/// assert_eq!(tagged, None); +/// ``` +pub type OptTaggedExplicit<T, E, const TAG: u32> = Option<TaggedExplicit<T, E, TAG>>; + +/// A helper object to parse `[ n ] IMPLICIT T OPTIONAL` +/// +/// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged +/// optional values. +/// +/// This helper expects context-specific tags. +/// Use `Option<` [`TaggedValue`] `>` for a more generic implementation. +/// +/// # Examples +/// +/// To parse a `[0] IMPLICIT INTEGER OPTIONAL` object: +/// +/// ```rust +/// use asn1_rs::{Error, FromBer, Integer, OptTaggedImplicit, TaggedValue}; +/// +/// let bytes = &[0xa0, 0x1, 0x2]; +/// +/// let (_, tagged) = OptTaggedImplicit::<Integer, Error, 0>::from_ber(bytes).unwrap(); +/// assert_eq!(tagged, Some(TaggedValue::implicit(Integer::from(2)))); +/// +/// // If tagged object is not present or has different tag, parsing +/// // also succeeds (returning None): +/// let (_, tagged) = OptTaggedImplicit::<Integer, Error, 0>::from_ber(&[]).unwrap(); +/// assert_eq!(tagged, None); +/// ``` +pub type OptTaggedImplicit<T, E, const TAG: u32> = Option<TaggedImplicit<T, E, TAG>>; diff --git a/rust/vendor/asn1-rs/src/asn1_types/tagged/parser.rs b/rust/vendor/asn1-rs/src/asn1_types/tagged/parser.rs new file mode 100644 index 0000000..243b081 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/tagged/parser.rs @@ -0,0 +1,78 @@ +use crate::*; +use core::marker::PhantomData; + +#[derive(Debug, PartialEq, Eq)] +pub struct TaggedParser<'a, TagKind, T, E = Error> { + pub header: Header<'a>, + pub inner: T, + + pub(crate) tag_kind: PhantomData<TagKind>, + pub(crate) _e: PhantomData<E>, +} + +impl<'a, TagKind, T, E> TaggedParser<'a, TagKind, T, E> { + pub const fn new(header: Header<'a>, inner: T) -> Self { + TaggedParser { + header, + inner, + tag_kind: PhantomData, + _e: PhantomData, + } + } + + pub const fn assert_class(&self, class: Class) -> Result<()> { + self.header.assert_class(class) + } + + pub const fn assert_tag(&self, tag: Tag) -> Result<()> { + self.header.assert_tag(tag) + } + + #[inline] + pub const fn class(&self) -> Class { + self.header.class + } + + #[inline] + pub const fn tag(&self) -> Tag { + self.header.tag + } +} + +impl<'a, TagKind, T, E> AsRef<T> for TaggedParser<'a, TagKind, T, E> { + fn as_ref(&self) -> &T { + &self.inner + } +} + +impl<'a, TagKind, T, E> TaggedParser<'a, TagKind, T, E> +where + Self: FromBer<'a, E>, + E: From<Error>, +{ + pub fn parse_ber(class: Class, tag: Tag, bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, t) = TaggedParser::<TagKind, T, E>::from_ber(bytes)?; + t.assert_class(class).map_err(|e| Err::Error(e.into()))?; + t.assert_tag(tag).map_err(|e| Err::Error(e.into()))?; + Ok((rem, t)) + } +} + +impl<'a, TagKind, T, E> TaggedParser<'a, TagKind, T, E> +where + Self: FromDer<'a, E>, + E: From<Error>, +{ + pub fn parse_der(class: Class, tag: Tag, bytes: &'a [u8]) -> ParseResult<'a, Self, E> { + let (rem, t) = TaggedParser::<TagKind, T, E>::from_der(bytes)?; + t.assert_class(class).map_err(|e| Err::Error(e.into()))?; + t.assert_tag(tag).map_err(|e| Err::Error(e.into()))?; + Ok((rem, t)) + } +} + +impl<'a, TagKind, T, E> DynTagged for TaggedParser<'a, TagKind, T, E> { + fn tag(&self) -> Tag { + self.tag() + } +} diff --git a/rust/vendor/asn1-rs/src/asn1_types/tagged/private.rs b/rust/vendor/asn1-rs/src/asn1_types/tagged/private.rs new file mode 100644 index 0000000..5d8708a --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/tagged/private.rs @@ -0,0 +1,42 @@ +use crate::{Class, Explicit, Implicit, TaggedValue}; + +/// A helper object to parse `[PRIVATE n] EXPLICIT T` +/// +/// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to +/// parse explicit private-tagged values. +/// +/// # Examples +/// +/// To parse a `[PRIVATE 0] EXPLICIT INTEGER` object: +/// +/// ```rust +/// use asn1_rs::{Error, FromBer, Integer, PrivateExplicit, TaggedValue}; +/// +/// let bytes = &[0xe0, 0x03, 0x2, 0x1, 0x2]; +/// +/// // If tagged object is present (and has expected tag), parsing succeeds: +/// let (_, tagged) = PrivateExplicit::<Integer, Error, 0>::from_ber(bytes).unwrap(); +/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2))); +/// ``` +pub type PrivateExplicit<T, E, const TAG: u32> = + TaggedValue<T, E, Explicit, { Class::PRIVATE }, TAG>; + +/// A helper object to parse `[PRIVATE n] IMPLICIT T` +/// +/// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to +/// parse implicit private-tagged values. +/// +/// # Examples +/// +/// To parse a `[PRIVATE 0] IMPLICIT INTEGER` object: +/// +/// ```rust +/// use asn1_rs::{Error, FromBer, Integer, PrivateImplicit, TaggedValue}; +/// +/// let bytes = &[0xe0, 0x1, 0x2]; +/// +/// let (_, tagged) = PrivateImplicit::<Integer, Error, 0>::from_ber(bytes).unwrap(); +/// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2_u8))); +/// ``` +pub type PrivateImplicit<T, E, const TAG: u32> = + TaggedValue<T, E, Implicit, { Class::PRIVATE }, TAG>; diff --git a/rust/vendor/asn1-rs/src/asn1_types/utctime.rs b/rust/vendor/asn1-rs/src/asn1_types/utctime.rs new file mode 100644 index 0000000..8604914 --- /dev/null +++ b/rust/vendor/asn1-rs/src/asn1_types/utctime.rs @@ -0,0 +1,222 @@ +use crate::datetime::decode_decimal; +use crate::*; +use core::convert::TryFrom; +use core::fmt; +#[cfg(feature = "datetime")] +use time::OffsetDateTime; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct UtcTime(pub ASN1DateTime); + +impl UtcTime { + pub const fn new(datetime: ASN1DateTime) -> Self { + UtcTime(datetime) + } + + pub fn from_bytes(bytes: &[u8]) -> Result<Self> { + // X.680 section 43 defines a UniversalTime as a VisibleString restricted to: + // + // a) the six digits YYMMDD where YY is the two low-order digits of the Christian year, MM is the month + // (counting January as 01), and DD is the day of the month (01 to 31); and + // b) either: + // 1) the four digits hhmm where hh is hour (00 to 23) and mm is minutes (00 to 59); or + // 2) the six digits hhmmss where hh and mm are as in 1) above, and ss is seconds (00 to 59); and + // c) either: + // 1) the character Z ; or + // 2) one of the characters + or - , followed by hhmm, where hh is hour and mm is minutes. + // + // XXX // RFC 5280 requires mandatory seconds and Z-normalized time zone + let (year, month, day, hour, minute, rem) = match bytes { + [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, rem @ ..] => { + let year = decode_decimal(Self::TAG, *year1, *year2)?; + let month = decode_decimal(Self::TAG, *mon1, *mon2)?; + let day = decode_decimal(Self::TAG, *day1, *day2)?; + let hour = decode_decimal(Self::TAG, *hour1, *hour2)?; + let minute = decode_decimal(Self::TAG, *min1, *min2)?; + (year, month, day, hour, minute, rem) + } + _ => return Err(Self::TAG.invalid_value("malformed time string (not yymmddhhmm)")), + }; + if rem.is_empty() { + return Err(Self::TAG.invalid_value("malformed time string")); + } + // check for seconds + let (second, rem) = match rem { + [sec1, sec2, rem @ ..] => { + let second = decode_decimal(Self::TAG, *sec1, *sec2)?; + (second, rem) + } + _ => (0, rem), + }; + if month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59 { + return Err(Self::TAG.invalid_value("time components with invalid values")); + } + if rem.is_empty() { + return Err(Self::TAG.invalid_value("malformed time string")); + } + let tz = match rem { + [b'Z'] => ASN1TimeZone::Z, + [b'+', h1, h2, m1, m2] => { + let hh = decode_decimal(Self::TAG, *h1, *h2)?; + let mm = decode_decimal(Self::TAG, *m1, *m2)?; + ASN1TimeZone::Offset(hh as i8, mm as i8) + } + [b'-', h1, h2, m1, m2] => { + let hh = decode_decimal(Self::TAG, *h1, *h2)?; + let mm = decode_decimal(Self::TAG, *m1, *m2)?; + ASN1TimeZone::Offset(-(hh as i8), mm as i8) + } + _ => return Err(Self::TAG.invalid_value("malformed time string: no time zone")), + }; + Ok(UtcTime(ASN1DateTime::new( + year as u32, + month, + day, + hour, + minute, + second, + None, + tz, + ))) + // match *bytes { + // [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => { + // let year = decode_decimal(Self::TAG, year1, year2)?; + // let month = decode_decimal(Self::TAG, mon1, mon2)?; + // let day = decode_decimal(Self::TAG, day1, day2)?; + // let hour = decode_decimal(Self::TAG, hour1, hour2)?; + // let minute = decode_decimal(Self::TAG, min1, min2)?; + // let second = decode_decimal(Self::TAG, sec1, sec2)?; + + // // RFC 5280 rules for interpreting the year + // let year = if year >= 50 { year + 1900 } else { year + 2000 }; + + // Ok(UtcTime::new(year, month, day, hour, minute, second)) + // } + // _ => Err(Error::InvalidValue), + // } + } + + /// Return a ISO 8601 combined date and time with time zone. + #[cfg(feature = "datetime")] + #[cfg_attr(docsrs, doc(cfg(feature = "datetime")))] + #[inline] + pub fn utc_datetime(&self) -> Result<OffsetDateTime> { + self.0.to_datetime() + } + + /// Return an adjusted ISO 8601 combined date and time with time zone. + /// According to Universal time definition in X.680 we add 2000 years + /// from 0 to 49 year and 1900 otherwise. + #[cfg(feature = "datetime")] + #[cfg_attr(docsrs, doc(cfg(feature = "datetime")))] + #[inline] + pub fn utc_adjusted_datetime(&self) -> Result<OffsetDateTime> { + self.0.to_datetime().and_then(|dt| { + let year = dt.year(); + // We follow the Universal time definition in X.680 for interpreting + // the adjusted year + let year = if year >= 50 { year + 1900 } else { year + 2000 }; + time::Date::from_calendar_date(year, dt.month(), dt.day()) + .map(|d| dt.replace_date(d)) + .map_err(|_e| Self::TAG.invalid_value("Invalid adjusted date")) + }) + } + + /// Returns the number of non-leap seconds since the midnight on January 1, 1970. + #[cfg(feature = "datetime")] + #[cfg_attr(docsrs, doc(cfg(feature = "datetime")))] + pub fn timestamp(&self) -> Result<i64> { + let dt = self.0.to_datetime()?; + Ok(dt.unix_timestamp()) + } +} + +impl<'a> TryFrom<Any<'a>> for UtcTime { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<UtcTime> { + TryFrom::try_from(&any) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for UtcTime { + type Error = Error; + + fn try_from(any: &'b Any<'a>) -> Result<UtcTime> { + any.tag().assert_eq(Self::TAG)?; + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_visible(b: &u8) -> bool { + 0x20 <= *b && *b <= 0x7f + } + if !any.data.iter().all(is_visible) { + return Err(Error::StringInvalidCharset); + } + + UtcTime::from_bytes(any.data) + } +} + +impl fmt::Display for UtcTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let dt = &self.0; + match dt.tz { + ASN1TimeZone::Z | ASN1TimeZone::Undefined => write!( + f, + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}Z", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second + ), + ASN1TimeZone::Offset(hh, mm) => { + let (s, hh) = if hh > 0 { ('+', hh) } else { ('-', -hh) }; + write!( + f, + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}{:02}{:02}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, s, hh, mm + ) + } + } + } +} + +impl CheckDerConstraints for UtcTime { + fn check_constraints(_any: &Any) -> Result<()> { + Ok(()) + } +} + +impl DerAutoDerive for UtcTime {} + +impl Tagged for UtcTime { + const TAG: Tag = Tag::UtcTime; +} + +#[cfg(feature = "std")] +impl ToDer for UtcTime { + fn to_der_len(&self) -> Result<usize> { + // data: + // - 6 bytes for YYMMDD + // - 6 for hhmmss in DER (X.690 section 11.8.2) + // - 1 for the character Z in DER (X.690 section 11.8.1) + // data length: 13 + // + // thus, length will always be on 1 byte (short length) and + // class+structure+tag also on 1 + // + // total: 15 = 1 (class+constructed+tag) + 1 (length) + 13 + Ok(15) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + // see above for length value + writer.write(&[Self::TAG.0 as u8, 13]).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + write!( + writer, + "{:02}{:02}{:02}{:02}{:02}{:02}Z", + self.0.year, self.0.month, self.0.day, self.0.hour, self.0.minute, self.0.second, + )?; + // write_fmt returns (), see above for length value + Ok(13) + } +} diff --git a/rust/vendor/asn1-rs/src/ber/mod.rs b/rust/vendor/asn1-rs/src/ber/mod.rs new file mode 100644 index 0000000..5feedaf --- /dev/null +++ b/rust/vendor/asn1-rs/src/ber/mod.rs @@ -0,0 +1,3 @@ +mod parser; + +pub use parser::*; diff --git a/rust/vendor/asn1-rs/src/ber/parser.rs b/rust/vendor/asn1-rs/src/ber/parser.rs new file mode 100644 index 0000000..fdfdb88 --- /dev/null +++ b/rust/vendor/asn1-rs/src/ber/parser.rs @@ -0,0 +1,169 @@ +use crate::error::*; +use crate::header::*; +use crate::{BerParser, DerParser, FromBer, Length, Tag}; +use nom::bytes::streaming::take; +use nom::{Err, Needed, Offset}; +use rusticata_macros::custom_check; + +/// Default maximum recursion limit +pub const MAX_RECURSION: usize = 50; + +/// Default maximum object size (2^32) +// pub const MAX_OBJECT_SIZE: usize = 4_294_967_295; + +pub trait GetObjectContent { + /// Return the raw content (bytes) of the next ASN.1 encoded object + /// + /// Note: if using BER and length is indefinite, terminating End-Of-Content is NOT included + fn get_object_content<'a>( + i: &'a [u8], + hdr: &'_ Header, + max_depth: usize, + ) -> ParseResult<'a, &'a [u8]>; +} + +impl GetObjectContent for BerParser { + fn get_object_content<'a>( + i: &'a [u8], + hdr: &'_ Header, + max_depth: usize, + ) -> ParseResult<'a, &'a [u8]> { + let start_i = i; + let (i, _) = ber_skip_object_content(i, hdr, max_depth)?; + let len = start_i.offset(i); + let (content, i) = start_i.split_at(len); + // if len is indefinite, there are 2 extra bytes for EOC + if hdr.length == Length::Indefinite { + let len = content.len(); + assert!(len >= 2); + Ok((i, &content[..len - 2])) + } else { + Ok((i, content)) + } + } +} + +impl GetObjectContent for DerParser { + /// Skip object content, accepting only DER + /// + /// This this function is for DER only, it cannot go into recursion (no indefinite length) + fn get_object_content<'a>( + i: &'a [u8], + hdr: &'_ Header, + _max_depth: usize, + ) -> ParseResult<'a, &'a [u8]> { + match hdr.length { + Length::Definite(l) => take(l)(i), + Length::Indefinite => Err(Err::Error(Error::DerConstraintFailed( + DerConstraint::IndefiniteLength, + ))), + } + } +} + +/// Skip object content, and return true if object was End-Of-Content +fn ber_skip_object_content<'a>( + i: &'a [u8], + hdr: &Header, + max_depth: usize, +) -> ParseResult<'a, bool> { + if max_depth == 0 { + return Err(Err::Error(Error::BerMaxDepth)); + } + match hdr.length { + Length::Definite(l) => { + if l == 0 && hdr.tag == Tag::EndOfContent { + return Ok((i, true)); + } + let (i, _) = take(l)(i)?; + Ok((i, false)) + } + Length::Indefinite => { + hdr.assert_constructed()?; + // read objects until EndOfContent (00 00) + // this is recursive + let mut i = i; + loop { + let (i2, header2) = Header::from_ber(i)?; + let (i3, eoc) = ber_skip_object_content(i2, &header2, max_depth - 1)?; + if eoc { + // return false, since top object was not EndOfContent + return Ok((i3, false)); + } + i = i3; + } + } + } +} + +/// Try to parse input bytes as u64 +#[inline] +pub(crate) fn bytes_to_u64(s: &[u8]) -> core::result::Result<u64, Error> { + let mut u: u64 = 0; + for &c in s { + if u & 0xff00_0000_0000_0000 != 0 { + return Err(Error::IntegerTooLarge); + } + u <<= 8; + u |= u64::from(c); + } + Ok(u) +} + +pub(crate) fn parse_identifier(i: &[u8]) -> ParseResult<(u8, u8, u32, &[u8])> { + if i.is_empty() { + Err(Err::Incomplete(Needed::new(1))) + } else { + let a = i[0] >> 6; + let b = u8::from(i[0] & 0b0010_0000 != 0); + let mut c = u32::from(i[0] & 0b0001_1111); + + let mut tag_byte_count = 1; + + if c == 0x1f { + c = 0; + loop { + // Make sure we don't read past the end of our data. + custom_check!(i, tag_byte_count >= i.len(), Error::InvalidTag)?; + + // With tag defined as u32 the most we can fit in is four tag bytes. + // (X.690 doesn't actually specify maximum tag width.) + custom_check!(i, tag_byte_count > 5, Error::InvalidTag)?; + + c = (c << 7) | (u32::from(i[tag_byte_count]) & 0x7f); + let done = i[tag_byte_count] & 0x80 == 0; + tag_byte_count += 1; + if done { + break; + } + } + } + + let (raw_tag, rem) = i.split_at(tag_byte_count); + + Ok((rem, (a, b, c, raw_tag))) + } +} + +/// Return the MSB and the rest of the first byte, or an error +pub(crate) fn parse_ber_length_byte(i: &[u8]) -> ParseResult<(u8, u8)> { + if i.is_empty() { + Err(Err::Incomplete(Needed::new(1))) + } else { + let a = i[0] >> 7; + let b = i[0] & 0b0111_1111; + Ok((&i[1..], (a, b))) + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! der_constraint_fail_if( + ($slice:expr, $cond:expr, $constraint:expr) => ( + { + if $cond { + return Err(::nom::Err::Error(Error::DerConstraintFailed($constraint))); + } + } + ); +); diff --git a/rust/vendor/asn1-rs/src/class.rs b/rust/vendor/asn1-rs/src/class.rs new file mode 100644 index 0000000..9a4f9de --- /dev/null +++ b/rust/vendor/asn1-rs/src/class.rs @@ -0,0 +1,94 @@ +use core::convert::TryFrom; +use core::fmt; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct BerClassFromIntError(pub(crate) ()); + +/// BER Object class of tag +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[repr(u8)] +pub enum Class { + /// `Universal` class of tags (`0b00`) + Universal = 0b00, + /// `Application` class of tags (`0b01`) + Application = 0b01, + /// `Context-Specific` class of tags (`0b10`) + ContextSpecific = 0b10, + /// `Private` class of tags (`0b11`) + Private = 0b11, +} + +impl Class { + /// `Universal` class of tags (`0b00`) + pub const UNIVERSAL: u8 = 0b00; + /// `Application` class of tags (`0b01`) + pub const APPLICATION: u8 = 0b01; + /// `Context-Specific` class of tags (`0b10`) + pub const CONTEXT_SPECIFIC: u8 = 0b10; + /// `Private` class of tags (`0b11`) + pub const PRIVATE: u8 = 0b11; + + pub const fn assert_eq(&self, class: Class) -> Result<(), crate::error::Error> { + if *self as u8 == class as u8 { + Ok(()) + } else { + Err(crate::error::Error::UnexpectedClass { + expected: Some(class), + actual: *self, + }) + } + } +} + +impl fmt::Display for Class { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Class::Universal => "UNIVERSAL", + Class::Application => "APPLICATION", + Class::ContextSpecific => "CONTEXT-SPECIFIC", + Class::Private => "PRIVATE", + }; + write!(f, "{}", s) + } +} + +impl TryFrom<u8> for Class { + type Error = BerClassFromIntError; + + #[inline] + fn try_from(value: u8) -> Result<Self, Self::Error> { + match value { + 0b00 => Ok(Class::Universal), + 0b01 => Ok(Class::Application), + 0b10 => Ok(Class::ContextSpecific), + 0b11 => Ok(Class::Private), + _ => Err(BerClassFromIntError(())), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn methods_class() { + let c = Class::Universal; + assert!(c.assert_eq(Class::Universal).is_ok()); + assert!(c.assert_eq(Class::Private).is_err()); + + assert_eq!(Class::Universal.to_string().as_str(), "UNIVERSAL"); + assert_eq!(Class::Application.to_string().as_str(), "APPLICATION"); + assert_eq!( + Class::ContextSpecific.to_string().as_str(), + "CONTEXT-SPECIFIC" + ); + assert_eq!(Class::Private.to_string().as_str(), "PRIVATE"); + + assert!(Class::try_from(0b00).is_ok()); + assert!(Class::try_from(0b01).is_ok()); + assert!(Class::try_from(0b10).is_ok()); + assert!(Class::try_from(0b11).is_ok()); + assert!(Class::try_from(4).is_err()); + } +} diff --git a/rust/vendor/asn1-rs/src/const_int.rs b/rust/vendor/asn1-rs/src/const_int.rs new file mode 100644 index 0000000..75beeaa --- /dev/null +++ b/rust/vendor/asn1-rs/src/const_int.rs @@ -0,0 +1,43 @@ +use crate::{Tag, Tagged}; + +#[derive(Debug)] +pub struct ConstInt { + buffer: [u8; 10], + n: usize, +} + +// XXX only ToBer/ToDer trait supported? + +impl Tagged for ConstInt { + const TAG: Tag = Tag::Integer; +} + +#[derive(Debug)] +pub struct IntBuilder {} + +impl IntBuilder { + pub const fn build(&self, i: u64) -> ConstInt { + let b = i.to_be_bytes(); + let mut out = [0u8; 10]; + out[0] = 0x4; + let src_len = b.len(); + let mut src_index = 0; + while src_index < src_len && b[src_index] == 0 { + src_index += 1; + } + out[1] = (src_len - src_index) as u8; + let mut dst_index = 2; + while src_index < src_len { + out[dst_index] = b[src_index]; + src_index += 1; + dst_index += 1; + } + // XXX will not work: we need to allocate a Vec + // also, we cannot just store the bytes (there are extra zeroes at end) + // Integer::new(&out[..dst_index]) + ConstInt { + buffer: out, + n: dst_index, + } + } +} diff --git a/rust/vendor/asn1-rs/src/datetime.rs b/rust/vendor/asn1-rs/src/datetime.rs new file mode 100644 index 0000000..613b44b --- /dev/null +++ b/rust/vendor/asn1-rs/src/datetime.rs @@ -0,0 +1,108 @@ +use crate::{Result, Tag}; +use alloc::format; +use alloc::string::ToString; +use core::fmt; +#[cfg(feature = "datetime")] +use time::OffsetDateTime; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum ASN1TimeZone { + /// No timezone provided + Undefined, + /// Coordinated universal time + Z, + /// Local zone, with offset to coordinated universal time + /// + /// `(offset_hour, offset_minute)` + Offset(i8, i8), +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct ASN1DateTime { + pub year: u32, + pub month: u8, + pub day: u8, + pub hour: u8, + pub minute: u8, + pub second: u8, + pub millisecond: Option<u16>, + pub tz: ASN1TimeZone, +} + +impl ASN1DateTime { + #[allow(clippy::too_many_arguments)] + pub const fn new( + year: u32, + month: u8, + day: u8, + hour: u8, + minute: u8, + second: u8, + millisecond: Option<u16>, + tz: ASN1TimeZone, + ) -> Self { + ASN1DateTime { + year, + month, + day, + hour, + minute, + second, + millisecond, + tz, + } + } + + #[cfg(feature = "datetime")] + fn to_time_datetime( + &self, + ) -> core::result::Result<OffsetDateTime, time::error::ComponentRange> { + use std::convert::TryFrom; + use time::{Date, Month, PrimitiveDateTime, Time, UtcOffset}; + + let month = Month::try_from(self.month)?; + let date = Date::from_calendar_date(self.year as i32, month, self.day)?; + let time = Time::from_hms_milli( + self.hour, + self.minute, + self.second, + self.millisecond.unwrap_or(0), + )?; + let primitive_date = PrimitiveDateTime::new(date, time); + let offset = match self.tz { + ASN1TimeZone::Offset(h, m) => UtcOffset::from_hms(h, m, 0)?, + ASN1TimeZone::Undefined | ASN1TimeZone::Z => UtcOffset::UTC, + }; + Ok(primitive_date.assume_offset(offset)) + } + + #[cfg(feature = "datetime")] + pub fn to_datetime(&self) -> Result<OffsetDateTime> { + use crate::Error; + + self.to_time_datetime().map_err(|_| Error::InvalidDateTime) + } +} + +impl fmt::Display for ASN1DateTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let fractional = match self.millisecond { + None => "".to_string(), + Some(v) => format!(".{}", v), + }; + write!( + f, + "{:04}{:02}{:02}{:02}{:02}{:02}{}Z", + self.year, self.month, self.day, self.hour, self.minute, self.second, fractional, + ) + } +} + +/// Decode 2-digit decimal value +pub(crate) fn decode_decimal(tag: Tag, hi: u8, lo: u8) -> Result<u8> { + if (b'0'..=b'9').contains(&hi) && (b'0'..=b'9').contains(&lo) { + Ok((hi - b'0') * 10 + (lo - b'0')) + } else { + Err(tag.invalid_value("expected digit")) + } +} diff --git a/rust/vendor/asn1-rs/src/derive.rs b/rust/vendor/asn1-rs/src/derive.rs new file mode 100644 index 0000000..faf862e --- /dev/null +++ b/rust/vendor/asn1-rs/src/derive.rs @@ -0,0 +1,322 @@ +/// # BerSequence custom derive +/// +/// `BerSequence` is a custom derive attribute, to derive a BER [`Sequence`](super::Sequence) parser automatically from the structure definition. +/// This attribute will automatically derive implementations for the following traits: +/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer) +/// - [`Tagged`](super::Tagged) +/// +/// `DerSequence` implies `BerSequence`, and will conflict with this custom derive. Use `BerSequence` when you only want the +/// above traits derived. +/// +/// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromBer`](super::FromBer) trait. +/// +/// See [`derive`](crate::doc::derive) documentation for more examples and documentation. +/// +/// ## Examples +/// +/// To parse the following ASN.1 structure: +/// <pre> +/// S ::= SEQUENCE { +/// a INTEGER(0..2^32), +/// b INTEGER(0..2^16), +/// c INTEGER(0..2^16), +/// } +/// </pre> +/// +/// Define a structure and add the `BerSequence` derive: +/// +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(BerSequence)] +/// struct S { +/// a: u32, +/// b: u16, +/// c: u16 +/// } +/// ``` +/// +/// ## Debugging +/// +/// To help debugging the generated code, the `#[debug_derive]` attribute has been added. +/// +/// When this attribute is specified, the generated code will be printed to `stderr` during compilation. +/// +/// Example: +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(BerSequence)] +/// #[debug_derive] +/// struct S { +/// a: u32, +/// } +/// ``` +pub use asn1_rs_derive::BerSequence; + +/// # DerSequence custom derive +/// +/// `DerSequence` is a custom derive attribute, to derive both BER and DER [`Sequence`](super::Sequence) parsers automatically from the structure definition. +/// This attribute will automatically derive implementations for the following traits: +/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer) +/// - [`Tagged`](super::Tagged) +/// - [`CheckDerConstraints`](super::CheckDerConstraints) +/// - [`FromDer`](super::FromDer) +/// +/// `DerSequence` implies `BerSequence`, and will conflict with this custom derive. +/// +/// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromDer`](super::FromDer) trait. +/// +/// See [`derive`](crate::doc::derive) documentation for more examples and documentation. +/// +/// ## Examples +/// +/// To parse the following ASN.1 structure: +/// <pre> +/// S ::= SEQUENCE { +/// a INTEGER(0..2^32), +/// b INTEGER(0..2^16), +/// c INTEGER(0..2^16), +/// } +/// </pre> +/// +/// Define a structure and add the `DerSequence` derive: +/// +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(DerSequence)] +/// struct S { +/// a: u32, +/// b: u16, +/// c: u16 +/// } +/// ``` +/// +/// ## Debugging +/// +/// To help debugging the generated code, the `#[debug_derive]` attribute has been added. +/// +/// When this attribute is specified, the generated code will be printed to `stderr` during compilation. +/// +/// Example: +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(DerSequence)] +/// #[debug_derive] +/// struct S { +/// a: u32, +/// } +/// ``` +pub use asn1_rs_derive::DerSequence; + +/// # BerSet custom derive +/// +/// `BerSet` is a custom derive attribute, to derive a BER [`Set`](super::Set) parser automatically from the structure definition. +/// This attribute will automatically derive implementations for the following traits: +/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer) +/// - [`Tagged`](super::Tagged) +/// +/// `DerSet` implies `BerSet`, and will conflict with this custom derive. Use `BerSet` when you only want the +/// above traits derived. +/// +/// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromBer`](super::FromBer) trait. +/// +/// See [`derive`](crate::doc::derive) documentation for more examples and documentation. +/// +/// ## Examples +/// +/// To parse the following ASN.1 structure: +/// <pre> +/// S ::= SET { +/// a INTEGER(0..2^32), +/// b INTEGER(0..2^16), +/// c INTEGER(0..2^16), +/// } +/// </pre> +/// +/// Define a structure and add the `BerSet` derive: +/// +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(BerSet)] +/// struct S { +/// a: u32, +/// b: u16, +/// c: u16 +/// } +/// ``` +/// +/// ## Debugging +/// +/// To help debugging the generated code, the `#[debug_derive]` attribute has been added. +/// +/// When this attribute is specified, the generated code will be printed to `stderr` during compilation. +/// +/// Example: +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(BerSet)] +/// #[debug_derive] +/// struct S { +/// a: u32, +/// } +/// ``` +pub use asn1_rs_derive::BerSet; + +/// # DerSet custom derive +/// +/// `DerSet` is a custom derive attribute, to derive both BER and DER [`Set`](super::Set) parsers automatically from the structure definition. +/// This attribute will automatically derive implementations for the following traits: +/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer) +/// - [`Tagged`](super::Tagged) +/// - [`CheckDerConstraints`](super::CheckDerConstraints) +/// - [`FromDer`](super::FromDer) +/// +/// `DerSet` implies `BerSet`, and will conflict with this custom derive. +/// +/// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromDer`](super::FromDer) trait. +/// +/// See [`derive`](crate::doc::derive) documentation for more examples and documentation. +/// +/// ## Examples +/// +/// To parse the following ASN.1 structure: +/// <pre> +/// S ::= SEt { +/// a INTEGER(0..2^32), +/// b INTEGER(0..2^16), +/// c INTEGER(0..2^16), +/// } +/// </pre> +/// +/// Define a structure and add the `DerSet` derive: +/// +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(DerSet)] +/// struct S { +/// a: u32, +/// b: u16, +/// c: u16 +/// } +/// ``` +/// +/// ## Debugging +/// +/// To help debugging the generated code, the `#[debug_derive]` attribute has been added. +/// +/// When this attribute is specified, the generated code will be printed to `stderr` during compilation. +/// +/// Example: +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(DerSet)] +/// #[debug_derive] +/// struct S { +/// a: u32, +/// } +/// ``` +pub use asn1_rs_derive::DerSet; + +/// # BerAlias custom derive +/// +/// `BerAlias` is a custom derive attribute, to derive a BER object parser automatically from the structure definition. +/// This attribute will automatically derive implementations for the following traits: +/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer) +/// - [`Tagged`](super::Tagged) +/// - [`CheckDerConstraints`](super::CheckDerConstraints) +/// - [`FromDer`](super::FromDer) +/// +/// `DerAlias` implies `BerAlias`, and will conflict with this custom derive. Use `BerAlias` when you only want the +/// above traits derived. +/// +/// When defining alias, only unnamed (tuple) structs with one field are supported. +/// +/// Parser will be automatically derived from the struct field. This field type must implement the `TryFrom<Any>` trait. +/// +/// See [`derive`](crate::doc::derive) documentation for more examples and documentation. +/// +/// ## Examples +/// +/// To parse the following ASN.1 object: +/// <pre> +/// MyInt ::= INTEGER(0..2^32) +/// </pre> +/// +/// Define a structure and add the `BerAlias` derive: +/// +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(BerAlias)] +/// struct S(pub u32); +/// ``` +/// +/// ## Debugging +/// +/// To help debugging the generated code, the `#[debug_derive]` attribute has been added. +/// +/// When this attribute is specified, the generated code will be printed to `stderr` during compilation. +/// +/// Example: +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(BerAlias)] +/// #[debug_derive] +/// struct S(pub u32); +/// ``` +pub use asn1_rs_derive::BerAlias; + +/// # DerAlias custom derive +/// +/// `DerAlias` is a custom derive attribute, to derive a DER object parser automatically from the structure definition. +/// This attribute will automatically derive implementations for the following traits: +/// - [`TryFrom<Any>`](super::Any), also providing [`FromBer`](super::FromBer) +/// - [`Tagged`](super::Tagged) +/// +/// `DerAlias` implies `BerAlias`, and will conflict with this custom derive. +/// +/// When defining alias, only unnamed (tuple) structs with one field are supported. +/// +/// Parser will be automatically derived from the struct field. This field type must implement the `TryFrom<Any>` and `FromDer` traits. +/// +/// See [`derive`](crate::doc::derive) documentation for more examples and documentation. +/// +/// ## Examples +/// +/// To parse the following ASN.1 object: +/// <pre> +/// MyInt ::= INTEGER(0..2^32) +/// </pre> +/// +/// Define a structure and add the `DerAlias` derive: +/// +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(DerAlias)] +/// struct S(pub u32); +/// ``` +/// +/// ## Debugging +/// +/// To help debugging the generated code, the `#[debug_derive]` attribute has been added. +/// +/// When this attribute is specified, the generated code will be printed to `stderr` during compilation. +/// +/// Example: +/// ```rust +/// use asn1_rs::*; +/// +/// #[derive(DerAlias)] +/// #[debug_derive] +/// struct S(pub u32); +/// ``` +pub use asn1_rs_derive::DerAlias; diff --git a/rust/vendor/asn1-rs/src/doc/mod.rs b/rust/vendor/asn1-rs/src/doc/mod.rs new file mode 100644 index 0000000..331b350 --- /dev/null +++ b/rust/vendor/asn1-rs/src/doc/mod.rs @@ -0,0 +1,7 @@ +//! Additional documentation: recipes, specific use cases and examples, etc. + +#[doc = include_str!("../../doc/RECIPES.md")] +pub mod recipes {} + +#[doc = include_str!("../../doc/DERIVE.md")] +pub mod derive {} diff --git a/rust/vendor/asn1-rs/src/error.rs b/rust/vendor/asn1-rs/src/error.rs new file mode 100644 index 0000000..e1414c3 --- /dev/null +++ b/rust/vendor/asn1-rs/src/error.rs @@ -0,0 +1,201 @@ +use crate::{Class, Tag}; +use alloc::str; +use alloc::string; +use alloc::string::String; +use displaydoc::Display; +use nom::error::{ErrorKind, FromExternalError, ParseError}; +use nom::IResult; +#[cfg(feature = "std")] +use std::io; +#[cfg(feature = "std")] +use thiserror::Error; + +#[cfg(feature = "std")] +impl std::error::Error for DerConstraint {} + +#[derive(Clone, Copy, Debug, Display, PartialEq, Eq)] +/// Error types for DER constraints +pub enum DerConstraint { + /// Indefinite length not allowed + IndefiniteLength, + /// Object must not be constructed + Constructed, + /// Object must be constructed + NotConstructed, + /// DateTime object is missing timezone + MissingTimeZone, + /// DateTime object is missing seconds + MissingSeconds, + /// Bitstring unused bits must be set to zero + UnusedBitsNotZero, + /// Boolean value must be 0x00 of 0xff + InvalidBoolean, + /// Integer must not be empty + IntegerEmpty, + /// Leading zeroes in Integer encoding + IntegerLeadingZeroes, + /// Leading 0xff in negative Integer encoding + IntegerLeadingFF, +} + +// XXX +// thiserror does not work in no_std +// see https://github.com/dtolnay/thiserror/pull/64 + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +/// The error type for operations of the [`FromBer`](crate::FromBer), +/// [`FromDer`](crate::FromDer), and associated traits. +#[derive(Clone, Debug, Display, PartialEq, Eq)] +// #[cfg_attr(feature = "std", derive(Error))] +pub enum Error { + /// BER object does not have the expected type + BerTypeError, + /// BER object does not have the expected value + BerValueError, + /// Invalid Length + InvalidLength, + /// Invalid Value when parsing object with tag {tag:?} {msg:} + InvalidValue { tag: Tag, msg: String }, + /// Invalid Tag + InvalidTag, + /// Unknown tag: {0:?} + UnknownTag(u32), + /// Unexpected Tag (expected: {expected:?}, actual: {actual:?}) + UnexpectedTag { expected: Option<Tag>, actual: Tag }, + /// Unexpected Class (expected: {expected:?}, actual: {actual:?}) + UnexpectedClass { + expected: Option<Class>, + actual: Class, + }, + + /// Indefinite length not allowed + IndefiniteLengthUnexpected, + + /// DER object was expected to be constructed (and found to be primitive) + ConstructExpected, + /// DER object was expected to be primitive (and found to be constructed) + ConstructUnexpected, + + /// Integer too large to fit requested type + IntegerTooLarge, + /// BER integer is negative, while an unsigned integer was requested + IntegerNegative, + /// BER recursive parsing reached maximum depth + BerMaxDepth, + + /// Invalid encoding or forbidden characters in string + StringInvalidCharset, + /// Invalid Date or Time + InvalidDateTime, + + /// DER Failed constraint + DerConstraintFailed(DerConstraint), + + /// Requesting borrowed data from a temporary object + LifetimeError, + /// Feature is not yet implemented + Unsupported, + + /// incomplete data, missing: {0:?} + Incomplete(nom::Needed), + + /// nom error: {0:?} + NomError(ErrorKind), +} + +impl Error { + /// Build an error from the provided invalid value + #[inline] + pub const fn invalid_value(tag: Tag, msg: String) -> Self { + Self::InvalidValue { tag, msg } + } + + /// Build an error from the provided unexpected class + #[inline] + pub const fn unexpected_class(expected: Option<Class>, actual: Class) -> Self { + Self::UnexpectedClass { expected, actual } + } + + /// Build an error from the provided unexpected tag + #[inline] + pub const fn unexpected_tag(expected: Option<Tag>, actual: Tag) -> Self { + Self::UnexpectedTag { expected, actual } + } +} + +impl<'a> ParseError<&'a [u8]> for Error { + fn from_error_kind(_input: &'a [u8], kind: ErrorKind) -> Self { + Error::NomError(kind) + } + fn append(_input: &'a [u8], kind: ErrorKind, _other: Self) -> Self { + Error::NomError(kind) + } +} + +impl From<Error> for nom::Err<Error> { + fn from(e: Error) -> Self { + nom::Err::Error(e) + } +} + +impl From<str::Utf8Error> for Error { + fn from(_: str::Utf8Error) -> Self { + Error::StringInvalidCharset + } +} + +impl From<string::FromUtf8Error> for Error { + fn from(_: string::FromUtf8Error) -> Self { + Error::StringInvalidCharset + } +} + +impl From<string::FromUtf16Error> for Error { + fn from(_: string::FromUtf16Error) -> Self { + Error::StringInvalidCharset + } +} + +impl From<nom::Err<Error>> for Error { + fn from(e: nom::Err<Error>) -> Self { + match e { + nom::Err::Incomplete(n) => Self::Incomplete(n), + nom::Err::Error(e) | nom::Err::Failure(e) => e, + } + } +} + +impl<I, E> FromExternalError<I, E> for Error { + fn from_external_error(_input: I, kind: ErrorKind, _e: E) -> Error { + Error::NomError(kind) + } +} + +/// Holds the result of BER/DER serialization functions +pub type ParseResult<'a, T, E = Error> = IResult<&'a [u8], T, E>; + +/// A specialized `Result` type for all operations from this crate. +pub type Result<T, E = Error> = core::result::Result<T, E>; + +/// The error type for serialization operations of the [`ToDer`](crate::ToDer) trait. +#[cfg(feature = "std")] +#[derive(Debug, Error)] +pub enum SerializeError { + #[error("ASN.1 error: {0:?}")] + ASN1Error(#[from] Error), + + #[error("Invalid Class {class:}")] + InvalidClass { class: u8 }, + + #[error("Invalid Length")] + InvalidLength, + + #[error("I/O error: {0:?}")] + IOError(#[from] io::Error), +} + +#[cfg(feature = "std")] +/// Holds the result of BER/DER encoding functions +pub type SerializeResult<T> = std::result::Result<T, SerializeError>; diff --git a/rust/vendor/asn1-rs/src/header.rs b/rust/vendor/asn1-rs/src/header.rs new file mode 100644 index 0000000..c2b9aa5 --- /dev/null +++ b/rust/vendor/asn1-rs/src/header.rs @@ -0,0 +1,490 @@ +use crate::ber::*; +use crate::der_constraint_fail_if; +use crate::error::*; +#[cfg(feature = "std")] +use crate::ToDer; +use crate::{BerParser, Class, DerParser, DynTagged, FromBer, FromDer, Length, Tag, ToStatic}; +use alloc::borrow::Cow; +use core::convert::TryFrom; +use nom::bytes::streaming::take; + +/// BER/DER object header (identifier and length) +#[derive(Clone, Debug)] +pub struct Header<'a> { + /// Object class: universal, application, context-specific, or private + pub(crate) class: Class, + /// Constructed attribute: true if constructed, else false + pub(crate) constructed: bool, + /// Tag number + pub(crate) tag: Tag, + /// Object length: value if definite, or indefinite + pub(crate) length: Length, + + /// Optionally, the raw encoding of the tag + /// + /// This is useful in some cases, where different representations of the same + /// BER tags have different meanings (BER only) + pub(crate) raw_tag: Option<Cow<'a, [u8]>>, +} + +impl<'a> Header<'a> { + /// Build a new BER/DER header from the provided values + pub const fn new(class: Class, constructed: bool, tag: Tag, length: Length) -> Self { + Header { + tag, + constructed, + class, + length, + raw_tag: None, + } + } + + /// Build a new BER/DER header from the provided tag, with default values for other fields + #[inline] + pub const fn new_simple(tag: Tag) -> Self { + let constructed = matches!(tag, Tag::Sequence | Tag::Set); + Self::new(Class::Universal, constructed, tag, Length::Definite(0)) + } + + /// Set the class of this `Header` + #[inline] + pub fn with_class(self, class: Class) -> Self { + Self { class, ..self } + } + + /// Set the constructed flags of this `Header` + #[inline] + pub fn with_constructed(self, constructed: bool) -> Self { + Self { + constructed, + ..self + } + } + + /// Set the tag of this `Header` + #[inline] + pub fn with_tag(self, tag: Tag) -> Self { + Self { tag, ..self } + } + + /// Set the length of this `Header` + #[inline] + pub fn with_length(self, length: Length) -> Self { + Self { length, ..self } + } + + /// Update header to add reference to raw tag + #[inline] + pub fn with_raw_tag(self, raw_tag: Option<Cow<'a, [u8]>>) -> Self { + Header { raw_tag, ..self } + } + + /// Return the class of this header. + #[inline] + pub const fn class(&self) -> Class { + self.class + } + + /// Return true if this header has the 'constructed' flag. + #[inline] + pub const fn constructed(&self) -> bool { + self.constructed + } + + /// Return the tag of this header. + #[inline] + pub const fn tag(&self) -> Tag { + self.tag + } + + /// Return the length of this header. + #[inline] + pub const fn length(&self) -> Length { + self.length + } + + /// Return the raw tag encoding, if it was stored in this object + #[inline] + pub fn raw_tag(&self) -> Option<&[u8]> { + self.raw_tag.as_ref().map(|cow| cow.as_ref()) + } + + /// Test if object is primitive + #[inline] + pub const fn is_primitive(&self) -> bool { + !self.constructed + } + + /// Test if object is constructed + #[inline] + pub const fn is_constructed(&self) -> bool { + self.constructed + } + + /// Return error if class is not the expected class + #[inline] + pub const fn assert_class(&self, class: Class) -> Result<()> { + self.class.assert_eq(class) + } + + /// Return error if tag is not the expected tag + #[inline] + pub const fn assert_tag(&self, tag: Tag) -> Result<()> { + self.tag.assert_eq(tag) + } + + /// Return error if object is not primitive + #[inline] + pub const fn assert_primitive(&self) -> Result<()> { + if self.is_primitive() { + Ok(()) + } else { + Err(Error::ConstructUnexpected) + } + } + + /// Return error if object is primitive + #[inline] + pub const fn assert_constructed(&self) -> Result<()> { + if !self.is_primitive() { + Ok(()) + } else { + Err(Error::ConstructExpected) + } + } + + /// Test if object class is Universal + #[inline] + pub const fn is_universal(&self) -> bool { + self.class as u8 == Class::Universal as u8 + } + /// Test if object class is Application + #[inline] + pub const fn is_application(&self) -> bool { + self.class as u8 == Class::Application as u8 + } + /// Test if object class is Context-specific + #[inline] + pub const fn is_contextspecific(&self) -> bool { + self.class as u8 == Class::ContextSpecific as u8 + } + /// Test if object class is Private + #[inline] + pub const fn is_private(&self) -> bool { + self.class as u8 == Class::Private as u8 + } + + /// Return error if object length is definite + #[inline] + pub const fn assert_definite(&self) -> Result<()> { + if self.length.is_definite() { + Ok(()) + } else { + Err(Error::DerConstraintFailed(DerConstraint::IndefiniteLength)) + } + } + + /// Get the content following a BER header + #[inline] + pub fn parse_ber_content<'i>(&'_ self, i: &'i [u8]) -> ParseResult<'i, &'i [u8]> { + // defaults to maximum depth 8 + // depth is used only if BER, and length is indefinite + BerParser::get_object_content(i, self, 8) + } + + /// Get the content following a DER header + #[inline] + pub fn parse_der_content<'i>(&'_ self, i: &'i [u8]) -> ParseResult<'i, &'i [u8]> { + self.assert_definite()?; + DerParser::get_object_content(i, self, 8) + } +} + +impl From<Tag> for Header<'_> { + #[inline] + fn from(tag: Tag) -> Self { + let constructed = matches!(tag, Tag::Sequence | Tag::Set); + Self::new(Class::Universal, constructed, tag, Length::Definite(0)) + } +} + +impl<'a> ToStatic for Header<'a> { + type Owned = Header<'static>; + + fn to_static(&self) -> Self::Owned { + let raw_tag: Option<Cow<'static, [u8]>> = + self.raw_tag.as_ref().map(|b| Cow::Owned(b.to_vec())); + Header { + tag: self.tag, + constructed: self.constructed, + class: self.class, + length: self.length, + raw_tag, + } + } +} + +impl<'a> FromBer<'a> for Header<'a> { + fn from_ber(bytes: &'a [u8]) -> ParseResult<Self> { + let (i1, el) = parse_identifier(bytes)?; + let class = match Class::try_from(el.0) { + Ok(c) => c, + Err(_) => unreachable!(), // Cannot fail, we have read exactly 2 bits + }; + let (i2, len) = parse_ber_length_byte(i1)?; + let (i3, len) = match (len.0, len.1) { + (0, l1) => { + // Short form: MSB is 0, the rest encodes the length (which can be 0) (8.1.3.4) + (i2, Length::Definite(usize::from(l1))) + } + (_, 0) => { + // Indefinite form: MSB is 1, the rest is 0 (8.1.3.6) + // If encoding is primitive, definite form shall be used (8.1.3.2) + if el.1 == 0 { + return Err(nom::Err::Error(Error::ConstructExpected)); + } + (i2, Length::Indefinite) + } + (_, l1) => { + // if len is 0xff -> error (8.1.3.5) + if l1 == 0b0111_1111 { + return Err(::nom::Err::Error(Error::InvalidLength)); + } + let (i3, llen) = take(l1)(i2)?; + match bytes_to_u64(llen) { + Ok(l) => { + let l = + usize::try_from(l).or(Err(::nom::Err::Error(Error::InvalidLength)))?; + (i3, Length::Definite(l)) + } + Err(_) => { + return Err(::nom::Err::Error(Error::InvalidLength)); + } + } + } + }; + let constructed = el.1 != 0; + let hdr = Header::new(class, constructed, Tag(el.2), len).with_raw_tag(Some(el.3.into())); + Ok((i3, hdr)) + } +} + +impl<'a> FromDer<'a> for Header<'a> { + fn from_der(bytes: &'a [u8]) -> ParseResult<Self> { + let (i1, el) = parse_identifier(bytes)?; + let class = match Class::try_from(el.0) { + Ok(c) => c, + Err(_) => unreachable!(), // Cannot fail, we have read exactly 2 bits + }; + let (i2, len) = parse_ber_length_byte(i1)?; + let (i3, len) = match (len.0, len.1) { + (0, l1) => { + // Short form: MSB is 0, the rest encodes the length (which can be 0) (8.1.3.4) + (i2, Length::Definite(usize::from(l1))) + } + (_, 0) => { + // Indefinite form is not allowed in DER (10.1) + return Err(::nom::Err::Error(Error::DerConstraintFailed( + DerConstraint::IndefiniteLength, + ))); + } + (_, l1) => { + // if len is 0xff -> error (8.1.3.5) + if l1 == 0b0111_1111 { + return Err(::nom::Err::Error(Error::InvalidLength)); + } + // DER(9.1) if len is 0 (indefinite form), obj must be constructed + der_constraint_fail_if!( + &i[1..], + len.1 == 0 && el.1 != 1, + DerConstraint::NotConstructed + ); + let (i3, llen) = take(l1)(i2)?; + match bytes_to_u64(llen) { + Ok(l) => { + // DER: should have been encoded in short form (< 127) + // XXX der_constraint_fail_if!(i, l < 127); + let l = + usize::try_from(l).or(Err(::nom::Err::Error(Error::InvalidLength)))?; + (i3, Length::Definite(l)) + } + Err(_) => { + return Err(::nom::Err::Error(Error::InvalidLength)); + } + } + } + }; + let constructed = el.1 != 0; + let hdr = Header::new(class, constructed, Tag(el.2), len).with_raw_tag(Some(el.3.into())); + Ok((i3, hdr)) + } +} + +impl DynTagged for (Class, bool, Tag) { + fn tag(&self) -> Tag { + self.2 + } +} + +#[cfg(feature = "std")] +impl ToDer for (Class, bool, Tag) { + fn to_der_len(&self) -> Result<usize> { + let (_, _, tag) = self; + match tag.0 { + 0..=30 => Ok(1), + t => { + let mut sz = 1; + let mut val = t; + loop { + if val <= 127 { + return Ok(sz + 1); + } else { + val >>= 7; + sz += 1; + } + } + } + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let (class, constructed, tag) = self; + let b0 = (*class as u8) << 6; + let b0 = b0 | if *constructed { 0b10_0000 } else { 0 }; + if tag.0 > 30 { + let b0 = b0 | 0b1_1111; + let mut sz = writer.write(&[b0])?; + let mut val = tag.0; + loop { + if val <= 127 { + sz += writer.write(&[val as u8])?; + return Ok(sz); + } else { + let b = (val & 0b0111_1111) as u8 | 0b1000_0000; + sz += writer.write(&[b])?; + val >>= 7; + } + } + } else { + let b0 = b0 | (tag.0 as u8); + let sz = writer.write(&[b0])?; + Ok(sz) + } + } + + fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + Ok(0) + } +} + +impl DynTagged for Header<'_> { + fn tag(&self) -> Tag { + self.tag + } +} + +#[cfg(feature = "std")] +impl ToDer for Header<'_> { + fn to_der_len(&self) -> Result<usize> { + let tag_len = (self.class, self.constructed, self.tag).to_der_len()?; + let len_len = self.length.to_der_len()?; + Ok(tag_len + len_len) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let sz = (self.class, self.constructed, self.tag).write_der_header(writer)?; + let sz = sz + self.length.write_der_header(writer)?; + Ok(sz) + } + + fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + Ok(0) + } + + fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + // use raw_tag if present + let sz = match &self.raw_tag { + Some(t) => writer.write(t)?, + None => (self.class, self.constructed, self.tag).write_der_header(writer)?, + }; + let sz = sz + self.length.write_der_header(writer)?; + Ok(sz) + } +} + +/// Compare two BER headers. `len` fields are compared only if both objects have it set (same for `raw_tag`) +impl<'a> PartialEq<Header<'a>> for Header<'a> { + fn eq(&self, other: &Header) -> bool { + self.class == other.class + && self.tag == other.tag + && self.constructed == other.constructed + && { + if self.length.is_null() && other.length.is_null() { + self.length == other.length + } else { + true + } + } + && { + // it tag is present for both, compare it + if self.raw_tag.as_ref().xor(other.raw_tag.as_ref()).is_none() { + self.raw_tag == other.raw_tag + } else { + true + } + } + } +} + +impl Eq for Header<'_> {} + +#[cfg(test)] +mod tests { + use crate::*; + use hex_literal::hex; + + /// Generic tests on methods, and coverage tests + #[test] + fn methods_header() { + // Getters + let input = &hex! {"02 01 00"}; + let (rem, header) = Header::from_ber(input).expect("parsing header failed"); + assert_eq!(header.class(), Class::Universal); + assert_eq!(header.tag(), Tag::Integer); + assert!(header.assert_primitive().is_ok()); + assert!(header.assert_constructed().is_err()); + assert!(header.is_universal()); + assert!(!header.is_application()); + assert!(!header.is_private()); + assert_eq!(rem, &input[2..]); + + // test PartialEq + let hdr2 = Header::new_simple(Tag::Integer); + assert_eq!(header, hdr2); + + // builder methods + let hdr3 = hdr2 + .with_class(Class::ContextSpecific) + .with_constructed(true) + .with_length(Length::Definite(1)); + assert!(hdr3.constructed()); + assert!(hdr3.is_constructed()); + assert!(hdr3.assert_constructed().is_ok()); + assert!(hdr3.is_contextspecific()); + let xx = hdr3.to_der_vec().expect("serialize failed"); + assert_eq!(&xx, &[0xa2, 0x01]); + + // indefinite length + let hdr4 = hdr3.with_length(Length::Indefinite); + assert!(hdr4.assert_definite().is_err()); + let xx = hdr4.to_der_vec().expect("serialize failed"); + assert_eq!(&xx, &[0xa2, 0x80]); + + // parse_*_content + let hdr = Header::new_simple(Tag(2)).with_length(Length::Definite(1)); + let (_, r) = hdr.parse_ber_content(&input[2..]).unwrap(); + assert_eq!(r, &input[2..]); + let (_, r) = hdr.parse_der_content(&input[2..]).unwrap(); + assert_eq!(r, &input[2..]); + } +} diff --git a/rust/vendor/asn1-rs/src/length.rs b/rust/vendor/asn1-rs/src/length.rs new file mode 100644 index 0000000..16688ae --- /dev/null +++ b/rust/vendor/asn1-rs/src/length.rs @@ -0,0 +1,180 @@ +use crate::{DynTagged, Error, Result, Tag}; +#[cfg(feature = "std")] +use crate::{SerializeResult, ToDer}; +use core::ops; + +/// BER Object Length +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Length { + /// Definite form (X.690 8.1.3.3) + Definite(usize), + /// Indefinite form (X.690 8.1.3.6) + Indefinite, +} + +impl Length { + /// Return true if length is definite and equal to 0 + #[inline] + pub fn is_null(&self) -> bool { + *self == Length::Definite(0) + } + + /// Get length of primitive object + #[inline] + pub fn definite(&self) -> Result<usize> { + match self { + Length::Definite(sz) => Ok(*sz), + Length::Indefinite => Err(Error::IndefiniteLengthUnexpected), + } + } + + /// Return true if length is definite + #[inline] + pub const fn is_definite(&self) -> bool { + matches!(self, Length::Definite(_)) + } + + /// Return error if length is not definite + #[inline] + pub const fn assert_definite(&self) -> Result<()> { + match self { + Length::Definite(_) => Ok(()), + Length::Indefinite => Err(Error::IndefiniteLengthUnexpected), + } + } +} + +impl From<usize> for Length { + fn from(l: usize) -> Self { + Length::Definite(l) + } +} + +impl ops::Add<Length> for Length { + type Output = Self; + + fn add(self, rhs: Length) -> Self::Output { + match self { + Length::Indefinite => self, + Length::Definite(lhs) => match rhs { + Length::Indefinite => rhs, + Length::Definite(rhs) => Length::Definite(lhs + rhs), + }, + } + } +} + +impl ops::Add<usize> for Length { + type Output = Self; + + fn add(self, rhs: usize) -> Self::Output { + match self { + Length::Definite(lhs) => Length::Definite(lhs + rhs), + Length::Indefinite => self, + } + } +} + +impl ops::AddAssign<usize> for Length { + fn add_assign(&mut self, rhs: usize) { + match self { + Length::Definite(ref mut lhs) => *lhs += rhs, + Length::Indefinite => (), + } + } +} + +impl DynTagged for Length { + fn tag(&self) -> Tag { + Tag(0) + } +} + +#[cfg(feature = "std")] +impl ToDer for Length { + fn to_der_len(&self) -> Result<usize> { + match self { + Length::Indefinite => Ok(1), + Length::Definite(l) => match l { + 0..=0x7f => Ok(1), + 0x80..=0xff => Ok(2), + 0x100..=0xffff => Ok(3), + 0x1_0000..=0xffff_ffff => Ok(4), + _ => Err(Error::InvalidLength), + }, + } + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + match *self { + Length::Indefinite => { + let sz = writer.write(&[0b1000_0000])?; + Ok(sz) + } + Length::Definite(l) => { + if l <= 127 { + // Short form + let sz = writer.write(&[l as u8])?; + Ok(sz) + } else { + // Long form + let b = l.to_be_bytes(); + // skip leading zeroes + // we do not have to test for length, l cannot be 0 + let mut idx = 0; + while b[idx] == 0 { + idx += 1; + } + let b = &b[idx..]; + // first byte: 0x80 + length of length + let b0 = 0x80 | (b.len() as u8); + let sz = writer.write(&[b0])?; + let sz = sz + writer.write(b)?; + Ok(sz) + } + } + } + } + + fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + Ok(0) + } +} + +#[cfg(test)] +mod tests { + use crate::*; + + /// Generic and coverage tests + #[test] + fn methods_length() { + let l = Length::from(2); + assert_eq!(l.definite(), Ok(2)); + assert!(l.assert_definite().is_ok()); + + let l = Length::Indefinite; + assert!(l.definite().is_err()); + assert!(l.assert_definite().is_err()); + + let l = Length::from(2); + assert_eq!(l + 2, Length::from(4)); + assert_eq!(l + Length::Indefinite, Length::Indefinite); + + let l = Length::Indefinite; + assert_eq!(l + 2, Length::Indefinite); + + let l = Length::from(2); + assert_eq!(l + Length::from(2), Length::from(4)); + + let l = Length::Indefinite; + assert_eq!(l + Length::from(2), Length::Indefinite); + + let mut l = Length::from(2); + l += 2; + assert_eq!(l.definite(), Ok(4)); + + let mut l = Length::Indefinite; + l += 2; + assert_eq!(l, Length::Indefinite); + } +} diff --git a/rust/vendor/asn1-rs/src/lib.rs b/rust/vendor/asn1-rs/src/lib.rs new file mode 100644 index 0000000..c1275f3 --- /dev/null +++ b/rust/vendor/asn1-rs/src/lib.rs @@ -0,0 +1,214 @@ +//! [](./LICENSE-MIT) +//! [](./LICENSE-APACHE) +//! [](https://docs.rs/asn1-rs) +//! [](https://crates.io/crates/asn1-rs) +//! [](https://crates.io/crates/asn1-rs) +//! [](https://github.com/rusticata/asn1-rs/actions) +//! [](#rust-version-requirements) +//! +//! # BER/DER Parsers/Encoders +//! +//! A set of parsers/encoders for Basic Encoding Rules (BER [[X.690]]) and Distinguished Encoding Rules(DER +//! [[X.690]]) formats, implemented with the [nom] parser combinator framework. +//! +//! It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken +//! to ensure security and safety of this crate, including design (recursion limit, defensive +//! programming), tests, and fuzzing. It also aims to be panic-free. +//! +//! This crate is a rewrite of [der-parser](https://crates.io/crates/der-parser) to propose a more data-oriented API, +//! and add generalized support for serialization. +//! +//! Many ideas were borrowed from the [crypto/utils/der](https://github.com/RustCrypto/utils/tree/master/der) crate (like +//! the `Any`/`TryFrom`/`FromDer` mechanism), adapted and merged into a generalized BER/DER crate. +//! Credits (and many thanks) go to Tony Arcieri for writing the original crate. +//! +//! # BER/DER parsers +//! +//! BER stands for Basic Encoding Rules, and is defined in [[X.690]]. It defines a set of rules to +//! encode and decode ASN.1 [[X.680]] objects in binary. +//! +//! [[X.690]] also defines Distinguished Encoding Rules (DER), which is BER with added rules to +//! ensure canonical and unequivocal binary representation of objects. +//! +//! The choice of which one to use is usually guided by the speficication of the data format based +//! on BER or DER: for example, X.509 uses DER as encoding representation. +//! +//! The main traits for parsing are the [`FromBer`] and [`FromDer`] traits. +//! These traits provide methods to parse binary input, and return either the remaining (unparsed) bytes +//! and the parsed object, or an error. +//! +//! The parsers follow the interface from [nom], and the [`ParseResult`] object is a specialized version +//! of `nom::IResult`. This means that most `nom` combinators (`map`, `many0`, etc.) can be used in +//! combination to objects and methods from this crate. Reading the nom documentation may +//! help understanding how to write and combine parsers and use the output. +//! +//! **Minimum Supported Rust Version**: 1.53.0 +//! +//! Note: if the `bits` feature is enabled, MSRV is 1.56.0 (due to `bitvec` 1.0) +//! +//! # Recipes +//! +//! See [doc::recipes] and [doc::derive] for more examples and recipes. +//! +//! ## Examples +//! +//! Parse 2 BER integers: +//! +//! ```rust +//! use asn1_rs::{Integer, FromBer}; +//! +//! let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01, +//! 0x02, 0x03, 0x01, 0x00, 0x00, +//! ]; +//! +//! let (rem, obj1) = Integer::from_ber(&bytes).expect("parsing failed"); +//! let (rem, obj2) = Integer::from_ber(&bytes).expect("parsing failed"); +//! +//! assert_eq!(obj1, Integer::from_u32(65537)); +//! ``` +//! +//! In the above example, the generic [`Integer`] type is used. This type can contain integers of any +//! size, but do not provide a simple API to manipulate the numbers. +//! +//! In most cases, the integer either has a limit, or is expected to fit into a primitive type. +//! To get a simple value, just use the `from_ber`/`from_der` methods on the primitive types: +//! +//! ```rust +//! use asn1_rs::FromBer; +//! +//! let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01, +//! 0x02, 0x03, 0x01, 0x00, 0x00, +//! ]; +//! +//! let (rem, obj1) = u32::from_ber(&bytes).expect("parsing failed"); +//! let (rem, obj2) = u32::from_ber(&rem).expect("parsing failed"); +//! +//! assert_eq!(obj1, 65537); +//! assert_eq!(obj2, 65536); +//! ``` +//! +//! If the parsing succeeds, but the integer cannot fit into the expected type, the method will return +//! an `IntegerTooLarge` error. +//! +//! # BER/DER encoders +//! +//! BER/DER encoding is symmetrical to decoding, using the traits `ToBer` and [`ToDer`] traits. +//! These traits provide methods to write encoded content to objects with the `io::Write` trait, +//! or return an allocated `Vec<u8>` with the encoded data. +//! If the serialization fails, an error is returned. +//! +//! ## Examples +//! +//! Writing 2 BER integers: +//! +//! ```rust +//! use asn1_rs::{Integer, ToDer}; +//! +//! let mut writer = Vec::new(); +//! +//! let obj1 = Integer::from_u32(65537); +//! let obj2 = Integer::from_u32(65536); +//! +//! let _ = obj1.write_der(&mut writer).expect("serialization failed"); +//! let _ = obj2.write_der(&mut writer).expect("serialization failed"); +//! +//! let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01, +//! 0x02, 0x03, 0x01, 0x00, 0x00, +//! ]; +//! assert_eq!(&writer, bytes); +//! ``` +//! +//! Similarly to `FromBer`/`FromDer`, serialization methods are also implemented for primitive types: +//! +//! ```rust +//! use asn1_rs::ToDer; +//! +//! let mut writer = Vec::new(); +//! +//! let _ = 65537.write_der(&mut writer).expect("serialization failed"); +//! let _ = 65536.write_der(&mut writer).expect("serialization failed"); +//! +//! let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01, +//! 0x02, 0x03, 0x01, 0x00, 0x00, +//! ]; +//! assert_eq!(&writer, bytes); +//! ``` +//! +//! If the parsing succeeds, but the integer cannot fit into the expected type, the method will return +//! an `IntegerTooLarge` error. +//! +//! ## Changes +//! +//! See `CHANGELOG.md`. +//! +//! # References +//! +//! - [[X.680]] Abstract Syntax Notation One (ASN.1): Specification of basic notation. +//! - [[X.690]] ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical +//! Encoding Rules (CER) and Distinguished Encoding Rules (DER). +//! +//! [X.680]: http://www.itu.int/rec/T-REC-X.680/en "Abstract Syntax Notation One (ASN.1): +//! Specification of basic notation." +//! [X.690]: https://www.itu.int/rec/T-REC-X.690/en "ASN.1 encoding rules: Specification of +//! Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules +//! (DER)." +//! [nom]: https://github.com/Geal/nom "Nom parser combinator framework" +#![deny(/*missing_docs,*/ + unstable_features, + unused_import_braces, + unused_qualifications, + // unreachable_pub +)] +#![forbid(unsafe_code)] +#![warn( +/* missing_docs, +rust_2018_idioms,*/ +missing_debug_implementations, +)] +// pragmas for doc +#![deny(rustdoc::broken_intra_doc_links)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc(test( +no_crate_inject, +attr(deny(warnings/*, rust_2018_idioms*/), allow(dead_code, unused_variables)) +))] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate core; + +// #[cfg(feature = "alloc")] +extern crate alloc; + +mod asn1_types; +mod ber; +mod class; +mod datetime; +mod derive; +mod error; +mod header; +mod length; +mod tag; +mod traits; + +pub use asn1_types::*; +pub use class::*; +pub use datetime::*; +pub use derive::*; +pub use error::*; +pub use header::*; +pub use length::*; +pub use tag::*; +pub use traits::*; + +pub use nom; +pub use nom::{Err, IResult, Needed}; + +#[doc(hidden)] +pub mod exports { + pub use alloc::borrow; + pub use asn1_rs_impl; +} + +#[cfg(doc)] +pub mod doc; diff --git a/rust/vendor/asn1-rs/src/tag.rs b/rust/vendor/asn1-rs/src/tag.rs new file mode 100644 index 0000000..c7130c1 --- /dev/null +++ b/rust/vendor/asn1-rs/src/tag.rs @@ -0,0 +1,74 @@ +use crate::{Error, Result}; +use alloc::string::ToString; +use rusticata_macros::newtype_enum; + +/// BER/DER Tag as defined in X.680 section 8.4 +/// +/// X.690 doesn't specify the maximum tag size so we're assuming that people +/// aren't going to need anything more than a u32. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Tag(pub u32); + +newtype_enum! { +impl display Tag { + EndOfContent = 0, + Boolean = 1, + Integer = 2, + BitString = 3, + OctetString = 4, + Null = 5, + Oid = 6, + ObjectDescriptor = 7, + External = 8, + RealType = 9, + Enumerated = 10, + EmbeddedPdv = 11, + Utf8String = 12, + RelativeOid = 13, + + Sequence = 16, + Set = 17, + NumericString = 18, + PrintableString = 19, + T61String = 20, + TeletexString = 20, + VideotexString = 21, + + Ia5String = 22, + UtcTime = 23, + GeneralizedTime = 24, + + GraphicString = 25, + VisibleString = 26, + GeneralString = 27, + + UniversalString = 28, + BmpString = 30, +} +} + +impl Tag { + pub const fn assert_eq(&self, tag: Tag) -> Result<()> { + if self.0 == tag.0 { + Ok(()) + } else { + Err(Error::UnexpectedTag { + expected: Some(tag), + actual: *self, + }) + } + } + + pub fn invalid_value(&self, msg: &str) -> Error { + Error::InvalidValue { + tag: *self, + msg: msg.to_string(), + } + } +} + +impl From<u32> for Tag { + fn from(v: u32) -> Self { + Tag(v) + } +} diff --git a/rust/vendor/asn1-rs/src/traits.rs b/rust/vendor/asn1-rs/src/traits.rs new file mode 100644 index 0000000..eea4cb9 --- /dev/null +++ b/rust/vendor/asn1-rs/src/traits.rs @@ -0,0 +1,348 @@ +use crate::error::*; +use crate::{Any, Class, Explicit, Implicit, Tag, TaggedParser}; +use core::convert::{TryFrom, TryInto}; +#[cfg(feature = "std")] +use std::io::Write; + +/// Phantom type representing a BER parser +#[doc(hidden)] +#[derive(Debug)] +pub enum BerParser {} + +/// Phantom type representing a DER parser +#[doc(hidden)] +#[derive(Debug)] +pub enum DerParser {} + +#[doc(hidden)] +pub trait ASN1Parser {} + +impl ASN1Parser for BerParser {} +impl ASN1Parser for DerParser {} + +pub trait Tagged { + const TAG: Tag; +} + +impl<T> Tagged for &'_ T +where + T: Tagged, +{ + const TAG: Tag = T::TAG; +} + +pub trait DynTagged { + fn tag(&self) -> Tag; +} + +impl<T> DynTagged for T +where + T: Tagged, +{ + fn tag(&self) -> Tag { + T::TAG + } +} + +/// Base trait for BER object parsers +/// +/// Library authors should usually not directly implement this trait, but should prefer implementing the +/// [`TryFrom<Any>`] trait, +/// which offers greater flexibility and provides an equivalent `FromBer` implementation for free. +/// +/// # Examples +/// +/// ``` +/// use asn1_rs::{Any, Result, Tag}; +/// use std::convert::TryFrom; +/// +/// // The type to be decoded +/// #[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// pub struct MyType(pub u32); +/// +/// impl<'a> TryFrom<Any<'a>> for MyType { +/// type Error = asn1_rs::Error; +/// +/// fn try_from(any: Any<'a>) -> Result<MyType> { +/// any.tag().assert_eq(Tag::Integer)?; +/// // for this fictive example, the type contains the number of characters +/// let n = any.data.len() as u32; +/// Ok(MyType(n)) +/// } +/// } +/// +/// // The above code provides a `FromBer` implementation for free. +/// +/// // Example of parsing code: +/// use asn1_rs::FromBer; +/// +/// let input = &[2, 1, 2]; +/// // Objects can be parsed using `from_ber`, which returns the remaining bytes +/// // and the parsed object: +/// let (rem, my_type) = MyType::from_ber(input).expect("parsing failed"); +/// ``` +pub trait FromBer<'a, E = Error>: Sized { + /// Attempt to parse input bytes into a BER object + fn from_ber(bytes: &'a [u8]) -> ParseResult<Self, E>; +} + +impl<'a, T, E> FromBer<'a, E> for T +where + T: TryFrom<Any<'a>, Error = E>, + E: From<Error>, +{ + fn from_ber(bytes: &'a [u8]) -> ParseResult<T, E> { + let (i, any) = Any::from_ber(bytes).map_err(nom::Err::convert)?; + let result = any.try_into().map_err(nom::Err::Error)?; + Ok((i, result)) + } +} + +/// Base trait for DER object parsers +/// +/// Library authors should usually not directly implement this trait, but should prefer implementing the +/// [`TryFrom<Any>`] + [`CheckDerConstraints`] traits, +/// which offers greater flexibility and provides an equivalent `FromDer` implementation for free +/// (in fact, it provides both [`FromBer`] and `FromDer`). +/// +/// Note: if you already implemented [`TryFrom<Any>`] and [`CheckDerConstraints`], +/// you can get a free [`FromDer`] implementation by implementing the +/// [`DerAutoDerive`] trait. This is not automatic, so it is also possible to manually +/// implement [`FromDer`] if preferred. +/// +/// # Examples +/// +/// ``` +/// use asn1_rs::{Any, CheckDerConstraints, DerAutoDerive, Result, Tag}; +/// use std::convert::TryFrom; +/// +/// // The type to be decoded +/// #[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// pub struct MyType(pub u32); +/// +/// impl<'a> TryFrom<Any<'a>> for MyType { +/// type Error = asn1_rs::Error; +/// +/// fn try_from(any: Any<'a>) -> Result<MyType> { +/// any.tag().assert_eq(Tag::Integer)?; +/// // for this fictive example, the type contains the number of characters +/// let n = any.data.len() as u32; +/// Ok(MyType(n)) +/// } +/// } +/// +/// impl CheckDerConstraints for MyType { +/// fn check_constraints(any: &Any) -> Result<()> { +/// any.header.assert_primitive()?; +/// Ok(()) +/// } +/// } +/// +/// impl DerAutoDerive for MyType {} +/// +/// // The above code provides a `FromDer` implementation for free. +/// +/// // Example of parsing code: +/// use asn1_rs::FromDer; +/// +/// let input = &[2, 1, 2]; +/// // Objects can be parsed using `from_der`, which returns the remaining bytes +/// // and the parsed object: +/// let (rem, my_type) = MyType::from_der(input).expect("parsing failed"); +/// ``` +pub trait FromDer<'a, E = Error>: Sized { + /// Attempt to parse input bytes into a DER object (enforcing constraints) + fn from_der(bytes: &'a [u8]) -> ParseResult<Self, E>; +} + +/// Trait to automatically derive `FromDer` +/// +/// This trait is only a marker to control if a DER parser should be automatically derived. It is +/// empty. +/// +/// This trait is used in combination with others: +/// after implementing [`TryFrom<Any>`] and [`CheckDerConstraints`] for a type, +/// a free [`FromDer`] implementation is provided by implementing the +/// [`DerAutoDerive`] trait. This is the most common case. +/// +/// However, this is not automatic so it is also possible to manually +/// implement [`FromDer`] if preferred. +/// Manual implementation is generally only needed for generic containers (for ex. `Vec<T>`), +/// because the default implementation adds a constraint on `T` to implement also `TryFrom<Any>` +/// and `CheckDerConstraints`. This is problematic when `T` only provides `FromDer`, and can be +/// solved by providing a manual implementation of [`FromDer`]. +pub trait DerAutoDerive {} + +impl<'a, T, E> FromDer<'a, E> for T +where + T: TryFrom<Any<'a>, Error = E>, + T: CheckDerConstraints, + T: DerAutoDerive, + E: From<Error>, +{ + fn from_der(bytes: &'a [u8]) -> ParseResult<T, E> { + // Note: Any::from_der checks than length is definite + let (i, any) = Any::from_der(bytes).map_err(nom::Err::convert)?; + <T as CheckDerConstraints>::check_constraints(&any) + .map_err(|e| nom::Err::Error(e.into()))?; + let result = any.try_into().map_err(nom::Err::Error)?; + Ok((i, result)) + } +} + +/// Verification of DER constraints +pub trait CheckDerConstraints { + fn check_constraints(any: &Any) -> Result<()>; +} + +/// Common trait for all objects that can be encoded using the DER representation +/// +/// # Examples +/// +/// Objects from this crate can be encoded as DER: +/// +/// ``` +/// use asn1_rs::{Integer, ToDer}; +/// +/// let int = Integer::from(4u32); +/// let mut writer = Vec::new(); +/// let sz = int.write_der(&mut writer).expect("serialization failed"); +/// +/// assert_eq!(&writer, &[0x02, 0x01, 0x04]); +/// # assert_eq!(sz, 3); +/// ``` +/// +/// Many of the primitive types can also directly be encoded as DER: +/// +/// ``` +/// use asn1_rs::ToDer; +/// +/// let mut writer = Vec::new(); +/// let sz = 4.write_der(&mut writer).expect("serialization failed"); +/// +/// assert_eq!(&writer, &[0x02, 0x01, 0x04]); +/// # assert_eq!(sz, 3); +/// ``` +#[cfg(feature = "std")] +pub trait ToDer +where + Self: DynTagged, +{ + /// Get the length of the object (including the header), when encoded + /// + // Since we are using DER, length cannot be Indefinite, so we can use `usize`. + // XXX can this function fail? + fn to_der_len(&self) -> Result<usize>; + + /// Write the DER encoded representation to a newly allocated `Vec<u8>`. + fn to_der_vec(&self) -> SerializeResult<Vec<u8>> { + let mut v = Vec::new(); + let _ = self.write_der(&mut v)?; + Ok(v) + } + + /// Similar to using `to_vec`, but uses provided values without changes. + /// This can generate an invalid encoding for a DER object. + fn to_der_vec_raw(&self) -> SerializeResult<Vec<u8>> { + let mut v = Vec::new(); + let _ = self.write_der_raw(&mut v)?; + Ok(v) + } + + /// Attempt to write the DER encoded representation (header and content) into this writer. + /// + /// # Examples + /// + /// ``` + /// use asn1_rs::{Integer, ToDer}; + /// + /// let int = Integer::from(4u32); + /// let mut writer = Vec::new(); + /// let sz = int.write_der(&mut writer).expect("serialization failed"); + /// + /// assert_eq!(&writer, &[0x02, 0x01, 0x04]); + /// # assert_eq!(sz, 3); + /// ``` + fn write_der(&self, writer: &mut dyn Write) -> SerializeResult<usize> { + let sz = self.write_der_header(writer)?; + let sz = sz + self.write_der_content(writer)?; + Ok(sz) + } + + /// Attempt to write the DER header to this writer. + fn write_der_header(&self, writer: &mut dyn Write) -> SerializeResult<usize>; + + /// Attempt to write the DER content (all except header) to this writer. + fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult<usize>; + + /// Similar to using `to_der`, but uses provided values without changes. + /// This can generate an invalid encoding for a DER object. + fn write_der_raw(&self, writer: &mut dyn Write) -> SerializeResult<usize> { + self.write_der(writer) + } +} + +#[cfg(feature = "std")] +impl<'a, T> ToDer for &'a T +where + T: ToDer, + &'a T: DynTagged, +{ + fn to_der_len(&self) -> Result<usize> { + (*self).to_der_len() + } + + fn write_der_header(&self, writer: &mut dyn Write) -> SerializeResult<usize> { + (*self).write_der_header(writer) + } + + fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult<usize> { + (*self).write_der_content(writer) + } +} + +/// Helper trait for creating tagged EXPLICIT values +/// +/// # Examples +/// +/// ``` +/// use asn1_rs::{AsTaggedExplicit, Class, Error, TaggedParser}; +/// +/// // create a `[1] EXPLICIT INTEGER` value +/// let tagged: TaggedParser<_, _, Error> = 4u32.explicit(Class::ContextSpecific, 1); +/// ``` +pub trait AsTaggedExplicit<'a, E = Error>: Sized { + fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E> { + TaggedParser::new_explicit(class, tag, self) + } +} + +impl<'a, T, E> AsTaggedExplicit<'a, E> for T where T: Sized + 'a {} + +/// Helper trait for creating tagged IMPLICIT values +/// +/// # Examples +/// +/// ``` +/// use asn1_rs::{AsTaggedImplicit, Class, Error, TaggedParser}; +/// +/// // create a `[1] IMPLICIT INTEGER` value, not constructed +/// let tagged: TaggedParser<_, _, Error> = 4u32.implicit(Class::ContextSpecific, false, 1); +/// ``` +pub trait AsTaggedImplicit<'a, E = Error>: Sized { + fn implicit( + self, + class: Class, + constructed: bool, + tag: u32, + ) -> TaggedParser<'a, Implicit, Self, E> { + TaggedParser::new_implicit(class, constructed, tag, self) + } +} + +impl<'a, T, E> AsTaggedImplicit<'a, E> for T where T: Sized + 'a {} + +pub trait ToStatic { + type Owned: 'static; + fn to_static(&self) -> Self::Owned; +} diff --git a/rust/vendor/asn1-rs/tests/ber.rs b/rust/vendor/asn1-rs/tests/ber.rs new file mode 100644 index 0000000..d1c6705 --- /dev/null +++ b/rust/vendor/asn1-rs/tests/ber.rs @@ -0,0 +1,518 @@ +use asn1_rs::*; +use hex_literal::hex; +use nom::Needed; +#[cfg(feature = "datetime")] +use time::macros::datetime; + +#[test] +fn from_ber_any() { + let input = &hex!("02 01 02 ff ff"); + let (rem, result) = Any::from_ber(input).expect("parsing failed"); + // dbg!(&result); + assert_eq!(rem, &[0xff, 0xff]); + assert_eq!(result.header.tag(), Tag::Integer); +} + +#[test] +fn from_ber_bitstring() { + // + // correct DER encoding + // + let input = &hex!("03 04 06 6e 5d c0"); + let (rem, result) = BitString::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.unused_bits, 6); + assert_eq!(&result.data[..], &input[3..]); + // + // correct encoding, but wrong padding bits (not all set to 0) + // + let input = &hex!("03 04 06 6e 5d e0"); + let (rem, result) = BitString::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.unused_bits, 6); + assert_eq!(&result.data[..], &input[3..]); + // + // long form of length (invalid, < 127) + // + let input = &hex!("03 81 04 06 6e 5d c0"); + let (rem, result) = BitString::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.unused_bits, 6); + assert_eq!(&result.data[..], &input[4..]); +} + +#[test] +fn from_ber_embedded_pdv() { + let input = &hex!("2b 0d a0 07 81 05 2a 03 04 05 06 82 02 aa a0"); + let (rem, result) = EmbeddedPdv::from_ber(input).expect("parsing failed"); + assert_eq!(rem, &[]); + assert_eq!( + result.identification, + PdvIdentification::Syntax(Oid::from(&[1, 2, 3, 4, 5, 6]).unwrap()) + ); + assert_eq!(result.data_value, &[0xaa, 0xa0]); +} + +#[test] +fn embedded_pdv_variants() { + // identification: syntaxes + let input = &hex!("2b 11 a0 0c a0 0a 80 02 2a 03 81 04 2a 03 04 05 82 01 00"); + let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); + assert!(rem.is_empty()); + assert!(matches!( + res.identification, + PdvIdentification::Syntaxes { .. } + )); + // identification: syntax + let input = &hex!("2b 09 a0 04 81 02 2a 03 82 01 00"); + let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); + assert!(rem.is_empty()); + assert!(matches!(res.identification, PdvIdentification::Syntax(_))); + // identification: presentation-context-id + let input = &hex!("2b 08 a0 03 82 01 02 82 01 00"); + let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); + assert!(rem.is_empty()); + assert!(matches!( + res.identification, + PdvIdentification::PresentationContextId(_) + )); + // identification: context-negotiation + let input = &hex!("2b 10 a0 0b a3 09 80 01 2a 81 04 2a 03 04 05 82 01 00"); + let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); + assert!(rem.is_empty()); + assert!(matches!( + res.identification, + PdvIdentification::ContextNegotiation { .. } + )); + // identification: transfer-syntax + let input = &hex!("2b 0b a0 06 84 04 2a 03 04 05 82 01 00"); + let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); + assert!(rem.is_empty()); + assert!(matches!( + res.identification, + PdvIdentification::TransferSyntax(_) + )); + // identification: fixed + let input = &hex!("2b 07 a0 02 85 00 82 01 00"); + let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); + assert!(rem.is_empty()); + assert!(matches!(res.identification, PdvIdentification::Fixed)); + // identification: invalid + let input = &hex!("2b 07 a0 02 86 00 82 01 00"); + let e = EmbeddedPdv::from_ber(input).expect_err("parsing should fail"); + assert!(matches!(e, Err::Error(Error::InvalidValue { .. }))); +} + +#[test] +fn from_ber_endofcontent() { + let input = &hex!("00 00"); + let (rem, _result) = EndOfContent::from_ber(input).expect("parsing failed"); + assert_eq!(rem, &[]); +} + +#[test] +fn from_ber_generalizedtime() { + let input = &hex!("18 0F 32 30 30 32 31 32 31 33 31 34 32 39 32 33 5A FF"); + let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); + assert_eq!(rem, &[0xff]); + #[cfg(feature = "datetime")] + { + let datetime = datetime! {2002-12-13 14:29:23 UTC}; + + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result; + // local time with fractional seconds + let input = b"\x18\x1019851106210627.3"; + let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.0.millisecond, Some(300)); + assert_eq!(result.0.tz, ASN1TimeZone::Undefined); + #[cfg(feature = "datetime")] + { + let datetime = datetime! {1985-11-06 21:06:27.300_000_000 UTC}; + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result; + // coordinated universal time with fractional seconds + let input = b"\x18\x1119851106210627.3Z"; + let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.0.millisecond, Some(300)); + assert_eq!(result.0.tz, ASN1TimeZone::Z); + #[cfg(feature = "datetime")] + { + let datetime = datetime! {1985-11-06 21:06:27.300_000_000 UTC}; + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result; + // coordinated universal time with fractional seconds + let input = b"\x18\x1219851106210627.03Z"; + let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.0.millisecond, Some(30)); + assert_eq!(result.0.tz, ASN1TimeZone::Z); + #[cfg(feature = "datetime")] + { + let datetime = datetime! {1985-11-06 21:06:27.03 UTC}; + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result; + // local time with fractional seconds, and with local time 5 hours retarded in relation to coordinated universal time. + let input = b"\x18\x1519851106210627.3-0500"; + let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.0.millisecond, Some(300)); + assert_eq!(result.0.tz, ASN1TimeZone::Offset(-5, 0)); + #[cfg(feature = "datetime")] + { + let datetime = datetime! {1985-11-06 21:06:27.300_000_000 -05:00}; + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result; +} + +#[test] +fn from_ber_int() { + let input = &hex!("02 01 02 ff ff"); + let (rem, result) = u8::from_ber(input).expect("parsing failed"); + assert_eq!(result, 2); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +fn from_ber_relative_oid() { + let input = &hex!("0d 04 c2 7b 03 02"); + let (rem, result) = Oid::from_ber_relative(input).expect("parsing failed"); + assert_eq!(result, Oid::from_relative(&[8571, 3, 2]).unwrap()); + assert_eq!(rem, &[]); +} + +#[test] +fn from_ber_length_incomplete() { + let input = &hex!("30"); + let res = u8::from_ber(input).expect_err("parsing should have failed"); + assert_eq!(res, nom::Err::Incomplete(Needed::new(1))); + let input = &hex!("02"); + let res = u8::from_ber(input).expect_err("parsing should have failed"); + assert_eq!(res, nom::Err::Incomplete(Needed::new(1))); + let input = &hex!("02 05"); + let res = u8::from_ber(input).expect_err("parsing should have failed"); + assert_eq!(res, nom::Err::Incomplete(Needed::new(5))); + let input = &hex!("02 85"); + let res = u8::from_ber(input).expect_err("parsing should have failed"); + assert_eq!(res, nom::Err::Incomplete(Needed::new(5))); + let input = &hex!("02 85 ff"); + let res = u8::from_ber(input).expect_err("parsing should have failed"); + assert_eq!(res, nom::Err::Incomplete(Needed::new(4))); +} + +#[test] +fn from_ber_length_invalid() { + let input = &hex!("02 ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10"); + let res = u8::from_ber(input).expect_err("parsing should have failed"); + assert_eq!(res, nom::Err::Error(Error::InvalidLength)); + let input = &hex!("02 85 ff ff ff ff ff 00"); + let res = u8::from_ber(input).expect_err("parsing should have failed"); + assert!(res.is_incomplete()); +} + +#[test] +fn from_ber_octetstring() { + let input = &hex!("04 05 41 41 41 41 41"); + let (rem, result) = OctetString::from_ber(input).expect("parsing failed"); + assert_eq!(result.as_ref(), b"AAAAA"); + assert_eq!(rem, &[]); +} + +#[test] +fn from_ber_real_binary() { + const EPSILON: f32 = 0.00001; + // binary, base = 2 + let input = &hex!("09 03 80 ff 01 ff ff"); + let (rem, result) = Real::from_ber(input).expect("parsing failed"); + assert_eq!(result, Real::binary(1.0, 2, -1)); + assert!((result.f32() - 0.5).abs() < EPSILON); + assert_eq!(rem, &[0xff, 0xff]); + // binary, base = 2 and scale factor + let input = &hex!("09 03 94 ff 0d ff ff"); + let (rem, result) = Real::from_ber(input).expect("parsing failed"); + assert_eq!(result, Real::binary(26.0, 2, -3).with_enc_base(8)); + assert!((result.f32() - 3.25).abs() < EPSILON); + assert_eq!(rem, &[0xff, 0xff]); + // binary, base = 16 + let input = &hex!("09 03 a0 fe 01 ff ff"); + let (rem, result) = Real::from_ber(input).expect("parsing failed"); + assert_eq!(result, Real::binary(1.0, 2, -8).with_enc_base(16)); + assert!((result.f32() - 0.00390625).abs() < EPSILON); + assert_eq!(rem, &[0xff, 0xff]); + // binary, exponent = 0 + let input = &hex!("09 03 80 00 01 ff ff"); + let (rem, result) = Real::from_ber(input).expect("parsing failed"); + assert_eq!(result, Real::binary(1.0, 2, 0)); + assert!((result.f32() - 1.0).abs() < EPSILON); + assert_eq!(rem, &[0xff, 0xff]); + // 2 octets for exponent and negative exponent + let input = &hex!("09 04 a1 ff 01 03 ff ff"); + let (rem, result) = Real::from_ber(input).expect("parsing failed"); + assert_eq!(result, Real::binary(3.0, 2, -1020).with_enc_base(16)); + let epsilon = 1e-311_f64; + assert!((result.f64() - 2.67e-307).abs() < epsilon); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +fn from_ber_real_f32() { + const EPSILON: f32 = 0.00001; + // binary, base = 2 + let input = &hex!("09 03 80 ff 01 ff ff"); + let (rem, result) = <f32>::from_ber(input).expect("parsing failed"); + assert!((result - 0.5).abs() < EPSILON); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +fn from_ber_real_f64() { + const EPSILON: f64 = 0.00001; + // binary, base = 2 + let input = &hex!("09 03 80 ff 01 ff ff"); + let (rem, result) = <f64>::from_ber(input).expect("parsing failed"); + assert!((result - 0.5).abs() < EPSILON); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +fn from_ber_real_special() { + // 0 + let input = &hex!("09 00 ff ff"); + let (rem, result) = Real::from_ber(input).expect("parsing failed"); + assert_eq!(result, Real::from(0.0)); + assert_eq!(rem, &[0xff, 0xff]); + // infinity + let input = &hex!("09 01 40 ff ff"); + let (rem, result) = Real::from_ber(input).expect("parsing failed"); + assert_eq!(result, Real::Infinity); + assert_eq!(rem, &[0xff, 0xff]); + // negative infinity + let input = &hex!("09 01 41 ff ff"); + let (rem, result) = Real::from_ber(input).expect("parsing failed"); + assert_eq!(result, Real::NegInfinity); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +#[allow(clippy::approx_constant)] +fn from_ber_real_string() { + // text representation, NR3 + let input = &hex!("09 07 03 33 31 34 45 2D 32 ff ff"); + let (rem, result) = Real::from_ber(input).expect("parsing failed"); + assert_eq!(result, Real::from(3.14)); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +#[allow(clippy::approx_constant)] +fn from_ber_real_string_primitive() { + // text representation, NR3 + let input = &hex!("09 07 03 33 31 34 45 2D 32 ff ff"); + let (rem, result) = f32::from_ber(input).expect("parsing failed"); + assert!((result - 3.14).abs() < 0.01); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +fn from_ber_sequence() { + let input = &hex!("30 05 02 03 01 00 01"); + let (rem, result) = Sequence::from_ber(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + // + let (_, i) = Sequence::from_ber_and_then(input, Integer::from_ber).expect("parsing failed"); + assert_eq!(i.as_u32(), Ok(0x10001)); +} + +#[test] +fn from_ber_sequence_vec() { + let input = &hex!("30 05 02 03 01 00 01"); + let (rem, result) = <Vec<u32>>::from_ber(input).expect("parsing failed"); + assert_eq!(&result, &[65537]); + assert_eq!(rem, &[]); +} + +#[test] +fn from_ber_sequence_of_vec() { + let input = &hex!("30 05 02 03 01 00 01"); + let (rem, result) = <Sequence>::from_ber(input).expect("parsing failed"); + let v = result + .ber_sequence_of::<u32, _>() + .expect("ber_sequence_of failed"); + assert_eq!(rem, &[]); + assert_eq!(&v, &[65537]); +} + +#[test] +fn from_ber_iter_sequence() { + let input = &hex!("30 0a 02 03 01 00 01 02 03 01 00 01"); + let (rem, result) = Sequence::from_ber(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + let v = result + .ber_iter() + .collect::<Result<Vec<u32>>>() + .expect("could not iterate sequence"); + assert_eq!(&v, &[65537, 65537]); +} + +#[test] +fn from_ber_set() { + let input = &hex!("31 05 02 03 01 00 01"); + let (rem, result) = Set::from_ber(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + // + let (_, i) = Set::from_ber_and_then(input, Integer::from_ber).expect("parsing failed"); + assert_eq!(i.as_u32(), Ok(0x10001)); +} + +#[test] +fn from_ber_set_of_vec() { + let input = &hex!("31 05 02 03 01 00 01"); + let (rem, result) = <Set>::from_ber(input).expect("parsing failed"); + let v = result.ber_set_of::<u32, _>().expect("ber_set_of failed"); + assert_eq!(rem, &[]); + assert_eq!(&v, &[65537]); +} + +#[test] +fn from_ber_iter_set() { + let input = &hex!("31 0a 02 03 01 00 01 02 03 01 00 01"); + let (rem, result) = Set::from_ber(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + let v = result + .ber_iter() + .collect::<Result<Vec<u32>>>() + .expect("could not iterate set"); + assert_eq!(&v, &[65537, 65537]); +} + +#[test] +fn from_ber_tag_custom() { + // canonical tag encoding + let input = &hex!("8f 02 12 34"); + let (rem, any) = Any::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(any.tag(), Tag(15)); + // non-canonical tag encoding + let input = &hex!("9f 0f 02 12 34"); + let (rem, any) = Any::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(any.tag(), Tag(15)); + assert_eq!(any.header.raw_tag(), Some(&[0x9f, 0x0f][..])); +} + +#[test] +fn from_ber_tag_incomplete() { + let input = &hex!("9f a2 a2"); + let res = Any::from_ber(input).expect_err("parsing should have failed"); + assert_eq!(res, nom::Err::Error(Error::InvalidTag)); +} + +#[test] +fn from_ber_tag_overflow() { + let input = &hex!("9f a2 a2 a2 a2 a2 a2 22 01 00"); + let res = Any::from_ber(input).expect_err("parsing should have failed"); + assert_eq!(res, nom::Err::Error(Error::InvalidTag)); +} + +#[test] +fn from_ber_tag_long() { + let input = &hex!("9f a2 22 01 00"); + let (rem, any) = Any::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(any.tag(), Tag(0x1122)); + assert_eq!(any.header.raw_tag(), Some(&[0x9f, 0xa2, 0x22][..])); +} + +#[test] +fn from_ber_iter_sequence_incomplete() { + let input = &hex!("30 09 02 03 01 00 01 02 03 01 00"); + let (rem, result) = Sequence::from_ber(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + let mut iter = result.ber_iter::<u32, Error>(); + assert_eq!(iter.next(), Some(Ok(65537))); + assert_eq!(iter.next(), Some(Err(Error::Incomplete(Needed::new(1))))); + assert_eq!(iter.next(), None); +} + +#[test] +fn from_ber_set_of() { + let input = &hex!("31 05 02 03 01 00 01"); + let (rem, result) = SetOf::<u32>::from_ber(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &[0x10001]); + assert_eq!(rem, &[]); + // not constructed + let input = &hex!("11 05 02 03 01 00 01"); + let err = SetOf::<u32>::from_ber(input).expect_err("should have failed"); + assert_eq!(err, Err::Error(Error::ConstructExpected)); +} + +#[test] +fn from_ber_tagged_explicit_optional() { + let input = &hex!("a0 03 02 01 02"); + let (rem, result) = + Option::<TaggedExplicit<u32, Error, 0>>::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert!(result.is_some()); + let tagged = result.unwrap(); + assert_eq!(tagged.tag(), Tag(0)); + assert_eq!(tagged.as_ref(), &2); + let (rem, result) = + Option::<TaggedExplicit<u32, Error, 1>>::from_ber(input).expect("parsing failed"); + assert!(result.is_none()); + assert_eq!(rem, input); + + // using OptTaggedExplicit + let (rem, result) = + OptTaggedExplicit::<u32, Error, 0>::from_ber(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert!(result.is_some()); + let tagged = result.unwrap(); + assert_eq!(tagged.tag(), Tag(0)); + assert_eq!(tagged.as_ref(), &2); + + // using OptTaggedParser + let (rem, result) = OptTaggedParser::from(0) + .parse_ber(input, |_, data| Integer::from_ber(data)) + .expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result, Some(Integer::from(2))); +} + +/// Generic tests on methods, and coverage tests +#[test] +fn from_ber_tagged_optional_cov() { + let p = + |input| OptTaggedParser::from(1).parse_ber::<_, Error, _>(input, |_, data| Ok((data, ()))); + // empty input + let input = &[]; + let (_, r) = p(input).expect("parsing failed"); + assert!(r.is_none()); + // wrong tag + let input = &hex!("a0 03 02 01 02"); + let (_, r) = p(input).expect("parsing failed"); + assert!(r.is_none()); + // wrong class + let input = &hex!("e1 03 02 01 02"); + let r = p(input); + assert!(r.is_err()); +} + +#[test] +fn from_ber_universalstring() { + let input = &hex!("1C 10 00000061 00000062 00000063 00000064"); + let (rem, result) = UniversalString::from_ber(input).expect("parsing failed"); + assert_eq!(result.as_ref(), "abcd"); + assert_eq!(rem, &[]); +} diff --git a/rust/vendor/asn1-rs/tests/compile_tests.rs b/rust/vendor/asn1-rs/tests/compile_tests.rs new file mode 100644 index 0000000..1491f57 --- /dev/null +++ b/rust/vendor/asn1-rs/tests/compile_tests.rs @@ -0,0 +1,6 @@ +#[test] +fn compile_fail() { + let t = trybuild::TestCases::new(); + t.pass("tests/run-pass/*.rs"); + t.compile_fail("tests/compile-fail/*.rs"); +} diff --git a/rust/vendor/asn1-rs/tests/cov.rs b/rust/vendor/asn1-rs/tests/cov.rs new file mode 100644 index 0000000..374f950 --- /dev/null +++ b/rust/vendor/asn1-rs/tests/cov.rs @@ -0,0 +1,99 @@ +//! Generic and coverage tests +use asn1_rs::*; +use std::io; + +#[test] +fn new_embedded_pdv() { + fn create_pdv(identification: PdvIdentification) -> EmbeddedPdv { + let pdv = EmbeddedPdv { + identification, + data_value_descriptor: None, + data_value: &[0x00, 0xff], + }; + assert!(pdv.data_value_descriptor.is_none()); + assert_eq!(pdv.data_value.len(), 2); + pdv + } + let identification = PdvIdentification::ContextNegotiation { + presentation_context_id: Integer::from(42_u8), + presentation_syntax: oid! { 1.2.3.4.5 }, + }; + let pdv1 = create_pdv(identification); + let identification = PdvIdentification::Syntaxes { + s_abstract: oid! { 1.2.3 }, + s_transfer: oid! { 1.2.3.4.5 }, + }; + let pdv2 = create_pdv(identification); + assert!(pdv1 != pdv2); + let identification = PdvIdentification::Syntaxes { + s_abstract: oid! { 1.2.3 }, + s_transfer: oid! { 1.2.3.4.5 }, + }; + let pdv3 = create_pdv(identification); + assert!(pdv3 == pdv2); +} + +#[test] +fn methods_error() { + let e = Error::invalid_value(Tag(0), "msg".to_string()); + assert_eq!( + e, + Error::InvalidValue { + tag: Tag(0), + msg: "msg".to_string(), + } + ); + // + let e = Error::unexpected_tag(None, Tag(0)); + assert_eq!( + e, + Error::UnexpectedTag { + expected: None, + actual: Tag(0), + } + ); + // + let e = Error::unexpected_class(None, Class::Application); + assert_eq!( + e, + Error::UnexpectedClass { + expected: None, + actual: Class::Application + } + ); + // + use nom::error::ParseError; + let e = Error::from_error_kind(&[], nom::error::ErrorKind::Fail); + let e = <asn1_rs::Error as ParseError<_>>::append(&[], nom::error::ErrorKind::Eof, e); + let s = format!("{}", e); + assert!(s.starts_with("nom error:")); + // + let e1 = Error::from(nom::Err::Error(Error::BerTypeError)); + let e2 = Error::from(nom::Err::Incomplete(nom::Needed::new(2))); + assert!(e1 != e2); + // + let e = SerializeError::from(Error::BerTypeError); + let s = format!("{}", e); + assert!(s.starts_with("ASN.1 error:")); + // + let e = SerializeError::InvalidClass { class: 4 }; + let s = format!("{}", e); + assert!(s.starts_with("Invalid Class")); + // + let e = SerializeError::from(io::Error::new(io::ErrorKind::Other, "msg")); + let s = format!("{}", e); + assert!(s.starts_with("I/O error:")); +} + +#[test] +fn methods_tag() { + let t = Tag::from(2); + assert_eq!(t, Tag::Integer); + // + let err = t.invalid_value("test"); + if let Error::InvalidValue { tag, .. } = err { + assert_eq!(tag, Tag::Integer); + } else { + unreachable!(); + } +} diff --git a/rust/vendor/asn1-rs/tests/der.rs b/rust/vendor/asn1-rs/tests/der.rs new file mode 100644 index 0000000..5fd87d7 --- /dev/null +++ b/rust/vendor/asn1-rs/tests/der.rs @@ -0,0 +1,649 @@ +use asn1_rs::*; +use hex_literal::hex; +use nom::sequence::pair; +use nom::Needed; +use std::collections::BTreeSet; +use std::convert::TryInto; + +#[test] +fn from_der_any() { + let input = &hex!("02 01 02 ff ff"); + let (rem, result) = Any::from_der(input).expect("parsing failed"); + // dbg!(&result); + assert_eq!(rem, &[0xff, 0xff]); + assert_eq!(result.header.tag(), Tag::Integer); +} + +#[test] +fn from_der_any_into() { + let input = &hex!("02 01 02 ff ff"); + let (rem, result) = Any::from_der(input).expect("parsing failed"); + assert_eq!(rem, &[0xff, 0xff]); + assert_eq!(result.header.tag(), Tag::Integer); + let i: u32 = result.try_into().unwrap(); + assert_eq!(i, 2); + // + let (_, result) = Any::from_der(input).expect("parsing failed"); + let i = result.u32().unwrap(); + assert_eq!(i, 2); +} + +#[test] +fn from_der_bitstring() { + // + // correct DER encoding + // + let input = &hex!("03 04 06 6e 5d c0"); + let (rem, result) = BitString::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.unused_bits, 6); + assert_eq!(&result.data[..], &input[3..]); + // + // correct encoding, but wrong padding bits (not all set to 0) + // + let input = &hex!("03 04 06 6e 5d e0"); + let res = BitString::from_der(input); + assert_eq!( + res, + Err(Err::Error(Error::DerConstraintFailed( + DerConstraint::UnusedBitsNotZero + ))) + ); + // + // long form of length (invalid, < 127) + // + // let input = &hex!("03 81 04 06 6e 5d c0"); + // let res = BitString::from_der(input); + // assert_eq!(res, Err(Err::Error(Error::DerConstraintFailed))); +} + +#[test] +fn from_der_bitstring_constructed() { + let bytes: &[u8] = &hex!("23 81 0c 03 03 00 0a 3b 03 05 04 5f 29 1c d0"); + assert_eq!( + BitString::from_der(bytes), + Err(Err::Error(Error::ConstructUnexpected)) + ); +} + +#[test] +fn from_der_bmpstring() { + // taken from https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-bmpstring + let input = &hex!("1e 08 00 55 00 73 00 65 00 72"); + let (rem, result) = BmpString::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), "User"); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_bool() { + let input = &hex!("01 01 00"); + let (rem, result) = Boolean::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result, Boolean::FALSE); + // + let input = &hex!("01 01 ff"); + let (rem, result) = Boolean::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result, Boolean::TRUE); + assert!(result.bool()); + // + let input = &hex!("01 01 7f"); + let res = Boolean::from_der(input); + assert_eq!( + res, + Err(Err::Error(Error::DerConstraintFailed( + DerConstraint::InvalidBoolean + ))) + ); + // bool type + let input = &hex!("01 01 00"); + let (rem, result) = <bool>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert!(!result); +} + +#[test] +fn from_der_embedded_pdv() { + let input = &hex!("2b 0d a0 07 81 05 2a 03 04 05 06 82 02 aa a0"); + let (rem, result) = EmbeddedPdv::from_der(input).expect("parsing failed"); + assert_eq!(rem, &[]); + assert_eq!( + result.identification, + PdvIdentification::Syntax(Oid::from(&[1, 2, 3, 4, 5, 6]).unwrap()) + ); + assert_eq!(result.data_value, &[0xaa, 0xa0]); +} + +#[test] +fn from_der_enumerated() { + let input = &hex!("0a 01 02"); + let (rem, result) = Enumerated::from_der(input).expect("parsing failed"); + assert_eq!(rem, &[]); + assert_eq!(result.0, 2); +} + +#[test] +fn from_der_generalizedtime() { + let input = &hex!("18 0F 32 30 30 32 31 32 31 33 31 34 32 39 32 33 5A FF"); + let (rem, result) = GeneralizedTime::from_der(input).expect("parsing failed"); + assert_eq!(rem, &[0xff]); + #[cfg(feature = "datetime")] + { + use time::macros::datetime; + let datetime = datetime! {2002-12-13 14:29:23 UTC}; + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result; + // local time with fractional seconds (should fail: no 'Z' at end) + let input = b"\x18\x1019851106210627.3"; + let result = GeneralizedTime::from_der(input).expect_err("should not parse"); + assert_eq!( + result, + nom::Err::Error(Error::DerConstraintFailed(DerConstraint::MissingTimeZone)) + ); + // coordinated universal time with fractional seconds + let input = b"\x18\x1119851106210627.3Z"; + let (rem, result) = GeneralizedTime::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.0.millisecond, Some(300)); + assert_eq!(result.0.tz, ASN1TimeZone::Z); + #[cfg(feature = "datetime")] + { + use time::macros::datetime; + let datetime = datetime! {1985-11-06 21:06:27.3 UTC}; + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result.to_string(); + // local time with fractional seconds, and with local time 5 hours retarded in relation to coordinated universal time. + // (should fail: no 'Z' at end) + let input = b"\x18\x1519851106210627.3-0500"; + let result = GeneralizedTime::from_der(input).expect_err("should not parse"); + assert_eq!( + result, + nom::Err::Error(Error::DerConstraintFailed(DerConstraint::MissingTimeZone)) + ); +} + +#[test] +fn from_der_indefinite_length() { + let bytes: &[u8] = &hex!("23 80 03 03 00 0a 3b 03 05 04 5f 29 1c d0 00 00"); + assert_eq!( + BitString::from_der(bytes), + Err(Err::Error(Error::DerConstraintFailed( + DerConstraint::IndefiniteLength + ))) + ); + let bytes: &[u8] = &hex!("02 80 01 00 00"); + assert!(Integer::from_der(bytes).is_err()); +} + +#[test] +fn from_der_int() { + let input = &hex!("02 01 02 ff ff"); + let (rem, result) = u8::from_der(input).expect("parsing failed"); + assert_eq!(result, 2); + assert_eq!(rem, &[0xff, 0xff]); + // attempt to parse a value too large for container type + let input = &hex!("02 03 00 ff ff"); + let err = u8::from_der(input).expect_err("parsing should fail"); + assert_eq!(err, Err::Error(Error::IntegerTooLarge)); + // attempt to parse a value too large (positive large value in signed integer) + let input = &hex!("02 03 00 ff ff"); + let err = i16::from_der(input).expect_err("parsing should fail"); + assert_eq!(err, Err::Error(Error::IntegerTooLarge)); +} + +#[test] +fn from_der_null() { + let input = &hex!("05 00 ff ff"); + let (rem, result) = Null::from_der(input).expect("parsing failed"); + assert_eq!(result, Null {}); + assert_eq!(rem, &[0xff, 0xff]); + // unit + let (rem, _unit) = <()>::from_der(input).expect("parsing failed"); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +fn from_der_octetstring() { + // coverage + use std::borrow::Cow; + let s = OctetString::new(b"1234"); + assert_eq!(s.as_cow().len(), 4); + assert_eq!(s.into_cow(), Cow::Borrowed(b"1234")); + // + let input = &hex!("04 05 41 41 41 41 41"); + let (rem, result) = OctetString::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), b"AAAAA"); + assert_eq!(rem, &[]); + // + let (rem, result) = <&[u8]>::from_der(input).expect("parsing failed"); + assert_eq!(result, b"AAAAA"); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_octetstring_as_slice() { + let input = &hex!("04 05 41 41 41 41 41"); + let (rem, result) = <&[u8]>::from_der(input).expect("parsing failed"); + assert_eq!(result, b"AAAAA"); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_oid() { + let input = &hex!("06 09 2a 86 48 86 f7 0d 01 01 05"); + let (rem, result) = Oid::from_der(input).expect("parsing failed"); + let expected = Oid::from(&[1, 2, 840, 113_549, 1, 1, 5]).unwrap(); + assert_eq!(result, expected); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_optional() { + let input = &hex!("30 0a 0a 03 00 00 01 02 03 01 00 01"); + let (rem, result) = Sequence::from_der_and_then(input, |input| { + let (i, obj0) = <Option<Enumerated>>::from_der(input)?; + let (i, obj1) = u32::from_der(i)?; + Ok((i, (obj0, obj1))) + }) + .expect("parsing failed"); + let expected = (Some(Enumerated::new(1)), 65537); + assert_eq!(result, expected); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_real_f32() { + const EPSILON: f32 = 0.00001; + // binary, base = 2 + let input = &hex!("09 03 80 ff 01 ff ff"); + let (rem, result) = <f32>::from_der(input).expect("parsing failed"); + assert!((result - 0.5).abs() < EPSILON); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +fn from_der_real_f64() { + const EPSILON: f64 = 0.00001; + // binary, base = 2 + let input = &hex!("09 03 80 ff 01 ff ff"); + let (rem, result) = <f64>::from_der(input).expect("parsing failed"); + assert!((result - 0.5).abs() < EPSILON); + assert_eq!(rem, &[0xff, 0xff]); +} + +#[test] +fn from_der_relative_oid() { + let input = &hex!("0d 04 c2 7b 03 02"); + let (rem, result) = Oid::from_der_relative(input).expect("parsing failed"); + let expected = Oid::from_relative(&[8571, 3, 2]).unwrap(); + assert_eq!(result, expected); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_sequence() { + let input = &hex!("30 05 02 03 01 00 01"); + let (rem, result) = Sequence::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_sequence_vec() { + let input = &hex!("30 05 02 03 01 00 01"); + let (rem, result) = <Vec<u32>>::from_der(input).expect("parsing failed"); + assert_eq!(&result, &[65537]); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_iter_sequence_parse() { + let input = &hex!("30 0a 02 03 01 00 01 02 03 01 00 01"); + let (rem, result) = Sequence::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + let (rem, v) = result + .parse(pair(u32::from_der, u32::from_der)) + .expect("parse sequence data"); + assert_eq!(v, (65537, 65537)); + assert!(rem.is_empty()); +} +#[test] +fn from_der_iter_sequence() { + let input = &hex!("30 0a 02 03 01 00 01 02 03 01 00 01"); + let (rem, result) = Sequence::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + let v = result + .der_iter() + .collect::<Result<Vec<u32>>>() + .expect("could not iterate sequence"); + assert_eq!(&v, &[65537, 65537]); +} + +#[test] +fn from_der_iter_sequence_incomplete() { + let input = &hex!("30 09 02 03 01 00 01 02 03 01 00"); + let (rem, result) = Sequence::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + let mut iter = result.der_iter::<u32, Error>(); + assert_eq!(iter.next(), Some(Ok(65537))); + assert_eq!(iter.next(), Some(Err(Error::Incomplete(Needed::new(1))))); + assert_eq!(iter.next(), None); +} + +#[test] +fn from_der_set() { + let input = &hex!("31 05 02 03 01 00 01"); + let (rem, result) = Set::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + // + let (_, i) = Set::from_der_and_then(input, Integer::from_der).expect("parsing failed"); + assert_eq!(i.as_u32(), Ok(0x10001)); +} + +#[test] +fn from_der_set_btreeset() { + let input = &hex!("31 05 02 03 01 00 01"); + let (rem, result) = <BTreeSet<u32>>::from_der(input).expect("parsing failed"); + assert!(result.contains(&65537)); + assert_eq!(result.len(), 1); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_set_of_vec() { + let input = &hex!("31 05 02 03 01 00 01"); + let (rem, result) = <Set>::from_der(input).expect("parsing failed"); + let v = result.der_set_of::<u32, _>().expect("ber_set_of failed"); + assert_eq!(rem, &[]); + assert_eq!(&v, &[65537]); +} + +#[test] +fn from_der_iter_set() { + let input = &hex!("31 0a 02 03 01 00 01 02 03 01 00 01"); + let (rem, result) = Set::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), &input[2..]); + assert_eq!(rem, &[]); + let v = result + .der_iter() + .collect::<Result<Vec<u32>>>() + .expect("could not iterate set"); + assert_eq!(&v, &[65537, 65537]); +} + +#[test] +fn from_der_utctime() { + let input = &hex!("17 0D 30 32 31 32 31 33 31 34 32 39 32 33 5A FF"); + let (rem, result) = UtcTime::from_der(input).expect("parsing failed"); + assert_eq!(rem, &[0xff]); + #[cfg(feature = "datetime")] + { + use time::macros::datetime; + let datetime = datetime! {2-12-13 14:29:23 UTC}; + + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result.to_string(); + // + let input = &hex!("17 11 30 32 31 32 31 33 31 34 32 39 32 33 2b 30 33 30 30 FF"); + let (rem, result) = UtcTime::from_der(input).expect("parsing failed"); + assert_eq!(rem, &[0xff]); + #[cfg(feature = "datetime")] + { + use time::macros::datetime; + let datetime = datetime! {2-12-13 14:29:23 +03:00}; + + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result.to_string(); + // + let input = &hex!("17 11 30 32 31 32 31 33 31 34 32 39 32 33 2d 30 33 30 30 FF"); + let (rem, result) = UtcTime::from_der(input).expect("parsing failed"); + assert_eq!(rem, &[0xff]); + #[cfg(feature = "datetime")] + { + use time::macros::datetime; + let datetime = datetime! {2-12-13 14:29:23 -03:00}; + + assert_eq!(result.utc_datetime(), Ok(datetime)); + } + let _ = result.to_string(); +} + +#[cfg(feature = "datetime")] +#[test] +fn utctime_adjusted_datetime() { + use time::macros::datetime; + + let input = &hex!("17 0D 30 32 31 32 31 33 31 34 32 39 32 33 5A FF"); + let (_, result) = UtcTime::from_der(input).expect("parsing failed"); + + assert_eq!( + result.utc_adjusted_datetime(), + Ok(datetime! {2002-12-13 14:29:23 UTC}) + ); + + let input = &hex!("17 0D 35 30 31 32 31 33 31 34 32 39 32 33 5A FF"); + let (_, result) = UtcTime::from_der(input).expect("parsing failed"); + + assert_eq!( + result.utc_adjusted_datetime(), + Ok(datetime! {1950-12-13 14:29:23 UTC}) + ); + let _ = result.to_string(); +} + +#[test] +fn from_der_utf8string() { + let input = &hex!("0c 0a 53 6f 6d 65 2d 53 74 61 74 65"); + let (rem, result) = Utf8String::from_der(input).expect("parsing failed"); + assert_eq!(result.as_ref(), "Some-State"); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_utf8string_as_str() { + let input = &hex!("0c 0a 53 6f 6d 65 2d 53 74 61 74 65"); + let (rem, result) = <&str>::from_der(input).expect("parsing failed"); + assert_eq!(result, "Some-State"); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_utf8string_as_string() { + let input = &hex!("0c 0a 53 6f 6d 65 2d 53 74 61 74 65"); + let (rem, result) = String::from_der(input).expect("parsing failed"); + assert_eq!(&result, "Some-State"); + assert_eq!(rem, &[]); +} + +#[test] +fn from_der_opt_int() { + let input = &hex!("02 01 02 ff ff"); + let (rem, result) = <Option<u8>>::from_der(input).expect("parsing failed"); + assert_eq!(result, Some(2)); + assert_eq!(rem, &[0xff, 0xff]); + // non-fatal error + let (rem, result) = <Option<Ia5String>>::from_der(input).expect("parsing failed"); + assert!(result.is_none()); + assert_eq!(rem, input); + // fatal error (correct tag, but incomplete) + let input = &hex!("02 03 02 01"); + let res = <Option<u8>>::from_der(input); + assert_eq!(res, Err(nom::Err::Incomplete(Needed::new(1)))); +} + +#[test] +fn from_der_tagged_explicit() { + let input = &hex!("a0 03 02 01 02"); + let (rem, result) = TaggedExplicit::<u32, Error, 0>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.tag(), Tag(0)); + assert_eq!(result.as_ref(), &2); +} + +#[test] +fn from_der_tagged_explicit_with_class() { + let input = &hex!("a0 03 02 01 02"); + // Note: the strange notation (using braces) is required by the compiler to use + // a constant instead of the numeric value. + let (rem, result) = + TaggedValue::<u32, Error, Explicit, { Class::CONTEXT_SPECIFIC }, 0>::from_der(input) + .expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.tag(), Tag(0)); + assert_eq!(result.as_ref(), &2); +} + +#[test] +fn from_der_tagged_explicit_any_tag() { + let input = &hex!("a0 03 02 01 02"); + let (rem, result) = TaggedParser::<Explicit, u32>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.tag(), Tag(0)); + assert_eq!(result.as_ref(), &2); +} + +#[test] +fn from_der_tagged_explicit_optional() { + let input = &hex!("a0 03 02 01 02"); + let (rem, result) = + Option::<TaggedExplicit<u32, Error, 0>>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert!(result.is_some()); + let tagged = result.unwrap(); + assert_eq!(tagged.tag(), Tag(0)); + assert_eq!(tagged.as_ref(), &2); + let (rem, result) = + Option::<TaggedExplicit<u32, Error, 1>>::from_der(input).expect("parsing failed"); + assert!(result.is_none()); + assert_eq!(rem, input); + + // using OptTaggedExplicit + let (rem, result) = + OptTaggedExplicit::<u32, Error, 0>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert!(result.is_some()); + let tagged = result.unwrap(); + assert_eq!(tagged.tag(), Tag(0)); + assert_eq!(tagged.as_ref(), &2); + + // using OptTaggedParser + let (rem, result) = OptTaggedParser::from(0) + .parse_der(input, |_, data| Integer::from_der(data)) + .expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result, Some(Integer::from(2))); +} + +#[test] +fn from_der_tagged_implicit() { + let input = &hex!("81 04 70 61 73 73"); + let (rem, result) = TaggedImplicit::<&str, Error, 1>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.tag(), Tag(1)); + assert_eq!(result.as_ref(), &"pass"); +} + +#[test] +fn from_der_tagged_implicit_with_class() { + let input = &hex!("81 04 70 61 73 73"); + // Note: the strange notation (using braces) is required by the compiler to use + // a constant instead of the numeric value. + let (rem, result) = + TaggedValue::<&str, Error, Implicit, { Class::CONTEXT_SPECIFIC }, 1>::from_der(input) + .expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.tag(), Tag(1)); + assert_eq!(result.as_ref(), &"pass"); +} + +#[test] +fn from_der_tagged_implicit_any_tag() { + let input = &hex!("81 04 70 61 73 73"); + let (rem, result) = TaggedParser::<Implicit, &str>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.tag(), Tag(1)); + assert_eq!(result.as_ref(), &"pass"); +} + +#[test] +fn from_der_tagged_implicit_optional() { + let input = &hex!("81 04 70 61 73 73"); + let (rem, result) = + Option::<TaggedImplicit<&str, Error, 1>>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert!(result.is_some()); + let tagged = result.unwrap(); + assert_eq!(tagged.tag(), Tag(1)); + assert_eq!(tagged.as_ref(), &"pass"); + let (rem, result) = + Option::<TaggedImplicit<&str, Error, 0>>::from_der(input).expect("parsing failed"); + assert!(result.is_none()); + assert_eq!(rem, input); + + // using OptTaggedExplicit + let (rem, result) = + OptTaggedImplicit::<&str, Error, 1>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert!(result.is_some()); + let tagged = result.unwrap(); + assert_eq!(tagged.tag(), Tag(1)); + assert_eq!(tagged.as_ref(), &"pass"); +} + +#[test] +fn from_der_tagged_implicit_all() { + let input = &hex!("81 04 70 61 73 73"); + let (rem, result) = + TaggedParser::<Implicit, Ia5String>::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + assert_eq!(result.tag(), Tag(1)); + assert_eq!(result.as_ref().as_ref(), "pass"); + + // try the API verifying class and tag + let _ = TaggedParser::<Implicit, Ia5String>::parse_der(Class::ContextSpecific, Tag(1), input) + .expect("parsing failed"); + + // test TagParser API + let parser = TaggedParserBuilder::implicit() + .with_class(Class::ContextSpecific) + .with_tag(Tag(1)) + .der_parser::<Ia5String>(); + let _ = parser(input).expect("parsing failed"); + + // try specifying the expected tag (correct tag) + let _ = parse_der_tagged_implicit::<_, Ia5String, _>(1)(input).expect("parsing failed"); + // try specifying the expected tag (incorrect tag) + let _ = parse_der_tagged_implicit::<_, Ia5String, _>(2)(input) + .expect_err("parsing should have failed"); +} + +/// Generic tests on methods, and coverage tests +#[test] +fn from_der_tagged_optional_cov() { + let p = + |input| OptTaggedParser::from(1).parse_der::<_, Error, _>(input, |_, data| Ok((data, ()))); + // empty input + let input = &[]; + let (_, r) = p(input).expect("parsing failed"); + assert!(r.is_none()); + // wrong tag + let input = &hex!("a0 03 02 01 02"); + let (_, r) = p(input).expect("parsing failed"); + assert!(r.is_none()); + // wrong class + let input = &hex!("e1 03 02 01 02"); + let r = p(input); + assert!(r.is_err()); + + let p = OptTaggedParser::from(Tag(1)); + let _ = format!("{:?}", p); +} diff --git a/rust/vendor/asn1-rs/tests/krb5.rs b/rust/vendor/asn1-rs/tests/krb5.rs new file mode 100644 index 0000000..d48ed2f --- /dev/null +++ b/rust/vendor/asn1-rs/tests/krb5.rs @@ -0,0 +1,108 @@ +//! Test implementation for Kerberos v5 +//! +//! This is mostly used to verify that required types and functions are implemented, +//! and that provided API is convenient. + +use asn1_rs::*; +use hex_literal::hex; + +const PRINCIPAL_NAME: &[u8] = &hex!("30 81 11 a0 03 02 01 00 a1 0a 30 81 07 1b 05 4a 6f 6e 65 73"); + +/// PrincipalName ::= SEQUENCE { +/// name-type [0] Int32, +/// name-string [1] SEQUENCE OF KerberosString +/// } +#[derive(Debug, PartialEq, Eq)] +pub struct PrincipalName { + pub name_type: NameType, + pub name_string: Vec<String>, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct NameType(pub i32); + +// KerberosString ::= GeneralString (IA5String) +pub type KerberosString<'a> = GeneralString<'a>; + +pub type KerberosStringList<'a> = Vec<KerberosString<'a>>; + +impl Tagged for PrincipalName { + const TAG: Tag = Tag::Sequence; +} + +impl<'a> FromDer<'a> for PrincipalName { + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { + // XXX in the example above, PRINCIPAL_NAME does not respect DER constraints (length is using long form while < 127) + let (rem, seq) = Sequence::from_ber(bytes)?; + seq.and_then(|data| { + let input = &data; + let (i, t) = parse_der_tagged_explicit::<_, u32, _>(0)(input)?; + let name_type = t.inner; + let name_type = NameType(name_type as i32); + let (_, t) = parse_der_tagged_explicit::<_, KerberosStringList, _>(1)(i)?; + let name_string = t.inner.iter().map(|s| s.string()).collect(); + Ok(( + rem, + PrincipalName { + name_type, + name_string, + }, + )) + }) + } +} + +impl ToDer for PrincipalName { + fn to_der_len(&self) -> Result<usize> { + let sz = self.name_type.0.to_der_len()? + 2 /* tagged */; + let sz = sz + self.name_string.to_der_len()? + 2 /* tagged */; + Ok(sz) + } + + fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + let len = self.to_der_len()?; + let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); + header.write_der_header(writer).map_err(Into::into) + } + + fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> { + // build DER sequence content + let sz1 = self + .name_type + .0 + .explicit(Class::ContextSpecific, 0) + .write_der(writer)?; + let sz2 = self + .name_string + .iter() + .map(|s| KerberosString::from(s.as_ref())) + .collect::<Vec<_>>() + .explicit(Class::ContextSpecific, 1) + .write_der(writer)?; + Ok(sz1 + sz2) + } +} + +#[test] +fn krb5_principalname() { + let input = PRINCIPAL_NAME; + let (rem, res) = PrincipalName::from_der(input).expect("parsing failed"); + assert!(rem.is_empty()); + let expected = PrincipalName { + name_type: NameType(0), + name_string: vec!["Jones".to_string()], + }; + assert_eq!(res, expected); +} + +#[test] +fn to_der_krb5_principalname() { + let principal = PrincipalName { + name_type: NameType(0), + name_string: vec!["Jones".to_string()], + }; + let v = PrincipalName::to_der_vec(&principal).expect("serialization failed"); + std::fs::write("/tmp/out.bin", &v).unwrap(); + let (_, principal2) = PrincipalName::from_der(&v).expect("parsing failed"); + assert!(principal.eq(&principal2)); +} diff --git a/rust/vendor/asn1-rs/tests/to_der.rs b/rust/vendor/asn1-rs/tests/to_der.rs new file mode 100644 index 0000000..eea7560 --- /dev/null +++ b/rust/vendor/asn1-rs/tests/to_der.rs @@ -0,0 +1,515 @@ +use asn1_rs::*; +use hex_literal::hex; +// use nom::HexDisplay; +use std::collections::BTreeSet; +use std::convert::{TryFrom, TryInto}; +use std::iter::FromIterator; + +macro_rules! test_simple_string { + ($t:ty, $s:expr) => { + let t = <$t>::from($s); + let v = t.to_der_vec().expect("serialization failed"); + assert_eq!(v[0] as u32, t.tag().0); + assert_eq!(v[1] as usize, t.as_ref().len()); + assert_eq!(&v[2..], $s.as_bytes()); + let (_, t2) = <$t>::from_der(&v).expect("decoding serialized object failed"); + assert!(t.eq(&t2)); + }; +} + +macro_rules! test_string_invalid_charset { + ($t:ty, $s:expr) => { + <$t>::test_valid_charset($s.as_bytes()).expect_err("should reject charset"); + }; +} + +#[test] +fn to_der_length() { + // indefinite length + let length = Length::Indefinite; + let v = length.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x80]); + // definite, short form + let length = Length::Definite(3); + let v = length.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x03]); + // definite, long form + let length = Length::Definite(250); + let v = length.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x81, 0xfa]); +} + +#[test] +fn to_der_length_long() { + let s = core::str::from_utf8(&[0x41; 256]).unwrap(); + let v = s.to_der_vec().expect("serialization failed"); + assert_eq!(&v[..4], &[0x0c, 0x82, 0x01, 0x00]); + assert_eq!(&v[4..], s.as_bytes()); +} + +#[test] +fn to_der_tag() { + // short tag, UNIVERSAL + let v = (Class::Universal, false, Tag(0x1a)) + .to_der_vec() + .expect("serialization failed"); + assert_eq!(&v, &[0x1a]); + // short tag, APPLICATION + let v = (Class::Application, false, Tag(0x1a)) + .to_der_vec() + .expect("serialization failed"); + assert_eq!(&v, &[0x1a | (0b01 << 6)]); + // short tag, constructed + let v = (Class::Universal, true, Tag(0x10)) + .to_der_vec() + .expect("serialization failed"); + assert_eq!(&v, &[0x30]); + // long tag, UNIVERSAL + let v = (Class::Universal, false, Tag(0x1a1a)) + .to_der_vec() + .expect("serialization failed"); + assert_eq!(&v, &[0b1_1111, 0x9a, 0x34]); +} + +#[test] +fn to_der_header() { + // simple header + let header = Header::new_simple(Tag::Integer); + let v = header.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x2, 0x0]); + // indefinite length + let header = Header::new(Class::Universal, false, Tag::Integer, Length::Indefinite); + let v = header.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x2, 0x80]); +} + +#[test] +fn to_der_any() { + let header = Header::new_simple(Tag::Integer); + let any = Any::new(header, &hex!("02")); + assert_eq!(any.to_der_len(), Ok(3)); + let v = any.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x02, 0x01, 0x02]); +} + +#[test] +fn to_der_any_raw() { + let header = Header::new(Class::Universal, false, Tag::Integer, Length::Definite(3)); + let any = Any::new(header, &hex!("02")); + // to_vec should compute the length + let v = any.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x02, 0x01, 0x02]); + // to_vec_raw will use the header as provided + let v = any.to_der_vec_raw().expect("serialization failed"); + assert_eq!(&v, &[0x02, 0x03, 0x02]); +} + +#[test] +fn to_der_bitstring() { + let bitstring = BitString::new(6, &hex!("6e 5d c0")); + let v = bitstring.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("03 04 06 6e 5d c0")); + let (_, result) = BitString::from_der(&v).expect("parsing failed"); + assert!(bitstring.eq(&result)); +} + +#[test] +fn to_der_bmpstring() { + let bmpstring = BmpString::new("User"); + assert_eq!(bmpstring.to_der_len(), Ok(10)); + let v = bmpstring.to_der_vec().expect("serialization failed"); + let expected = &hex!("1e 08 00 55 00 73 00 65 00 72"); + assert_eq!(&v, expected); + assert!(BmpString::test_valid_charset(&v[2..]).is_ok()); + let (_, result) = BmpString::from_der(&v).expect("parsing failed"); + assert!(bmpstring.eq(&result)); + // for coverage + let b1 = BmpString::from("s"); + let s = b1.string(); + let b2 = BmpString::from(s); + assert_eq!(b1, b2); + // long string + let sz = 256; + let s = str::repeat("a", sz); + let bmpstring = BmpString::new(&s); + assert_eq!(bmpstring.to_der_len(), Ok(4 + 2 * s.len())); + let _v = bmpstring.to_der_vec().expect("serialization failed"); +} + +#[test] +fn to_der_bool() { + let v = Boolean::new(0xff) + .to_der_vec() + .expect("serialization failed"); + assert_eq!(&v, &[0x01, 0x01, 0xff]); + // + let v = false.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x01, 0x01, 0x00]); + // + let v = true.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x01, 0x01, 0xff]); + // raw value (not 0 of 0xff) + let v = Boolean::new(0x8a) + .to_der_vec_raw() + .expect("serialization failed"); + assert_eq!(&v, &[0x01, 0x01, 0x8a]); +} + +#[test] +fn to_der_enumerated() { + let v = Enumerated(2).to_der_vec().expect("serialization failed"); + assert_eq!(Enumerated(2).to_der_len(), Ok(3)); + assert_eq!(&v, &[0x0a, 0x01, 0x02]); + // + let (_, result) = Enumerated::from_der(&v).expect("parsing failed"); + assert_eq!(result, Enumerated(2)); +} + +#[test] +fn to_der_generalizedtime() { + // date without millisecond + let dt = ASN1DateTime::new(1999, 12, 31, 23, 59, 59, None, ASN1TimeZone::Z); + let time = GeneralizedTime::new(dt); + let v = time.to_der_vec().expect("serialization failed"); + assert_eq!(&v[..2], &hex!("18 0f")); + assert_eq!(&v[2..], b"19991231235959Z"); + let (_, time2) = GeneralizedTime::from_der(&v).expect("decoding serialized object failed"); + assert!(time.eq(&time2)); + assert_eq!(time.to_der_len(), Ok(0x11)); + // + // date with millisecond + let dt = ASN1DateTime::new(1999, 12, 31, 23, 59, 59, Some(123), ASN1TimeZone::Z); + let time = GeneralizedTime::new(dt); + let v = time.to_der_vec().expect("serialization failed"); + assert_eq!(&v[..2], &hex!("18 13")); + assert_eq!(&v[2..], b"19991231235959.123Z"); + let (_, time2) = GeneralizedTime::from_der(&v).expect("decoding serialized object failed"); + assert!(time.eq(&time2)); +} + +#[test] +fn to_der_graphicstring() { + test_simple_string!(GraphicString, "123456"); + test_string_invalid_charset!(GraphicString, "é23456"); +} + +fn encode_decode_assert_int<T>(t: T, expected: &[u8]) +where + T: ToDer + std::fmt::Debug + Eq, + for<'a> T: TryFrom<Integer<'a>, Error = Error>, +{ + let v = t.to_der_vec().expect("serialization failed"); + assert_eq!(&v, expected); + let (_, obj) = Integer::from_der(&v).expect("decoding serialized object failed"); + let t2: T = obj.try_into().unwrap(); + assert_eq!(t, t2); +} + +#[test] +fn to_der_integer() { + let int = Integer::new(&hex!("02")); + let v = int.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x02, 0x01, 0x02]); + // from_u32 + let int = Integer::from_u32(2); + let v = int.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &[0x02, 0x01, 0x02]); + // impl ToDer for primitive types + encode_decode_assert_int(2u32, &[0x02, 0x01, 0x02]); + // signed i32 (> 0) + encode_decode_assert_int(4, &[0x02, 0x01, 0x04]); + // signed i32 (< 0) + encode_decode_assert_int(-4, &[0x02, 0x01, 0xfc]); + // negative number + encode_decode_assert_int(-1i8, &[0x02, 0x01, 0xff]); +} + +#[test] +fn to_der_null() { + let bytes: &[u8] = &hex!("05 00"); + let s = Null::new(); + assert_eq!(s.to_der_len(), Ok(2)); + let v = s.to_der_vec().expect("serialization failed"); + assert_eq!(&v, bytes); + // unit + assert_eq!(().to_der_len(), Ok(2)); + let (_, s2) = <()>::from_der(&v).expect("decoding serialized object failed"); + assert!(().eq(&s2)); + let v2 = ().to_der_vec().expect("serialization failed"); + assert_eq!(&v2, bytes); + // invalid null encodings + let bytes: &[u8] = &hex!("05 01 00"); + let _ = Null::from_ber(bytes).expect_err("should fail"); + let _ = <()>::from_ber(bytes).expect_err("should fail"); +} + +#[test] +fn to_der_numericstring() { + test_simple_string!(NumericString, "123456"); + test_string_invalid_charset!(NumericString, "abcdef"); + test_string_invalid_charset!(NumericString, "1a"); +} + +#[test] +fn to_der_objectdescriptor() { + test_simple_string!(ObjectDescriptor, "abcdef"); + test_string_invalid_charset!(ObjectDescriptor, "abcdéf"); +} + +#[test] +fn to_der_octetstring() { + let bytes: &[u8] = &hex!("01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"); + let s = OctetString::from(bytes); + let v = s.to_der_vec().expect("serialization failed"); + assert_eq!(s.to_der_len(), Ok(bytes.len() + 2)); + assert_eq!(&v[..2], &hex!("04 0f")); + assert_eq!(&v[2..], bytes); + let (_, s2) = OctetString::from_der(&v).expect("decoding serialized object failed"); + assert!(s.eq(&s2)); + // + let v = bytes.to_der_vec().expect("serialization failed"); + assert_eq!(bytes.to_der_len(), Ok(bytes.len() + 2)); + assert_eq!(&v[..2], &hex!("04 0f")); + assert_eq!(&v[2..], bytes); + let (_, s2) = OctetString::from_der(&v).expect("decoding serialized object failed"); + assert!(s.eq(&s2)); +} + +#[test] +fn to_der_real_binary() { + // base = 2, value = 4 + let r = Real::binary(2.0, 2, 1); + let v = r.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("09 03 80 02 01")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert!((r.f64() - result.f64()).abs() < f64::EPSILON); + // + // base = 2, value = 0.5 + let r = Real::binary(0.5, 2, 0); + let v = r.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("09 03 80 ff 01")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert!((r.f64() - result.f64()).abs() < f64::EPSILON); + // + // base = 2, value = 3.25, but change encoding base (8) + let r = Real::binary(3.25, 2, 0).with_enc_base(8); + let v = r.to_der_vec().expect("serialization failed"); + // note: this encoding has a scale factor (not DER compliant) + assert_eq!(&v, &hex!("09 03 94 ff 0d")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert!((r.f64() - result.f64()).abs() < f64::EPSILON); + // + // base = 2, value = 0.00390625, but change encoding base (16) + let r = Real::binary(0.00390625, 2, 0).with_enc_base(16); + let v = r.to_der_vec().expect("serialization failed"); + // note: this encoding has a scale factor (not DER compliant) + assert_eq!(&v, &hex!("09 03 a0 fe 01")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert!((r.f64() - result.f64()).abs() < f64::EPSILON); + // + // 2 octets for exponent, negative exponent and abs(exponent) is all 1's and fills the whole octet(s) + let r = Real::binary(3.0, 2, -1020); + let v = r.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("09 04 81 fc 04 03")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert!((r.f64() - result.f64()).abs() < f64::EPSILON); + // + // 3 octets for exponent, and + // check that first 9 bits for exponent are not all 1's + let r = Real::binary(1.0, 2, 262140); + let v = r.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("09 05 82 03 ff fc 01")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + // XXX value cannot be represented as f64 (inf) + assert!(result.f64().is_infinite()); + // + // >3 octets for exponent, and + // mantissa < 0 + let r = Real::binary(-1.0, 2, 76354972); + let v = r.to_der_vec().expect("serialization failed"); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert_eq!(&v, &hex!("09 07 c3 04 04 8d 15 9c 01")); + // XXX value cannot be represented as f64 (-inf) + assert!(result.f64().is_infinite()); +} + +#[test] +fn to_der_real_special() { + // ZERO + let r = Real::Zero; + let v = r.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("09 00")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert!(r.eq(&result)); + // INFINITY + let r = Real::Infinity; + let v = r.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("09 01 40")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert!(r.eq(&result)); + // MINUS INFINITY + let r = Real::NegInfinity; + let v = r.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("09 01 41")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert!(r.eq(&result)); +} + +#[test] +fn to_der_real_string() { + // non-zero value, base 10 + let r = Real::new(1.2345); + let v = r.to_der_vec().expect("serialization failed"); + // assert_eq!(&v, &hex!("09 00")); + let (_, result) = Real::from_der(&v).expect("parsing failed"); + assert!(r.eq(&result)); +} + +#[test] +fn to_der_sequence() { + let it = [2, 3, 4].iter(); + let seq = Sequence::from_iter_to_der(it).unwrap(); + let v = seq.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("30 09 02 01 02 02 01 03 02 01 04")); + let (_, seq2) = Sequence::from_der(&v).expect("decoding serialized object failed"); + assert_eq!(seq, seq2); + // Vec<T>::ToDer + let v = vec![2, 3, 4].to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("30 09 02 01 02 02 01 03 02 01 04")); +} + +#[test] +fn to_der_set() { + let it = [2u8, 3, 4].iter(); + let set = Set::from_iter_to_der(it).unwrap(); + let v = set.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("31 09 02 01 02 02 01 03 02 01 04")); + // let (_, set2) = Set::from_der(&v).expect("decoding serialized object failed"); + // assert_eq!(set, set2); + // BTreeSet<T>::ToDer + let set2 = BTreeSet::from_iter(vec![2, 3, 4]); + let v = set2.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("31 09 02 01 02 02 01 03 02 01 04")); +} + +#[test] +fn to_der_str() { + let s = "abcdef"; + assert_eq!(s.to_der_len(), Ok(2 + s.len())); + let v = s.to_der_vec().expect("serialization failed"); + assert_eq!(&v[..2], &hex!("0c 06")); + assert_eq!(&v[2..], b"abcdef"); + let (_, s2) = Utf8String::from_der(&v).expect("decoding serialized object failed"); + assert!(s.eq(s2.as_ref())); + // long string + let sz = 256; + let s = str::repeat("a", sz); + let s = s.as_str(); + assert_eq!(s.to_der_len(), Ok(4 + sz)); + let v = s.to_der_vec().expect("serialization failed"); + assert_eq!(v.len(), 4 + sz); +} + +#[test] +fn to_der_string() { + let s = "abcdef".to_string(); + assert_eq!(s.to_der_len(), Ok(2 + s.len())); + let v = s.to_der_vec().expect("serialization failed"); + assert_eq!(&v[..2], &hex!("0c 06")); + assert_eq!(&v[2..], b"abcdef"); + let (_, s2) = Utf8String::from_der(&v).expect("decoding serialized object failed"); + assert!(s.eq(s2.as_ref())); + // long string + let sz = 256; + let s = str::repeat("a", sz); + assert_eq!(s.to_der_len(), Ok(4 + sz)); + let v = s.to_der_vec().expect("serialization failed"); + assert_eq!(v.len(), 4 + sz); +} + +#[test] +fn to_der_tagged_explicit() { + let tagged = TaggedParser::new_explicit(Class::ContextSpecific, 1, 2u32); + let v = tagged.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("a1 03 02 01 02")); + let (_, t2) = + TaggedParser::<Explicit, u32>::from_der(&v).expect("decoding serialized object failed"); + assert!(tagged.eq(&t2)); + // TaggedValue API + let tagged = TaggedValue::explicit(2u32); + let v = tagged.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("a1 03 02 01 02")); + let (_, t2) = + TaggedExplicit::<u32, Error, 1>::from_der(&v).expect("decoding serialized object failed"); + assert!(tagged.eq(&t2)); +} + +#[test] +fn to_der_tagged_implicit() { + let tagged = TaggedParser::new_implicit(Class::ContextSpecific, false, 1, 2u32); + let v = tagged.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("81 01 02")); + let (_, t2) = + TaggedParser::<Implicit, u32>::from_der(&v).expect("decoding serialized object failed"); + assert!(tagged.eq(&t2)); + // TaggedValue API + let tagged = TaggedValue::implicit(2u32); + let v = tagged.to_der_vec().expect("serialization failed"); + assert_eq!(&v, &hex!("81 01 02")); + let (_, t2) = + TaggedImplicit::<u32, Error, 1>::from_der(&v).expect("decoding serialized object failed"); + assert!(tagged.eq(&t2)); +} + +#[test] +fn to_der_teletexstring() { + test_simple_string!(TeletexString, "abcdef"); +} + +#[test] +fn to_der_utctime() { + let dt = ASN1DateTime::new(99, 12, 31, 23, 59, 59, None, ASN1TimeZone::Z); + let time = UtcTime::new(dt); + let v = time.to_der_vec().expect("serialization failed"); + assert_eq!(&v[..2], &hex!("17 0d")); + assert_eq!(&v[2..], b"991231235959Z"); + let (_, time2) = UtcTime::from_der(&v).expect("decoding serialized object failed"); + assert!(time.eq(&time2)); +} + +#[test] +fn to_der_universalstring() { + const S: &str = "abcdef"; + let s = UniversalString::from(S); + assert_eq!(s.to_der_len(), Ok(2 + 4 * S.len())); + let v = s.to_der_vec().expect("serialization failed"); + assert_eq!( + &v, + &hex!("1c 18 00000061 00000062 00000063 00000064 00000065 00000066") + ); + let (_, s2) = UniversalString::from_der(&v).expect("decoding serialized object failed"); + assert!(s.eq(&s2)); + // long string + let sz = 256; + let s = str::repeat("a", sz); + let s = UniversalString::from(s); + assert_eq!(s.to_der_len(), Ok(4 + 4 * sz)); + let v = s.to_der_vec().expect("serialization failed"); + assert_eq!(v.len(), 4 + 4 * sz); +} + +#[test] +fn to_der_utf8string() { + test_simple_string!(Utf8String, "abcdef"); +} + +#[test] +fn to_der_visiblestring() { + test_simple_string!(VisibleString, "abcdef"); + test_string_invalid_charset!(VisibleString, "abcdéf"); +} + +#[test] +fn to_der_videotexstring() { + test_simple_string!(VideotexString, "abcdef"); +} diff --git a/rust/vendor/asn1-rs/tests/x509.rs b/rust/vendor/asn1-rs/tests/x509.rs new file mode 100644 index 0000000..31fa595 --- /dev/null +++ b/rust/vendor/asn1-rs/tests/x509.rs @@ -0,0 +1,158 @@ +//! Test implementation for X.509 +//! +//! This is mostly used to verify that required types and functions are implemented, +//! and that provided API is convenient. + +use asn1_rs::{ + nom, Any, CheckDerConstraints, Choice, Error, FromBer, FromDer, Oid, ParseResult, Sequence, + SetOf, Tag, Tagged, +}; +use hex_literal::hex; +use nom::sequence::pair; +use std::convert::{TryFrom, TryInto}; + +const DN: &[u8] = &hex!( + " +30 45 31 0b 30 09 06 03 55 04 06 13 02 46 52 +31 13 30 11 06 03 55 04 08 0c 0a 53 6f 6d 65 +2d 53 74 61 74 65 31 21 30 1f 06 03 55 04 0a +0c 18 49 6e 74 65 72 6e 65 74 20 57 69 64 67 +69 74 73 20 50 74 79 20 4c 74 64 +" +); + +// Name ::= CHOICE { -- only one possibility for now -- +// rdnSequence RDNSequence } + +// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName +#[derive(Debug)] +pub struct Name<'a> { + pub rdn_sequence: Vec<RelativeDistinguishedName<'a>>, +} + +impl<'a> FromDer<'a> for Name<'a> { + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { + let (rem, rdn_sequence) = <Vec<RelativeDistinguishedName>>::from_der(bytes)?; + let dn = Name { rdn_sequence }; + Ok((rem, dn)) + } +} + +// RelativeDistinguishedName ::= +// SET SIZE (1..MAX) OF AttributeTypeAndValue +#[derive(Debug)] +pub struct RelativeDistinguishedName<'a> { + pub v: Vec<AttributeTypeAndValue<'a>>, +} + +impl<'a> FromDer<'a> for RelativeDistinguishedName<'a> { + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { + let (rem, set) = SetOf::<AttributeTypeAndValue>::from_der(bytes)?; + let v: Vec<_> = set.into(); + if v.is_empty() { + return Err(nom::Err::Failure(Error::InvalidLength)); + } + Ok((rem, RelativeDistinguishedName { v })) + } +} + +// AttributeTypeAndValue ::= SEQUENCE { +// type AttributeType, +// value AttributeValue } +#[derive(Debug)] +pub struct AttributeTypeAndValue<'a> { + pub oid: Oid<'a>, + pub value: AttributeValue<'a>, +} + +impl<'a> FromBer<'a> for AttributeTypeAndValue<'a> { + fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> { + let (rem, seq) = Sequence::from_der(bytes)?; + let (_, (oid, value)) = + seq.parse_into(|i| pair(Oid::from_der, AttributeValue::from_der)(i))?; + let attr = AttributeTypeAndValue { oid, value }; + Ok((rem, attr)) + } +} + +impl<'a> FromDer<'a> for AttributeTypeAndValue<'a> { + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { + let (rem, seq) = Sequence::from_der(bytes)?; + let (_, (oid, value)) = + seq.parse_into(|i| pair(Oid::from_der, AttributeValue::from_der)(i))?; + let attr = AttributeTypeAndValue { oid, value }; + Ok((rem, attr)) + } +} + +impl<'a> CheckDerConstraints for AttributeTypeAndValue<'a> { + fn check_constraints(any: &Any) -> asn1_rs::Result<()> { + any.tag().assert_eq(Sequence::TAG)?; + Ok(()) + } +} + +// AttributeType ::= OBJECT IDENTIFIER + +// AttributeValue ::= ANY -- DEFINED BY AttributeType +#[derive(Debug)] +pub enum AttributeValue<'a> { + DirectoryString(DirectoryString), + Other(Any<'a>), +} + +impl<'a> FromDer<'a> for AttributeValue<'a> { + fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { + let (rem, any) = Any::from_der(bytes)?; + let ds = if DirectoryString::can_decode(any.tag()) { + AttributeValue::DirectoryString(any.try_into()?) + } else { + AttributeValue::Other(any) + }; + Ok((rem, ds)) + } +} + +// DirectoryString ::= CHOICE { +// teletexString TeletexString (SIZE (1..MAX)), +// printableString PrintableString (SIZE (1..MAX)), +// universalString UniversalString (SIZE (1..MAX)), +// utf8String UTF8String (SIZE (1..MAX)), +// bmpString BMPString (SIZE (1..MAX)) } +#[derive(Debug)] +pub enum DirectoryString { + Printable(String), + Utf8(String), +} + +impl Choice for DirectoryString { + fn can_decode(tag: Tag) -> bool { + matches!(tag, Tag::PrintableString | Tag::Utf8String) + } +} + +impl<'a> TryFrom<Any<'a>> for DirectoryString { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self, Self::Error> { + match any.tag() { + Tag::PrintableString => { + let s = any.printablestring()?; + Ok(DirectoryString::Printable(s.string())) + } + Tag::Utf8String => { + let s = any.string()?; + Ok(DirectoryString::Utf8(s)) + } + _ => Err(Error::InvalidTag), + } + } +} + +#[test] +fn x509_decode_dn() { + let (rem, dn) = Name::from_der(DN).expect("parsing failed"); + assert!(rem.is_empty()); + // dbg!(&dn); + assert_eq!(dn.rdn_sequence.len(), 3); +} |