From 1e8528f579c6494c84d6b648f4e0ed935497f43e Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sat, 9 Mar 2024 21:25:53 +0200 Subject: [PATCH 1/5] fix(crypto-helper): asn1: improved default hex bytes rendering. It turned out that rendering absolute all bytes of the asn1 node is a bad idea (ironically). It has caused big performance problems and latency. Now we render only first `MAX_BYTES_TO_RENDER` / 2 and last `MAX_BYTES_TO_RENDER` / 2 bytes of the asn1 node if the overall amount of bytes is greater than `MAX_BYTES_TO_RENDER`. Currently, `MAX_BYTES_TO_RENDER` is equal to `512`. --- src/asn1/hex_view.rs | 48 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/asn1/hex_view.rs b/src/asn1/hex_view.rs index 0d613f9f..d43dd384 100644 --- a/src/asn1/hex_view.rs +++ b/src/asn1/hex_view.rs @@ -30,6 +30,8 @@ pub fn hex_viewer(props: &HexViewerProps) -> Html { } } +const MAX_BYTES_TO_RENDER: usize = 512; + fn format_bytes( meta: &RawAsn1EntityData, bytes: &[u8], @@ -42,17 +44,47 @@ fn format_bytes( let length_len = meta.length_range().len(); let data_len = meta.data_range().len(); - bytes.iter().for_each(|byte| { - let set_cur_node_enter = set_cur_node.clone(); - let onmouseenter = Callback::from(move |_: MouseEvent| set_cur_node_enter.emit(HighlightAction::Show(asn1_node_id))); - let set_cur_node = set_cur_node.clone(); - let onmouseleave = Callback::from(move |_: MouseEvent| set_cur_node.emit(HighlightAction::Hide(asn1_node_id))); + let set_cur_node_enter = set_cur_node.clone(); + let onmouseenter = + Callback::from(move |_: MouseEvent| set_cur_node_enter.emit(HighlightAction::Show(asn1_node_id))); + let set_cur_node_leave = set_cur_node.clone(); + let onmouseleave = + Callback::from(move |_: MouseEvent| set_cur_node_leave.emit(HighlightAction::Hide(asn1_node_id))); + + let bytes_len = bytes.len(); + if bytes_len > MAX_BYTES_TO_RENDER { + format_bytes( + meta, + &bytes[0..MAX_BYTES_TO_RENDER / 2], + asn1_node_id, + class.clone(), + set_cur_node.clone(), + formatted_bytes, + ); + formatted_bytes.push(html! { - + - }) - }); + }); + + format_bytes( + meta, + &bytes[bytes_len - MAX_BYTES_TO_RENDER / 2..], + asn1_node_id, + class, + set_cur_node.clone(), + formatted_bytes, + ); + } else { + bytes.iter().for_each(|byte| { + formatted_bytes.push(html! { + + + + }) + }); + } } fn build_hex_bytes( From 71bb147d9764cf01d53559ffbdaf9bfa8c8f05e5 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sat, 9 Mar 2024 23:42:27 +0200 Subject: [PATCH 2/5] refactor(crypto-helper): asn1: use reference counting to reduce data cloning. Previously, for every hex byte node, NodeOptions component, copy operation, and so on, the app has cloned entire node bytes. This patch introduces the `RcSlice` structure. This structure represents the slice with multiple owners. But actually, it's just a `Vec` in the `Rc` plus range. These changes affect the hex viewer and `NodeOptions` components the most. We don't want to cause a big refactoring for now. We have better performance and memory efficiency. `Vec`s cloning now is not a bottleneck. But the noticeable freezes are still present. --- src/asn1/hex_view.rs | 66 +++++++++++++++++++++++---------- src/asn1/macros.rs | 2 +- src/asn1/node_options.rs | 11 ++++-- src/asn1/scheme/oid.rs | 3 +- src/asn1/scheme/primitive.rs | 7 ++-- src/asn1/scheme/sequence.rs | 3 +- src/asn1/scheme/set.rs | 3 +- src/asn1/scheme/strings.rs | 11 +++--- src/asn1/scheme/tag.rs | 9 +++-- src/asn1/scheme/time.rs | 5 ++- src/common/mod.rs | 2 + src/common/rc_slice.rs | 71 ++++++++++++++++++++++++++++++++++++ 12 files changed, 152 insertions(+), 41 deletions(-) create mode 100644 src/common/rc_slice.rs diff --git a/src/asn1/hex_view.rs b/src/asn1/hex_view.rs index d43dd384..4d7d35e7 100644 --- a/src/asn1/hex_view.rs +++ b/src/asn1/hex_view.rs @@ -5,6 +5,7 @@ use yew::{classes, function_component, html, Callback, Classes, Html, Properties use crate::asn1::node_options::NodeOptions; use crate::asn1::{compare_ids, HighlightAction}; +use crate::common::RcSlice; #[derive(PartialEq, Properties, Clone)] pub struct HexViewerProps { @@ -34,6 +35,7 @@ const MAX_BYTES_TO_RENDER: usize = 512; fn format_bytes( meta: &RawAsn1EntityData, + raw_bytes: RcSlice, bytes: &[u8], asn1_node_id: u64, class: Classes, @@ -55,6 +57,7 @@ fn format_bytes( if bytes_len > MAX_BYTES_TO_RENDER { format_bytes( meta, + raw_bytes.clone(), &bytes[0..MAX_BYTES_TO_RENDER / 2], asn1_node_id, class.clone(), @@ -64,12 +67,13 @@ fn format_bytes( formatted_bytes.push(html! { - + }); format_bytes( meta, + raw_bytes, &bytes[bytes_len - MAX_BYTES_TO_RENDER / 2..], asn1_node_id, class, @@ -80,7 +84,7 @@ fn format_bytes( bytes.iter().for_each(|byte| { formatted_bytes.push(html! { - + }) }); @@ -104,6 +108,7 @@ fn build_hex_bytes( let onmouseleave = Callback::from(move |_: MouseEvent| tag_set_cur_node.emit(HighlightAction::Hide(asn1_node_id))); let meta = asn1.meta(); + let raw_bytes = RcSlice::new(meta.raw_bytes().to_vec(), 0, meta.raw_bytes().len()); let offset = meta.tag_position(); let length_len = meta.length_range().len(); @@ -121,12 +126,13 @@ fn build_hex_bytes( {onmouseenter} {onmouseleave} > - + }); format_bytes( meta, + raw_bytes.clone(), asn1.meta().length_bytes(), asn1_node_id, if select_all { @@ -142,6 +148,7 @@ fn build_hex_bytes( build_data_bytes( asn1, + raw_bytes, asn1_node_id, cur_node, set_cur_node, @@ -152,6 +159,7 @@ fn build_hex_bytes( fn build_data_bytes( asn1: &Asn1<'_>, + raw_bytes: RcSlice, asn1_node_id: u64, cur_node: &Option, set_cur_node: Callback, @@ -163,6 +171,7 @@ fn build_data_bytes( cur_node: &Option, set_cur_node: Callback, asn1: &Asn1<'_>, + raw_bytes: RcSlice, bytes: &mut Vec, select_all: bool, ) { @@ -170,6 +179,7 @@ fn build_data_bytes( format_bytes( asn1.meta(), + raw_bytes, asn1.meta().data_bytes(), asn1_node_id, if if_selected || select_all { @@ -198,25 +208,43 @@ fn build_data_bytes( } Asn1Type::OctetString(octet) => match octet.inner() { Some(asn1) => build_hex_bytes(asn1, cur_node, set_cur_node.clone(), bytes, select_all), - None => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), + None => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all), }, - Asn1Type::Utf8String(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::IA5String(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::PrintableString(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::GeneralString(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::NumericString(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::VisibleString(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::UtcTime(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::GeneralizedTime(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), + Asn1Type::Utf8String(_) => { + default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all) + } + Asn1Type::IA5String(_) => { + default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all) + } + Asn1Type::PrintableString(_) => { + default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all) + } + Asn1Type::GeneralString(_) => { + default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all) + } + Asn1Type::NumericString(_) => { + default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all) + } + Asn1Type::VisibleString(_) => { + default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all) + } + Asn1Type::UtcTime(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all), + Asn1Type::GeneralizedTime(_) => { + default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all) + } Asn1Type::BitString(bit) => match bit.inner() { Some(asn1) => build_hex_bytes(asn1, cur_node, set_cur_node.clone(), bytes, select_all), - None => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), + None => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all), }, - Asn1Type::BmpString(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::Bool(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::Null(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::Integer(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), - Asn1Type::ObjectIdentifier(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), + Asn1Type::BmpString(_) => { + default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all) + } + Asn1Type::Bool(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all), + Asn1Type::Null(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all), + Asn1Type::Integer(_) => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all), + Asn1Type::ObjectIdentifier(_) => { + default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all) + } Asn1Type::ExplicitTag(explicit) => { let set_cur_node = set_cur_node.clone(); explicit @@ -226,7 +254,7 @@ fn build_data_bytes( } Asn1Type::ImplicitTag(implicit) => match implicit.inner_asn1() { Some(asn1) => build_hex_bytes(asn1, cur_node, set_cur_node.clone(), bytes, select_all), - None => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, bytes, select_all), + None => default_bytes(asn1_node_id, cur_node, set_cur_node, asn1, raw_bytes, bytes, select_all), }, Asn1Type::ApplicationTag(application) => { let set_cur_node = set_cur_node.clone(); diff --git a/src/asn1/macros.rs b/src/asn1/macros.rs index 9dbd20ac..6bf16bf0 100644 --- a/src/asn1/macros.rs +++ b/src/asn1/macros.rs @@ -16,7 +16,7 @@ macro_rules! define_string_node { html! {
- + {props.node.string().to_owned()}
} diff --git a/src/asn1/node_options.rs b/src/asn1/node_options.rs index 4bb5d949..6c19b116 100644 --- a/src/asn1/node_options.rs +++ b/src/asn1/node_options.rs @@ -2,10 +2,12 @@ use yew::{function_component, html, use_state, Callback, Html, Properties}; use yew_hooks::use_clipboard; use yew_notifications::{use_notification, Notification, NotificationType}; +use crate::common::RcSlice; + #[derive(PartialEq, Properties, Clone)] pub struct NodeOptionsProps { pub name: String, - pub node_bytes: Vec, + pub node_bytes: RcSlice, pub offset: usize, pub length_len: usize, pub data_len: usize, @@ -28,9 +30,10 @@ pub fn node_options(props: &NodeOptionsProps) -> Html { let clipboard = use_clipboard(); let notifications = use_notification::(); - let value_raw = props.node_bytes[props.length_len + 1..].to_vec(); + let node_bytes_len = props.node_bytes.len(); + let value_raw = props.node_bytes.with_range(props.length_len + 1, node_bytes_len); let copy_value = Callback::from(move |_| { - clipboard.write_text(hex::encode(&value_raw)); + clipboard.write_text(hex::encode(value_raw.data())); notifications.spawn(Notification::from_description_and_type( NotificationType::Info, @@ -42,7 +45,7 @@ pub fn node_options(props: &NodeOptionsProps) -> Html { let notifications = use_notification::(); let node_raw = props.node_bytes.clone(); let copy_node = Callback::from(move |_| { - clipboard.write_text(hex::encode(&node_raw)); + clipboard.write_text(hex::encode(node_raw.data())); notifications.spawn(Notification::from_description_and_type( NotificationType::Info, diff --git a/src/asn1/scheme/oid.rs b/src/asn1/scheme/oid.rs index 0868cc84..d29caffc 100644 --- a/src/asn1/scheme/oid.rs +++ b/src/asn1/scheme/oid.rs @@ -2,6 +2,7 @@ use asn1_parser::{ObjectIdentifier, OwnedRawAsn1EntityData}; use yew::{function_component, html, Html, Properties}; use crate::asn1::node_options::NodeOptions; +use crate::common::RcSlice; #[derive(PartialEq, Properties, Clone)] pub struct ObjectIdentifierProps { @@ -19,7 +20,7 @@ pub fn bool(props: &ObjectIdentifierProps) -> Html { html! {
- + {&formatted} {{ let (name, url) = oid_name(&formatted); diff --git a/src/asn1/scheme/primitive.rs b/src/asn1/scheme/primitive.rs index 4f34c19a..2ba44af8 100644 --- a/src/asn1/scheme/primitive.rs +++ b/src/asn1/scheme/primitive.rs @@ -2,6 +2,7 @@ use asn1_parser::{Bool, OwnedInteger, OwnedRawAsn1EntityData}; use yew::{function_component, html, Html, Properties}; use crate::asn1::node_options::NodeOptions; +use crate::common::RcSlice; #[derive(PartialEq, Properties, Clone)] pub struct BoolNodeProps { @@ -17,7 +18,7 @@ pub fn bool(props: &BoolNodeProps) -> Html { html! {
- + {if props.node.value() {html! { {"true"} }} else {html! { @@ -40,7 +41,7 @@ pub fn null(props: &NullNodeProps) -> Html { html! {
- +
} } @@ -59,7 +60,7 @@ pub fn integer(props: &IntegerNodeProps) -> Html { html! {
- + {format!("{}", props.node.as_big_uint())}
} diff --git a/src/asn1/scheme/sequence.rs b/src/asn1/scheme/sequence.rs index a1675a9e..641ea6df 100644 --- a/src/asn1/scheme/sequence.rs +++ b/src/asn1/scheme/sequence.rs @@ -4,6 +4,7 @@ use yew::{function_component, html, Callback, Html, Properties}; use crate::asn1::node_options::NodeOptions; use crate::asn1::scheme::build_asn1_schema; use crate::asn1::HighlightAction; +use crate::common::RcSlice; #[derive(PartialEq, Properties, Clone)] pub struct SequenceNodeProps { @@ -30,7 +31,7 @@ pub fn sequence(props: &SequenceNodeProps) -> Html { html! {
- + {format!("({} fields)", fields.len())}
diff --git a/src/asn1/scheme/set.rs b/src/asn1/scheme/set.rs index 635c244e..584ed4d5 100644 --- a/src/asn1/scheme/set.rs +++ b/src/asn1/scheme/set.rs @@ -4,6 +4,7 @@ use yew::{function_component, html, Callback, Html, Properties}; use crate::asn1::node_options::NodeOptions; use crate::asn1::scheme::build_asn1_schema; use crate::asn1::HighlightAction; +use crate::common::RcSlice; #[derive(PartialEq, Properties, Clone)] pub struct SetNodeProps { @@ -30,7 +31,7 @@ pub fn set(props: &SetNodeProps) -> Html { html! {
- + {format!("({} fields)", fields.len())}
diff --git a/src/asn1/scheme/strings.rs b/src/asn1/scheme/strings.rs index 51fcea8b..8fa72500 100644 --- a/src/asn1/scheme/strings.rs +++ b/src/asn1/scheme/strings.rs @@ -7,6 +7,7 @@ use yew::{function_component, html, Callback, Html, Properties}; use crate::asn1::node_options::NodeOptions; use crate::asn1::scheme::build_asn1_schema; use crate::asn1::HighlightAction; +use crate::common::RcSlice; #[derive(PartialEq, Properties, Clone)] pub struct OctetStringNodeProps { @@ -28,7 +29,7 @@ pub fn octet_string(props: &OctetStringNodeProps) -> Html { Some(asn1) => html! {
- + {format!("({} bytes)", octets.len())}
@@ -43,7 +44,7 @@ pub fn octet_string(props: &OctetStringNodeProps) -> Html { }; html! {
- + {format!("({} bytes)", octets.len())} {encoded_octets}
@@ -79,7 +80,7 @@ pub fn bit_string(props: &BitStringNodeProps) -> Html { Some(asn1) => html! {
- + {format!("({} bits)", bits_amount)}
@@ -90,7 +91,7 @@ pub fn bit_string(props: &BitStringNodeProps) -> Html { None => { html! {
- + {format!("({} bits)", bits_amount)} {bits}
@@ -121,7 +122,7 @@ pub fn bmp_string(props: &BmpStringNodeProps) -> Html { html! {
- + {s}
} diff --git a/src/asn1/scheme/tag.rs b/src/asn1/scheme/tag.rs index 6de7550a..f4c4bdb5 100644 --- a/src/asn1/scheme/tag.rs +++ b/src/asn1/scheme/tag.rs @@ -6,6 +6,7 @@ use yew::{function_component, html, Callback, Html, Properties}; use crate::asn1::node_options::NodeOptions; use crate::asn1::scheme::build_asn1_schema; use crate::asn1::HighlightAction; +use crate::common::RcSlice; #[derive(PartialEq, Properties, Clone)] pub struct ExplicitTagProps { @@ -32,7 +33,7 @@ pub fn explicit_tag(props: &ExplicitTagProps) -> Html { html! {
- +
{inner_components} @@ -66,7 +67,7 @@ pub fn application_tag(props: &ApplicationTagProps) -> Html { html! {
- +
{inner_components} @@ -93,7 +94,7 @@ pub fn implicit_tag(props: &ImplicitTagProps) -> Html { Some(asn1) => html! {
- +
{build_asn1_schema(asn1, &props.cur_node, &props.set_cur_node)} @@ -102,7 +103,7 @@ pub fn implicit_tag(props: &ImplicitTagProps) -> Html { }, None => html! {
- + {format!("({} bytes)", octets.len())} {if let Ok(s) = from_utf8(octets) { html! { {s} diff --git a/src/asn1/scheme/time.rs b/src/asn1/scheme/time.rs index bb9fe8de..ede9690d 100644 --- a/src/asn1/scheme/time.rs +++ b/src/asn1/scheme/time.rs @@ -2,6 +2,7 @@ use asn1_parser::{GeneralizedTime, OwnedRawAsn1EntityData, UtcTime}; use yew::{function_component, html, Html, Properties}; use crate::asn1::node_options::NodeOptions; +use crate::common::RcSlice; #[derive(PartialEq, Properties, Clone)] pub struct UtcTimeNodeProps { @@ -17,7 +18,7 @@ pub fn utc_time_string(props: &UtcTimeNodeProps) -> Html { html! {
- + {format_utc_time(&props.node)}
} @@ -37,7 +38,7 @@ pub fn general_time_string(props: &GeneralizedTimeNodeProps) -> Html { html! {
- + {format_generalized_time(&props.node)}
} diff --git a/src/common/mod.rs b/src/common/mod.rs index bec9513d..bf4ac8f9 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,12 +1,14 @@ mod byte_input; mod bytes_viewer; mod checkbox; +mod rc_slice; mod simple_output; mod switch; mod table; pub use byte_input::{build_byte_input, ByteInput}; pub use checkbox::Checkbox; +pub use rc_slice::RcSlice; pub use simple_output::build_simple_output; pub use switch::Switch; pub use table::TableView; diff --git a/src/common/rc_slice.rs b/src/common/rc_slice.rs new file mode 100644 index 00000000..e2b75eca --- /dev/null +++ b/src/common/rc_slice.rs @@ -0,0 +1,71 @@ +use std::rc::Rc; + +macro_rules! check_boundaries { + ($start:expr, $end:expr, $arr:expr) => { + if $start > $end { + panic!("start can not be greater then end: start({}) > end({}).", $start, $end); + } + if $arr.len() < $end { + panic!( + "end can not be greater then slice len: slice_len({}) < end({}).", + $arr.len(), + $end + ) + } + }; +} + +#[derive(Debug, PartialEq)] +pub struct RcSlice { + data: Rc>, + start: usize, + end: usize, +} + +impl RcSlice { + pub fn new(data: Vec, start: usize, end: usize) -> Self { + check_boundaries!(start, end, data); + Self { + data: Rc::new(data), + start, + end, + } + } + + pub fn data(&self) -> &[u8] { + &self.data[self.start..self.end] + } + + pub fn with_range(&self, start: usize, end: usize) -> Self { + check_boundaries!(start, end, self.data); + Self { + data: Rc::clone(&self.data), + start, + end, + } + } + + pub fn len(&self) -> usize { + self.data.len() + } +} + +impl Clone for RcSlice { + fn clone(&self) -> Self { + Self { + data: Rc::clone(&self.data), + start: self.start, + end: self.end, + } + } +} + +impl From<&[u8]> for RcSlice { + fn from(value: &[u8]) -> Self { + Self { + data: Rc::new(value.to_vec()), + start: 0, + end: value.len(), + } + } +} From 7a5e21b52abc21880ccb627bc2082b01767c8b16 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 10 Mar 2024 00:48:23 +0200 Subject: [PATCH 3/5] refactor(crypto-helper): asn1: improve performance using rendering and formatting hacks. Reduced different conversions from `T` into `Classes` as much as possible. It was unexpected, but a lot of `Classes` object constructions can cause performance issues. Replaced the `format!("{:02x?}", byte)` macro invocation with a simple function with a big match that returns a static string. --- src/asn1/hex_view.rs | 29 ++-- src/common/bytes_viewer.rs | 4 +- src/common/mod.rs | 270 ++++++++++++++++++++++++++++++++++++- 3 files changed, 284 insertions(+), 19 deletions(-) diff --git a/src/asn1/hex_view.rs b/src/asn1/hex_view.rs index 4d7d35e7..104a766f 100644 --- a/src/asn1/hex_view.rs +++ b/src/asn1/hex_view.rs @@ -1,11 +1,11 @@ use asn1_parser::{Asn1, Asn1Entity, Asn1Type, OwnedAsn1, RawAsn1EntityData}; use web_sys::MouseEvent; use yew::virtual_dom::VNode; -use yew::{classes, function_component, html, Callback, Classes, Html, Properties}; +use yew::{function_component, html, Callback, Classes, Html, Properties}; use crate::asn1::node_options::NodeOptions; use crate::asn1::{compare_ids, HighlightAction}; -use crate::common::RcSlice; +use crate::common::{hex_format_byte, RcSlice}; #[derive(PartialEq, Properties, Clone)] pub struct HexViewerProps { @@ -38,7 +38,7 @@ fn format_bytes( raw_bytes: RcSlice, bytes: &[u8], asn1_node_id: u64, - class: Classes, + class: &'static str, set_cur_node: Callback, formatted_bytes: &mut Vec, ) { @@ -52,6 +52,7 @@ fn format_bytes( let set_cur_node_leave = set_cur_node.clone(); let onmouseleave = Callback::from(move |_: MouseEvent| set_cur_node_leave.emit(HighlightAction::Hide(asn1_node_id))); + let yew_class = Classes::from(&["asn1-hex-byte", class] as &[&'static str]); let bytes_len = bytes.len(); if bytes_len > MAX_BYTES_TO_RENDER { @@ -60,14 +61,14 @@ fn format_bytes( raw_bytes.clone(), &bytes[0..MAX_BYTES_TO_RENDER / 2], asn1_node_id, - class.clone(), + class, set_cur_node.clone(), formatted_bytes, ); formatted_bytes.push(html! { - - + + }); @@ -83,8 +84,8 @@ fn format_bytes( } else { bytes.iter().for_each(|byte| { formatted_bytes.push(html! { - - + + }) }); @@ -126,7 +127,7 @@ fn build_hex_bytes( {onmouseenter} {onmouseleave} > - + }); @@ -136,11 +137,11 @@ fn build_hex_bytes( asn1.meta().length_bytes(), asn1_node_id, if select_all { - classes!("asn1-hex-byte-data-selected") + "asn1-hex-byte-data-selected" } else if if_selected { - classes!("asn1-hex-byte-len-selected") + "asn1-hex-byte-len-selected" } else { - classes!("asn1-hex-byte-len") + "asn1-hex-byte-len" }, set_cur_node.clone(), bytes, @@ -183,9 +184,9 @@ fn build_data_bytes( asn1.meta().data_bytes(), asn1_node_id, if if_selected || select_all { - classes!("asn1-hex-byte-data-selected") + "asn1-hex-byte-data-selected" } else { - classes!("asn1-hex-byte-data") + "asn1-hex-byte-data" }, set_cur_node, bytes, diff --git a/src/common/bytes_viewer.rs b/src/common/bytes_viewer.rs index 36715c85..645d4a14 100644 --- a/src/common/bytes_viewer.rs +++ b/src/common/bytes_viewer.rs @@ -1,5 +1,7 @@ use yew::{classes, function_component, html, Html, Properties}; +use crate::common::hex_format_byte; + #[derive(PartialEq, Eq, Properties)] pub struct BytesViewerProps { pub bytes: Vec, @@ -21,7 +23,7 @@ fn byte_color_class(byte: u8) -> &'static str { fn byte_component(byte: u8) -> Html { html! { - {format!("{:02x?}", byte)} + {hex_format_byte(byte)} } } diff --git a/src/common/mod.rs b/src/common/mod.rs index bf4ac8f9..7b7c3da4 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -13,7 +13,7 @@ pub use simple_output::build_simple_output; pub use switch::Switch; pub use table::TableView; use web_sys::MouseEvent; -use yew::{classes, Callback, Classes, UseStateSetter}; +use yew::{Callback, UseStateSetter}; use crate::utils::{decode_base64, decode_binary, decode_decimal}; @@ -118,11 +118,11 @@ fn parse_bytes(raw: &str, format: BytesFormat) -> Result, String> { } } -fn get_format_button_class(selected: bool) -> Classes { +fn get_format_button_class(selected: bool) -> &'static str { if selected { - classes!("format-button", "format-button-selected") + "format-button format-button-selected" } else { - classes!("format-button") + "format-button" } } @@ -131,3 +131,265 @@ fn get_set_format_callback(format: BytesFormat, set_format: UseStateSetter &'static str { + match byte { + 0 => "00", + 1 => "01", + 2 => "02", + 3 => "03", + 4 => "04", + 5 => "05", + 6 => "06", + 7 => "07", + 8 => "08", + 9 => "09", + 10 => "0a", + 11 => "0b", + 12 => "0c", + 13 => "0d", + 14 => "0e", + 15 => "0f", + 16 => "10", + 17 => "11", + 18 => "12", + 19 => "13", + 20 => "14", + 21 => "15", + 22 => "16", + 23 => "17", + 24 => "18", + 25 => "19", + 26 => "1a", + 27 => "1b", + 28 => "1c", + 29 => "1d", + 30 => "1e", + 31 => "1f", + 32 => "20", + 33 => "21", + 34 => "22", + 35 => "23", + 36 => "24", + 37 => "25", + 38 => "26", + 39 => "27", + 40 => "28", + 41 => "29", + 42 => "2a", + 43 => "2b", + 44 => "2c", + 45 => "2d", + 46 => "2e", + 47 => "2f", + 48 => "30", + 49 => "31", + 50 => "32", + 51 => "33", + 52 => "34", + 53 => "35", + 54 => "36", + 55 => "37", + 56 => "38", + 57 => "39", + 58 => "3a", + 59 => "3b", + 60 => "3c", + 61 => "3d", + 62 => "3e", + 63 => "3f", + 64 => "40", + 65 => "41", + 66 => "42", + 67 => "43", + 68 => "44", + 69 => "45", + 70 => "46", + 71 => "47", + 72 => "48", + 73 => "49", + 74 => "4a", + 75 => "4b", + 76 => "4c", + 77 => "4d", + 78 => "4e", + 79 => "4f", + 80 => "50", + 81 => "51", + 82 => "52", + 83 => "53", + 84 => "54", + 85 => "55", + 86 => "56", + 87 => "57", + 88 => "58", + 89 => "59", + 90 => "5a", + 91 => "5b", + 92 => "5c", + 93 => "5d", + 94 => "5e", + 95 => "5f", + 96 => "60", + 97 => "61", + 98 => "62", + 99 => "63", + 100 => "64", + 101 => "65", + 102 => "66", + 103 => "67", + 104 => "68", + 105 => "69", + 106 => "6a", + 107 => "6b", + 108 => "6c", + 109 => "6d", + 110 => "6e", + 111 => "6f", + 112 => "70", + 113 => "71", + 114 => "72", + 115 => "73", + 116 => "74", + 117 => "75", + 118 => "76", + 119 => "77", + 120 => "78", + 121 => "79", + 122 => "7a", + 123 => "7b", + 124 => "7c", + 125 => "7d", + 126 => "7e", + 127 => "7f", + 128 => "80", + 129 => "81", + 130 => "82", + 131 => "83", + 132 => "84", + 133 => "85", + 134 => "86", + 135 => "87", + 136 => "88", + 137 => "89", + 138 => "8a", + 139 => "8b", + 140 => "8c", + 141 => "8d", + 142 => "8e", + 143 => "8f", + 144 => "90", + 145 => "91", + 146 => "92", + 147 => "93", + 148 => "94", + 149 => "95", + 150 => "96", + 151 => "97", + 152 => "98", + 153 => "99", + 154 => "9a", + 155 => "9b", + 156 => "9c", + 157 => "9d", + 158 => "9e", + 159 => "9f", + 160 => "a0", + 161 => "a1", + 162 => "a2", + 163 => "a3", + 164 => "a4", + 165 => "a5", + 166 => "a6", + 167 => "a7", + 168 => "a8", + 169 => "a9", + 170 => "aa", + 171 => "ab", + 172 => "ac", + 173 => "ad", + 174 => "ae", + 175 => "af", + 176 => "b0", + 177 => "b1", + 178 => "b2", + 179 => "b3", + 180 => "b4", + 181 => "b5", + 182 => "b6", + 183 => "b7", + 184 => "b8", + 185 => "b9", + 186 => "ba", + 187 => "bb", + 188 => "bc", + 189 => "bd", + 190 => "be", + 191 => "bf", + 192 => "c0", + 193 => "c1", + 194 => "c2", + 195 => "c3", + 196 => "c4", + 197 => "c5", + 198 => "c6", + 199 => "c7", + 200 => "c8", + 201 => "c9", + 202 => "ca", + 203 => "cb", + 204 => "cc", + 205 => "cd", + 206 => "ce", + 207 => "cf", + 208 => "d0", + 209 => "d1", + 210 => "d2", + 211 => "d3", + 212 => "d4", + 213 => "d5", + 214 => "d6", + 215 => "d7", + 216 => "d8", + 217 => "d9", + 218 => "da", + 219 => "db", + 220 => "dc", + 221 => "dd", + 222 => "de", + 223 => "df", + 224 => "e0", + 225 => "e1", + 226 => "e2", + 227 => "e3", + 228 => "e4", + 229 => "e5", + 230 => "e6", + 231 => "e7", + 232 => "e8", + 233 => "e9", + 234 => "ea", + 235 => "eb", + 236 => "ec", + 237 => "ed", + 238 => "ee", + 239 => "ef", + 240 => "f0", + 241 => "f1", + 242 => "f2", + 243 => "f3", + 244 => "f4", + 245 => "f5", + 246 => "f6", + 247 => "f7", + 248 => "f8", + 249 => "f9", + 250 => "fa", + 251 => "fb", + 252 => "fc", + 253 => "fd", + 254 => "fe", + 255 => "ff", + } +} From 4150fdcc95ea11302b0d1e6d3553b9c78636fcbb Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 10 Mar 2024 01:30:35 +0200 Subject: [PATCH 4/5] fix(asn1-parser): utctime: neeeded buf length calculation. Add one byte to the overall length. This byte represents the 'Z' chat, that usuallu is placed in the end of the utc time object. --- crates/asn1-parser/Cargo.toml | 2 ++ crates/asn1-parser/src/asn1.rs | 2 +- crates/asn1-parser/src/lib.rs | 4 ++++ crates/asn1-parser/src/time/utc_time.rs | 3 ++- crates/asn1-parser/tests/decode_encode.rs | 12 ++++++++++++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/asn1-parser/Cargo.toml b/crates/asn1-parser/Cargo.toml index 33362448..4051b8c9 100644 --- a/crates/asn1-parser/Cargo.toml +++ b/crates/asn1-parser/Cargo.toml @@ -13,6 +13,7 @@ default-fearures = [] std = [] [dev-dependencies] +env_logger = "0.11.3" prop-strategies = { path = "../prop-strategies" } proptest = "1.2.0" @@ -22,3 +23,4 @@ num-bigint-dig = { version = "0.8.4", default-features = false } num-traits = { version = "0.2.17", default-features = false } oid = { version = "0.2.1", default-features = false } paste = "1.0.14" +env_logger = "0.11.3" diff --git a/crates/asn1-parser/src/asn1.rs b/crates/asn1-parser/src/asn1.rs index 1f160756..3e17a492 100644 --- a/crates/asn1-parser/src/asn1.rs +++ b/crates/asn1-parser/src/asn1.rs @@ -212,7 +212,7 @@ impl MetaInfo for Asn1Type<'_> { /// Information about raw data of the asn1 entity #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct RawAsn1EntityData<'data> { - /// Raw input bytes for the current asn1 node + /// Raw input bytes for the *current* asn1 node pub raw_data: Cow<'data, [u8]>, /// Position of the tag in the input data diff --git a/crates/asn1-parser/src/lib.rs b/crates/asn1-parser/src/lib.rs index 9d8f3f70..2ae8465f 100644 --- a/crates/asn1-parser/src/lib.rs +++ b/crates/asn1-parser/src/lib.rs @@ -5,6 +5,10 @@ extern crate alloc; #[macro_use] mod macros; +#[allow(unused_imports)] +#[macro_use] +extern crate log; + mod asn1; mod constructors; mod error; diff --git a/crates/asn1-parser/src/time/utc_time.rs b/crates/asn1-parser/src/time/utc_time.rs index 8f07b2e1..1908a6da 100644 --- a/crates/asn1-parser/src/time/utc_time.rs +++ b/crates/asn1-parser/src/time/utc_time.rs @@ -31,7 +31,8 @@ impl UtcTime { } fn calc_data_len(&self) -> usize { - 2 /* year */ + 2 /* month */ + 2 /* day */ + 2 /* hour */ + 2 /* minute */ + self.second.is_some().then_some(2).unwrap_or_default() + 2 /* year */ + 2 /* month */ + 2 /* day */ + 2 /* hour */ + 2 /* minute */ + self.second.is_some().then_some(2).unwrap_or_default() + 1 + /* 'Z' */ } } diff --git a/crates/asn1-parser/tests/decode_encode.rs b/crates/asn1-parser/tests/decode_encode.rs index d646267b..c1b4f2d4 100644 --- a/crates/asn1-parser/tests/decode_encode.rs +++ b/crates/asn1-parser/tests/decode_encode.rs @@ -121,3 +121,15 @@ fn decode_default() { let asn1 = Asn1::decode_buff(raw).unwrap(); println!("{:?}", asn1); } + +#[test] +fn decode_utc() { + std::env::set_var("RUST_LOG", "trace"); + env_logger::init(); + + let raw = &[23, 13, 49, 56, 48, 55, 49, 54, 49, 52, 53, 54, 51, 53, 90]; + let asn1 = Asn1::decode_buff(raw).unwrap(); + + let mut encoded = vec![0; asn1.needed_buf_size()]; + asn1.encode_buff(&mut encoded).expect("ASN1 encoding should not fail"); +} From 28570c4254bf064b78e557bd4b599047b9a152b0 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Sun, 10 Mar 2024 01:32:38 +0200 Subject: [PATCH 5/5] feat(crypto-helper): asn1: improve loadong from local storage. Now the data will also be set in the input textarea during loading from local storage. --- Cargo.lock | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/asn1.rs | 1 + 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5392ae9e..a204e127 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,63 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anymap2" version = "0.13.0" @@ -38,6 +95,7 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" name = "asn1-parser" version = "0.1.0" dependencies = [ + "env_logger", "log", "num-bigint-dig", "num-traits", @@ -227,6 +285,12 @@ dependencies = [ "inout", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -394,6 +458,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "errno" version = "0.3.8" @@ -791,6 +878,12 @@ dependencies = [ "itoa", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "implicit-clone" version = "0.3.9" @@ -884,9 +977,9 @@ checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "md-5" @@ -1485,6 +1578,29 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-syntax" version = "0.8.2" @@ -1942,6 +2058,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.6.1" diff --git a/src/asn1.rs b/src/asn1.rs index 84993c23..dcaee5c3 100644 --- a/src/asn1.rs +++ b/src/asn1.rs @@ -124,6 +124,7 @@ pub fn asn1_parser_page() -> Html { error!("Can not decode asn1: {:?}", err); } } + raw_asn1_setter.set(bytes); } } return;