From 4a8e014cdc60a14d02d2473e733927c12b7a0458 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 29 Jun 2023 23:17:34 +0300 Subject: [PATCH 01/74] feat(asn1-page): init page; --- src/asn1.rs | 8 ++++++++ src/header.rs | 1 + src/main.rs | 5 +++++ 3 files changed, 14 insertions(+) create mode 100644 src/asn1.rs diff --git a/src/asn1.rs b/src/asn1.rs new file mode 100644 index 0000000..358cc56 --- /dev/null +++ b/src/asn1.rs @@ -0,0 +1,8 @@ +use yew::{function_component, html, Html}; + +#[function_component(Asn1ParserPage)] +pub fn asn1_parser_page() -> Html { + html! { +
{"asn1 parser"}
+ } +} diff --git a/src/header.rs b/src/header.rs index c46153d..eb6b7a3 100644 --- a/src/header.rs +++ b/src/header.rs @@ -9,6 +9,7 @@ pub fn header() -> Html {
to={Route::CryptoHelper}>{"Crypto helper"}> to={Route::Jwt}>{"JWT"}> + to={Route::Asn1Parser}>{"Asn1 parser"}> to={Route::About}>{"About"}>
} diff --git a/src/main.rs b/src/main.rs index 4fc057f..e628694 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod about; +mod asn1; mod common; mod crypto_helper; mod footer; @@ -9,6 +10,7 @@ mod url_query_params; mod utils; use about::About; +use asn1::Asn1ParserPage; use crypto_helper::CryptoHelper; use footer::footer; use header::Header; @@ -22,6 +24,8 @@ use yew_router::{BrowserRouter, Routable, Switch}; enum Route { #[at("/")] Home, + #[at("/asn1")] + Asn1Parser, #[at("/crypto-helper")] CryptoHelper, #[at("/jwt")] @@ -36,6 +40,7 @@ enum Route { fn switch(routes: Route) -> Html { match routes { Route::Home => html! { }, + Route::Asn1Parser => html! { }, Route::CryptoHelper => html! { }, Route::Jwt => html! { }, Route::About => html! { }, From e46cdd86fb34653bb303e20619578073e27184dc Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 29 Jun 2023 23:58:29 +0300 Subject: [PATCH 02/74] feat(crates): asn1-parser: init crate. add it to the workspace; --- Cargo.lock | 4 ++++ Cargo.toml | 5 ++++- crates/asn1-parser/Cargo.toml | 6 ++++++ crates/asn1-parser/README.md | 10 ++++++++++ crates/asn1-parser/src/lib.rs | 14 ++++++++++++++ 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 crates/asn1-parser/Cargo.toml create mode 100644 crates/asn1-parser/README.md create mode 100644 crates/asn1-parser/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 378caa2..4313f76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,10 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" +[[package]] +name = "asn1-parser" +version = "0.1.0" + [[package]] name = "autocfg" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 08cc952..fe9e1e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,14 @@ name = "web-app" version = "0.10.0" description = "The crypto-helper is an online app that helps to work with the diferent crypto algorithms." edition = "2021" -authors = ["Pavlo Myroniuk "] +authors = ["Pavlo Myroniuk "] readme = "README.md" license-file = "LICENSE" repository = "https://github.com/TheBestTvarynka/crypto-helper" +[workspace] +members = ["crates/asn1-parser"] + [dependencies] yew = { version = "0.20", features = ["csr"] } yew-router = "0.17.0" diff --git a/crates/asn1-parser/Cargo.toml b/crates/asn1-parser/Cargo.toml new file mode 100644 index 0000000..7b6fb69 --- /dev/null +++ b/crates/asn1-parser/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "asn1-parser" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/crates/asn1-parser/README.md b/crates/asn1-parser/README.md new file mode 100644 index 0000000..db1a484 --- /dev/null +++ b/crates/asn1-parser/README.md @@ -0,0 +1,10 @@ + +# `asn1` parser + +> **Yet another asn1 parser? https://users.rust-lang.org/t/comparison-of-way-too-many-rust-asn-1-der-libraries** + +Yes! + +> **Why?** + +This `asn1` parser is aimed to parse input bytes and return an AST as the result. It's not considered an ultimate `asn1` parsing library. You can use it, for example, for asn1 structures visualization (like I do it). diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/crates/asn1-parser/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 283e7163f4c0dc94b27f000ad1f9af19f4b1f596 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 2 Jul 2023 10:31:21 +0300 Subject: [PATCH 03/74] feat(crates): asn1-parser: update README.md; --- crates/asn1-parser/README.md | 7 +++++++ crates/asn1-parser/src/lib.rs | 14 ++------------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/asn1-parser/README.md b/crates/asn1-parser/README.md index db1a484..be9954c 100644 --- a/crates/asn1-parser/README.md +++ b/crates/asn1-parser/README.md @@ -8,3 +8,10 @@ Yes! > **Why?** This `asn1` parser is aimed to parse input bytes and return an AST as the result. It's not considered an ultimate `asn1` parsing library. You can use it, for example, for asn1 structures visualization (like I do it). + + +## Supported `asn1` types + +- [ ] BitString + +## Usage example diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index 7d12d9a..dc62de3 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -1,14 +1,4 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} -#[cfg(test)] -mod tests { - use super::*; +pub const A: u8 = 1; + - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} From 259887b344bf07ba02a409185932a59438922d8b Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 2 Jul 2023 10:48:28 +0300 Subject: [PATCH 04/74] feat(crates): asn1-parser: Improve readme.md: add list of `asn1` types with links to quick reference. --- crates/asn1-parser/README.md | 44 +++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/crates/asn1-parser/README.md b/crates/asn1-parser/README.md index be9954c..cfeb063 100644 --- a/crates/asn1-parser/README.md +++ b/crates/asn1-parser/README.md @@ -1,7 +1,7 @@ # `asn1` parser -> **Yet another asn1 parser? https://users.rust-lang.org/t/comparison-of-way-too-many-rust-asn-1-der-libraries** +> **Yet another `asn1` parser? https://users.rust-lang.org/t/comparison-of-way-too-many-rust-asn-1-der-libraries** Yes! @@ -9,9 +9,47 @@ Yes! This `asn1` parser is aimed to parse input bytes and return an AST as the result. It's not considered an ultimate `asn1` parsing library. You can use it, for example, for asn1 structures visualization (like I do it). - ## Supported `asn1` types -- [ ] BitString +- [ ] [BitString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/bitstring.html) +- [ ] [BmpString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/bmpstring.html) +- [ ] [GraphicString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/graphicstring.html) +- [ ] [IA5String](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/ia5string.html) +- [ ] [GeneralString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/generalstring.html) +- [ ] [PrintableString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/printablestring.html) +- [ ] [OctetString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/octetstring.html) +- [ ] [NumericString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/numericstring.html) +- [ ] [UniversalString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/universalstring.html) +- [ ] [VisibleString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/visiblestring.html) +- [ ] [VideotextString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/videotexstring.html) +- [ ] [Utf8String](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/utf8string.html) + +- [ ] [Date](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/utf8string.html) +- [ ] [DateTime](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/datetime.html) +- [ ] [Duration](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/duration.html) +- [ ] [GeneralizedTime](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/generalizedtime.html) +- [ ] [Time](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/time.html) +- [ ] [UtcTime](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/utctime.html) +- [ ] [TimeOfDay](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/timeofday.html) + +- [ ] [Integer](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/integer.html) +- [ ] [Boolean](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/boolean.html) +- [ ] [Null](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/null.html) +- [ ] [ObjectIdentifier](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/object-identifier.html) +- [ ] [Real](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/real.html) + +- [ ] [Sequence](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequence.html) +- [ ] [SequenceOf](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequenceof.html) +- [ ] [Set](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/set.html) +- [ ] [SetOf](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/setof.html) +- [ ] [Choice](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/choice.html) + +- [ ] [ExplicitTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html) +- [ ] [ImplicitTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html) +- [ ] [ApplicationTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html) ## Usage example + +```rust +todo!() +``` \ No newline at end of file From e1098747c19c134236816fa085143d14ea329053 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 2 Jul 2023 10:50:31 +0300 Subject: [PATCH 05/74] feat(crates): asn1-parser: update README.md; --- crates/asn1-parser/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/asn1-parser/README.md b/crates/asn1-parser/README.md index cfeb063..13e120b 100644 --- a/crates/asn1-parser/README.md +++ b/crates/asn1-parser/README.md @@ -24,6 +24,8 @@ This `asn1` parser is aimed to parse input bytes and return an AST as the result - [ ] [VideotextString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/videotexstring.html) - [ ] [Utf8String](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/utf8string.html) +--- + - [ ] [Date](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/utf8string.html) - [ ] [DateTime](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/datetime.html) - [ ] [Duration](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/duration.html) @@ -32,18 +34,24 @@ This `asn1` parser is aimed to parse input bytes and return an AST as the result - [ ] [UtcTime](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/utctime.html) - [ ] [TimeOfDay](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/timeofday.html) +--- + - [ ] [Integer](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/integer.html) - [ ] [Boolean](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/boolean.html) - [ ] [Null](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/null.html) - [ ] [ObjectIdentifier](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/object-identifier.html) - [ ] [Real](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/real.html) +--- + - [ ] [Sequence](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequence.html) - [ ] [SequenceOf](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequenceof.html) - [ ] [Set](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/set.html) - [ ] [SetOf](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/setof.html) - [ ] [Choice](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/choice.html) +--- + - [ ] [ExplicitTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html) - [ ] [ImplicitTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html) - [ ] [ApplicationTag](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/asn1-tags.html) From 1c46592a5c448170c8c17377fb6ffac28edd25b6 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 2 Jul 2023 22:36:29 +0300 Subject: [PATCH 06/74] feat(crates): asn1-parser: * initialize module structure. * add `Asn1` struct, `Asn1Type` enum. Implement getters for `Asn1` structure. * add structures (empty) for octet/utf8 strings and sequence. --- crates/asn1-parser/src/asn1.rs | 61 +++++++++++++++++++ crates/asn1-parser/src/constructors/mod.rs | 3 + .../asn1-parser/src/constructors/sequence.rs | 6 ++ crates/asn1-parser/src/length.rs | 1 + crates/asn1-parser/src/lib.rs | 10 ++- crates/asn1-parser/src/string/mod.rs | 5 ++ crates/asn1-parser/src/string/octet_string.rs | 4 ++ crates/asn1-parser/src/string/utf8_string.rs | 2 + 8 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 crates/asn1-parser/src/asn1.rs create mode 100644 crates/asn1-parser/src/constructors/mod.rs create mode 100644 crates/asn1-parser/src/constructors/sequence.rs create mode 100644 crates/asn1-parser/src/length.rs create mode 100644 crates/asn1-parser/src/string/mod.rs create mode 100644 crates/asn1-parser/src/string/octet_string.rs create mode 100644 crates/asn1-parser/src/string/utf8_string.rs diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs new file mode 100644 index 0000000..144774c --- /dev/null +++ b/crates/asn1-parser/src/asn1.rs @@ -0,0 +1,61 @@ +use std::ops::Range; + +use crate::{OctetString, Sequence, Utf8String}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Asn1Type<'data> { + Sequence(Sequence<'data>), + OctetString(OctetString<'data>), + Utf8String(Utf8String), +} + +/// [`Asn1`] structure represents generic `asn1` value. +/// It contains raw data and parsed values. +pub struct Asn1<'data> { + /// Raw input bytes + raw_data: &'data [u8], + + /// Position of the tag in the input data + tag: usize, + + /// Range that corresponds to the encoded length bytes + length: Range, + + /// Range that corresponds to the inner raw data + data: Range, + + /// Parsed asn1 data + asn1_type: Asn1Type<'data>, +} + +impl Asn1<'_> { + pub fn tag_position(&self) -> usize { + self.tag + } + + pub fn length_range(&self) -> Range { + self.length.clone() + } + + pub fn data_range(&self) -> Range { + self.data.clone() + } + + pub fn length_bytes(&self) -> &[u8] { + &self.raw_data[self.length.clone()] + } + + pub fn data_bytes(&self) -> &[u8] { + &self.raw_data[self.data.clone()] + } + + pub fn asn1(&self) -> &Asn1Type<'_> { + &self.asn1_type + } +} + +impl<'data> Asn1<'data> { + pub fn parse(_input: &'data [u8]) -> Self { + todo!() + } +} diff --git a/crates/asn1-parser/src/constructors/mod.rs b/crates/asn1-parser/src/constructors/mod.rs new file mode 100644 index 0000000..6083187 --- /dev/null +++ b/crates/asn1-parser/src/constructors/mod.rs @@ -0,0 +1,3 @@ +mod sequence; + +pub use sequence::Sequence; diff --git a/crates/asn1-parser/src/constructors/sequence.rs b/crates/asn1-parser/src/constructors/sequence.rs new file mode 100644 index 0000000..98b3161 --- /dev/null +++ b/crates/asn1-parser/src/constructors/sequence.rs @@ -0,0 +1,6 @@ +use crate::Asn1Type; + +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct Sequence<'data> { + fields: Vec>, +} diff --git a/crates/asn1-parser/src/length.rs b/crates/asn1-parser/src/length.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/crates/asn1-parser/src/length.rs @@ -0,0 +1 @@ + diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index dc62de3..b32334e 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -1,4 +1,8 @@ +mod asn1; +mod constructors; +mod length; +mod string; -pub const A: u8 = 1; - - +pub use asn1::{Asn1, Asn1Type}; +pub use constructors::*; +pub use string::*; diff --git a/crates/asn1-parser/src/string/mod.rs b/crates/asn1-parser/src/string/mod.rs new file mode 100644 index 0000000..52b1ab2 --- /dev/null +++ b/crates/asn1-parser/src/string/mod.rs @@ -0,0 +1,5 @@ +mod octet_string; +mod utf8_string; + +pub use octet_string::OctetString; +pub use utf8_string::Utf8String; diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs new file mode 100644 index 0000000..795a5b0 --- /dev/null +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -0,0 +1,4 @@ +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct OctetString<'data> { + octets: &'data [u8], +} diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs new file mode 100644 index 0000000..9e4313c --- /dev/null +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -0,0 +1,2 @@ +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct Utf8String {} From 4c68e6e7706e1362930b1fe62aab168450e59b80 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 2 Jul 2023 23:28:35 +0300 Subject: [PATCH 07/74] feat(crates): asn1-parser: * Make crate no-std. * Add `Reader` struct with `new` and `read` methods. * Add simple `Error` struct. It will be extended in the future. * Add `Asn1Decode` and `Asn1Entity` traits. --- crates/asn1-parser/Cargo.toml | 2 -- crates/asn1-parser/README.md | 2 ++ crates/asn1-parser/src/asn1.rs | 2 +- .../asn1-parser/src/constructors/sequence.rs | 10 ++++++- crates/asn1-parser/src/error.rs | 10 +++++++ crates/asn1-parser/src/lib.rs | 27 +++++++++++++++++++ crates/asn1-parser/src/reader.rs | 27 +++++++++++++++++++ crates/asn1-parser/src/tag.rs | 5 ++++ 8 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 crates/asn1-parser/src/error.rs create mode 100644 crates/asn1-parser/src/reader.rs create mode 100644 crates/asn1-parser/src/tag.rs diff --git a/crates/asn1-parser/Cargo.toml b/crates/asn1-parser/Cargo.toml index 7b6fb69..bc24323 100644 --- a/crates/asn1-parser/Cargo.toml +++ b/crates/asn1-parser/Cargo.toml @@ -2,5 +2,3 @@ name = "asn1-parser" version = "0.1.0" edition = "2021" - -[dependencies] diff --git a/crates/asn1-parser/README.md b/crates/asn1-parser/README.md index 13e120b..5652792 100644 --- a/crates/asn1-parser/README.md +++ b/crates/asn1-parser/README.md @@ -1,4 +1,6 @@ +This crate has `no_std` support. + # `asn1` parser > **Yet another `asn1` parser? https://users.rust-lang.org/t/comparison-of-way-too-many-rust-asn-1-der-libraries** diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index 144774c..b179990 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -1,4 +1,4 @@ -use std::ops::Range; +use core::ops::Range; use crate::{OctetString, Sequence, Utf8String}; diff --git a/crates/asn1-parser/src/constructors/sequence.rs b/crates/asn1-parser/src/constructors/sequence.rs index 98b3161..f3e968c 100644 --- a/crates/asn1-parser/src/constructors/sequence.rs +++ b/crates/asn1-parser/src/constructors/sequence.rs @@ -1,6 +1,14 @@ +use alloc::vec::Vec; + use crate::Asn1Type; -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Sequence<'data> { fields: Vec>, } + +impl Default for Sequence<'_> { + fn default() -> Self { + Self { fields: Vec::new() } + } +} diff --git a/crates/asn1-parser/src/error.rs b/crates/asn1-parser/src/error.rs new file mode 100644 index 0000000..70ed133 --- /dev/null +++ b/crates/asn1-parser/src/error.rs @@ -0,0 +1,10 @@ +#[derive(Debug)] +pub struct Error { + message: &'static str, +} + +impl Error { + pub fn from(message: &'static str) -> Self { + Self { message } + } +} diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index b32334e..de07b8a 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -1,8 +1,35 @@ +#![no_std] + +extern crate alloc; + mod asn1; mod constructors; +mod error; mod length; +mod reader; mod string; +mod tag; pub use asn1::{Asn1, Asn1Type}; pub use constructors::*; +pub use error::Error; +use reader::Reader; pub use string::*; +pub use tag::Tag; + +pub type Asn1Result = Result; + +/// General trait for decoding asn1 entities. +pub trait Asn1Decode: Sized { + /// Check if the provided tag belongs to decoding implementation. + fn compare_tags(tag: &Tag) -> bool; + + /// Decodes the asn1 entity using provided Reader. + fn decode(reader: &mut Reader) -> Asn1Result; +} + +/// Every asn1 entity should implement this trait. +pub trait Asn1Entity { + /// Returns asn1 tag of the entity + fn tag(&self) -> &Tag; +} diff --git a/crates/asn1-parser/src/reader.rs b/crates/asn1-parser/src/reader.rs new file mode 100644 index 0000000..e308db7 --- /dev/null +++ b/crates/asn1-parser/src/reader.rs @@ -0,0 +1,27 @@ +use crate::{Asn1Result, Error}; + +pub struct Reader<'data> { + position: usize, + inner: &'data [u8], +} + +impl<'data> Reader<'data> { + pub fn new(data: &'data [u8]) -> Self { + Self { + position: 0, + inner: data, + } + } + + pub fn read(&mut self, len: usize) -> Asn1Result<&'data [u8]> { + if self.position + len < self.inner.len() { + return Err(Error::from("Outside")); + } + + let data = &self.inner[self.position..(self.position + len)]; + + self.position += len; + + Ok(data) + } +} diff --git a/crates/asn1-parser/src/tag.rs b/crates/asn1-parser/src/tag.rs new file mode 100644 index 0000000..59d07d1 --- /dev/null +++ b/crates/asn1-parser/src/tag.rs @@ -0,0 +1,5 @@ +pub struct Tag(u8); + +impl Tag { + // +} From 55c971154b9e35577b1fad1f38a2c877cc44e4da Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 3 Jul 2023 00:17:13 +0300 Subject: [PATCH 08/74] feat(crates): asn1-parser: * Implement length reading. * `macros`: init module. Add check_tag macro. * reader: add more methods: `data_in_range`, `position`, `read_exact`, `read_byte`; * `Asn1` structure: make inner fields crate-public. * `Sequence`: replace custom `Default` impl with derived one. * Improve `Asn1Decode` trait: add `'data` lifetime. * Implement `OctetString` decoding. Add simple test to show how it works. --- crates/asn1-parser/src/asn1.rs | 11 ++-- .../asn1-parser/src/constructors/sequence.rs | 8 +-- crates/asn1-parser/src/length.rs | 29 +++++++++ crates/asn1-parser/src/lib.rs | 10 ++- crates/asn1-parser/src/macros.rs | 9 +++ crates/asn1-parser/src/reader.rs | 36 +++++++++- crates/asn1-parser/src/string/octet_string.rs | 65 +++++++++++++++++++ crates/asn1-parser/src/tag.rs | 3 +- 8 files changed, 155 insertions(+), 16 deletions(-) create mode 100644 crates/asn1-parser/src/macros.rs diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index b179990..ebaaf3f 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -11,21 +11,22 @@ pub enum Asn1Type<'data> { /// [`Asn1`] structure represents generic `asn1` value. /// It contains raw data and parsed values. +#[derive(Debug)] pub struct Asn1<'data> { /// Raw input bytes - raw_data: &'data [u8], + pub(crate) raw_data: &'data [u8], /// Position of the tag in the input data - tag: usize, + pub(crate) tag: usize, /// Range that corresponds to the encoded length bytes - length: Range, + pub(crate) length: Range, /// Range that corresponds to the inner raw data - data: Range, + pub(crate) data: Range, /// Parsed asn1 data - asn1_type: Asn1Type<'data>, + pub(crate) asn1_type: Asn1Type<'data>, } impl Asn1<'_> { diff --git a/crates/asn1-parser/src/constructors/sequence.rs b/crates/asn1-parser/src/constructors/sequence.rs index f3e968c..a8ad45a 100644 --- a/crates/asn1-parser/src/constructors/sequence.rs +++ b/crates/asn1-parser/src/constructors/sequence.rs @@ -2,13 +2,7 @@ use alloc::vec::Vec; use crate::Asn1Type; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Sequence<'data> { fields: Vec>, } - -impl Default for Sequence<'_> { - fn default() -> Self { - Self { fields: Vec::new() } - } -} diff --git a/crates/asn1-parser/src/length.rs b/crates/asn1-parser/src/length.rs index 8b13789..a338b6a 100644 --- a/crates/asn1-parser/src/length.rs +++ b/crates/asn1-parser/src/length.rs @@ -1 +1,30 @@ +use core::mem::size_of; +use core::ops::Range; +use crate::reader::Reader; +use crate::{Asn1Result, Error}; + +const USIZE_LEN: usize = size_of::(); + +pub fn read_len(reader: &mut Reader) -> Asn1Result<(usize, Range)> { + let before = reader.position(); + + let length = match reader.read_byte()? { + n @ 128..=255 => { + let len = n as usize & 127; + if len > USIZE_LEN { + return Err(Error::from("Invalid length bytes")); + } + + let mut num = [0; USIZE_LEN]; + reader.read_exact(&mut num[USIZE_LEN - len..])?; + + usize::from_be_bytes(num) + } + n => n as usize, + }; + + let after = reader.position(); + + Ok((length, before..after)) +} diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index de07b8a..279b8e5 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -2,6 +2,9 @@ extern crate alloc; +#[macro_use] +mod macros; + mod asn1; mod constructors; mod error; @@ -20,12 +23,15 @@ pub use tag::Tag; pub type Asn1Result = Result; /// General trait for decoding asn1 entities. -pub trait Asn1Decode: Sized { +pub trait Asn1Decode<'data>: Sized { /// Check if the provided tag belongs to decoding implementation. fn compare_tags(tag: &Tag) -> bool; /// Decodes the asn1 entity using provided Reader. - fn decode(reader: &mut Reader) -> Asn1Result; + fn decode(reader: &mut Reader<'data>) -> Asn1Result; + + /// Decodes the asn1 entity using provided Reader. + fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result>; } /// Every asn1 entity should implement this trait. diff --git a/crates/asn1-parser/src/macros.rs b/crates/asn1-parser/src/macros.rs new file mode 100644 index 0000000..ccf23ca --- /dev/null +++ b/crates/asn1-parser/src/macros.rs @@ -0,0 +1,9 @@ +macro_rules! check_tag { + (in: $reader:ident) => { + let tag = Tag($reader.read_byte()?); + + if Self::TAG != tag { + return Err(crate::Error::from("Invalid OctetString tag")); + } + }; +} diff --git a/crates/asn1-parser/src/reader.rs b/crates/asn1-parser/src/reader.rs index e308db7..e52838a 100644 --- a/crates/asn1-parser/src/reader.rs +++ b/crates/asn1-parser/src/reader.rs @@ -1,3 +1,5 @@ +use core::ops::Range; + use crate::{Asn1Result, Error}; pub struct Reader<'data> { @@ -13,8 +15,20 @@ impl<'data> Reader<'data> { } } + pub fn position(&self) -> usize { + self.position + } + + pub fn data_in_range(&self, range: Range) -> Asn1Result<&'data [u8]> { + if range.end > self.inner.len() { + return Err(Error::from("Invalid range")); + } + + Ok(&self.inner[range]) + } + pub fn read(&mut self, len: usize) -> Asn1Result<&'data [u8]> { - if self.position + len < self.inner.len() { + if self.position + len > self.inner.len() { return Err(Error::from("Outside")); } @@ -24,4 +38,24 @@ impl<'data> Reader<'data> { Ok(data) } + + pub fn read_exact(&mut self, buff: &mut [u8]) -> Asn1Result<()> { + buff.copy_from_slice(self.read(buff.len())?); + + Ok(()) + } + + pub fn read_byte(&mut self) -> Asn1Result { + Ok(self.read(1)?[0]) + } +} + +pub fn read_data<'data>(reader: &mut Reader<'data>, len: usize) -> Asn1Result<(&'data [u8], Range)> { + let before = reader.position(); + + let data = reader.read(len)?; + + let after = reader.position(); + + Ok((data, before..after)) } diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index 795a5b0..7225c5e 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -1,4 +1,69 @@ +use crate::length::read_len; +use crate::reader::{read_data, Reader}; +use crate::{Asn1, Asn1Decode, Asn1Result, Asn1Type, Tag}; + #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct OctetString<'data> { octets: &'data [u8], } + +impl OctetString<'_> { + pub const TAG: Tag = Tag(4); +} + +impl<'data> Asn1Decode<'data> for OctetString<'data> { + fn compare_tags(tag: &Tag) -> bool { + OctetString::TAG == *tag + } + + fn decode(reader: &mut Reader<'data>) -> Asn1Result { + check_tag!(in: reader); + + let (len, _len_range) = read_len(reader)?; + + let (data, _data_range) = read_data(reader, len)?; + + Ok(Self { octets: data }) + } + + fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result> { + let tag_position = reader.position(); + check_tag!(in: reader); + + let (len, len_range) = read_len(reader)?; + + let (data, data_range) = read_data(reader, len)?; + + Ok(Asn1 { + raw_data: reader.data_in_range(tag_position..data_range.end)?, + tag: tag_position, + length: len_range, + data: data_range, + asn1_type: Asn1Type::OctetString(Self { octets: data }), + }) + } +} + +#[cfg(test)] +mod tests { + extern crate std; + + use std::println; + + use crate::reader::Reader; + use crate::{Asn1Decode, OctetString}; + + #[test] + fn decoding_example() { + let raw = [4, 8, 0, 17, 34, 51, 68, 85, 102, 119]; + + let octet_string = OctetString::decode_asn1(&mut Reader::new(&raw)).unwrap(); + + println!("{:?}", octet_string); + + assert_eq!(octet_string.tag_position(), 0); + assert_eq!(octet_string.length_bytes(), &[8]); + assert_eq!(octet_string.length_range(), 1..2); + assert_eq!(&raw[octet_string.data_range()], &[0, 17, 34, 51, 68, 85, 102, 119]) + } +} diff --git a/crates/asn1-parser/src/tag.rs b/crates/asn1-parser/src/tag.rs index 59d07d1..d788a1d 100644 --- a/crates/asn1-parser/src/tag.rs +++ b/crates/asn1-parser/src/tag.rs @@ -1,4 +1,5 @@ -pub struct Tag(u8); +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Tag(pub(crate) u8); impl Tag { // From a0b2bfabf7864d276ff0e1c2c507704c0148ae60 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 3 Jul 2023 00:34:23 +0300 Subject: [PATCH 09/74] feat(crates): asn1-parser: implement `Utf8String` decoding. --- crates/asn1-parser/src/asn1.rs | 2 +- crates/asn1-parser/src/error.rs | 8 ++ crates/asn1-parser/src/string/octet_string.rs | 14 ++-- crates/asn1-parser/src/string/utf8_string.rs | 83 ++++++++++++++++++- 4 files changed, 98 insertions(+), 9 deletions(-) diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index ebaaf3f..26e4f9d 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -6,7 +6,7 @@ use crate::{OctetString, Sequence, Utf8String}; pub enum Asn1Type<'data> { Sequence(Sequence<'data>), OctetString(OctetString<'data>), - Utf8String(Utf8String), + Utf8String(Utf8String<'data>), } /// [`Asn1`] structure represents generic `asn1` value. diff --git a/crates/asn1-parser/src/error.rs b/crates/asn1-parser/src/error.rs index 70ed133..3b3f479 100644 --- a/crates/asn1-parser/src/error.rs +++ b/crates/asn1-parser/src/error.rs @@ -1,3 +1,5 @@ +use core::str::Utf8Error; + #[derive(Debug)] pub struct Error { message: &'static str, @@ -8,3 +10,9 @@ impl Error { Self { message } } } + +impl From for Error { + fn from(_value: Utf8Error) -> Self { + Self { message: "utf8error" } + } +} diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index 7225c5e..e67440b 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -1,6 +1,6 @@ use crate::length::read_len; use crate::reader::{read_data, Reader}; -use crate::{Asn1, Asn1Decode, Asn1Result, Asn1Type, Tag}; +use crate::{Asn1, Asn1Decode, Asn1Entity, Asn1Result, Asn1Type, Tag}; #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct OctetString<'data> { @@ -44,12 +44,14 @@ impl<'data> Asn1Decode<'data> for OctetString<'data> { } } +impl Asn1Entity for OctetString<'_> { + fn tag(&self) -> &Tag { + &OctetString::TAG + } +} + #[cfg(test)] mod tests { - extern crate std; - - use std::println; - use crate::reader::Reader; use crate::{Asn1Decode, OctetString}; @@ -59,8 +61,6 @@ mod tests { let octet_string = OctetString::decode_asn1(&mut Reader::new(&raw)).unwrap(); - println!("{:?}", octet_string); - assert_eq!(octet_string.tag_position(), 0); assert_eq!(octet_string.length_bytes(), &[8]); assert_eq!(octet_string.length_range(), 1..2); diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs index 9e4313c..4ef71b0 100644 --- a/crates/asn1-parser/src/string/utf8_string.rs +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -1,2 +1,83 @@ +use core::str::from_utf8; + +use crate::length::read_len; +use crate::reader::{read_data, Reader}; +use crate::{Asn1, Asn1Decode, Asn1Entity, Asn1Result, Asn1Type, Tag}; + #[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct Utf8String {} +pub struct Utf8String<'data> { + string: &'data str, +} + +impl Utf8String<'_> { + pub const TAG: Tag = Tag(12); +} + +impl<'data> Asn1Decode<'data> for Utf8String<'data> { + fn compare_tags(tag: &Tag) -> bool { + Utf8String::TAG == *tag + } + + fn decode(reader: &mut Reader<'data>) -> Asn1Result { + check_tag!(in: reader); + + let (len, _len_range) = read_len(reader)?; + + let (data, _data_range) = read_data(reader, len)?; + + Ok(Self { + string: from_utf8(data)?, + }) + } + + fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result> { + let tag_position = reader.position(); + check_tag!(in: reader); + + let (len, len_range) = read_len(reader)?; + + let (data, data_range) = read_data(reader, len)?; + + Ok(Asn1 { + raw_data: reader.data_in_range(tag_position..data_range.end)?, + tag: tag_position, + length: len_range, + data: data_range, + asn1_type: Asn1Type::Utf8String(Self { + string: from_utf8(data)?, + }), + }) + } +} + +impl Asn1Entity for Utf8String<'_> { + fn tag(&self) -> &Tag { + &Utf8String::TAG + } +} + +#[cfg(test)] +mod tests { + use crate::reader::Reader; + use crate::{Asn1Decode, Asn1Type, Utf8String}; + + #[test] + fn decoding_example() { + let raw = [ + 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, + ]; + + let utf8_string = Utf8String::decode_asn1(&mut Reader::new(&raw)).unwrap(); + + assert_eq!(utf8_string.tag_position(), 0); + assert_eq!(utf8_string.length_bytes(), &[15]); + assert_eq!(utf8_string.length_range(), 1..2); + assert_eq!(&raw[utf8_string.data_range()], b"thebesttvarynka"); + assert_eq!( + utf8_string.asn1(), + &Asn1Type::Utf8String(Utf8String { + string: "thebesttvarynka" + }) + ); + } +} From c62a50f43b95f3216936f3bf3c0d4bfa62d206df Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 5 Jul 2023 18:08:37 +0300 Subject: [PATCH 10/74] feat(crates): as1-parser: * string: add owned version of the octet and utf8 strings; * update Cargo.toml: add author, description, etc. --- crates/asn1-parser/Cargo.toml | 5 ++++ crates/asn1-parser/src/string/mod.rs | 2 +- crates/asn1-parser/src/string/octet_string.rs | 25 ++++++++++++++++--- crates/asn1-parser/src/string/utf8_string.rs | 24 ++++++++++++++---- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/crates/asn1-parser/Cargo.toml b/crates/asn1-parser/Cargo.toml index bc24323..9842dc3 100644 --- a/crates/asn1-parser/Cargo.toml +++ b/crates/asn1-parser/Cargo.toml @@ -1,4 +1,9 @@ [package] name = "asn1-parser" +description = "This `asn1` parser is aimed to parse input bytes and return an AST as the result." version = "0.1.0" edition = "2021" +authors = ["Pavlo Myroniuk "] +readme = "README.md" +license-file = "../../LICENSE" +repository = "https://github.com/TheBestTvarynka/crypto-helper" diff --git a/crates/asn1-parser/src/string/mod.rs b/crates/asn1-parser/src/string/mod.rs index 52b1ab2..b690be4 100644 --- a/crates/asn1-parser/src/string/mod.rs +++ b/crates/asn1-parser/src/string/mod.rs @@ -1,5 +1,5 @@ mod octet_string; mod utf8_string; -pub use octet_string::OctetString; +pub use octet_string::{OctetString, OwnedOctetString}; pub use utf8_string::Utf8String; diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index e67440b..0682a3d 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -1,16 +1,29 @@ +use alloc::borrow::Cow; +use alloc::vec::Vec; + use crate::length::read_len; use crate::reader::{read_data, Reader}; use crate::{Asn1, Asn1Decode, Asn1Entity, Asn1Result, Asn1Type, Tag}; -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct OctetString<'data> { - octets: &'data [u8], + octets: Cow<'data, [u8]>, } +pub type OwnedOctetString = OctetString<'static>; + impl OctetString<'_> { pub const TAG: Tag = Tag(4); } +impl From> for OwnedOctetString { + fn from(data: Vec) -> Self { + Self { + octets: Cow::Owned(data), + } + } +} + impl<'data> Asn1Decode<'data> for OctetString<'data> { fn compare_tags(tag: &Tag) -> bool { OctetString::TAG == *tag @@ -23,7 +36,9 @@ impl<'data> Asn1Decode<'data> for OctetString<'data> { let (data, _data_range) = read_data(reader, len)?; - Ok(Self { octets: data }) + Ok(Self { + octets: Cow::Borrowed(data), + }) } fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result> { @@ -39,7 +54,9 @@ impl<'data> Asn1Decode<'data> for OctetString<'data> { tag: tag_position, length: len_range, data: data_range, - asn1_type: Asn1Type::OctetString(Self { octets: data }), + asn1_type: Asn1Type::OctetString(Self { + octets: Cow::Borrowed(data), + }), }) } } diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs index 4ef71b0..bdb48ff 100644 --- a/crates/asn1-parser/src/string/utf8_string.rs +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -1,18 +1,30 @@ +use alloc::borrow::Cow; +use alloc::string::String; use core::str::from_utf8; use crate::length::read_len; use crate::reader::{read_data, Reader}; use crate::{Asn1, Asn1Decode, Asn1Entity, Asn1Result, Asn1Type, Tag}; -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Utf8String<'data> { - string: &'data str, + string: Cow<'data, str>, } +pub type OwnedUtf8String = Utf8String<'static>; + impl Utf8String<'_> { pub const TAG: Tag = Tag(12); } +impl From for OwnedUtf8String { + fn from(data: String) -> Self { + Self { + string: Cow::Owned(data), + } + } +} + impl<'data> Asn1Decode<'data> for Utf8String<'data> { fn compare_tags(tag: &Tag) -> bool { Utf8String::TAG == *tag @@ -26,7 +38,7 @@ impl<'data> Asn1Decode<'data> for Utf8String<'data> { let (data, _data_range) = read_data(reader, len)?; Ok(Self { - string: from_utf8(data)?, + string: Cow::Borrowed(from_utf8(data)?), }) } @@ -44,7 +56,7 @@ impl<'data> Asn1Decode<'data> for Utf8String<'data> { length: len_range, data: data_range, asn1_type: Asn1Type::Utf8String(Self { - string: from_utf8(data)?, + string: Cow::Borrowed(from_utf8(data)?), }), }) } @@ -58,6 +70,8 @@ impl Asn1Entity for Utf8String<'_> { #[cfg(test)] mod tests { + use alloc::borrow::Cow; + use crate::reader::Reader; use crate::{Asn1Decode, Asn1Type, Utf8String}; @@ -76,7 +90,7 @@ mod tests { assert_eq!( utf8_string.asn1(), &Asn1Type::Utf8String(Utf8String { - string: "thebesttvarynka" + string: Cow::Borrowed("thebesttvarynka"), }) ); } From aeee2a1e3bf2318f453baa814ecf59acfbac2474 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 5 Jul 2023 22:23:42 +0300 Subject: [PATCH 11/74] feat(crates): asn1-parser: add simple writer; --- crates/asn1-parser/src/writer.rs | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 crates/asn1-parser/src/writer.rs diff --git a/crates/asn1-parser/src/writer.rs b/crates/asn1-parser/src/writer.rs new file mode 100644 index 0000000..b9d1686 --- /dev/null +++ b/crates/asn1-parser/src/writer.rs @@ -0,0 +1,39 @@ +use crate::{Asn1Result, Error}; + +#[derive(Debug)] +pub struct Writer<'data> { + position: usize, + inner: &'data mut [u8], +} + +impl<'data> Writer<'data> { + pub fn new(data: &'data mut [u8]) -> Self { + Self { + position: 0, + inner: data, + } + } + + pub fn write_byte(&mut self, byte: u8) -> Asn1Result<()> { + if self.position == self.inner.len() { + return Err(Error::from("Buffer is too small")); + } + + self.inner[self.position] = byte; + self.position += 1; + + Ok(()) + } + + pub fn write_slice(&mut self, slice: &[u8]) -> Asn1Result<()> { + let slice_len = slice.len(); + if self.position + slice_len > self.inner.len() { + return Err(Error::from("Buffer is too small")); + } + + self.inner[self.position..self.position + slice_len].copy_from_slice(slice); + self.position += slice_len; + + Ok(()) + } +} From f2b114ecc7e193cab1e2c653a143782345516995 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 5 Jul 2023 22:24:02 +0300 Subject: [PATCH 12/74] feat(crates): asn1-parser: reader: implement peek_byte method; --- crates/asn1-parser/src/reader.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/asn1-parser/src/reader.rs b/crates/asn1-parser/src/reader.rs index e52838a..177f2bf 100644 --- a/crates/asn1-parser/src/reader.rs +++ b/crates/asn1-parser/src/reader.rs @@ -48,6 +48,14 @@ impl<'data> Reader<'data> { pub fn read_byte(&mut self) -> Asn1Result { Ok(self.read(1)?[0]) } + + pub fn peek_byte(&self) -> Asn1Result { + if self.position == self.inner.len() { + return Err(Error::from("End of the buffer")); + } + + Ok(self.inner[0]) + } } pub fn read_data<'data>(reader: &mut Reader<'data>, len: usize) -> Asn1Result<(&'data [u8], Range)> { From a1e2fef16f32c2d855125900bd5c45667221704f Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 5 Jul 2023 22:24:34 +0300 Subject: [PATCH 13/74] feat(crates): asn1-parser: length: implement write_len and len_size functions; --- crates/asn1-parser/src/length.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/asn1-parser/src/length.rs b/crates/asn1-parser/src/length.rs index a338b6a..5049d85 100644 --- a/crates/asn1-parser/src/length.rs +++ b/crates/asn1-parser/src/length.rs @@ -2,10 +2,12 @@ use core::mem::size_of; use core::ops::Range; use crate::reader::Reader; +use crate::writer::Writer; use crate::{Asn1Result, Error}; const USIZE_LEN: usize = size_of::(); +/// Reads length from the reader pub fn read_len(reader: &mut Reader) -> Asn1Result<(usize, Range)> { let before = reader.position(); @@ -28,3 +30,26 @@ pub fn read_len(reader: &mut Reader) -> Asn1Result<(usize, Range)> { Ok((length, before..after)) } + +/// Writes asn1 length into provided writer +pub fn write_len(length: usize, writer: &mut Writer) -> Asn1Result<()> { + if length < 128 { + writer.write_byte(length.try_into()?) + } else { + let count_bytes: u8 = (USIZE_LEN - (length.leading_zeros() / 8) as usize).try_into()?; + writer.write_byte(count_bytes | 0x80)?; + + let mut buf = [0; USIZE_LEN]; + buf.copy_from_slice(&length.to_be_bytes()); + writer.write_slice(&buf[USIZE_LEN - usize::from(count_bytes)..]) + } +} + +/// Returns how many bytes encoded length will take +pub fn len_size(data_len: usize) -> usize { + if data_len < 128 { + 1 + } else { + 1 + USIZE_LEN - (data_len.leading_zeros() / 8) as usize + } +} From 7e4b771b9335539e96a6e715102ecccb4668cd9c Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 5 Jul 2023 22:25:40 +0300 Subject: [PATCH 14/74] feat(crates): asn1-parser: * Add `Asn1Encode` crate. * Add `From` implementation for the `Error`. * Implement `Asn1Decode`, `Asn1Encode`, `Asn1Entity` for the `Asn1Type`. * Implement `Asn1Encode` for `OctetString` and `Utf8String`. --- crates/asn1-parser/src/asn1.rs | 62 ++++++++++++++++++- crates/asn1-parser/src/error.rs | 9 +++ crates/asn1-parser/src/lib.rs | 16 +++++ crates/asn1-parser/src/string/mod.rs | 2 +- crates/asn1-parser/src/string/octet_string.rs | 33 ++++++++-- crates/asn1-parser/src/string/utf8_string.rs | 31 ++++++++-- 6 files changed, 141 insertions(+), 12 deletions(-) diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index 26e4f9d..985bdf3 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -1,14 +1,72 @@ use core::ops::Range; -use crate::{OctetString, Sequence, Utf8String}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Error, OctetString, Tag, Utf8String}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Asn1Type<'data> { - Sequence(Sequence<'data>), + // Sequence(Sequence<'data>), OctetString(OctetString<'data>), Utf8String(Utf8String<'data>), } +impl Asn1Entity for Asn1Type<'_> { + fn tag(&self) -> &crate::Tag { + match self { + // Asn1Type::Sequence(_) => todo!(), + Asn1Type::OctetString(octet) => octet.tag(), + Asn1Type::Utf8String(utf8) => utf8.tag(), + } + } +} + +impl<'data> Asn1Decode<'data> for Asn1Type<'data> { + fn compare_tags(tag: &Tag) -> bool { + OctetString::compare_tags(tag) || Utf8String::compare_tags(tag) + } + + fn decode(reader: &mut Reader<'data>) -> Asn1Result { + let tag = Tag(reader.peek_byte()?); + + if OctetString::compare_tags(&tag) { + Ok(Asn1Type::OctetString(OctetString::decode(reader)?)) + } else if Utf8String::compare_tags(&tag) { + Ok(Asn1Type::Utf8String(Utf8String::decode(reader)?)) + } else { + Err(Error::from("Invalid data")) + } + } + + fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result> { + let tag = Tag(reader.peek_byte()?); + + if OctetString::compare_tags(&tag) { + OctetString::decode_asn1(reader) + } else if Utf8String::compare_tags(&tag) { + Utf8String::decode_asn1(reader) + } else { + Err(Error::from("Invalid data")) + } + } +} + +impl Asn1Encode for Asn1Type<'_> { + fn needed_buf_size(&self) -> usize { + match self { + Asn1Type::OctetString(octet) => octet.needed_buf_size(), + Asn1Type::Utf8String(utf8) => utf8.needed_buf_size(), + } + } + + fn encode(&self, writer: &mut Writer) -> Asn1Result<()> { + match self { + Asn1Type::OctetString(octet) => octet.encode(writer), + Asn1Type::Utf8String(utf8) => utf8.encode(writer), + } + } +} + /// [`Asn1`] structure represents generic `asn1` value. /// It contains raw data and parsed values. #[derive(Debug)] diff --git a/crates/asn1-parser/src/error.rs b/crates/asn1-parser/src/error.rs index 3b3f479..deb05b1 100644 --- a/crates/asn1-parser/src/error.rs +++ b/crates/asn1-parser/src/error.rs @@ -1,3 +1,4 @@ +use core::num::TryFromIntError; use core::str::Utf8Error; #[derive(Debug)] @@ -16,3 +17,11 @@ impl From for Error { Self { message: "utf8error" } } } + +impl From for Error { + fn from(_value: TryFromIntError) -> Self { + Self { + message: "Numbers conversion error", + } + } +} diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index 279b8e5..365f24b 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -12,6 +12,7 @@ mod length; mod reader; mod string; mod tag; +mod writer; pub use asn1::{Asn1, Asn1Type}; pub use constructors::*; @@ -19,6 +20,7 @@ pub use error::Error; use reader::Reader; pub use string::*; pub use tag::Tag; +use writer::Writer; pub type Asn1Result = Result; @@ -34,6 +36,20 @@ pub trait Asn1Decode<'data>: Sized { fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result>; } +/// General trait for encoding asn1 entities +pub trait Asn1Encode { + /// Returns needed buffer size for asn1 entity encoding + fn needed_buf_size(&self) -> usize; + + /// Encodes asn1 entity into provided buffer + fn encode_buff(&self, buf: &mut [u8]) -> Asn1Result<()> { + self.encode(&mut Writer::new(buf)) + } + + //// Encodes asn1 entity into provided writer + fn encode(&self, writer: &mut Writer) -> Asn1Result<()>; +} + /// Every asn1 entity should implement this trait. pub trait Asn1Entity { /// Returns asn1 tag of the entity diff --git a/crates/asn1-parser/src/string/mod.rs b/crates/asn1-parser/src/string/mod.rs index b690be4..374fd8e 100644 --- a/crates/asn1-parser/src/string/mod.rs +++ b/crates/asn1-parser/src/string/mod.rs @@ -2,4 +2,4 @@ mod octet_string; mod utf8_string; pub use octet_string::{OctetString, OwnedOctetString}; -pub use utf8_string::Utf8String; +pub use utf8_string::{OwnedUtf8String, Utf8String}; diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index 0682a3d..4be64ad 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -1,9 +1,10 @@ use alloc::borrow::Cow; use alloc::vec::Vec; -use crate::length::read_len; +use crate::length::{len_size, read_len, write_len}; use crate::reader::{read_data, Reader}; -use crate::{Asn1, Asn1Decode, Asn1Entity, Asn1Result, Asn1Type, Tag}; +use crate::writer::Writer; +use crate::{Asn1, Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Asn1Type, Tag}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct OctetString<'data> { @@ -67,13 +68,27 @@ impl Asn1Entity for OctetString<'_> { } } +impl Asn1Encode for OctetString<'_> { + fn needed_buf_size(&self) -> usize { + let data_len = self.octets.len(); + + 1 /* tag */ + len_size(data_len) + data_len + } + + fn encode(&self, writer: &mut Writer) -> Asn1Result<()> { + writer.write_byte(Self::TAG.0)?; + write_len(self.octets.len(), writer)?; + writer.write_slice(&self.octets) + } +} + #[cfg(test)] mod tests { use crate::reader::Reader; - use crate::{Asn1Decode, OctetString}; + use crate::{Asn1Decode, Asn1Encode, OctetString}; #[test] - fn decoding_example() { + fn example() { let raw = [4, 8, 0, 17, 34, 51, 68, 85, 102, 119]; let octet_string = OctetString::decode_asn1(&mut Reader::new(&raw)).unwrap(); @@ -81,6 +96,14 @@ mod tests { assert_eq!(octet_string.tag_position(), 0); assert_eq!(octet_string.length_bytes(), &[8]); assert_eq!(octet_string.length_range(), 1..2); - assert_eq!(&raw[octet_string.data_range()], &[0, 17, 34, 51, 68, 85, 102, 119]) + assert_eq!(&raw[octet_string.data_range()], &[0, 17, 34, 51, 68, 85, 102, 119]); + + let mut encoded = [0; 10]; + + assert_eq!(octet_string.asn1().needed_buf_size(), 10); + + octet_string.asn1().encode_buff(&mut encoded).unwrap(); + + assert_eq!(encoded, raw); } } diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs index bdb48ff..1545be2 100644 --- a/crates/asn1-parser/src/string/utf8_string.rs +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -2,9 +2,10 @@ use alloc::borrow::Cow; use alloc::string::String; use core::str::from_utf8; -use crate::length::read_len; +use crate::length::{len_size, read_len, write_len}; use crate::reader::{read_data, Reader}; -use crate::{Asn1, Asn1Decode, Asn1Entity, Asn1Result, Asn1Type, Tag}; +use crate::writer::Writer; +use crate::{Asn1, Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Asn1Type, Tag}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Utf8String<'data> { @@ -68,15 +69,29 @@ impl Asn1Entity for Utf8String<'_> { } } +impl Asn1Encode for Utf8String<'_> { + fn needed_buf_size(&self) -> usize { + let data_len = self.string.len(); + + 1 /* tag */ + len_size(data_len) + data_len + } + + fn encode(&self, writer: &mut Writer) -> Asn1Result<()> { + writer.write_byte(Self::TAG.0)?; + write_len(self.string.len(), writer)?; + writer.write_slice(self.string.as_bytes()) + } +} + #[cfg(test)] mod tests { use alloc::borrow::Cow; use crate::reader::Reader; - use crate::{Asn1Decode, Asn1Type, Utf8String}; + use crate::{Asn1Decode, Asn1Encode, Asn1Type, Utf8String}; #[test] - fn decoding_example() { + fn example() { let raw = [ 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, ]; @@ -93,5 +108,13 @@ mod tests { string: Cow::Borrowed("thebesttvarynka"), }) ); + + let mut encoded = [0; 17]; + + assert_eq!(utf8_string.asn1().needed_buf_size(), 17); + + utf8_string.asn1().encode_buff(&mut encoded).unwrap(); + + assert_eq!(encoded, raw); } } From ac7bf7088d2dad6e5e9aa5066fa24c6c414c1bbc Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 5 Jul 2023 23:27:17 +0300 Subject: [PATCH 15/74] feat(crates): prop-strategies: init crate. add bytes, string, any_asn1_type, any_octet_string, any_utf8_string strategies; --- Cargo.lock | 292 ++++++++++++++++++++++++++- Cargo.toml | 5 +- crates/prop-strategies/Cargo.toml | 13 ++ crates/prop-strategies/README.md | 4 + crates/prop-strategies/src/lib.rs | 25 +++ crates/prop-strategies/src/string.rs | 18 ++ 6 files changed, 346 insertions(+), 11 deletions(-) create mode 100644 crates/prop-strategies/Cargo.toml create mode 100644 crates/prop-strategies/README.md create mode 100644 crates/prop-strategies/src/lib.rs create mode 100644 crates/prop-strategies/src/string.rs diff --git a/Cargo.lock b/Cargo.lock index 4313f76..fbd404e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,10 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" [[package]] name = "asn1-parser" version = "0.1.0" +dependencies = [ + "prop-strategies", + "proptest", +] [[package]] name = "autocfg" @@ -81,6 +85,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "block-buffer" version = "0.10.4" @@ -316,6 +341,36 @@ dependencies = [ "zeroize", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "ff" version = "0.12.1" @@ -337,6 +392,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "form_urlencoded" version = "1.1.0" @@ -640,6 +701,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + [[package]] name = "hex" version = "0.4.3" @@ -705,6 +772,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.2", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "itoa" version = "1.0.6" @@ -761,6 +848,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "log" version = "0.4.17" @@ -855,7 +948,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -1242,6 +1335,40 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "prop-strategies" +version = "0.1.0" +dependencies = [ + "asn1-parser", + "proptest", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.26" @@ -1281,6 +1408,30 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "rfc6979" version = "0.3.1" @@ -1339,12 +1490,38 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustix" +version = "0.37.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rustversion" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.13" @@ -1571,6 +1748,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "thiserror" version = "1.0.40" @@ -1618,7 +1809,7 @@ checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "pin-project-lite", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1670,6 +1861,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.8" @@ -1699,6 +1896,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1831,7 +2037,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.1", ] [[package]] @@ -1840,13 +2055,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -1855,42 +2085,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "yew" version = "0.20.0" diff --git a/Cargo.toml b/Cargo.toml index fe9e1e3..c25d76c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,10 @@ license-file = "LICENSE" repository = "https://github.com/TheBestTvarynka/crypto-helper" [workspace] -members = ["crates/asn1-parser"] +members = [ + "crates/asn1-parser", + "crates/prop-strategies" +] [dependencies] yew = { version = "0.20", features = ["csr"] } diff --git a/crates/prop-strategies/Cargo.toml b/crates/prop-strategies/Cargo.toml new file mode 100644 index 0000000..74041ee --- /dev/null +++ b/crates/prop-strategies/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "prop-strategies" +description = "This crate contains prop strategies for the asn1-parser crate." +version = "0.1.0" +edition = "2021" +authors = ["Pavlo Myroniuk "] +readme = "README.md" +license-file = "../../LICENSE" +repository = "https://github.com/TheBestTvarynka/crypto-helper" + +[dependencies] +proptest = "1.2.0" +asn1-parser = { path = "../asn1-parser" } diff --git a/crates/prop-strategies/README.md b/crates/prop-strategies/README.md new file mode 100644 index 0000000..6b02ab1 --- /dev/null +++ b/crates/prop-strategies/README.md @@ -0,0 +1,4 @@ + +# prop-strategies + +This crate contains [`prop`](https://docs.rs/proptest/latest/proptest/) strategies for the [`asn1-parser`](../asn1-parser/). diff --git a/crates/prop-strategies/src/lib.rs b/crates/prop-strategies/src/lib.rs new file mode 100644 index 0000000..bf21ed9 --- /dev/null +++ b/crates/prop-strategies/src/lib.rs @@ -0,0 +1,25 @@ +mod string; + +use asn1_parser::{Asn1Type, OwnedAsn1Type}; +use proptest::collection::vec; +use proptest::prelude::any; +use proptest::prop_oneof; +use proptest::strategy::Strategy; +pub use string::*; + +pub fn bytes(size: usize) -> impl Strategy> { + vec(any::(), 0..size).no_shrink() +} + +pub fn string(len: usize) -> impl Strategy { + vec(any::(), len) + .prop_map(|v| v.iter().collect::()) + .no_shrink() +} + +pub fn any_asn1_type() -> impl Strategy { + prop_oneof![ + any_octet_string().prop_map(Asn1Type::OctetString), + any_utf8_string().prop_map(Asn1Type::Utf8String), + ] +} diff --git a/crates/prop-strategies/src/string.rs b/crates/prop-strategies/src/string.rs new file mode 100644 index 0000000..6c42e2b --- /dev/null +++ b/crates/prop-strategies/src/string.rs @@ -0,0 +1,18 @@ +use asn1_parser::{OwnedOctetString, OwnedUtf8String}; +use proptest::prop_compose; + +use crate::{bytes, string}; + +prop_compose! { + pub fn any_octet_string() + (data in bytes(1024)) -> OwnedOctetString { + OwnedOctetString::from(data) + } +} + +prop_compose! { + pub fn any_utf8_string() + (data in string(1024)) -> OwnedUtf8String { + OwnedUtf8String::from(data) + } +} From ef443f2c108f6a81fd3dab2e98f4d75194d0d3fa Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Wed, 5 Jul 2023 23:27:39 +0300 Subject: [PATCH 16/74] feat(crates): asn1-parser: write integration prop tests (`asn1` and `asn1_type`); --- crates/asn1-parser/Cargo.toml | 4 ++ crates/asn1-parser/src/asn1.rs | 6 +++ crates/asn1-parser/src/error.rs | 6 +++ crates/asn1-parser/src/lib.rs | 12 +++++- .../tests/decode_encode.proptest-regressions | 7 ++++ crates/asn1-parser/tests/decode_encode.rs | 42 +++++++++++++++++++ 6 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 crates/asn1-parser/tests/decode_encode.proptest-regressions create mode 100644 crates/asn1-parser/tests/decode_encode.rs diff --git a/crates/asn1-parser/Cargo.toml b/crates/asn1-parser/Cargo.toml index 9842dc3..6777ba1 100644 --- a/crates/asn1-parser/Cargo.toml +++ b/crates/asn1-parser/Cargo.toml @@ -7,3 +7,7 @@ authors = ["Pavlo Myroniuk "] readme = "README.md" license-file = "../../LICENSE" repository = "https://github.com/TheBestTvarynka/crypto-helper" + +[dev-dependencies] +prop-strategies = { path = "../prop-strategies" } +proptest = "1.2.0" \ No newline at end of file diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index 985bdf3..46c36c5 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -11,6 +11,8 @@ pub enum Asn1Type<'data> { Utf8String(Utf8String<'data>), } +pub type OwnedAsn1Type = Asn1Type<'static>; + impl Asn1Entity for Asn1Type<'_> { fn tag(&self) -> &crate::Tag { match self { @@ -100,6 +102,10 @@ impl Asn1<'_> { self.data.clone() } + pub fn raw_bytes(&self) -> &[u8] { + self.raw_data + } + pub fn length_bytes(&self) -> &[u8] { &self.raw_data[self.length.clone()] } diff --git a/crates/asn1-parser/src/error.rs b/crates/asn1-parser/src/error.rs index deb05b1..5dd3754 100644 --- a/crates/asn1-parser/src/error.rs +++ b/crates/asn1-parser/src/error.rs @@ -6,6 +6,12 @@ pub struct Error { message: &'static str, } +impl Error { + pub fn message(&self) -> &str { + self.message + } +} + impl Error { pub fn from(message: &'static str) -> Self { Self { message } diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index 365f24b..a62f201 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -14,7 +14,7 @@ mod string; mod tag; mod writer; -pub use asn1::{Asn1, Asn1Type}; +pub use asn1::{Asn1, Asn1Type, OwnedAsn1Type}; pub use constructors::*; pub use error::Error; use reader::Reader; @@ -32,8 +32,18 @@ pub trait Asn1Decode<'data>: Sized { /// Decodes the asn1 entity using provided Reader. fn decode(reader: &mut Reader<'data>) -> Asn1Result; + /// Decodes the asn1 entity using provided buffer. + fn decode_buff(buff: &'data [u8]) -> Asn1Result { + Self::decode(&mut Reader::new(buff)) + } + /// Decodes the asn1 entity using provided Reader. fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result>; + + /// Decodes the asn1 entity using provided Reader. + fn decode_asn1_buff(buff: &'data [u8]) -> Asn1Result> { + Self::decode_asn1(&mut Reader::new(buff)) + } } /// General trait for encoding asn1 entities diff --git a/crates/asn1-parser/tests/decode_encode.proptest-regressions b/crates/asn1-parser/tests/decode_encode.proptest-regressions new file mode 100644 index 0000000..bc0e460 --- /dev/null +++ b/crates/asn1-parser/tests/decode_encode.proptest-regressions @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 70dd598aa0f615c38319841af46e112c297e5b2f090a3c6ad1daf04919689eed # shrinks to asn1 = OctetString(OctetString { octets: [212, 198, 222, 108, 83, 148, 187, 119, 80, 38, 119, 95, 181, 157, 122, 47, 14, 213, 152, 173, 113, 1, 189, 238, 244, 93, 44, 50, 132, 216, 68, 178, 12, 164, 64, 183, 159, 84, 237, 18, 124, 54, 254, 253, 240, 19, 227, 89, 132, 74, 216, 107, 16, 199, 30, 94, 83, 209, 122, 177, 100, 200, 212, 11, 242, 237, 201, 132, 83, 0, 9, 97, 87, 85, 82, 235, 205, 93, 217, 17, 99, 93, 159, 231, 130, 0, 76, 217, 247, 255, 201, 202, 150, 79, 120, 32, 50, 185, 38, 253, 0, 183, 233, 217, 74, 167, 69, 209, 15, 103, 102, 254, 205, 41, 46, 124, 2, 21, 197, 10, 126, 167, 174, 98, 107, 76, 50, 213, 197, 185, 100, 250, 234, 190, 20, 145, 0, 116, 80, 83, 118, 216, 248, 47, 251, 124, 169, 16, 116, 248, 117, 17, 95, 170, 69, 163, 203, 164, 196, 99, 0, 213, 2, 22, 103, 253, 85, 142, 144, 72, 216, 240, 57, 16, 78, 172, 142, 211, 100, 9, 190, 173, 234, 125, 89, 14, 202, 54, 201, 233, 88, 144, 209, 147, 188, 237, 106, 9, 21, 1, 250, 198, 133, 208, 148, 131, 66, 191, 56, 63, 70, 176, 91, 14, 25, 81, 29, 219, 183, 3, 162, 43, 241, 25, 45, 171, 212, 4, 217, 249, 69, 184, 242, 154, 238, 161] }) diff --git a/crates/asn1-parser/tests/decode_encode.rs b/crates/asn1-parser/tests/decode_encode.rs new file mode 100644 index 0000000..d7779ed --- /dev/null +++ b/crates/asn1-parser/tests/decode_encode.rs @@ -0,0 +1,42 @@ +use asn1_parser::{Asn1Decode, Asn1Encode, Asn1Entity, Asn1Type}; +use prop_strategies::any_asn1_type; +use proptest::proptest; + +#[test] +fn asn1_type() { + proptest!(|(asn1 in any_asn1_type())| { + let asn1_tag = asn1.tag(); + + let buff_len = asn1.needed_buf_size(); + let mut buff = vec![0; buff_len]; + + asn1.encode_buff(&mut buff).unwrap(); + + let decoded = Asn1Type::decode_buff(&buff).unwrap(); + + assert_eq!(decoded.needed_buf_size(), buff_len); + assert_eq!(decoded, asn1); + assert_eq!(decoded.tag(), asn1_tag); + }) +} + +#[test] +fn asn1() { + proptest!(|(asn1 in any_asn1_type())| { + let asn1_tag = asn1.tag(); + + let buff_len = asn1.needed_buf_size(); + let mut buff = vec![0; buff_len]; + + asn1.encode_buff(&mut buff).unwrap(); + + let decoded = Asn1Type::decode_asn1_buff(&buff).unwrap(); + + assert_eq!(decoded.asn1().needed_buf_size(), buff_len); + assert_eq!(1 + decoded.length_bytes().len() + decoded.data_bytes().len(), buff_len); + assert_eq!(decoded.asn1(), &asn1); + assert_eq!(decoded.asn1().tag(), asn1_tag); + assert_eq!(decoded.tag_position(), 0); + assert_eq!(decoded.raw_bytes(), buff); + }) +} From a0ec26949e133eb6f4deda0593046d05fc82f5c2 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 6 Jul 2023 01:05:26 +0300 Subject: [PATCH 17/74] feat(crates): asn1-parser: implement sequence; --- crates/asn1-parser/README.md | 6 +- crates/asn1-parser/src/asn1.rs | 20 +-- crates/asn1-parser/src/constructors/mod.rs | 2 +- .../asn1-parser/src/constructors/sequence.rs | 125 +++++++++++++++++- crates/asn1-parser/src/reader.rs | 7 +- crates/asn1-parser/src/string/octet_string.rs | 2 +- crates/asn1-parser/src/string/utf8_string.rs | 10 +- crates/asn1-parser/src/tag.rs | 12 ++ 8 files changed, 165 insertions(+), 19 deletions(-) diff --git a/crates/asn1-parser/README.md b/crates/asn1-parser/README.md index 5652792..b8ebd8f 100644 --- a/crates/asn1-parser/README.md +++ b/crates/asn1-parser/README.md @@ -19,12 +19,12 @@ This `asn1` parser is aimed to parse input bytes and return an AST as the result - [ ] [IA5String](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/ia5string.html) - [ ] [GeneralString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/generalstring.html) - [ ] [PrintableString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/printablestring.html) -- [ ] [OctetString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/octetstring.html) +- [X] [OctetString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/octetstring.html) - [ ] [NumericString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/numericstring.html) - [ ] [UniversalString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/universalstring.html) - [ ] [VisibleString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/visiblestring.html) - [ ] [VideotextString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/videotexstring.html) -- [ ] [Utf8String](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/utf8string.html) +- [X] [Utf8String](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/utf8string.html) --- @@ -46,7 +46,7 @@ This `asn1` parser is aimed to parse input bytes and return an AST as the result --- -- [ ] [Sequence](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequence.html) +- [X] [Sequence](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequence.html) - [ ] [SequenceOf](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequenceof.html) - [ ] [Set](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/set.html) - [ ] [SetOf](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/setof.html) diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index 46c36c5..05d0c14 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -2,11 +2,11 @@ use core::ops::Range; use crate::reader::Reader; use crate::writer::Writer; -use crate::{Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Error, OctetString, Tag, Utf8String}; +use crate::{Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Error, OctetString, Sequence, Tag, Utf8String}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Asn1Type<'data> { - // Sequence(Sequence<'data>), + Sequence(Sequence<'data>), OctetString(OctetString<'data>), Utf8String(Utf8String<'data>), } @@ -16,9 +16,9 @@ pub type OwnedAsn1Type = Asn1Type<'static>; impl Asn1Entity for Asn1Type<'_> { fn tag(&self) -> &crate::Tag { match self { - // Asn1Type::Sequence(_) => todo!(), Asn1Type::OctetString(octet) => octet.tag(), Asn1Type::Utf8String(utf8) => utf8.tag(), + Asn1Type::Sequence(sequence) => sequence.tag(), } } } @@ -35,6 +35,8 @@ impl<'data> Asn1Decode<'data> for Asn1Type<'data> { Ok(Asn1Type::OctetString(OctetString::decode(reader)?)) } else if Utf8String::compare_tags(&tag) { Ok(Asn1Type::Utf8String(Utf8String::decode(reader)?)) + } else if Sequence::compare_tags(&tag) { + Ok(Asn1Type::Sequence(Sequence::decode(reader)?)) } else { Err(Error::from("Invalid data")) } @@ -47,6 +49,8 @@ impl<'data> Asn1Decode<'data> for Asn1Type<'data> { OctetString::decode_asn1(reader) } else if Utf8String::compare_tags(&tag) { Utf8String::decode_asn1(reader) + } else if Sequence::compare_tags(&tag) { + Sequence::decode_asn1(reader) } else { Err(Error::from("Invalid data")) } @@ -58,6 +62,7 @@ impl Asn1Encode for Asn1Type<'_> { match self { Asn1Type::OctetString(octet) => octet.needed_buf_size(), Asn1Type::Utf8String(utf8) => utf8.needed_buf_size(), + Asn1Type::Sequence(sequence) => sequence.needed_buf_size(), } } @@ -65,13 +70,14 @@ impl Asn1Encode for Asn1Type<'_> { match self { Asn1Type::OctetString(octet) => octet.encode(writer), Asn1Type::Utf8String(utf8) => utf8.encode(writer), + Asn1Type::Sequence(sequence) => sequence.encode(writer), } } } /// [`Asn1`] structure represents generic `asn1` value. /// It contains raw data and parsed values. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Asn1<'data> { /// Raw input bytes pub(crate) raw_data: &'data [u8], @@ -118,9 +124,3 @@ impl Asn1<'_> { &self.asn1_type } } - -impl<'data> Asn1<'data> { - pub fn parse(_input: &'data [u8]) -> Self { - todo!() - } -} diff --git a/crates/asn1-parser/src/constructors/mod.rs b/crates/asn1-parser/src/constructors/mod.rs index 6083187..4c98b4a 100644 --- a/crates/asn1-parser/src/constructors/mod.rs +++ b/crates/asn1-parser/src/constructors/mod.rs @@ -1,3 +1,3 @@ mod sequence; -pub use sequence::Sequence; +pub use sequence::{OwnedSequence, Sequence}; diff --git a/crates/asn1-parser/src/constructors/sequence.rs b/crates/asn1-parser/src/constructors/sequence.rs index a8ad45a..e52822b 100644 --- a/crates/asn1-parser/src/constructors/sequence.rs +++ b/crates/asn1-parser/src/constructors/sequence.rs @@ -1,8 +1,129 @@ use alloc::vec::Vec; -use crate::Asn1Type; +use crate::length::{len_size, read_len, write_len}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{Asn1, Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Asn1Type, Tag}; #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Sequence<'data> { - fields: Vec>, + fields: Vec>, +} + +pub type OwnedSequence = Sequence<'static>; + +impl Sequence<'_> { + pub const TAG: Tag = Tag(0x30); +} + +impl Asn1Entity for Sequence<'_> { + fn tag(&self) -> &Tag { + &Self::TAG + } +} + +impl Asn1Encode for Sequence<'_> { + fn needed_buf_size(&self) -> usize { + let data_len = self.fields.iter().map(|f| f.asn1().needed_buf_size()).sum(); + + 1 /* tag */ + len_size(data_len) + data_len + } + + fn encode(&self, writer: &mut Writer) -> Asn1Result<()> { + writer.write_byte(Self::TAG.into())?; + + let data_len = self.fields.iter().map(|f| f.asn1().needed_buf_size()).sum(); + write_len(data_len, writer)?; + + self.fields.iter().try_for_each(|f| f.asn1().encode(writer)) + } +} + +impl<'data> Asn1Decode<'data> for Sequence<'data> { + fn compare_tags(tag: &Tag) -> bool { + &Self::TAG == tag + } + + fn decode(reader: &mut Reader<'data>) -> Asn1Result { + check_tag!(in: reader); + + let (len, _len_range) = read_len(reader)?; + + let mut fields = Vec::new(); + + let position = reader.position(); + while reader.position() - position < len { + fields.push(Asn1Type::decode_asn1(reader)?); + } + + Ok(Self { fields }) + } + + fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result> { + let tag_position = reader.position(); + check_tag!(in: reader); + + let (len, len_range) = read_len(reader)?; + let data_range = len_range.end..len_range.end + len; + + let mut fields = Vec::new(); + + let position = reader.position(); + while reader.position() - position < len { + fields.push(Asn1Type::decode_asn1(reader)?); + } + + Ok(Asn1 { + raw_data: reader.data_in_range(tag_position..len_range.end + len)?, + tag: tag_position, + length: len_range, + data: data_range, + asn1_type: Asn1Type::Sequence(Sequence { fields }), + }) + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use crate::{Asn1, Asn1Decode, Asn1Type, OctetString, Sequence, Utf8String}; + + #[test] + fn example() { + let raw = [ + 48, 27, 4, 8, 0, 17, 34, 51, 68, 85, 102, 119, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, + 121, 110, 107, 97, + ]; + + let decoded = Sequence::decode_asn1_buff(&raw).unwrap(); + + assert_eq!( + decoded, + Asn1 { + raw_data: &raw, + tag: 0, + length: 1..2, + data: 2..29, + asn1_type: Asn1Type::Sequence(Sequence { + fields: vec![ + Asn1 { + raw_data: &[4, 8, 0, 17, 34, 51, 68, 85, 102, 119], + tag: 2, + length: 3..4, + data: 4..12, + asn1_type: Asn1Type::OctetString(OctetString::from(vec![0, 17, 34, 51, 68, 85, 102, 119])) + }, + Asn1 { + raw_data: &[12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97], + tag: 12, + length: 13..14, + data: 14..29, + asn1_type: Asn1Type::Utf8String(Utf8String::from("thebesttvarynka")) + }, + ] + }), + } + ); + } } diff --git a/crates/asn1-parser/src/reader.rs b/crates/asn1-parser/src/reader.rs index 177f2bf..c5e3ccd 100644 --- a/crates/asn1-parser/src/reader.rs +++ b/crates/asn1-parser/src/reader.rs @@ -2,6 +2,7 @@ use core::ops::Range; use crate::{Asn1Result, Error}; +#[derive(Debug)] pub struct Reader<'data> { position: usize, inner: &'data [u8], @@ -19,6 +20,10 @@ impl<'data> Reader<'data> { self.position } + pub fn empty(&self) -> bool { + self.position == self.inner.len() + } + pub fn data_in_range(&self, range: Range) -> Asn1Result<&'data [u8]> { if range.end > self.inner.len() { return Err(Error::from("Invalid range")); @@ -54,7 +59,7 @@ impl<'data> Reader<'data> { return Err(Error::from("End of the buffer")); } - Ok(self.inner[0]) + Ok(self.inner[self.position]) } } diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index 4be64ad..8b851dd 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -76,7 +76,7 @@ impl Asn1Encode for OctetString<'_> { } fn encode(&self, writer: &mut Writer) -> Asn1Result<()> { - writer.write_byte(Self::TAG.0)?; + writer.write_byte(Self::TAG.into())?; write_len(self.octets.len(), writer)?; writer.write_slice(&self.octets) } diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs index 1545be2..66139bf 100644 --- a/crates/asn1-parser/src/string/utf8_string.rs +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -26,6 +26,14 @@ impl From for OwnedUtf8String { } } +impl From<&'static str> for OwnedUtf8String { + fn from(data: &'static str) -> Self { + Self { + string: Cow::Borrowed(data), + } + } +} + impl<'data> Asn1Decode<'data> for Utf8String<'data> { fn compare_tags(tag: &Tag) -> bool { Utf8String::TAG == *tag @@ -77,7 +85,7 @@ impl Asn1Encode for Utf8String<'_> { } fn encode(&self, writer: &mut Writer) -> Asn1Result<()> { - writer.write_byte(Self::TAG.0)?; + writer.write_byte(Self::TAG.into())?; write_len(self.string.len(), writer)?; writer.write_slice(self.string.as_bytes()) } diff --git a/crates/asn1-parser/src/tag.rs b/crates/asn1-parser/src/tag.rs index d788a1d..8a6c97e 100644 --- a/crates/asn1-parser/src/tag.rs +++ b/crates/asn1-parser/src/tag.rs @@ -4,3 +4,15 @@ pub struct Tag(pub(crate) u8); impl Tag { // } + +impl From for Tag { + fn from(tag: u8) -> Self { + Self(tag) + } +} + +impl From for u8 { + fn from(tag: Tag) -> Self { + tag.0 + } +} From 056ee44a12c154287fcad646c5d01ad244993910 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 6 Jul 2023 23:25:26 +0300 Subject: [PATCH 18/74] feat(crates): asn1-parser: implement BitString. prop-strategies: add strategy for bitstring. --- crates/asn1-parser/src/asn1.rs | 10 +- .../asn1-parser/src/constructors/sequence.rs | 6 ++ crates/asn1-parser/src/string/bit_string.rs | 99 +++++++++++++++++++ crates/asn1-parser/src/string/mod.rs | 2 + crates/prop-strategies/src/lib.rs | 3 + crates/prop-strategies/src/string.rs | 15 ++- 6 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 crates/asn1-parser/src/string/bit_string.rs diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index 05d0c14..a2529dc 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -2,13 +2,14 @@ use core::ops::Range; use crate::reader::Reader; use crate::writer::Writer; -use crate::{Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Error, OctetString, Sequence, Tag, Utf8String}; +use crate::{Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, BitString, Error, OctetString, Sequence, Tag, Utf8String}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Asn1Type<'data> { Sequence(Sequence<'data>), OctetString(OctetString<'data>), Utf8String(Utf8String<'data>), + BitString(BitString<'data>), } pub type OwnedAsn1Type = Asn1Type<'static>; @@ -19,6 +20,7 @@ impl Asn1Entity for Asn1Type<'_> { Asn1Type::OctetString(octet) => octet.tag(), Asn1Type::Utf8String(utf8) => utf8.tag(), Asn1Type::Sequence(sequence) => sequence.tag(), + Asn1Type::BitString(bit) => bit.tag(), } } } @@ -37,6 +39,8 @@ impl<'data> Asn1Decode<'data> for Asn1Type<'data> { Ok(Asn1Type::Utf8String(Utf8String::decode(reader)?)) } else if Sequence::compare_tags(&tag) { Ok(Asn1Type::Sequence(Sequence::decode(reader)?)) + } else if BitString::compare_tags(&tag) { + Ok(Asn1Type::BitString(BitString::decode(reader)?)) } else { Err(Error::from("Invalid data")) } @@ -51,6 +55,8 @@ impl<'data> Asn1Decode<'data> for Asn1Type<'data> { Utf8String::decode_asn1(reader) } else if Sequence::compare_tags(&tag) { Sequence::decode_asn1(reader) + } else if BitString::compare_tags(&tag) { + BitString::decode_asn1(reader) } else { Err(Error::from("Invalid data")) } @@ -63,6 +69,7 @@ impl Asn1Encode for Asn1Type<'_> { Asn1Type::OctetString(octet) => octet.needed_buf_size(), Asn1Type::Utf8String(utf8) => utf8.needed_buf_size(), Asn1Type::Sequence(sequence) => sequence.needed_buf_size(), + Asn1Type::BitString(bit) => bit.needed_buf_size(), } } @@ -71,6 +78,7 @@ impl Asn1Encode for Asn1Type<'_> { Asn1Type::OctetString(octet) => octet.encode(writer), Asn1Type::Utf8String(utf8) => utf8.encode(writer), Asn1Type::Sequence(sequence) => sequence.encode(writer), + Asn1Type::BitString(bit) => bit.encode(writer), } } } diff --git a/crates/asn1-parser/src/constructors/sequence.rs b/crates/asn1-parser/src/constructors/sequence.rs index e52822b..80bfc0d 100644 --- a/crates/asn1-parser/src/constructors/sequence.rs +++ b/crates/asn1-parser/src/constructors/sequence.rs @@ -16,6 +16,12 @@ impl Sequence<'_> { pub const TAG: Tag = Tag(0x30); } +impl<'data> From>> for Sequence<'data> { + fn from(fields: Vec>) -> Self { + Self { fields } + } +} + impl Asn1Entity for Sequence<'_> { fn tag(&self) -> &Tag { &Self::TAG diff --git a/crates/asn1-parser/src/string/bit_string.rs b/crates/asn1-parser/src/string/bit_string.rs new file mode 100644 index 0000000..07bff5a --- /dev/null +++ b/crates/asn1-parser/src/string/bit_string.rs @@ -0,0 +1,99 @@ +use alloc::borrow::Cow; +use alloc::vec::Vec; + +use crate::length::{len_size, read_len, write_len}; +use crate::reader::{read_data, Reader}; +use crate::writer::Writer; +use crate::{Asn1, Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Asn1Type, Error, Tag}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BitString<'data> { + bits: Cow<'data, [u8]>, +} + +pub type OwnedBitString = BitString<'static>; + +impl BitString<'_> { + pub const TAG: Tag = Tag(3); + + pub fn from_raw_vec(bits_amount: usize, mut bits: Vec) -> Asn1Result { + let all_bits_amount = bits.len() * 8; + + if bits_amount > all_bits_amount { + return Err(Error::from("Too many bits")); + } + + if all_bits_amount - bits_amount >= 8 { + return Err(Error::from("Too many unused bits")); + } + + let unused_bits: u8 = (all_bits_amount - bits_amount).try_into()?; + bits.insert(0, unused_bits); + + Ok(Self { bits: Cow::Owned(bits) }) + } +} + +// we assume here that firs vector byte contains amount of unused bytes +impl From> for BitString<'_> { + fn from(bits: Vec) -> Self { + Self { bits: Cow::Owned(bits) } + } +} + +impl Asn1Entity for BitString<'_> { + fn tag(&self) -> &Tag { + &Self::TAG + } +} + +impl<'data> Asn1Decode<'data> for BitString<'data> { + fn compare_tags(tag: &Tag) -> bool { + Self::TAG == *tag + } + + fn decode(reader: &mut Reader<'data>) -> Asn1Result { + check_tag!(in: reader); + + let (len, _len_range) = read_len(reader)?; + + let (data, _data_range) = read_data(reader, len)?; + + Ok(Self { + bits: Cow::Borrowed(data), + }) + } + + fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result> { + let tag_position = reader.position(); + check_tag!(in: reader); + + let (len, len_range) = read_len(reader)?; + + let (data, data_range) = read_data(reader, len)?; + + Ok(Asn1 { + raw_data: reader.data_in_range(tag_position..data_range.end)?, + tag: tag_position, + length: len_range, + data: data_range, + asn1_type: Asn1Type::BitString(Self { + bits: Cow::Borrowed(data), + }), + }) + } +} + +impl Asn1Encode for BitString<'_> { + fn needed_buf_size(&self) -> usize { + let data_len = self.bits.len(); + + 1 /* tag */ + len_size(data_len) + data_len + } + + fn encode(&self, writer: &mut Writer) -> Asn1Result<()> { + writer.write_byte(Self::TAG.into())?; + write_len(self.bits.len(), writer)?; + writer.write_slice(&self.bits) + } +} diff --git a/crates/asn1-parser/src/string/mod.rs b/crates/asn1-parser/src/string/mod.rs index 374fd8e..c0a3b99 100644 --- a/crates/asn1-parser/src/string/mod.rs +++ b/crates/asn1-parser/src/string/mod.rs @@ -1,5 +1,7 @@ +mod bit_string; mod octet_string; mod utf8_string; +pub use bit_string::{BitString, OwnedBitString}; pub use octet_string::{OctetString, OwnedOctetString}; pub use utf8_string::{OwnedUtf8String, Utf8String}; diff --git a/crates/prop-strategies/src/lib.rs b/crates/prop-strategies/src/lib.rs index bf21ed9..deab3c7 100644 --- a/crates/prop-strategies/src/lib.rs +++ b/crates/prop-strategies/src/lib.rs @@ -1,6 +1,8 @@ +// mod constructors; mod string; use asn1_parser::{Asn1Type, OwnedAsn1Type}; +// pub use constructors::*; use proptest::collection::vec; use proptest::prelude::any; use proptest::prop_oneof; @@ -21,5 +23,6 @@ pub fn any_asn1_type() -> impl Strategy { prop_oneof![ any_octet_string().prop_map(Asn1Type::OctetString), any_utf8_string().prop_map(Asn1Type::Utf8String), + any_bit_string().prop_map(Asn1Type::BitString), ] } diff --git a/crates/prop-strategies/src/string.rs b/crates/prop-strategies/src/string.rs index 6c42e2b..a008acd 100644 --- a/crates/prop-strategies/src/string.rs +++ b/crates/prop-strategies/src/string.rs @@ -1,4 +1,4 @@ -use asn1_parser::{OwnedOctetString, OwnedUtf8String}; +use asn1_parser::{BitString, OwnedBitString, OwnedOctetString, OwnedUtf8String}; use proptest::prop_compose; use crate::{bytes, string}; @@ -16,3 +16,16 @@ prop_compose! { OwnedUtf8String::from(data) } } + +prop_compose! { + pub fn any_bit_string() + ( + data in bytes(1024), + unused_bits in 0..8_usize, + ) -> OwnedBitString { + BitString::from_raw_vec( + if data.is_empty() { 0 } else { data.len() * 8 - unused_bits }, + data, + ).unwrap() + } +} From 183f2d4798b999ee3f3353848a95828e914a6e14 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Thu, 6 Jul 2023 23:49:44 +0300 Subject: [PATCH 19/74] feat(crates): asn1-parser: implement Bool. prop-strategies: add strategy for bool. --- crates/asn1-parser/README.md | 4 +- crates/asn1-parser/src/asn1.rs | 13 ++- .../asn1-parser/src/generic_types/boolean.rs | 91 +++++++++++++++++++ crates/asn1-parser/src/generic_types/mod.rs | 3 + crates/asn1-parser/src/lib.rs | 2 + crates/asn1-parser/src/string/bit_string.rs | 2 +- crates/asn1-parser/src/string/octet_string.rs | 2 +- crates/asn1-parser/src/string/utf8_string.rs | 2 +- crates/prop-strategies/src/generic_types.rs | 7 ++ crates/prop-strategies/src/lib.rs | 5 +- 10 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 crates/asn1-parser/src/generic_types/boolean.rs create mode 100644 crates/asn1-parser/src/generic_types/mod.rs create mode 100644 crates/prop-strategies/src/generic_types.rs diff --git a/crates/asn1-parser/README.md b/crates/asn1-parser/README.md index b8ebd8f..a02381e 100644 --- a/crates/asn1-parser/README.md +++ b/crates/asn1-parser/README.md @@ -13,7 +13,7 @@ This `asn1` parser is aimed to parse input bytes and return an AST as the result ## Supported `asn1` types -- [ ] [BitString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/bitstring.html) +- [X] [BitString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/bitstring.html) - [ ] [BmpString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/bmpstring.html) - [ ] [GraphicString](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/graphicstring.html) - [ ] [IA5String](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/ia5string.html) @@ -39,7 +39,7 @@ This `asn1` parser is aimed to parse input bytes and return an AST as the result --- - [ ] [Integer](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/integer.html) -- [ ] [Boolean](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/boolean.html) +- [X] [Boolean](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/boolean.html) - [ ] [Null](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/null.html) - [ ] [ObjectIdentifier](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/object-identifier.html) - [ ] [Real](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/real.html) diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index a2529dc..63524cf 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -2,7 +2,9 @@ use core::ops::Range; use crate::reader::Reader; use crate::writer::Writer; -use crate::{Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, BitString, Error, OctetString, Sequence, Tag, Utf8String}; +use crate::{ + Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, BitString, Bool, Error, OctetString, Sequence, Tag, Utf8String, +}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Asn1Type<'data> { @@ -10,6 +12,8 @@ pub enum Asn1Type<'data> { OctetString(OctetString<'data>), Utf8String(Utf8String<'data>), BitString(BitString<'data>), + + Bool(Bool), } pub type OwnedAsn1Type = Asn1Type<'static>; @@ -21,6 +25,7 @@ impl Asn1Entity for Asn1Type<'_> { Asn1Type::Utf8String(utf8) => utf8.tag(), Asn1Type::Sequence(sequence) => sequence.tag(), Asn1Type::BitString(bit) => bit.tag(), + Asn1Type::Bool(boolean) => boolean.tag(), } } } @@ -41,6 +46,8 @@ impl<'data> Asn1Decode<'data> for Asn1Type<'data> { Ok(Asn1Type::Sequence(Sequence::decode(reader)?)) } else if BitString::compare_tags(&tag) { Ok(Asn1Type::BitString(BitString::decode(reader)?)) + } else if Bool::compare_tags(&tag) { + Ok(Asn1Type::Bool(Bool::decode(reader)?)) } else { Err(Error::from("Invalid data")) } @@ -57,6 +64,8 @@ impl<'data> Asn1Decode<'data> for Asn1Type<'data> { Sequence::decode_asn1(reader) } else if BitString::compare_tags(&tag) { BitString::decode_asn1(reader) + } else if Bool::compare_tags(&tag) { + Bool::decode_asn1(reader) } else { Err(Error::from("Invalid data")) } @@ -70,6 +79,7 @@ impl Asn1Encode for Asn1Type<'_> { Asn1Type::Utf8String(utf8) => utf8.needed_buf_size(), Asn1Type::Sequence(sequence) => sequence.needed_buf_size(), Asn1Type::BitString(bit) => bit.needed_buf_size(), + Asn1Type::Bool(boolean) => boolean.needed_buf_size(), } } @@ -79,6 +89,7 @@ impl Asn1Encode for Asn1Type<'_> { Asn1Type::Utf8String(utf8) => utf8.encode(writer), Asn1Type::Sequence(sequence) => sequence.encode(writer), Asn1Type::BitString(bit) => bit.encode(writer), + Asn1Type::Bool(boolean) => boolean.encode(writer), } } } diff --git a/crates/asn1-parser/src/generic_types/boolean.rs b/crates/asn1-parser/src/generic_types/boolean.rs new file mode 100644 index 0000000..a3fe5a5 --- /dev/null +++ b/crates/asn1-parser/src/generic_types/boolean.rs @@ -0,0 +1,91 @@ +use crate::length::{read_len, write_len}; +use crate::reader::{read_data, Reader}; +use crate::writer::Writer; +use crate::{Asn1, Asn1Decode, Asn1Entity, Asn1Result, Asn1Type, Error, Tag, Asn1Encode}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Bool { + flag: bool, +} + +impl Bool { + pub const TAG: Tag = Tag(1); +} + +impl From for Bool { + fn from(flag: bool) -> Self { + Self { flag } + } +} + +impl TryFrom for Bool { + type Error = Error; + + fn try_from(flag: u8) -> Result { + match flag { + 0 => Ok(Self { flag: false }), + 0xff => Ok(Self { flag: true }), + _ => Err(Error::from("Invalid bool value")), + } + } +} + +impl Asn1Entity for Bool { + fn tag(&self) -> &Tag { + &Self::TAG + } +} + +impl<'data> Asn1Decode<'data> for Bool { + fn compare_tags(tag: &Tag) -> bool { + Self::TAG == *tag + } + + fn decode(reader: &mut Reader<'_>) -> Asn1Result { + check_tag!(in: reader); + + let (len, _len_range) = read_len(reader)?; + + if len != 1 { + return Err(Error::from("Bool length must be equal to 1")); + } + + Ok(reader.read_byte()?.try_into()?) + } + + fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result> { + let tag_position = reader.position(); + check_tag!(in: reader); + + let (len, len_range) = read_len(reader)?; + + if len != 1 { + return Err(Error::from("Bool length must be equal to 1")); + } + + let (data, data_range) = read_data(reader, len)?; + + Ok(Asn1 { + raw_data: reader.data_in_range(tag_position..data_range.end)?, + tag: tag_position, + length: len_range, + data: data_range, + asn1_type: Asn1Type::Bool(data[0].try_into()?), + }) + } +} + +impl Asn1Encode for Bool { + fn needed_buf_size(&self) -> usize { + 1 /* tag */ + 1 /* len */ + 1 /* bool value */ + } + + fn encode(&self, writer: &mut Writer) -> Asn1Result<()> { + writer.write_byte(Self::TAG.into())?; + write_len(1, writer)?; + writer.write_byte(match self.flag { + true => 0xff, + false => 0 + }) + } +} diff --git a/crates/asn1-parser/src/generic_types/mod.rs b/crates/asn1-parser/src/generic_types/mod.rs new file mode 100644 index 0000000..fbbeadb --- /dev/null +++ b/crates/asn1-parser/src/generic_types/mod.rs @@ -0,0 +1,3 @@ +mod boolean; + +pub use boolean::Bool; diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index a62f201..7dc4121 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -8,6 +8,7 @@ mod macros; mod asn1; mod constructors; mod error; +mod generic_types; mod length; mod reader; mod string; @@ -17,6 +18,7 @@ mod writer; pub use asn1::{Asn1, Asn1Type, OwnedAsn1Type}; pub use constructors::*; pub use error::Error; +pub use generic_types::*; use reader::Reader; pub use string::*; pub use tag::Tag; diff --git a/crates/asn1-parser/src/string/bit_string.rs b/crates/asn1-parser/src/string/bit_string.rs index 07bff5a..5749a36 100644 --- a/crates/asn1-parser/src/string/bit_string.rs +++ b/crates/asn1-parser/src/string/bit_string.rs @@ -57,7 +57,7 @@ impl<'data> Asn1Decode<'data> for BitString<'data> { let (len, _len_range) = read_len(reader)?; - let (data, _data_range) = read_data(reader, len)?; + let data = reader.read(len)?; Ok(Self { bits: Cow::Borrowed(data), diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index 8b851dd..cbbfdbb 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -35,7 +35,7 @@ impl<'data> Asn1Decode<'data> for OctetString<'data> { let (len, _len_range) = read_len(reader)?; - let (data, _data_range) = read_data(reader, len)?; + let data = reader.read(len)?; Ok(Self { octets: Cow::Borrowed(data), diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs index 66139bf..aa661e5 100644 --- a/crates/asn1-parser/src/string/utf8_string.rs +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -44,7 +44,7 @@ impl<'data> Asn1Decode<'data> for Utf8String<'data> { let (len, _len_range) = read_len(reader)?; - let (data, _data_range) = read_data(reader, len)?; + let data = reader.read(len)?; Ok(Self { string: Cow::Borrowed(from_utf8(data)?), diff --git a/crates/prop-strategies/src/generic_types.rs b/crates/prop-strategies/src/generic_types.rs new file mode 100644 index 0000000..fa4837a --- /dev/null +++ b/crates/prop-strategies/src/generic_types.rs @@ -0,0 +1,7 @@ +use asn1_parser::Bool; +use proptest::strategy::Strategy; +use proptest::prelude::any; + +pub fn any_bool() -> impl Strategy { + any::().prop_map(Bool::from) +} \ No newline at end of file diff --git a/crates/prop-strategies/src/lib.rs b/crates/prop-strategies/src/lib.rs index deab3c7..9429008 100644 --- a/crates/prop-strategies/src/lib.rs +++ b/crates/prop-strategies/src/lib.rs @@ -1,8 +1,10 @@ // mod constructors; +mod generic_types; mod string; use asn1_parser::{Asn1Type, OwnedAsn1Type}; // pub use constructors::*; +pub use generic_types::*; use proptest::collection::vec; use proptest::prelude::any; use proptest::prop_oneof; @@ -24,5 +26,6 @@ pub fn any_asn1_type() -> impl Strategy { any_octet_string().prop_map(Asn1Type::OctetString), any_utf8_string().prop_map(Asn1Type::Utf8String), any_bit_string().prop_map(Asn1Type::BitString), - ] + any_bool().prop_map(Asn1Type::Bool), + ].no_shrink() } From a1bc8df394fcfc6c9f8139d0ba649df2737edcf5 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sat, 8 Jul 2023 14:22:04 +0300 Subject: [PATCH 20/74] feat(crates): asn1-parser: implement ExplicitTag --- crates/asn1-parser/src/asn1.rs | 21 +++- .../asn1-parser/src/constructors/sequence.rs | 26 ++-- .../asn1-parser/src/generic_types/boolean.rs | 16 +-- crates/asn1-parser/src/lib.rs | 8 +- crates/asn1-parser/src/string/bit_string.rs | 15 +-- crates/asn1-parser/src/string/octet_string.rs | 17 +-- crates/asn1-parser/src/string/utf8_string.rs | 17 +-- crates/asn1-parser/src/tags/explicit_tag.rs | 116 ++++++++++++++++++ crates/asn1-parser/src/tags/mod.rs | 3 + 9 files changed, 190 insertions(+), 49 deletions(-) create mode 100644 crates/asn1-parser/src/tags/explicit_tag.rs create mode 100644 crates/asn1-parser/src/tags/mod.rs diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index 63524cf..3494474 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -1,9 +1,11 @@ +use alloc::boxed::Box; use core::ops::Range; use crate::reader::Reader; use crate::writer::Writer; use crate::{ - Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, BitString, Bool, Error, OctetString, Sequence, Tag, Utf8String, + Asn1Decoder, Asn1Encoder, Asn1Entity, Asn1Result, BitString, Bool, Error, ExplicitTag, OctetString, Sequence, Tag, + Utf8String, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -14,23 +16,26 @@ pub enum Asn1Type<'data> { BitString(BitString<'data>), Bool(Bool), + + ExplicitTag(ExplicitTag<'data>), } pub type OwnedAsn1Type = Asn1Type<'static>; impl Asn1Entity for Asn1Type<'_> { - fn tag(&self) -> &crate::Tag { + fn tag(&self) -> Tag { match self { Asn1Type::OctetString(octet) => octet.tag(), Asn1Type::Utf8String(utf8) => utf8.tag(), Asn1Type::Sequence(sequence) => sequence.tag(), Asn1Type::BitString(bit) => bit.tag(), Asn1Type::Bool(boolean) => boolean.tag(), + Asn1Type::ExplicitTag(e) => e.tag(), } } } -impl<'data> Asn1Decode<'data> for Asn1Type<'data> { +impl<'data> Asn1Decoder<'data> for Asn1Type<'data> { fn compare_tags(tag: &Tag) -> bool { OctetString::compare_tags(tag) || Utf8String::compare_tags(tag) } @@ -48,6 +53,8 @@ impl<'data> Asn1Decode<'data> for Asn1Type<'data> { Ok(Asn1Type::BitString(BitString::decode(reader)?)) } else if Bool::compare_tags(&tag) { Ok(Asn1Type::Bool(Bool::decode(reader)?)) + } else if ExplicitTag::compare_tags(&tag) { + Ok(Asn1Type::ExplicitTag(ExplicitTag::decode(reader)?)) } else { Err(Error::from("Invalid data")) } @@ -66,13 +73,15 @@ impl<'data> Asn1Decode<'data> for Asn1Type<'data> { BitString::decode_asn1(reader) } else if Bool::compare_tags(&tag) { Bool::decode_asn1(reader) + } else if ExplicitTag::compare_tags(&tag) { + ExplicitTag::decode_asn1(reader) } else { Err(Error::from("Invalid data")) } } } -impl Asn1Encode for Asn1Type<'_> { +impl Asn1Encoder for Asn1Type<'_> { fn needed_buf_size(&self) -> usize { match self { Asn1Type::OctetString(octet) => octet.needed_buf_size(), @@ -80,6 +89,7 @@ impl Asn1Encode for Asn1Type<'_> { Asn1Type::Sequence(sequence) => sequence.needed_buf_size(), Asn1Type::BitString(bit) => bit.needed_buf_size(), Asn1Type::Bool(boolean) => boolean.needed_buf_size(), + Asn1Type::ExplicitTag(e) => e.needed_buf_size(), } } @@ -90,6 +100,7 @@ impl Asn1Encode for Asn1Type<'_> { Asn1Type::Sequence(sequence) => sequence.encode(writer), Asn1Type::BitString(bit) => bit.encode(writer), Asn1Type::Bool(boolean) => boolean.encode(writer), + Asn1Type::ExplicitTag(e) => e.encode(writer), } } } @@ -111,7 +122,7 @@ pub struct Asn1<'data> { pub(crate) data: Range, /// Parsed asn1 data - pub(crate) asn1_type: Asn1Type<'data>, + pub(crate) asn1_type: Box>, } impl Asn1<'_> { diff --git a/crates/asn1-parser/src/constructors/sequence.rs b/crates/asn1-parser/src/constructors/sequence.rs index 80bfc0d..e613e6e 100644 --- a/crates/asn1-parser/src/constructors/sequence.rs +++ b/crates/asn1-parser/src/constructors/sequence.rs @@ -1,9 +1,10 @@ +use alloc::boxed::Box; use alloc::vec::Vec; use crate::length::{len_size, read_len, write_len}; use crate::reader::Reader; use crate::writer::Writer; -use crate::{Asn1, Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Asn1Type, Tag}; +use crate::{Asn1, Asn1Decoder, Asn1Encoder, Asn1Entity, Asn1Result, Asn1Type, Tag}; #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Sequence<'data> { @@ -23,12 +24,12 @@ impl<'data> From>> for Sequence<'data> { } impl Asn1Entity for Sequence<'_> { - fn tag(&self) -> &Tag { - &Self::TAG + fn tag(&self) -> Tag { + Self::TAG } } -impl Asn1Encode for Sequence<'_> { +impl Asn1Encoder for Sequence<'_> { fn needed_buf_size(&self) -> usize { let data_len = self.fields.iter().map(|f| f.asn1().needed_buf_size()).sum(); @@ -45,7 +46,7 @@ impl Asn1Encode for Sequence<'_> { } } -impl<'data> Asn1Decode<'data> for Sequence<'data> { +impl<'data> Asn1Decoder<'data> for Sequence<'data> { fn compare_tags(tag: &Tag) -> bool { &Self::TAG == tag } @@ -84,16 +85,17 @@ impl<'data> Asn1Decode<'data> for Sequence<'data> { tag: tag_position, length: len_range, data: data_range, - asn1_type: Asn1Type::Sequence(Sequence { fields }), + asn1_type: Box::new(Asn1Type::Sequence(Sequence { fields })), }) } } #[cfg(test)] mod tests { + use alloc::boxed::Box; use alloc::vec; - use crate::{Asn1, Asn1Decode, Asn1Type, OctetString, Sequence, Utf8String}; + use crate::{Asn1, Asn1Decoder, Asn1Type, OctetString, Sequence, Utf8String}; #[test] fn example() { @@ -111,24 +113,26 @@ mod tests { tag: 0, length: 1..2, data: 2..29, - asn1_type: Asn1Type::Sequence(Sequence { + asn1_type: Box::new(Asn1Type::Sequence(Sequence { fields: vec![ Asn1 { raw_data: &[4, 8, 0, 17, 34, 51, 68, 85, 102, 119], tag: 2, length: 3..4, data: 4..12, - asn1_type: Asn1Type::OctetString(OctetString::from(vec![0, 17, 34, 51, 68, 85, 102, 119])) + asn1_type: Box::new(Asn1Type::OctetString(OctetString::from(vec![ + 0, 17, 34, 51, 68, 85, 102, 119 + ]))), }, Asn1 { raw_data: &[12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97], tag: 12, length: 13..14, data: 14..29, - asn1_type: Asn1Type::Utf8String(Utf8String::from("thebesttvarynka")) + asn1_type: Box::new(Asn1Type::Utf8String(Utf8String::from("thebesttvarynka"))) }, ] - }), + })), } ); } diff --git a/crates/asn1-parser/src/generic_types/boolean.rs b/crates/asn1-parser/src/generic_types/boolean.rs index a3fe5a5..cdbadc0 100644 --- a/crates/asn1-parser/src/generic_types/boolean.rs +++ b/crates/asn1-parser/src/generic_types/boolean.rs @@ -1,7 +1,9 @@ +use alloc::boxed::Box; + use crate::length::{read_len, write_len}; use crate::reader::{read_data, Reader}; use crate::writer::Writer; -use crate::{Asn1, Asn1Decode, Asn1Entity, Asn1Result, Asn1Type, Error, Tag, Asn1Encode}; +use crate::{Asn1, Asn1Decoder, Asn1Encoder, Asn1Entity, Asn1Result, Asn1Type, Error, Tag}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Bool { @@ -31,12 +33,12 @@ impl TryFrom for Bool { } impl Asn1Entity for Bool { - fn tag(&self) -> &Tag { - &Self::TAG + fn tag(&self) -> Tag { + Self::TAG } } -impl<'data> Asn1Decode<'data> for Bool { +impl<'data> Asn1Decoder<'data> for Bool { fn compare_tags(tag: &Tag) -> bool { Self::TAG == *tag } @@ -70,12 +72,12 @@ impl<'data> Asn1Decode<'data> for Bool { tag: tag_position, length: len_range, data: data_range, - asn1_type: Asn1Type::Bool(data[0].try_into()?), + asn1_type: Box::new(Asn1Type::Bool(data[0].try_into()?)), }) } } -impl Asn1Encode for Bool { +impl Asn1Encoder for Bool { fn needed_buf_size(&self) -> usize { 1 /* tag */ + 1 /* len */ + 1 /* bool value */ } @@ -85,7 +87,7 @@ impl Asn1Encode for Bool { write_len(1, writer)?; writer.write_byte(match self.flag { true => 0xff, - false => 0 + false => 0, }) } } diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index 7dc4121..ec11569 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -13,6 +13,7 @@ mod length; mod reader; mod string; mod tag; +mod tags; mod writer; pub use asn1::{Asn1, Asn1Type, OwnedAsn1Type}; @@ -22,12 +23,13 @@ pub use generic_types::*; use reader::Reader; pub use string::*; pub use tag::Tag; +pub use tags::*; use writer::Writer; pub type Asn1Result = Result; /// General trait for decoding asn1 entities. -pub trait Asn1Decode<'data>: Sized { +pub trait Asn1Decoder<'data>: Sized { /// Check if the provided tag belongs to decoding implementation. fn compare_tags(tag: &Tag) -> bool; @@ -49,7 +51,7 @@ pub trait Asn1Decode<'data>: Sized { } /// General trait for encoding asn1 entities -pub trait Asn1Encode { +pub trait Asn1Encoder { /// Returns needed buffer size for asn1 entity encoding fn needed_buf_size(&self) -> usize; @@ -65,5 +67,5 @@ pub trait Asn1Encode { /// Every asn1 entity should implement this trait. pub trait Asn1Entity { /// Returns asn1 tag of the entity - fn tag(&self) -> &Tag; + fn tag(&self) -> Tag; } diff --git a/crates/asn1-parser/src/string/bit_string.rs b/crates/asn1-parser/src/string/bit_string.rs index 5749a36..a5fddc2 100644 --- a/crates/asn1-parser/src/string/bit_string.rs +++ b/crates/asn1-parser/src/string/bit_string.rs @@ -1,10 +1,11 @@ use alloc::borrow::Cow; +use alloc::boxed::Box; use alloc::vec::Vec; use crate::length::{len_size, read_len, write_len}; use crate::reader::{read_data, Reader}; use crate::writer::Writer; -use crate::{Asn1, Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Asn1Type, Error, Tag}; +use crate::{Asn1, Asn1Decoder, Asn1Encoder, Asn1Entity, Asn1Result, Asn1Type, Error, Tag}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct BitString<'data> { @@ -42,12 +43,12 @@ impl From> for BitString<'_> { } impl Asn1Entity for BitString<'_> { - fn tag(&self) -> &Tag { - &Self::TAG + fn tag(&self) -> Tag { + Self::TAG } } -impl<'data> Asn1Decode<'data> for BitString<'data> { +impl<'data> Asn1Decoder<'data> for BitString<'data> { fn compare_tags(tag: &Tag) -> bool { Self::TAG == *tag } @@ -77,14 +78,14 @@ impl<'data> Asn1Decode<'data> for BitString<'data> { tag: tag_position, length: len_range, data: data_range, - asn1_type: Asn1Type::BitString(Self { + asn1_type: Box::new(Asn1Type::BitString(Self { bits: Cow::Borrowed(data), - }), + })), }) } } -impl Asn1Encode for BitString<'_> { +impl Asn1Encoder for BitString<'_> { fn needed_buf_size(&self) -> usize { let data_len = self.bits.len(); diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index cbbfdbb..f031eb2 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -1,10 +1,11 @@ use alloc::borrow::Cow; +use alloc::boxed::Box; use alloc::vec::Vec; use crate::length::{len_size, read_len, write_len}; use crate::reader::{read_data, Reader}; use crate::writer::Writer; -use crate::{Asn1, Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Asn1Type, Tag}; +use crate::{Asn1, Asn1Decoder, Asn1Encoder, Asn1Entity, Asn1Result, Asn1Type, Tag}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct OctetString<'data> { @@ -25,7 +26,7 @@ impl From> for OwnedOctetString { } } -impl<'data> Asn1Decode<'data> for OctetString<'data> { +impl<'data> Asn1Decoder<'data> for OctetString<'data> { fn compare_tags(tag: &Tag) -> bool { OctetString::TAG == *tag } @@ -55,20 +56,20 @@ impl<'data> Asn1Decode<'data> for OctetString<'data> { tag: tag_position, length: len_range, data: data_range, - asn1_type: Asn1Type::OctetString(Self { + asn1_type: Box::new(Asn1Type::OctetString(Self { octets: Cow::Borrowed(data), - }), + })), }) } } impl Asn1Entity for OctetString<'_> { - fn tag(&self) -> &Tag { - &OctetString::TAG + fn tag(&self) -> Tag { + OctetString::TAG } } -impl Asn1Encode for OctetString<'_> { +impl Asn1Encoder for OctetString<'_> { fn needed_buf_size(&self) -> usize { let data_len = self.octets.len(); @@ -85,7 +86,7 @@ impl Asn1Encode for OctetString<'_> { #[cfg(test)] mod tests { use crate::reader::Reader; - use crate::{Asn1Decode, Asn1Encode, OctetString}; + use crate::{Asn1Decoder, Asn1Encoder, OctetString}; #[test] fn example() { diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs index aa661e5..f0620a0 100644 --- a/crates/asn1-parser/src/string/utf8_string.rs +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -1,11 +1,12 @@ use alloc::borrow::Cow; +use alloc::boxed::Box; use alloc::string::String; use core::str::from_utf8; use crate::length::{len_size, read_len, write_len}; use crate::reader::{read_data, Reader}; use crate::writer::Writer; -use crate::{Asn1, Asn1Decode, Asn1Encode, Asn1Entity, Asn1Result, Asn1Type, Tag}; +use crate::{Asn1, Asn1Decoder, Asn1Encoder, Asn1Entity, Asn1Result, Asn1Type, Tag}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Utf8String<'data> { @@ -34,7 +35,7 @@ impl From<&'static str> for OwnedUtf8String { } } -impl<'data> Asn1Decode<'data> for Utf8String<'data> { +impl<'data> Asn1Decoder<'data> for Utf8String<'data> { fn compare_tags(tag: &Tag) -> bool { Utf8String::TAG == *tag } @@ -64,20 +65,20 @@ impl<'data> Asn1Decode<'data> for Utf8String<'data> { tag: tag_position, length: len_range, data: data_range, - asn1_type: Asn1Type::Utf8String(Self { + asn1_type: Box::new(Asn1Type::Utf8String(Self { string: Cow::Borrowed(from_utf8(data)?), - }), + })), }) } } impl Asn1Entity for Utf8String<'_> { - fn tag(&self) -> &Tag { - &Utf8String::TAG + fn tag(&self) -> Tag { + Utf8String::TAG } } -impl Asn1Encode for Utf8String<'_> { +impl Asn1Encoder for Utf8String<'_> { fn needed_buf_size(&self) -> usize { let data_len = self.string.len(); @@ -96,7 +97,7 @@ mod tests { use alloc::borrow::Cow; use crate::reader::Reader; - use crate::{Asn1Decode, Asn1Encode, Asn1Type, Utf8String}; + use crate::{Asn1Decoder, Asn1Encoder, Asn1Type, Utf8String}; #[test] fn example() { diff --git a/crates/asn1-parser/src/tags/explicit_tag.rs b/crates/asn1-parser/src/tags/explicit_tag.rs new file mode 100644 index 0000000..de389a8 --- /dev/null +++ b/crates/asn1-parser/src/tags/explicit_tag.rs @@ -0,0 +1,116 @@ +use alloc::boxed::Box; + +use crate::length::{len_size, read_len, write_len}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{Asn1, Asn1Decoder, Asn1Encoder, Asn1Entity, Asn1Result, Asn1Type, Error, Tag}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExplicitTag<'data> { + tag: u8, + inner: Asn1<'data>, +} + +pub type OwnedExplicitTag = ExplicitTag<'static>; + +impl Asn1Entity for ExplicitTag<'_> { + fn tag(&self) -> Tag { + Tag(self.tag) + } +} + +impl<'data> Asn1Decoder<'data> for ExplicitTag<'data> { + fn compare_tags(tag: &Tag) -> bool { + let raw_tag = tag.0; + + raw_tag & 0xC0 == 0x80 && raw_tag & 0x20 == 0x20 + } + + fn decode(reader: &mut Reader<'data>) -> Asn1Result { + let tag = reader.read_byte()?; + + if !Self::compare_tags(&Tag(tag)) { + return Err(Error::from("Invalid explicit tag")); + } + + let (len, _len_range) = read_len(reader)?; + + let inner = Asn1Type::decode_asn1(reader)?; + + if len != inner.raw_bytes().len() { + return Err(Error::from( + "Invalid explicit tag len. Inner entity raw data len is not the same as explicit tag len.", + )); + } + + Ok(Self { tag, inner }) + } + + fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result> { + let tag_position = reader.position(); + let tag = reader.read_byte()?; + + if !Self::compare_tags(&Tag(tag)) { + return Err(Error::from("Invalid explicit tag")); + } + + let (len, len_range) = read_len(reader)?; + + let inner = Asn1Type::decode_asn1(reader)?; + + if len != inner.raw_bytes().len() { + return Err(Error::from( + "Invalid explicit tag len. Inner entity raw data len is not the same as explicit tag len.", + )); + } + + let inner_data_range = inner.data_range(); + + Ok(Asn1 { + raw_data: reader.data_in_range(tag_position..inner_data_range.end)?, + tag: tag_position, + length: len_range, + data: inner.tag_position()..inner_data_range.end, + asn1_type: Box::new(Asn1Type::ExplicitTag(Self { tag, inner })), + }) + } +} + +impl Asn1Encoder for ExplicitTag<'_> { + fn needed_buf_size(&self) -> usize { + let data_len = self.inner.asn1().needed_buf_size(); + + 1 /* tag */ + len_size(data_len) + data_len + } + + fn encode(&self, writer: &mut Writer) -> Asn1Result<()> { + writer.write_byte(self.tag)?; + + let data_len = self.inner.asn1().needed_buf_size(); + write_len(data_len, writer)?; + + self.inner.asn1().encode(writer) + } +} + +#[cfg(test)] +mod tests { + extern crate std; + + use std::dbg; + + use crate::{Asn1Decoder, Asn1Type}; + + #[test] + fn example() { + let raw = [ + 48, 50, 161, 17, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 162, 9, + 12, 7, 113, 107, 97, 116, 105, 111, 110, 163, 18, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, + 221, 80, 86, 181, 110, + ]; + + let asn1 = Asn1Type::decode_asn1_buff(&raw).unwrap(); + + dbg!("{:?}", asn1); + } +} diff --git a/crates/asn1-parser/src/tags/mod.rs b/crates/asn1-parser/src/tags/mod.rs new file mode 100644 index 0000000..0653456 --- /dev/null +++ b/crates/asn1-parser/src/tags/mod.rs @@ -0,0 +1,3 @@ +mod explicit_tag; + +pub use explicit_tag::{ExplicitTag, OwnedExplicitTag}; From f601a9830b72318c24838b50bb845860077aa4b0 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sat, 8 Jul 2023 14:23:30 +0300 Subject: [PATCH 21/74] feat(crates): prop-strategies: add bool generator to asn1 generator; --- crates/asn1-parser/tests/decode_encode.rs | 2 +- crates/prop-strategies/src/generic_types.rs | 4 ++-- crates/prop-strategies/src/lib.rs | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/asn1-parser/tests/decode_encode.rs b/crates/asn1-parser/tests/decode_encode.rs index d7779ed..b411ca1 100644 --- a/crates/asn1-parser/tests/decode_encode.rs +++ b/crates/asn1-parser/tests/decode_encode.rs @@ -1,4 +1,4 @@ -use asn1_parser::{Asn1Decode, Asn1Encode, Asn1Entity, Asn1Type}; +use asn1_parser::{Asn1Decoder, Asn1Encoder, Asn1Entity, Asn1Type}; use prop_strategies::any_asn1_type; use proptest::proptest; diff --git a/crates/prop-strategies/src/generic_types.rs b/crates/prop-strategies/src/generic_types.rs index fa4837a..4eb91c5 100644 --- a/crates/prop-strategies/src/generic_types.rs +++ b/crates/prop-strategies/src/generic_types.rs @@ -1,7 +1,7 @@ use asn1_parser::Bool; -use proptest::strategy::Strategy; use proptest::prelude::any; +use proptest::strategy::Strategy; pub fn any_bool() -> impl Strategy { any::().prop_map(Bool::from) -} \ No newline at end of file +} diff --git a/crates/prop-strategies/src/lib.rs b/crates/prop-strategies/src/lib.rs index 9429008..695a677 100644 --- a/crates/prop-strategies/src/lib.rs +++ b/crates/prop-strategies/src/lib.rs @@ -27,5 +27,6 @@ pub fn any_asn1_type() -> impl Strategy { any_utf8_string().prop_map(Asn1Type::Utf8String), any_bit_string().prop_map(Asn1Type::BitString), any_bool().prop_map(Asn1Type::Bool), - ].no_shrink() + ] + .no_shrink() } From 930b8554e0725c5a673d8467fe3a05ee00e63e65 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sat, 8 Jul 2023 23:44:47 +0300 Subject: [PATCH 22/74] feat(crates): asn1-parser: make raw_data in Asn1 with Cow. Add OwnedAsn1 type; --- crates/asn1-parser/src/asn1.rs | 18 ++++++++++++++++-- .../asn1-parser/src/constructors/sequence.rs | 11 ++++++----- .../asn1-parser/src/generic_types/boolean.rs | 3 ++- crates/asn1-parser/src/lib.rs | 2 +- crates/asn1-parser/src/string/bit_string.rs | 2 +- crates/asn1-parser/src/string/octet_string.rs | 2 +- crates/asn1-parser/src/string/utf8_string.rs | 2 +- crates/asn1-parser/src/tags/explicit_tag.rs | 3 ++- 8 files changed, 30 insertions(+), 13 deletions(-) diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index 3494474..22b0887 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -1,3 +1,4 @@ +use alloc::borrow::Cow; use alloc::boxed::Box; use core::ops::Range; @@ -110,7 +111,7 @@ impl Asn1Encoder for Asn1Type<'_> { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Asn1<'data> { /// Raw input bytes - pub(crate) raw_data: &'data [u8], + pub(crate) raw_data: Cow<'data, [u8]>, /// Position of the tag in the input data pub(crate) tag: usize, @@ -125,6 +126,8 @@ pub struct Asn1<'data> { pub(crate) asn1_type: Box>, } +pub type OwnedAsn1 = Asn1<'static>; + impl Asn1<'_> { pub fn tag_position(&self) -> usize { self.tag @@ -139,7 +142,7 @@ impl Asn1<'_> { } pub fn raw_bytes(&self) -> &[u8] { - self.raw_data + self.raw_data.as_ref() } pub fn length_bytes(&self) -> &[u8] { @@ -154,3 +157,14 @@ impl Asn1<'_> { &self.asn1_type } } + +impl Default for Asn1<'_> { + fn default() -> Self { + // those values are just for testing purpose during development + Asn1Type::decode_asn1_buff(&[ + 48, 50, 161, 17, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 162, 9, + 12, 7, 113, 107, 97, 116, 105, 111, 110, 163, 18, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, + 221, 80, 86, 181, 110, + ]).unwrap() + } +} diff --git a/crates/asn1-parser/src/constructors/sequence.rs b/crates/asn1-parser/src/constructors/sequence.rs index e613e6e..947ec3f 100644 --- a/crates/asn1-parser/src/constructors/sequence.rs +++ b/crates/asn1-parser/src/constructors/sequence.rs @@ -1,3 +1,4 @@ +use alloc::borrow::Cow; use alloc::boxed::Box; use alloc::vec::Vec; @@ -81,7 +82,7 @@ impl<'data> Asn1Decoder<'data> for Sequence<'data> { } Ok(Asn1 { - raw_data: reader.data_in_range(tag_position..len_range.end + len)?, + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..len_range.end + len)?), tag: tag_position, length: len_range, data: data_range, @@ -92,7 +93,7 @@ impl<'data> Asn1Decoder<'data> for Sequence<'data> { #[cfg(test)] mod tests { - use alloc::boxed::Box; + use alloc::{boxed::Box, borrow::Cow}; use alloc::vec; use crate::{Asn1, Asn1Decoder, Asn1Type, OctetString, Sequence, Utf8String}; @@ -109,14 +110,14 @@ mod tests { assert_eq!( decoded, Asn1 { - raw_data: &raw, + raw_data: Cow::Borrowed(&raw), tag: 0, length: 1..2, data: 2..29, asn1_type: Box::new(Asn1Type::Sequence(Sequence { fields: vec![ Asn1 { - raw_data: &[4, 8, 0, 17, 34, 51, 68, 85, 102, 119], + raw_data: Cow::Borrowed(&[4, 8, 0, 17, 34, 51, 68, 85, 102, 119]), tag: 2, length: 3..4, data: 4..12, @@ -125,7 +126,7 @@ mod tests { ]))), }, Asn1 { - raw_data: &[12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97], + raw_data: Cow::Borrowed(&[12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97]), tag: 12, length: 13..14, data: 14..29, diff --git a/crates/asn1-parser/src/generic_types/boolean.rs b/crates/asn1-parser/src/generic_types/boolean.rs index cdbadc0..8feb1ea 100644 --- a/crates/asn1-parser/src/generic_types/boolean.rs +++ b/crates/asn1-parser/src/generic_types/boolean.rs @@ -1,3 +1,4 @@ +use alloc::borrow::Cow; use alloc::boxed::Box; use crate::length::{read_len, write_len}; @@ -68,7 +69,7 @@ impl<'data> Asn1Decoder<'data> for Bool { let (data, data_range) = read_data(reader, len)?; Ok(Asn1 { - raw_data: reader.data_in_range(tag_position..data_range.end)?, + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), tag: tag_position, length: len_range, data: data_range, diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index ec11569..d3a935e 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -16,7 +16,7 @@ mod tag; mod tags; mod writer; -pub use asn1::{Asn1, Asn1Type, OwnedAsn1Type}; +pub use asn1::{Asn1, Asn1Type, OwnedAsn1Type, OwnedAsn1}; pub use constructors::*; pub use error::Error; pub use generic_types::*; diff --git a/crates/asn1-parser/src/string/bit_string.rs b/crates/asn1-parser/src/string/bit_string.rs index a5fddc2..80b6000 100644 --- a/crates/asn1-parser/src/string/bit_string.rs +++ b/crates/asn1-parser/src/string/bit_string.rs @@ -74,7 +74,7 @@ impl<'data> Asn1Decoder<'data> for BitString<'data> { let (data, data_range) = read_data(reader, len)?; Ok(Asn1 { - raw_data: reader.data_in_range(tag_position..data_range.end)?, + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), tag: tag_position, length: len_range, data: data_range, diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index f031eb2..37dbc83 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -52,7 +52,7 @@ impl<'data> Asn1Decoder<'data> for OctetString<'data> { let (data, data_range) = read_data(reader, len)?; Ok(Asn1 { - raw_data: reader.data_in_range(tag_position..data_range.end)?, + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), tag: tag_position, length: len_range, data: data_range, diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs index f0620a0..ad29519 100644 --- a/crates/asn1-parser/src/string/utf8_string.rs +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -61,7 +61,7 @@ impl<'data> Asn1Decoder<'data> for Utf8String<'data> { let (data, data_range) = read_data(reader, len)?; Ok(Asn1 { - raw_data: reader.data_in_range(tag_position..data_range.end)?, + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), tag: tag_position, length: len_range, data: data_range, diff --git a/crates/asn1-parser/src/tags/explicit_tag.rs b/crates/asn1-parser/src/tags/explicit_tag.rs index de389a8..544f628 100644 --- a/crates/asn1-parser/src/tags/explicit_tag.rs +++ b/crates/asn1-parser/src/tags/explicit_tag.rs @@ -1,3 +1,4 @@ +use alloc::borrow::Cow; use alloc::boxed::Box; use crate::length::{len_size, read_len, write_len}; @@ -67,7 +68,7 @@ impl<'data> Asn1Decoder<'data> for ExplicitTag<'data> { let inner_data_range = inner.data_range(); Ok(Asn1 { - raw_data: reader.data_in_range(tag_position..inner_data_range.end)?, + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..inner_data_range.end)?), tag: tag_position, length: len_range, data: inner.tag_position()..inner_data_range.end, From 42f3f5fca0ca6314d39144502f9161a03bda257a Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sat, 8 Jul 2023 23:45:09 +0300 Subject: [PATCH 23/74] feat(asn1-debugger): init page and styles; --- Cargo.lock | 1 + Cargo.toml | 3 +++ index.html | 3 +++ public/styles/asn1/page.scss | 5 ++++ src/asn1.rs | 48 ++++++++++++++++++++++++++++++++++-- src/asn1/asn1_viewer.rs | 17 +++++++++++++ src/common/byte_input.rs | 8 +++--- src/header.rs | 4 +-- 8 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 public/styles/asn1/page.scss create mode 100644 src/asn1/asn1_viewer.rs diff --git a/Cargo.lock b/Cargo.lock index fbd404e..54cb325 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1992,6 +1992,7 @@ dependencies = [ name = "web-app" version = "0.10.0" dependencies = [ + "asn1-parser", "base64 0.13.1", "bcrypt", "flate2", diff --git a/Cargo.toml b/Cargo.toml index c25d76c..016c101 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,3 +50,6 @@ rsa = "0.7.2" yew-hooks = "0.2.0" bcrypt = "0.14.0" flate2 = { version = "1.0.26", features = ["zlib"] } + +# asn1 +asn1-parser = { path = "./crates/asn1-parser" } diff --git a/index.html b/index.html index 3344ddc..25f417e 100644 --- a/index.html +++ b/index.html @@ -24,5 +24,8 @@ + + + \ No newline at end of file diff --git a/public/styles/asn1/page.scss b/public/styles/asn1/page.scss new file mode 100644 index 0000000..39580ba --- /dev/null +++ b/public/styles/asn1/page.scss @@ -0,0 +1,5 @@ + +.asn1-page { + margin-top: 1em; + width: 98%; +} \ No newline at end of file diff --git a/src/asn1.rs b/src/asn1.rs index 358cc56..863a612 100644 --- a/src/asn1.rs +++ b/src/asn1.rs @@ -1,8 +1,52 @@ -use yew::{function_component, html, Html}; +mod asn1_viewer; + +use asn1_parser::Asn1; +use web_sys::KeyboardEvent; +use yew::{function_component, html, use_state, Callback, Html, classes}; + +use crate::{common::{ByteInput, Checkbox}, asn1::asn1_viewer::Asn1Viewer}; + +const TEST_ASN1: &[u8] = &[ + 48, 50, 161, 17, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 162, 9, + 12, 7, 113, 107, 97, 116, 105, 111, 110, 163, 18, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, + 221, 80, 86, 181, 110, +]; #[function_component(Asn1ParserPage)] pub fn asn1_parser_page() -> Html { + let auto_decode = use_state(|| true); + let raw_asn1 = use_state(|| TEST_ASN1.to_vec()); + let parsed_asn1 = use_state(|| Asn1::default()); + + let set_auto_decode = auto_decode.setter(); + let set_checked = Callback::from(move |checked| { + set_auto_decode.set(checked); + }); + + let parse_asn1 = Callback::from(move |_: ()| { + // + }); + let go = parse_asn1.clone(); + let onclick = Callback::from(move |_| { + parse_asn1.emit(()); + }); + + let onkeydown = Callback::from(move |event: KeyboardEvent| { + if event.ctrl_key() && event.code() == "Enter" { + go.emit(()); + } + }); + + let raw_asn1_setter = raw_asn1.setter(); + html! { -
{"asn1 parser"}
+
+ +
+ + +
+ +
} } diff --git a/src/asn1/asn1_viewer.rs b/src/asn1/asn1_viewer.rs new file mode 100644 index 0000000..fd21457 --- /dev/null +++ b/src/asn1/asn1_viewer.rs @@ -0,0 +1,17 @@ +use asn1_parser::OwnedAsn1; +use yew::{Html, function_component, Properties, html}; + +#[derive(PartialEq, Properties, Clone)] +pub struct Asn1ViewerProps { + pub data: Vec, + pub structure: OwnedAsn1, +} + +#[function_component(Asn1Viewer)] +pub fn asn1_viewer(props: &Asn1ViewerProps) -> Html { + html! { +
+ {format!("{:?}", props.structure)} +
+ } +} \ No newline at end of file diff --git a/src/common/byte_input.rs b/src/common/byte_input.rs index 82226ff..7289020 100644 --- a/src/common/byte_input.rs +++ b/src/common/byte_input.rs @@ -7,11 +7,11 @@ use crate::common::{encode_bytes, get_format_button_class, get_set_format_callba #[derive(PartialEq, Properties, Clone)] pub struct ByteInputProps { #[prop_or(BytesFormat::Hex)] - format: BytesFormat, + pub format: BytesFormat, #[prop_or_default] - placeholder: String, - bytes: Vec, - setter: Callback>, + pub placeholder: String, + pub bytes: Vec, + pub setter: Callback>, } #[function_component(ByteInput)] diff --git a/src/header.rs b/src/header.rs index eb6b7a3..acdf686 100644 --- a/src/header.rs +++ b/src/header.rs @@ -8,8 +8,8 @@ pub fn header() -> Html { html! {
to={Route::CryptoHelper}>{"Crypto helper"}> - to={Route::Jwt}>{"JWT"}> - to={Route::Asn1Parser}>{"Asn1 parser"}> + to={Route::Jwt}>{"JWT debugger"}> + to={Route::Asn1Parser}>{"Asn1 debugger"}> to={Route::About}>{"About"}>
} From 0dc6f36650837b20c34fc633daaae6b747172cec Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 9 Jul 2023 22:48:02 +0300 Subject: [PATCH 24/74] feat(crates): asn1-parser: move raw properties to a separate RawAsn1EntityData structure. Refactor code; --- crates/asn1-parser/src/asn1.rs | 42 ++++++++++++----- .../asn1-parser/src/constructors/sequence.rs | 47 ++++++++++++------- .../asn1-parser/src/generic_types/boolean.rs | 11 +++-- crates/asn1-parser/src/lib.rs | 2 +- crates/asn1-parser/src/string/bit_string.rs | 11 +++-- crates/asn1-parser/src/string/octet_string.rs | 22 +++++---- crates/asn1-parser/src/string/utf8_string.rs | 19 ++++---- crates/asn1-parser/src/tags/explicit_tag.rs | 17 ++++--- crates/asn1-parser/tests/decode_encode.rs | 6 +-- 9 files changed, 112 insertions(+), 65 deletions(-) diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index 22b0887..f62ab56 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -106,29 +106,25 @@ impl Asn1Encoder for Asn1Type<'_> { } } -/// [`Asn1`] structure represents generic `asn1` value. -/// It contains raw data and parsed values. +/// Information about raw data of the asn1 entity #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Asn1<'data> { +pub struct RawAsn1EntityData<'data> { /// Raw input bytes - pub(crate) raw_data: Cow<'data, [u8]>, + pub raw_data: Cow<'data, [u8]>, /// Position of the tag in the input data - pub(crate) tag: usize, + pub tag: usize, /// Range that corresponds to the encoded length bytes - pub(crate) length: Range, + pub length: Range, /// Range that corresponds to the inner raw data - pub(crate) data: Range, - - /// Parsed asn1 data - pub(crate) asn1_type: Box>, + pub data: Range, } -pub type OwnedAsn1 = Asn1<'static>; +pub type OwnedRawAsn1EntityData = RawAsn1EntityData<'static>; -impl Asn1<'_> { +impl RawAsn1EntityData<'_> { pub fn tag_position(&self) -> usize { self.tag } @@ -152,6 +148,25 @@ impl Asn1<'_> { pub fn data_bytes(&self) -> &[u8] { &self.raw_data[self.data.clone()] } +} + +/// [`Asn1`] structure represents generic `asn1` value. +/// It contains raw data and parsed values. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Asn1<'data> { + /// Information about raw data of the asn1 + pub(crate) raw_data: RawAsn1EntityData<'data>, + + /// Parsed asn1 data + pub(crate) asn1_type: Box>, +} + +pub type OwnedAsn1 = Asn1<'static>; + +impl Asn1<'_> { + pub fn raw_entity_data(&self) -> &RawAsn1EntityData<'_> { + &self.raw_data + } pub fn asn1(&self) -> &Asn1Type<'_> { &self.asn1_type @@ -165,6 +180,7 @@ impl Default for Asn1<'_> { 48, 50, 161, 17, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 162, 9, 12, 7, 113, 107, 97, 116, 105, 111, 110, 163, 18, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110, - ]).unwrap() + ]) + .unwrap() } } diff --git a/crates/asn1-parser/src/constructors/sequence.rs b/crates/asn1-parser/src/constructors/sequence.rs index 947ec3f..27e25ff 100644 --- a/crates/asn1-parser/src/constructors/sequence.rs +++ b/crates/asn1-parser/src/constructors/sequence.rs @@ -2,6 +2,7 @@ use alloc::borrow::Cow; use alloc::boxed::Box; use alloc::vec::Vec; +use crate::asn1::RawAsn1EntityData; use crate::length::{len_size, read_len, write_len}; use crate::reader::Reader; use crate::writer::Writer; @@ -82,10 +83,12 @@ impl<'data> Asn1Decoder<'data> for Sequence<'data> { } Ok(Asn1 { - raw_data: Cow::Borrowed(reader.data_in_range(tag_position..len_range.end + len)?), - tag: tag_position, - length: len_range, - data: data_range, + raw_data: RawAsn1EntityData { + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..len_range.end + len)?), + tag: tag_position, + length: len_range, + data: data_range, + }, asn1_type: Box::new(Asn1Type::Sequence(Sequence { fields })), }) } @@ -93,9 +96,11 @@ impl<'data> Asn1Decoder<'data> for Sequence<'data> { #[cfg(test)] mod tests { - use alloc::{boxed::Box, borrow::Cow}; + use alloc::borrow::Cow; + use alloc::boxed::Box; use alloc::vec; + use crate::asn1::RawAsn1EntityData; use crate::{Asn1, Asn1Decoder, Asn1Type, OctetString, Sequence, Utf8String}; #[test] @@ -110,26 +115,34 @@ mod tests { assert_eq!( decoded, Asn1 { - raw_data: Cow::Borrowed(&raw), - tag: 0, - length: 1..2, - data: 2..29, + raw_data: RawAsn1EntityData { + raw_data: Cow::Borrowed(&raw), + tag: 0, + length: 1..2, + data: 2..29, + }, asn1_type: Box::new(Asn1Type::Sequence(Sequence { fields: vec![ Asn1 { - raw_data: Cow::Borrowed(&[4, 8, 0, 17, 34, 51, 68, 85, 102, 119]), - tag: 2, - length: 3..4, - data: 4..12, + raw_data: RawAsn1EntityData { + raw_data: Cow::Borrowed(&[4, 8, 0, 17, 34, 51, 68, 85, 102, 119]), + tag: 2, + length: 3..4, + data: 4..12, + }, asn1_type: Box::new(Asn1Type::OctetString(OctetString::from(vec![ 0, 17, 34, 51, 68, 85, 102, 119 ]))), }, Asn1 { - raw_data: Cow::Borrowed(&[12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97]), - tag: 12, - length: 13..14, - data: 14..29, + raw_data: RawAsn1EntityData { + raw_data: Cow::Borrowed(&[ + 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97 + ]), + tag: 12, + length: 13..14, + data: 14..29, + }, asn1_type: Box::new(Asn1Type::Utf8String(Utf8String::from("thebesttvarynka"))) }, ] diff --git a/crates/asn1-parser/src/generic_types/boolean.rs b/crates/asn1-parser/src/generic_types/boolean.rs index 8feb1ea..e916216 100644 --- a/crates/asn1-parser/src/generic_types/boolean.rs +++ b/crates/asn1-parser/src/generic_types/boolean.rs @@ -1,6 +1,7 @@ use alloc::borrow::Cow; use alloc::boxed::Box; +use crate::asn1::RawAsn1EntityData; use crate::length::{read_len, write_len}; use crate::reader::{read_data, Reader}; use crate::writer::Writer; @@ -69,10 +70,12 @@ impl<'data> Asn1Decoder<'data> for Bool { let (data, data_range) = read_data(reader, len)?; Ok(Asn1 { - raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), - tag: tag_position, - length: len_range, - data: data_range, + raw_data: RawAsn1EntityData { + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), + tag: tag_position, + length: len_range, + data: data_range, + }, asn1_type: Box::new(Asn1Type::Bool(data[0].try_into()?)), }) } diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index d3a935e..df81640 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -16,7 +16,7 @@ mod tag; mod tags; mod writer; -pub use asn1::{Asn1, Asn1Type, OwnedAsn1Type, OwnedAsn1}; +pub use asn1::{Asn1, Asn1Type, OwnedAsn1, OwnedAsn1Type, OwnedRawAsn1EntityData, RawAsn1EntityData}; pub use constructors::*; pub use error::Error; pub use generic_types::*; diff --git a/crates/asn1-parser/src/string/bit_string.rs b/crates/asn1-parser/src/string/bit_string.rs index 80b6000..27e9b9a 100644 --- a/crates/asn1-parser/src/string/bit_string.rs +++ b/crates/asn1-parser/src/string/bit_string.rs @@ -2,6 +2,7 @@ use alloc::borrow::Cow; use alloc::boxed::Box; use alloc::vec::Vec; +use crate::asn1::RawAsn1EntityData; use crate::length::{len_size, read_len, write_len}; use crate::reader::{read_data, Reader}; use crate::writer::Writer; @@ -74,10 +75,12 @@ impl<'data> Asn1Decoder<'data> for BitString<'data> { let (data, data_range) = read_data(reader, len)?; Ok(Asn1 { - raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), - tag: tag_position, - length: len_range, - data: data_range, + raw_data: RawAsn1EntityData { + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), + tag: tag_position, + length: len_range, + data: data_range, + }, asn1_type: Box::new(Asn1Type::BitString(Self { bits: Cow::Borrowed(data), })), diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index 37dbc83..a89a8e0 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -2,6 +2,7 @@ use alloc::borrow::Cow; use alloc::boxed::Box; use alloc::vec::Vec; +use crate::asn1::RawAsn1EntityData; use crate::length::{len_size, read_len, write_len}; use crate::reader::{read_data, Reader}; use crate::writer::Writer; @@ -52,10 +53,12 @@ impl<'data> Asn1Decoder<'data> for OctetString<'data> { let (data, data_range) = read_data(reader, len)?; Ok(Asn1 { - raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), - tag: tag_position, - length: len_range, - data: data_range, + raw_data: RawAsn1EntityData { + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), + tag: tag_position, + length: len_range, + data: data_range, + }, asn1_type: Box::new(Asn1Type::OctetString(Self { octets: Cow::Borrowed(data), })), @@ -94,10 +97,13 @@ mod tests { let octet_string = OctetString::decode_asn1(&mut Reader::new(&raw)).unwrap(); - assert_eq!(octet_string.tag_position(), 0); - assert_eq!(octet_string.length_bytes(), &[8]); - assert_eq!(octet_string.length_range(), 1..2); - assert_eq!(&raw[octet_string.data_range()], &[0, 17, 34, 51, 68, 85, 102, 119]); + assert_eq!(octet_string.raw_data.tag_position(), 0); + assert_eq!(octet_string.raw_data.length_bytes(), &[8]); + assert_eq!(octet_string.raw_data.length_range(), 1..2); + assert_eq!( + &raw[octet_string.raw_data.data_range()], + &[0, 17, 34, 51, 68, 85, 102, 119] + ); let mut encoded = [0; 10]; diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs index ad29519..eac25b7 100644 --- a/crates/asn1-parser/src/string/utf8_string.rs +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -3,6 +3,7 @@ use alloc::boxed::Box; use alloc::string::String; use core::str::from_utf8; +use crate::asn1::RawAsn1EntityData; use crate::length::{len_size, read_len, write_len}; use crate::reader::{read_data, Reader}; use crate::writer::Writer; @@ -61,10 +62,12 @@ impl<'data> Asn1Decoder<'data> for Utf8String<'data> { let (data, data_range) = read_data(reader, len)?; Ok(Asn1 { - raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), - tag: tag_position, - length: len_range, - data: data_range, + raw_data: RawAsn1EntityData { + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..data_range.end)?), + tag: tag_position, + length: len_range, + data: data_range, + }, asn1_type: Box::new(Asn1Type::Utf8String(Self { string: Cow::Borrowed(from_utf8(data)?), })), @@ -107,10 +110,10 @@ mod tests { let utf8_string = Utf8String::decode_asn1(&mut Reader::new(&raw)).unwrap(); - assert_eq!(utf8_string.tag_position(), 0); - assert_eq!(utf8_string.length_bytes(), &[15]); - assert_eq!(utf8_string.length_range(), 1..2); - assert_eq!(&raw[utf8_string.data_range()], b"thebesttvarynka"); + assert_eq!(utf8_string.raw_data.tag_position(), 0); + assert_eq!(utf8_string.raw_data.length_bytes(), &[15]); + assert_eq!(utf8_string.raw_data.length_range(), 1..2); + assert_eq!(&raw[utf8_string.raw_data.data_range()], b"thebesttvarynka"); assert_eq!( utf8_string.asn1(), &Asn1Type::Utf8String(Utf8String { diff --git a/crates/asn1-parser/src/tags/explicit_tag.rs b/crates/asn1-parser/src/tags/explicit_tag.rs index 544f628..a450c54 100644 --- a/crates/asn1-parser/src/tags/explicit_tag.rs +++ b/crates/asn1-parser/src/tags/explicit_tag.rs @@ -1,6 +1,7 @@ use alloc::borrow::Cow; use alloc::boxed::Box; +use crate::asn1::RawAsn1EntityData; use crate::length::{len_size, read_len, write_len}; use crate::reader::Reader; use crate::writer::Writer; @@ -38,7 +39,7 @@ impl<'data> Asn1Decoder<'data> for ExplicitTag<'data> { let inner = Asn1Type::decode_asn1(reader)?; - if len != inner.raw_bytes().len() { + if len != inner.raw_data.raw_bytes().len() { return Err(Error::from( "Invalid explicit tag len. Inner entity raw data len is not the same as explicit tag len.", )); @@ -59,19 +60,21 @@ impl<'data> Asn1Decoder<'data> for ExplicitTag<'data> { let inner = Asn1Type::decode_asn1(reader)?; - if len != inner.raw_bytes().len() { + if len != inner.raw_data.raw_bytes().len() { return Err(Error::from( "Invalid explicit tag len. Inner entity raw data len is not the same as explicit tag len.", )); } - let inner_data_range = inner.data_range(); + let inner_data_range = inner.raw_data.data_range(); Ok(Asn1 { - raw_data: Cow::Borrowed(reader.data_in_range(tag_position..inner_data_range.end)?), - tag: tag_position, - length: len_range, - data: inner.tag_position()..inner_data_range.end, + raw_data: RawAsn1EntityData { + raw_data: Cow::Borrowed(reader.data_in_range(tag_position..inner_data_range.end)?), + tag: tag_position, + length: len_range, + data: inner.raw_data.tag_position()..inner_data_range.end, + }, asn1_type: Box::new(Asn1Type::ExplicitTag(Self { tag, inner })), }) } diff --git a/crates/asn1-parser/tests/decode_encode.rs b/crates/asn1-parser/tests/decode_encode.rs index b411ca1..0c5063a 100644 --- a/crates/asn1-parser/tests/decode_encode.rs +++ b/crates/asn1-parser/tests/decode_encode.rs @@ -33,10 +33,10 @@ fn asn1() { let decoded = Asn1Type::decode_asn1_buff(&buff).unwrap(); assert_eq!(decoded.asn1().needed_buf_size(), buff_len); - assert_eq!(1 + decoded.length_bytes().len() + decoded.data_bytes().len(), buff_len); + assert_eq!(1 + decoded.raw_entity_data().length_bytes().len() + decoded.raw_entity_data().data_bytes().len(), buff_len); assert_eq!(decoded.asn1(), &asn1); assert_eq!(decoded.asn1().tag(), asn1_tag); - assert_eq!(decoded.tag_position(), 0); - assert_eq!(decoded.raw_bytes(), buff); + assert_eq!(decoded.raw_entity_data().tag_position(), 0); + assert_eq!(decoded.raw_entity_data().raw_bytes(), buff); }) } From 7f733c1e1515fc4bdf0747e7dbf29da2518a928c Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 9 Jul 2023 23:46:04 +0300 Subject: [PATCH 25/74] feat(crates): asn1-parser: implement .to_owned method for Sequence, OctetString, Utf8String, Asn1, Asn1Type, RawAsn1EntityData; --- crates/asn1-parser/src/asn1.rs | 34 +++++++++++++++++-- .../asn1-parser/src/constructors/sequence.rs | 10 ++++++ crates/asn1-parser/src/string/octet_string.rs | 10 ++++++ crates/asn1-parser/src/string/utf8_string.rs | 12 ++++++- 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index f62ab56..a6fc86d 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -23,6 +23,20 @@ pub enum Asn1Type<'data> { pub type OwnedAsn1Type = Asn1Type<'static>; +impl Asn1Type<'_> { + pub fn to_owned(&self) -> OwnedAsn1Type { + match self { + Asn1Type::Sequence(s) => Asn1Type::Sequence(s.to_owned()), + Asn1Type::OctetString(o) => Asn1Type::OctetString(o.to_owned()), + Asn1Type::Utf8String(u) => Asn1Type::Utf8String(u.to_owned()), + // Asn1Type::BitString(b) => Asn1Type::BitString(b.to_owned()), + // Asn1Type::Bool(b) => Asn1Type::Bool(b), + // Asn1Type::ExplicitTag(_) => todo!(), + _ => unimplemented!(), + } + } +} + impl Asn1Entity for Asn1Type<'_> { fn tag(&self) -> Tag { match self { @@ -148,6 +162,15 @@ impl RawAsn1EntityData<'_> { pub fn data_bytes(&self) -> &[u8] { &self.raw_data[self.data.clone()] } + + pub fn to_owned(&self) -> OwnedRawAsn1EntityData { + RawAsn1EntityData { + raw_data: self.raw_data.to_vec().into(), + tag: self.tag, + length: self.length.clone(), + data: self.data.clone(), + } + } } /// [`Asn1`] structure represents generic `asn1` value. @@ -171,15 +194,20 @@ impl Asn1<'_> { pub fn asn1(&self) -> &Asn1Type<'_> { &self.asn1_type } + + pub fn to_owned(&self) -> OwnedAsn1 { + Asn1 { + raw_data: self.raw_data.to_owned(), + asn1_type: Box::new(self.asn1_type.to_owned()), + } + } } impl Default for Asn1<'_> { fn default() -> Self { // those values are just for testing purpose during development Asn1Type::decode_asn1_buff(&[ - 48, 50, 161, 17, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 162, 9, - 12, 7, 113, 107, 97, 116, 105, 111, 110, 163, 18, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, - 221, 80, 86, 181, 110, + 48, 44, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 12, 7, 113, 107, 97, 116, 105, 111, 110, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110 ]) .unwrap() } diff --git a/crates/asn1-parser/src/constructors/sequence.rs b/crates/asn1-parser/src/constructors/sequence.rs index 27e25ff..383726e 100644 --- a/crates/asn1-parser/src/constructors/sequence.rs +++ b/crates/asn1-parser/src/constructors/sequence.rs @@ -17,6 +17,16 @@ pub type OwnedSequence = Sequence<'static>; impl Sequence<'_> { pub const TAG: Tag = Tag(0x30); + + pub fn fields(&self) -> &[Asn1<'_>] { + &self.fields + } + + pub fn to_owned(&self) -> OwnedSequence { + Sequence { + fields: self.fields.iter().map(|f| f.to_owned()).collect(), + } + } } impl<'data> From>> for Sequence<'data> { diff --git a/crates/asn1-parser/src/string/octet_string.rs b/crates/asn1-parser/src/string/octet_string.rs index a89a8e0..4d55ee0 100644 --- a/crates/asn1-parser/src/string/octet_string.rs +++ b/crates/asn1-parser/src/string/octet_string.rs @@ -17,6 +17,16 @@ pub type OwnedOctetString = OctetString<'static>; impl OctetString<'_> { pub const TAG: Tag = Tag(4); + + pub fn octets(&self) -> &[u8] { + &self.octets + } + + pub fn to_owned(&self) -> OwnedOctetString { + OctetString { + octets: self.octets.to_vec().into(), + } + } } impl From> for OwnedOctetString { diff --git a/crates/asn1-parser/src/string/utf8_string.rs b/crates/asn1-parser/src/string/utf8_string.rs index eac25b7..1538144 100644 --- a/crates/asn1-parser/src/string/utf8_string.rs +++ b/crates/asn1-parser/src/string/utf8_string.rs @@ -1,6 +1,6 @@ use alloc::borrow::Cow; use alloc::boxed::Box; -use alloc::string::String; +use alloc::string::{String, ToString}; use core::str::from_utf8; use crate::asn1::RawAsn1EntityData; @@ -18,6 +18,16 @@ pub type OwnedUtf8String = Utf8String<'static>; impl Utf8String<'_> { pub const TAG: Tag = Tag(12); + + pub fn string(&self) -> &str { + &self.string + } + + pub fn to_owned(&self) -> OwnedUtf8String { + Utf8String { + string: self.string.to_string().into(), + } + } } impl From for OwnedUtf8String { From ec5898fd7f2ff26caa972d1389003e971e9f212d Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 9 Jul 2023 23:46:46 +0300 Subject: [PATCH 26/74] feat(asn1-debugger): implement basic asn1 structure rendering (without styles); --- src/asn1.rs | 15 ++++++++----- src/asn1/asn1_viewer.rs | 8 ++++--- src/asn1/hex_buffer.rs | 15 +++++++++++++ src/asn1/scheme.rs | 42 +++++++++++++++++++++++++++++++++++++ src/asn1/scheme/sequence.rs | 25 ++++++++++++++++++++++ src/asn1/scheme/strings.rs | 35 +++++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 src/asn1/hex_buffer.rs create mode 100644 src/asn1/scheme.rs create mode 100644 src/asn1/scheme/sequence.rs create mode 100644 src/asn1/scheme/strings.rs diff --git a/src/asn1.rs b/src/asn1.rs index 863a612..5efcbcf 100644 --- a/src/asn1.rs +++ b/src/asn1.rs @@ -1,15 +1,20 @@ mod asn1_viewer; +mod hex_buffer; +mod scheme; use asn1_parser::Asn1; use web_sys::KeyboardEvent; -use yew::{function_component, html, use_state, Callback, Html, classes}; +use yew::{classes, function_component, html, use_state, Callback, Html}; -use crate::{common::{ByteInput, Checkbox}, asn1::asn1_viewer::Asn1Viewer}; +use crate::asn1::asn1_viewer::Asn1Viewer; +use crate::common::{ByteInput, Checkbox}; const TEST_ASN1: &[u8] = &[ - 48, 50, 161, 17, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 162, 9, - 12, 7, 113, 107, 97, 116, 105, 111, 110, 163, 18, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, - 221, 80, 86, 181, 110, + // 48, 50, 161, 17, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 162, 9, 12, 7, + // 113, 107, 97, 116, 105, 111, 110, 163, 18, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, + // 181, 110, + +48, 44, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 12, 7, 113, 107, 97, 116, 105, 111, 110, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110 ]; #[function_component(Asn1ParserPage)] diff --git a/src/asn1/asn1_viewer.rs b/src/asn1/asn1_viewer.rs index fd21457..175f781 100644 --- a/src/asn1/asn1_viewer.rs +++ b/src/asn1/asn1_viewer.rs @@ -1,5 +1,7 @@ use asn1_parser::OwnedAsn1; -use yew::{Html, function_component, Properties, html}; +use yew::{function_component, html, Html, Properties}; + +use crate::asn1::scheme::build_asn1_schema; #[derive(PartialEq, Properties, Clone)] pub struct Asn1ViewerProps { @@ -11,7 +13,7 @@ pub struct Asn1ViewerProps { pub fn asn1_viewer(props: &Asn1ViewerProps) -> Html { html! {
- {format!("{:?}", props.structure)} + {build_asn1_schema(&props.structure)}
} -} \ No newline at end of file +} diff --git a/src/asn1/hex_buffer.rs b/src/asn1/hex_buffer.rs new file mode 100644 index 0000000..3e977a0 --- /dev/null +++ b/src/asn1/hex_buffer.rs @@ -0,0 +1,15 @@ +use yew::{function_component, html, Html, Properties}; + +#[derive(PartialEq, Properties, Clone)] +pub struct HexBufferProps { + pub data: Vec, +} + +#[function_component(HexBuffer)] +pub fn hex_buffer(props: &HexBufferProps) -> Html { + html! { +
+ {"hex buffer"} +
+ } +} diff --git a/src/asn1/scheme.rs b/src/asn1/scheme.rs new file mode 100644 index 0000000..d9c7166 --- /dev/null +++ b/src/asn1/scheme.rs @@ -0,0 +1,42 @@ +mod sequence; +mod strings; + +use asn1_parser::{Asn1Type, Asn1}; +use yew::{function_component, html, Html, Properties, virtual_dom::VNode}; + +use crate::asn1::scheme::strings::{OctetStringNode, Utf8StringNode}; + +use self::sequence::SequenceNode; + +// #[derive(PartialEq, Properties, Clone)] +// pub struct Asn1SchemeProps { +// pub schema: OwnedAsn1, +// } + +pub fn build_asn1_schema(asn1: &Asn1<'_>) -> VNode { + match asn1.asn1() { + Asn1Type::OctetString(octet) => html! { + + }, + Asn1Type::Utf8String(utf8) => html! { + + }, + Asn1Type::Sequence(sequence) => html! { + + }, + // Asn1Type::BitString(bit) => bit.tag(), + // Asn1Type::Bool(boolean) => boolean.tag(), + // Asn1Type::ExplicitTag(e) => e.tag(), + _ => unimplemented!(""), + } +} + +// #[function_component(Asn1Scheme)] +// pub fn asn1_scheme(props: &Asn1SchemeProps) -> Html { +// let node = ; + +// html! { +//
+//
+// } +// } diff --git a/src/asn1/scheme/sequence.rs b/src/asn1/scheme/sequence.rs new file mode 100644 index 0000000..7897490 --- /dev/null +++ b/src/asn1/scheme/sequence.rs @@ -0,0 +1,25 @@ +use asn1_parser::OwnedSequence; +use yew::{Html, html, function_component, Properties}; + +use crate::asn1::scheme::build_asn1_schema; + +#[derive(PartialEq, Properties, Clone)] +pub struct SequenceNodeProps { + pub node: OwnedSequence, +} + +#[function_component(SequenceNode)] +pub fn sequence(props: &SequenceNodeProps) -> Html { + let fields = props.node.fields(); + + let fields_components = fields.iter().map(|f| build_asn1_schema(f)).collect::>(); + + html! { +
+ {format!("Sequence ({} fields)", fields.len())} +
+ {fields_components} +
+
+ } +} \ No newline at end of file diff --git a/src/asn1/scheme/strings.rs b/src/asn1/scheme/strings.rs new file mode 100644 index 0000000..d330067 --- /dev/null +++ b/src/asn1/scheme/strings.rs @@ -0,0 +1,35 @@ +use asn1_parser::{OwnedUtf8String, OwnedOctetString}; +use yew::{Html, html, function_component, Properties}; + +#[derive(PartialEq, Properties, Clone)] +pub struct Utf8StringNodeProps { + pub node: OwnedUtf8String, +} + +#[function_component(Utf8StringNode)] +pub fn utf8_string(props: &Utf8StringNodeProps) -> Html { + html! { +
+ {"UTF8String"} + {props.node.string().to_owned()} +
+ } +} + +#[derive(PartialEq, Properties, Clone)] +pub struct OctetStringNodeProps { + pub node: OwnedOctetString, +} + +#[function_component(OctetStringNode)] +pub fn utf8_string(props: &OctetStringNodeProps) -> Html { + let octets = props.node.octets(); + + html! { +
+ {"Octet String"} + {format!("({} bytes)", octets.len())} + {hex::encode(octets)} +
+ } +} \ No newline at end of file From a9d4a658fe56feafb5b68cd49658ec8975e1b114 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Mon, 10 Jul 2023 00:07:25 +0300 Subject: [PATCH 27/74] feat(asn1-debugger): add basic styles for sequence, octet/utf8 strings; --- crates/asn1-parser/src/asn1.rs | 2 +- index.html | 1 + public/styles/asn1/node.scss | 36 ++++++++++++++++++++++++++++++++++ public/styles/asn1/page.scss | 1 + public/styles/style.scss | 4 ++-- src/asn1/scheme/sequence.rs | 7 +++++-- src/asn1/scheme/strings.rs | 11 +++++++---- 7 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 public/styles/asn1/node.scss diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index a6fc86d..ea805ec 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -207,7 +207,7 @@ impl Default for Asn1<'_> { fn default() -> Self { // those values are just for testing purpose during development Asn1Type::decode_asn1_buff(&[ - 48, 44, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 12, 7, 113, 107, 97, 116, 105, 111, 110, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110 +48, 90, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 12, 7, 113, 107, 97, 116, 105, 111, 110, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110, 48, 44, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 12, 7, 113, 107, 97, 116, 105, 111, 110, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110 ]) .unwrap() } diff --git a/index.html b/index.html index 25f417e..e156848 100644 --- a/index.html +++ b/index.html @@ -27,5 +27,6 @@ + \ No newline at end of file diff --git a/public/styles/asn1/node.scss b/public/styles/asn1/node.scss new file mode 100644 index 0000000..71923b9 --- /dev/null +++ b/public/styles/asn1/node.scss @@ -0,0 +1,36 @@ + +.terminal-asn1-node { + display: inline-flex; + gap: 0.5em; +} + +.terminal-asn1-node:hover > span:first-child { + text-decoration: underline; + cursor: pointer; +} + +.asn1-constructor-header { + display: inline-flex; + gap: 0.5em; +} + +.asn1-constructor-header:hover { + font-weight: bold; + cursor: pointer; +} + +.asn1-constructor-body { + display: flex; + flex-direction: column; + justify-content: center; + gap: 0; + + padding-left: 1.5em; + border-left: 2px solid #70a66c; +} + +.asn1-node-info-label { + font-size: 0.8em; + color: #4c5159; + align-self: center; +} diff --git a/public/styles/asn1/page.scss b/public/styles/asn1/page.scss index 39580ba..5e31e7b 100644 --- a/public/styles/asn1/page.scss +++ b/public/styles/asn1/page.scss @@ -2,4 +2,5 @@ .asn1-page { margin-top: 1em; width: 98%; + color: #403735; } \ No newline at end of file diff --git a/public/styles/style.scss b/public/styles/style.scss index b4c7b03..096c951 100644 --- a/public/styles/style.scss +++ b/public/styles/style.scss @@ -99,8 +99,8 @@ article { .bytes-container { background-color: #403735; - padding: 0.2em; - border-radius: 3px; + padding: 0.05em; + border-radius: 2px; } .byte-null { diff --git a/src/asn1/scheme/sequence.rs b/src/asn1/scheme/sequence.rs index 7897490..7d684dc 100644 --- a/src/asn1/scheme/sequence.rs +++ b/src/asn1/scheme/sequence.rs @@ -16,8 +16,11 @@ pub fn sequence(props: &SequenceNodeProps) -> Html { html! {
- {format!("Sequence ({} fields)", fields.len())} -
+
+ {"Sequence"} + {format!("({} fields)", fields.len())} +
+
{fields_components}
diff --git a/src/asn1/scheme/strings.rs b/src/asn1/scheme/strings.rs index d330067..f2ebcaa 100644 --- a/src/asn1/scheme/strings.rs +++ b/src/asn1/scheme/strings.rs @@ -1,6 +1,8 @@ use asn1_parser::{OwnedUtf8String, OwnedOctetString}; use yew::{Html, html, function_component, Properties}; +use crate::common::BytesViewer; + #[derive(PartialEq, Properties, Clone)] pub struct Utf8StringNodeProps { pub node: OwnedUtf8String, @@ -9,7 +11,7 @@ pub struct Utf8StringNodeProps { #[function_component(Utf8StringNode)] pub fn utf8_string(props: &Utf8StringNodeProps) -> Html { html! { -
+
{"UTF8String"} {props.node.string().to_owned()}
@@ -26,10 +28,11 @@ pub fn utf8_string(props: &OctetStringNodeProps) -> Html { let octets = props.node.octets(); html! { -
+
{"Octet String"} - {format!("({} bytes)", octets.len())} - {hex::encode(octets)} + {format!("({} bytes)", octets.len())} + // {hex::encode(octets)} +
} } \ No newline at end of file From 8bd1bf71b593ee940d9c53dec4742874987c33b5 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sat, 18 Nov 2023 18:17:01 +0200 Subject: [PATCH 28/74] prop-strategies: implement recursive asn1_type generation --- crates/prop-strategies/src/constructors.rs | 29 ++++++++++++++++++++++ crates/prop-strategies/src/lib.rs | 8 ++++-- 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 crates/prop-strategies/src/constructors.rs diff --git a/crates/prop-strategies/src/constructors.rs b/crates/prop-strategies/src/constructors.rs new file mode 100644 index 0000000..7c04c37 --- /dev/null +++ b/crates/prop-strategies/src/constructors.rs @@ -0,0 +1,29 @@ +use asn1_parser::{Asn1Type, OwnedAsn1, OwnedAsn1Type, OwnedSequence}; +use proptest::collection::vec; +use proptest::prop_oneof; +use proptest::strategy::Strategy; + +use crate::{any_bit_string, any_bool, any_octet_string, any_utf8_string}; + +fn any_leaf_asn1_type() -> impl Strategy { + prop_oneof![ + any_octet_string().prop_map(Asn1Type::OctetString), + any_utf8_string().prop_map(Asn1Type::Utf8String), + any_bit_string().prop_map(Asn1Type::BitString), + any_bool().prop_map(Asn1Type::Bool), + ] + .no_shrink() +} + +pub fn recursive_empty_asn1_type() -> impl Strategy { + any_leaf_asn1_type().prop_recursive(16, 64, 32, |inner| { + vec(inner, 1..16).prop_map(|fields| { + Asn1Type::Sequence(OwnedSequence::from( + fields + .into_iter() + .map(|asn1_type| OwnedAsn1::new(Default::default(), Box::new(asn1_type))) + .collect::>(), + )) + }) + }) +} diff --git a/crates/prop-strategies/src/lib.rs b/crates/prop-strategies/src/lib.rs index 695a677..519c819 100644 --- a/crates/prop-strategies/src/lib.rs +++ b/crates/prop-strategies/src/lib.rs @@ -1,9 +1,9 @@ -// mod constructors; +mod constructors; mod generic_types; mod string; use asn1_parser::{Asn1Type, OwnedAsn1Type}; -// pub use constructors::*; +pub use constructors::*; pub use generic_types::*; use proptest::collection::vec; use proptest::prelude::any; @@ -11,6 +11,8 @@ use proptest::prop_oneof; use proptest::strategy::Strategy; pub use string::*; +use crate::constructors::recursive_empty_asn1_type; + pub fn bytes(size: usize) -> impl Strategy> { vec(any::(), 0..size).no_shrink() } @@ -21,12 +23,14 @@ pub fn string(len: usize) -> impl Strategy { .no_shrink() } +#[allow(clippy::arc_with_non_send_sync)] pub fn any_asn1_type() -> impl Strategy { prop_oneof![ any_octet_string().prop_map(Asn1Type::OctetString), any_utf8_string().prop_map(Asn1Type::Utf8String), any_bit_string().prop_map(Asn1Type::BitString), any_bool().prop_map(Asn1Type::Bool), + recursive_empty_asn1_type(), ] .no_shrink() } From 2943c1a3566b6ed5221fbaa14976e8596b6092fc Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sat, 18 Nov 2023 18:19:12 +0200 Subject: [PATCH 29/74] asn1-parser: small improvements --- crates/asn1-parser/src/asn1.rs | 35 ++++++++++++++++--- .../asn1-parser/src/generic_types/boolean.rs | 6 +++- crates/asn1-parser/src/string/bit_string.rs | 10 ++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index ea805ec..13be487 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -29,8 +29,8 @@ impl Asn1Type<'_> { Asn1Type::Sequence(s) => Asn1Type::Sequence(s.to_owned()), Asn1Type::OctetString(o) => Asn1Type::OctetString(o.to_owned()), Asn1Type::Utf8String(u) => Asn1Type::Utf8String(u.to_owned()), - // Asn1Type::BitString(b) => Asn1Type::BitString(b.to_owned()), - // Asn1Type::Bool(b) => Asn1Type::Bool(b), + Asn1Type::BitString(b) => Asn1Type::BitString(b.to_owned()), + Asn1Type::Bool(b) => Asn1Type::Bool(b.clone()), // Asn1Type::ExplicitTag(_) => todo!(), _ => unimplemented!(), } @@ -121,7 +121,7 @@ impl Asn1Encoder for Asn1Type<'_> { } /// Information about raw data of the asn1 entity -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct RawAsn1EntityData<'data> { /// Raw input bytes pub raw_data: Cow<'data, [u8]>, @@ -187,6 +187,10 @@ pub struct Asn1<'data> { pub type OwnedAsn1 = Asn1<'static>; impl Asn1<'_> { + pub fn new<'data>(raw_data: RawAsn1EntityData<'data>, asn1_type: Box>) -> Asn1<'data> { + Asn1 { raw_data, asn1_type } + } + pub fn raw_entity_data(&self) -> &RawAsn1EntityData<'_> { &self.raw_data } @@ -207,7 +211,30 @@ impl Default for Asn1<'_> { fn default() -> Self { // those values are just for testing purpose during development Asn1Type::decode_asn1_buff(&[ -48, 90, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 12, 7, 113, 107, 97, 116, 105, 111, 110, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110, 48, 44, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 12, 7, 113, 107, 97, 116, 105, 111, 110, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110 + 48, 130, 2, 30, 4, 7, 58, 232, 40, 24, 17, 216, 176, 4, 11, 216, 44, 74, 26, 137, 109, 11, 173, 211, 185, + 135, 48, 129, 255, 3, 5, 7, 67, 253, 55, 182, 12, 48, 125, 242, 181, 151, 128, 241, 174, 168, 128, 42, 243, + 146, 170, 138, 47, 10, 123, 242, 187, 129, 138, 241, 183, 182, 166, 195, 168, 240, 146, 188, 162, 226, 128, + 174, 104, 61, 100, 103, 242, 147, 175, 190, 36, 61, 103, 96, 58, 36, 1, 1, 0, 12, 48, 127, 241, 148, 178, + 169, 61, 243, 128, 178, 143, 195, 141, 0, 0, 59, 58, 51, 58, 242, 154, 140, 138, 243, 136, 172, 139, 96, + 58, 46, 240, 159, 149, 180, 119, 97, 39, 226, 128, 174, 241, 185, 169, 191, 241, 178, 167, 141, 36, 4, 5, + 50, 178, 83, 20, 77, 4, 1, 140, 12, 58, 242, 167, 134, 180, 61, 239, 187, 191, 195, 181, 242, 159, 149, + 180, 47, 96, 242, 140, 178, 173, 226, 128, 174, 240, 159, 149, 180, 242, 189, 135, 177, 88, 194, 165, 0, + 58, 114, 209, 168, 241, 172, 148, 154, 240, 159, 149, 180, 41, 37, 242, 190, 156, 163, 235, 135, 172, 194, + 165, 1, 1, 0, 4, 12, 117, 86, 112, 180, 20, 202, 224, 28, 58, 3, 133, 90, 4, 15, 212, 214, 53, 154, 145, 2, + 117, 175, 243, 103, 181, 102, 19, 251, 188, 3, 20, 5, 221, 77, 67, 230, 172, 240, 96, 163, 227, 181, 175, + 194, 248, 31, 235, 105, 46, 230, 38, 3, 17, 5, 155, 82, 70, 117, 135, 62, 42, 165, 241, 155, 147, 173, 209, + 54, 160, 138, 4, 4, 129, 51, 94, 101, 48, 46, 12, 44, 231, 158, 163, 58, 50, 63, 96, 10, 53, 111, 51, 123, + 63, 240, 184, 179, 161, 209, 168, 123, 242, 150, 186, 157, 243, 170, 137, 140, 200, 186, 242, 155, 156, + 156, 92, 0, 27, 195, 149, 92, 242, 156, 178, 152, 3, 18, 0, 206, 190, 97, 201, 75, 125, 225, 116, 109, 226, + 236, 4, 19, 9, 7, 185, 100, 12, 57, 243, 176, 171, 184, 58, 92, 243, 166, 183, 187, 243, 132, 154, 159, + 243, 165, 189, 155, 61, 36, 46, 66, 239, 191, 189, 240, 177, 152, 141, 241, 168, 156, 143, 13, 194, 134, + 39, 240, 172, 142, 137, 46, 240, 159, 149, 180, 10, 243, 133, 180, 182, 69, 242, 128, 164, 156, 122, 1, 1, + 0, 3, 16, 1, 66, 115, 229, 233, 85, 68, 237, 69, 93, 254, 218, 104, 75, 133, 241, 4, 18, 75, 232, 138, 24, + 247, 158, 233, 154, 181, 156, 155, 252, 71, 105, 43, 215, 211, 160, 3, 23, 2, 102, 29, 9, 2, 92, 205, 26, + 162, 54, 221, 33, 80, 194, 82, 99, 110, 161, 116, 102, 123, 221, 240, 12, 61, 241, 164, 185, 138, 243, 177, + 154, 148, 243, 150, 162, 184, 209, 168, 123, 46, 240, 147, 130, 133, 63, 242, 147, 170, 174, 226, 128, 174, + 11, 241, 174, 152, 137, 27, 37, 10, 241, 164, 144, 156, 243, 148, 132, 139, 241, 150, 160, 154, 241, 169, + 185, 175, 226, 128, 174, 226, 128, 174, 0, 70, 45, ]) .unwrap() } diff --git a/crates/asn1-parser/src/generic_types/boolean.rs b/crates/asn1-parser/src/generic_types/boolean.rs index e916216..97c6252 100644 --- a/crates/asn1-parser/src/generic_types/boolean.rs +++ b/crates/asn1-parser/src/generic_types/boolean.rs @@ -14,6 +14,10 @@ pub struct Bool { impl Bool { pub const TAG: Tag = Tag(1); + + pub fn value(&self) -> bool { + self.flag + } } impl From for Bool { @@ -54,7 +58,7 @@ impl<'data> Asn1Decoder<'data> for Bool { return Err(Error::from("Bool length must be equal to 1")); } - Ok(reader.read_byte()?.try_into()?) + reader.read_byte()?.try_into() } fn decode_asn1(reader: &mut Reader<'data>) -> Asn1Result> { diff --git a/crates/asn1-parser/src/string/bit_string.rs b/crates/asn1-parser/src/string/bit_string.rs index 27e9b9a..719f3ed 100644 --- a/crates/asn1-parser/src/string/bit_string.rs +++ b/crates/asn1-parser/src/string/bit_string.rs @@ -18,6 +18,10 @@ pub type OwnedBitString = BitString<'static>; impl BitString<'_> { pub const TAG: Tag = Tag(3); + pub fn bits(&self) -> &[u8] { + self.bits.as_ref() + } + pub fn from_raw_vec(bits_amount: usize, mut bits: Vec) -> Asn1Result { let all_bits_amount = bits.len() * 8; @@ -34,6 +38,12 @@ impl BitString<'_> { Ok(Self { bits: Cow::Owned(bits) }) } + + pub fn to_owned(&self) -> OwnedBitString { + OwnedBitString { + bits: self.bits.to_vec().into(), + } + } } // we assume here that firs vector byte contains amount of unused bytes From 62049714935621bc3c0d00e7cfe001cd5f23b73b Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sat, 18 Nov 2023 18:42:27 +0200 Subject: [PATCH 30/74] crypto-helper: asn1: improve rendering. implement new components. fix clippy warnings; --- src/asn1.rs | 32 +++++++++++++++++++++++++++----- src/asn1/hex_buffer.rs | 2 +- src/asn1/scheme.rs | 26 +++++++++++++++++++------- src/asn1/scheme/primitive.rs | 17 +++++++++++++++++ src/asn1/scheme/sequence.rs | 4 ++-- src/asn1/scheme/strings.rs | 26 ++++++++++++++++++++++---- src/common/byte_input.rs | 5 ++++- 7 files changed, 92 insertions(+), 20 deletions(-) create mode 100644 src/asn1/scheme/primitive.rs diff --git a/src/asn1.rs b/src/asn1.rs index 5efcbcf..f4e17ad 100644 --- a/src/asn1.rs +++ b/src/asn1.rs @@ -9,19 +9,41 @@ use yew::{classes, function_component, html, use_state, Callback, Html}; use crate::asn1::asn1_viewer::Asn1Viewer; use crate::common::{ByteInput, Checkbox}; -const TEST_ASN1: &[u8] = &[ +pub const TEST_ASN1: &[u8] = &[ // 48, 50, 161, 17, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 162, 9, 12, 7, // 113, 107, 97, 116, 105, 111, 110, 163, 18, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, // 181, 110, - -48, 44, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 12, 7, 113, 107, 97, 116, 105, 111, 110, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110 + // 48, 44, 12, 15, 116, 104, 101, 98, 101, 115, 116, 116, 118, 97, 114, 121, 110, 107, 97, 12, 7, 113, 107, 97, 116, + // 105, 111, 110, 4, 16, 252, 179, 92, 152, 40, 255, 170, 90, 80, 236, 156, 221, 80, 86, 181, 110, + 48, 130, 2, 30, 4, 7, 58, 232, 40, 24, 17, 216, 176, 4, 11, 216, 44, 74, 26, 137, 109, 11, 173, 211, 185, 135, 48, + 129, 255, 3, 5, 7, 67, 253, 55, 182, 12, 48, 125, 242, 181, 151, 128, 241, 174, 168, 128, 42, 243, 146, 170, 138, + 47, 10, 123, 242, 187, 129, 138, 241, 183, 182, 166, 195, 168, 240, 146, 188, 162, 226, 128, 174, 104, 61, 100, + 103, 242, 147, 175, 190, 36, 61, 103, 96, 58, 36, 1, 1, 0, 12, 48, 127, 241, 148, 178, 169, 61, 243, 128, 178, 143, + 195, 141, 0, 0, 59, 58, 51, 58, 242, 154, 140, 138, 243, 136, 172, 139, 96, 58, 46, 240, 159, 149, 180, 119, 97, + 39, 226, 128, 174, 241, 185, 169, 191, 241, 178, 167, 141, 36, 4, 5, 50, 178, 83, 20, 77, 4, 1, 140, 12, 58, 242, + 167, 134, 180, 61, 239, 187, 191, 195, 181, 242, 159, 149, 180, 47, 96, 242, 140, 178, 173, 226, 128, 174, 240, + 159, 149, 180, 242, 189, 135, 177, 88, 194, 165, 0, 58, 114, 209, 168, 241, 172, 148, 154, 240, 159, 149, 180, 41, + 37, 242, 190, 156, 163, 235, 135, 172, 194, 165, 1, 1, 0, 4, 12, 117, 86, 112, 180, 20, 202, 224, 28, 58, 3, 133, + 90, 4, 15, 212, 214, 53, 154, 145, 2, 117, 175, 243, 103, 181, 102, 19, 251, 188, 3, 20, 5, 221, 77, 67, 230, 172, + 240, 96, 163, 227, 181, 175, 194, 248, 31, 235, 105, 46, 230, 38, 3, 17, 5, 155, 82, 70, 117, 135, 62, 42, 165, + 241, 155, 147, 173, 209, 54, 160, 138, 4, 4, 129, 51, 94, 101, 48, 46, 12, 44, 231, 158, 163, 58, 50, 63, 96, 10, + 53, 111, 51, 123, 63, 240, 184, 179, 161, 209, 168, 123, 242, 150, 186, 157, 243, 170, 137, 140, 200, 186, 242, + 155, 156, 156, 92, 0, 27, 195, 149, 92, 242, 156, 178, 152, 3, 18, 0, 206, 190, 97, 201, 75, 125, 225, 116, 109, + 226, 236, 4, 19, 9, 7, 185, 100, 12, 57, 243, 176, 171, 184, 58, 92, 243, 166, 183, 187, 243, 132, 154, 159, 243, + 165, 189, 155, 61, 36, 46, 66, 239, 191, 189, 240, 177, 152, 141, 241, 168, 156, 143, 13, 194, 134, 39, 240, 172, + 142, 137, 46, 240, 159, 149, 180, 10, 243, 133, 180, 182, 69, 242, 128, 164, 156, 122, 1, 1, 0, 3, 16, 1, 66, 115, + 229, 233, 85, 68, 237, 69, 93, 254, 218, 104, 75, 133, 241, 4, 18, 75, 232, 138, 24, 247, 158, 233, 154, 181, 156, + 155, 252, 71, 105, 43, 215, 211, 160, 3, 23, 2, 102, 29, 9, 2, 92, 205, 26, 162, 54, 221, 33, 80, 194, 82, 99, 110, + 161, 116, 102, 123, 221, 240, 12, 61, 241, 164, 185, 138, 243, 177, 154, 148, 243, 150, 162, 184, 209, 168, 123, + 46, 240, 147, 130, 133, 63, 242, 147, 170, 174, 226, 128, 174, 11, 241, 174, 152, 137, 27, 37, 10, 241, 164, 144, + 156, 243, 148, 132, 139, 241, 150, 160, 154, 241, 169, 185, 175, 226, 128, 174, 226, 128, 174, 0, 70, 45, ]; #[function_component(Asn1ParserPage)] pub fn asn1_parser_page() -> Html { let auto_decode = use_state(|| true); let raw_asn1 = use_state(|| TEST_ASN1.to_vec()); - let parsed_asn1 = use_state(|| Asn1::default()); + let parsed_asn1 = use_state(Asn1::default); let set_auto_decode = auto_decode.setter(); let set_checked = Callback::from(move |checked| { @@ -46,7 +68,7 @@ pub fn asn1_parser_page() -> Html { html! {
- +
diff --git a/src/asn1/hex_buffer.rs b/src/asn1/hex_buffer.rs index 3e977a0..727fedb 100644 --- a/src/asn1/hex_buffer.rs +++ b/src/asn1/hex_buffer.rs @@ -6,7 +6,7 @@ pub struct HexBufferProps { } #[function_component(HexBuffer)] -pub fn hex_buffer(props: &HexBufferProps) -> Html { +pub fn hex_buffer(_props: &HexBufferProps) -> Html { html! {
{"hex buffer"} diff --git a/src/asn1/scheme.rs b/src/asn1/scheme.rs index d9c7166..1979ebf 100644 --- a/src/asn1/scheme.rs +++ b/src/asn1/scheme.rs @@ -1,12 +1,14 @@ +mod primitive; mod sequence; mod strings; -use asn1_parser::{Asn1Type, Asn1}; -use yew::{function_component, html, Html, Properties, virtual_dom::VNode}; - -use crate::asn1::scheme::strings::{OctetStringNode, Utf8StringNode}; +use asn1_parser::{Asn1, Asn1Type}; +use yew::html; +use yew::virtual_dom::VNode; use self::sequence::SequenceNode; +use crate::asn1::scheme::primitive::BoolNode; +use crate::asn1::scheme::strings::{BitStringNode, OctetStringNode, Utf8StringNode}; // #[derive(PartialEq, Properties, Clone)] // pub struct Asn1SchemeProps { @@ -24,10 +26,20 @@ pub fn build_asn1_schema(asn1: &Asn1<'_>) -> VNode { Asn1Type::Sequence(sequence) => html! { }, - // Asn1Type::BitString(bit) => bit.tag(), - // Asn1Type::Bool(boolean) => boolean.tag(), + Asn1Type::BitString(bit) => html! { + + }, + Asn1Type::Bool(boolean) => html! { + + }, // Asn1Type::ExplicitTag(e) => e.tag(), - _ => unimplemented!(""), + a => { + log::error!("{:?}", a); + // unimplemented!("{:?}", a) + html! { + {format!("unimlemented: {:?}", a)} + } + } } } diff --git a/src/asn1/scheme/primitive.rs b/src/asn1/scheme/primitive.rs new file mode 100644 index 0000000..311857e --- /dev/null +++ b/src/asn1/scheme/primitive.rs @@ -0,0 +1,17 @@ +use asn1_parser::Bool; +use yew::{function_component, html, Html, Properties}; + +#[derive(PartialEq, Properties, Clone)] +pub struct BoolNodeProps { + pub node: Bool, +} + +#[function_component(BoolNode)] +pub fn bool(props: &BoolNodeProps) -> Html { + html! { +
+ {"Bool"} + {props.node.value()} +
+ } +} diff --git a/src/asn1/scheme/sequence.rs b/src/asn1/scheme/sequence.rs index 7d684dc..7eb6ddc 100644 --- a/src/asn1/scheme/sequence.rs +++ b/src/asn1/scheme/sequence.rs @@ -1,5 +1,5 @@ use asn1_parser::OwnedSequence; -use yew::{Html, html, function_component, Properties}; +use yew::{function_component, html, Html, Properties}; use crate::asn1::scheme::build_asn1_schema; @@ -25,4 +25,4 @@ pub fn sequence(props: &SequenceNodeProps) -> Html {
} -} \ No newline at end of file +} diff --git a/src/asn1/scheme/strings.rs b/src/asn1/scheme/strings.rs index f2ebcaa..c3bf7d6 100644 --- a/src/asn1/scheme/strings.rs +++ b/src/asn1/scheme/strings.rs @@ -1,5 +1,5 @@ -use asn1_parser::{OwnedUtf8String, OwnedOctetString}; -use yew::{Html, html, function_component, Properties}; +use asn1_parser::{OwnedBitString, OwnedOctetString, OwnedUtf8String}; +use yew::{function_component, html, Html, Properties}; use crate::common::BytesViewer; @@ -24,7 +24,7 @@ pub struct OctetStringNodeProps { } #[function_component(OctetStringNode)] -pub fn utf8_string(props: &OctetStringNodeProps) -> Html { +pub fn octet_string(props: &OctetStringNodeProps) -> Html { let octets = props.node.octets(); html! { @@ -35,4 +35,22 @@ pub fn utf8_string(props: &OctetStringNodeProps) -> Html {
} -} \ No newline at end of file +} +#[derive(PartialEq, Properties, Clone)] +pub struct BitStringNodeProps { + pub node: OwnedBitString, +} + +#[function_component(BitStringNode)] +pub fn bit_string(props: &BitStringNodeProps) -> Html { + let octets = props.node.bits(); + + html! { +
+ {"Bit String"} + {format!("({} bytes)", octets.len())} + // {hex::encode(octets)} + +
+ } +} diff --git a/src/common/byte_input.rs b/src/common/byte_input.rs index 7289020..8595ed3 100644 --- a/src/common/byte_input.rs +++ b/src/common/byte_input.rs @@ -12,6 +12,8 @@ pub struct ByteInputProps { pub placeholder: String, pub bytes: Vec, pub setter: Callback>, + #[prop_or(2)] + pub rows: u16, } #[function_component(ByteInput)] @@ -21,6 +23,7 @@ pub fn byte_input(props: &ByteInputProps) -> Html { bytes, setter, placeholder, + rows, } = &props; let raw_value = use_state(|| encode_bytes(bytes, *format)); @@ -89,7 +92,7 @@ pub fn byte_input(props: &ByteInputProps) -> Html { }).collect::() }