From 8a853d823ce3d4f3beebf3fc9176a5a3d7779535 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 1 Mar 2024 22:20:43 +0200 Subject: [PATCH 1/4] feat(crypto-helper): use globally all log macros. improve logging code --- src/asn1.rs | 4 ++-- src/crypto_helper.rs | 2 ++ src/main.rs | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/asn1.rs b/src/asn1.rs index 88e10660..f4dec698 100644 --- a/src/asn1.rs +++ b/src/asn1.rs @@ -83,7 +83,7 @@ pub fn asn1_parser_page() -> Html { let raw_data = (*raw_asn1).clone(); let parse_asn1 = Callback::from(move |_| match Asn1::decode_buff(&raw_data) { Ok(asn1) => { - log::debug!("parsed!"); + debug!("parsed!"); asn1_setter.set(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned())); } Err(error) => notifications.spawn(Notification::new( @@ -122,7 +122,7 @@ pub fn asn1_parser_page() -> Html { let url_query_params::Asn1 { asn1: asn1_data } = asn1; match Asn1::decode_buff(&asn1_data) { Ok(asn1) => { - log::debug!("parsed!"); + debug!("parsed!"); asn1_setter.set(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned())); } Err(error) => notifications.spawn(Notification::new( diff --git a/src/crypto_helper.rs b/src/crypto_helper.rs index c4879b5c..2a374157 100644 --- a/src/crypto_helper.rs +++ b/src/crypto_helper.rs @@ -75,6 +75,8 @@ pub fn crypto_helper() -> Html { move |_: &[(); 0]| { let query = &location.search; + debug!("query len: {}", query.len()); + if query.len() < 2 { return; } diff --git a/src/main.rs b/src/main.rs index e6286942..d9b22a64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate log; + mod about; mod asn1; mod common; From f52be568f7fe86e06ada33f1efc90220f7b3b198 Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 1 Mar 2024 22:42:30 +0200 Subject: [PATCH 2/4] feat(crypto-helper): crypto-helper: implement local storage support; --- Cargo.toml | 2 +- src/crypto_helper.rs | 62 ++++++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 434a3a58..af003c83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ yew = { version = "0.20", features = ["csr"] } yew-router = "0.17.0" yew-notifications = { git = "https://github.com/TheBestTvarynka/yew-notifications.git", features = ["standard-notification"] } +yew-hooks = "0.2.0" # wasm js-sys = "0.3.60" @@ -47,7 +48,6 @@ sha1 = "0.10.5" hmac-sha256 = "1.1.5" hmac-sha512 = { version = "1.1.2", features = ["sha384"] } rsa = "0.7.2" -yew-hooks = "0.2.0" bcrypt = "0.14.0" flate2 = { version = "1.0.26", features = ["zlib"] } diff --git a/src/crypto_helper.rs b/src/crypto_helper.rs index 2a374157..456610b8 100644 --- a/src/crypto_helper.rs +++ b/src/crypto_helper.rs @@ -14,13 +14,15 @@ use picky_krb::crypto::{ChecksumSuite, CipherSuite}; use sha1::{Digest, Sha1}; use web_sys::KeyboardEvent; use yew::{function_component, html, use_effect_with_deps, use_state, Callback, Html}; -use yew_hooks::{use_clipboard, use_location}; +use yew_hooks::{use_clipboard, use_local_storage, use_location}; use yew_notifications::{use_notification, Notification, NotificationType}; use self::computations::{process_krb_cipher, process_krb_hmac, process_rsa, process_zlib}; use crate::crypto_helper::computations::process_bcrypt; use crate::url_query_params::generate_crypto_helper_link; +const CRYPTO_HELPER_LOCAL_STORAGE_KEY: &str = "CRYPTO_HELPER_DATA"; + fn convert(algrithm: &Algorithm) -> Result, String> { match algrithm { Algorithm::Md5(input) => Ok(md5::compute(input).to_vec()), @@ -71,31 +73,59 @@ pub fn crypto_helper() -> Html { let algorithm_setter = algorithm.setter(); let location = use_location(); let notifications = notification_manager.clone(); + let local_storage = use_local_storage::(CRYPTO_HELPER_LOCAL_STORAGE_KEY.to_owned()); use_effect_with_deps( move |_: &[(); 0]| { let query = &location.search; - debug!("query len: {}", query.len()); - - if query.len() < 2 { - return; - } - - match serde_qs::from_str(&query[1..]) { - Ok(algorithm) => { - algorithm_setter.set(algorithm); + // First, we try to load data from the url. + // question mark + one any other char + if query.len() >= 2 { + match serde_qs::from_str(&query[1..]) { + Ok(algorithm) => { + algorithm_setter.set(algorithm); + } + Err(err) => notifications.spawn(Notification::new( + NotificationType::Error, + "Can not load data from url", + err.to_string(), + Notification::NOTIFICATION_LIFETIME, + )), + } + } else { + // Otherwise, we try to find a data in the local storage. + let raw_data = if let Some(raw_data) = (*local_storage).as_ref() { + raw_data.as_str() + } else { + return; + }; + match serde_json::from_str(raw_data) { + Ok(algorithm) => { + algorithm_setter.set(algorithm); + } + Err(err) => notifications.spawn(Notification::new( + NotificationType::Error, + "Can not load data from the local storage", + err.to_string(), + Notification::NOTIFICATION_LIFETIME, + )), } - Err(err) => notifications.spawn(Notification::new( - NotificationType::Error, - "Can not load data from url", - err.to_string(), - Notification::NOTIFICATION_LIFETIME, - )), } }, [], ); + let local_storage = use_local_storage::(CRYPTO_HELPER_LOCAL_STORAGE_KEY.to_owned()); + use_effect_with_deps( + move |algorithm| { + let algorithm: &Algorithm = &*algorithm; + local_storage.set( + serde_json::to_string(algorithm).expect("algorithm serialization into json string should never fail"), + ); + }, + algorithm.clone(), + ); + let algorithm_data = (*algorithm).clone(); let clipboard = use_clipboard(); let share_by_link = Callback::from(move |_| { From 3972b840afd4f4bb0f24f2d3d8e7ec80a2d4a03e Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 1 Mar 2024 23:15:13 +0200 Subject: [PATCH 3/4] feat(crypto-helper): jwt: implement local storage support. refactor(crypto-helper & jwt): move serde module to root. small refactoring. --- src/crypto_helper.rs | 3 +-- src/crypto_helper/algorithm.rs | 2 +- src/jwt.rs | 26 +++++++++++++++++++++++++- src/jwt/jwt.rs | 5 ++++- src/jwt/jwt_utils.rs | 2 +- src/jwt/signature.rs | 8 +++++++- src/main.rs | 1 + src/{crypto_helper => }/serde.rs | 0 src/url_query_params.rs | 2 +- 9 files changed, 41 insertions(+), 8 deletions(-) rename src/{crypto_helper => }/serde.rs (100%) diff --git a/src/crypto_helper.rs b/src/crypto_helper.rs index 456610b8..b3b2832f 100644 --- a/src/crypto_helper.rs +++ b/src/crypto_helper.rs @@ -4,7 +4,6 @@ mod info; mod input; mod macros; mod output; -pub mod serde; pub use algorithm::Algorithm; use info::Info; @@ -118,7 +117,7 @@ pub fn crypto_helper() -> Html { let local_storage = use_local_storage::(CRYPTO_HELPER_LOCAL_STORAGE_KEY.to_owned()); use_effect_with_deps( move |algorithm| { - let algorithm: &Algorithm = &*algorithm; + let algorithm: &Algorithm = algorithm; local_storage.set( serde_json::to_string(algorithm).expect("algorithm serialization into json string should never fail"), ); diff --git a/src/crypto_helper/algorithm.rs b/src/crypto_helper/algorithm.rs index 0ad0e658..a3397240 100644 --- a/src/crypto_helper/algorithm.rs +++ b/src/crypto_helper/algorithm.rs @@ -4,7 +4,7 @@ use rsa::pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey}; use rsa::{RsaPrivateKey, RsaPublicKey}; use serde::{Deserialize, Serialize}; -use super::serde::*; +use crate::serde::*; pub const MD5: &str = "MD5"; pub const SHA1: &str = "SHA1"; diff --git a/src/jwt.rs b/src/jwt.rs index 53d53fd1..34993b28 100644 --- a/src/jwt.rs +++ b/src/jwt.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use web_sys::{HtmlInputElement, KeyboardEvent}; use yew::{function_component, html, use_effect_with_deps, use_state, Callback, Html, TargetCast}; -use yew_hooks::use_location; +use yew_hooks::{use_local_storage, use_location}; use yew_notifications::{use_notification, Notification, NotificationType}; use crate::common::Checkbox; @@ -20,6 +20,7 @@ use crate::jwt::jwt_utils::JwtUtils; use crate::jwt::jwte::Jwte; use crate::url_query_params; +const JWT_LOCAL_STORAGE_KEY: &str = "JWT_DATA"; const TEST_JWT: &str = "eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZSIsImV4cCI6MTY3MDAwNDI1NCwiaWF0IjoxNjcwMDA0MjU0fQ.ZGsN42vr-bM4uxXowtlNl7xRerkdKu6i29VS8DFQ4Tw"; #[function_component(Jwt)] @@ -86,11 +87,23 @@ pub fn jwt() -> Html { let jwt_setter = raw_jwt.setter(); let jwte_setter = jwte.setter(); let notifications = use_notification::(); + let local_storage = use_local_storage::(JWT_LOCAL_STORAGE_KEY.to_owned()); use_effect_with_deps( move |_: &[(); 0]| { let query = &location.search; if query.len() < 2 { + // URL query params is empty. We try to local JWT from local storage. + if let Some(raw_jwt) = (*local_storage).as_ref() { + match serde_json::from_str(raw_jwt.as_str()) { + Ok(jwt) => { + jwte_setter.set(Some(Jwte::Jwt(jwt))); + } + Err(err) => { + error!("Can not load JWT from local storage: {:?}", err); + } + } + } return; } @@ -125,6 +138,17 @@ pub fn jwt() -> Html { [], ); + let local_storage = use_local_storage::(JWT_LOCAL_STORAGE_KEY.to_owned()); + use_effect_with_deps( + move |jwte| { + let jwte: &Option = jwte; + if let Some(Jwte::Jwt(jwt)) = jwte { + local_storage.set(serde_json::to_string(jwt).expect("JWT serialization should not fail")); + } + }, + jwte.clone(), + ); + let jwte_setter = jwte.setter(); let set_jwt = Callback::from(move |jwt| { jwte_setter.set(Some(Jwte::Jwt(jwt))); diff --git a/src/jwt/jwt.rs b/src/jwt/jwt.rs index 60ee399f..3585fb24 100644 --- a/src/jwt/jwt.rs +++ b/src/jwt/jwt.rs @@ -1,11 +1,13 @@ +use serde::{Deserialize, Serialize}; use serde_json::Value; use super::signature::JwtSignatureAlgorithm; +use crate::serde::{deserialize_bytes, serialize_bytes}; pub mod editor; pub mod viewer; -#[derive(Debug, PartialEq, Eq, Default, Clone)] +#[derive(Debug, PartialEq, Eq, Default, Clone, Serialize, Deserialize)] pub struct Jwt { pub raw_header: String, pub parsed_header: String, @@ -15,6 +17,7 @@ pub struct Jwt { pub raw_signature: String, pub parsed_signature: String, + #[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")] pub signature: Vec, pub signature_algorithm: JwtSignatureAlgorithm, diff --git a/src/jwt/jwt_utils.rs b/src/jwt/jwt_utils.rs index 6df5c7aa..94429a73 100644 --- a/src/jwt/jwt_utils.rs +++ b/src/jwt/jwt_utils.rs @@ -427,7 +427,7 @@ fn validate_signature(jwt: &Jwt, spawn_notification: Callback) -> Some(jwt.signature == calculated_signature) } -fn generate_jwt(jwt: &Jwt, spawn_notification: Callback) -> Option> { +pub fn generate_jwt(jwt: &Jwt, spawn_notification: Callback) -> Option> { let signature = calculate_signature(jwt, spawn_notification)?; let header = base64::encode_config(jwt.parsed_header.as_bytes(), base64::URL_SAFE_NO_PAD); diff --git a/src/jwt/signature.rs b/src/jwt/signature.rs index 61f8c7a4..79daef96 100644 --- a/src/jwt/signature.rs +++ b/src/jwt/signature.rs @@ -1,22 +1,28 @@ use std::fmt::{self, Display}; +use serde::{Deserialize, Serialize}; use serde_json::Value; +use crate::serde::{deserialize_bytes, serialize_bytes}; + const JWT_SIGNATURE_ALGORITHMS: [&str; 10] = [ "HS256", "HS512", "none", "RS256", "HS384", "RS384", "RS512", "ES256", "ES384", "ES512", ]; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub enum JwtSignatureAlgorithm { None, /// HMAC using SHA-256 + #[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")] Hs256(Vec), /// HMAC using SHA-384 + #[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")] Hs384(Vec), /// HMAC using SHA-512 + #[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")] Hs512(Vec), /// RSASSA-PKCS1-v1_5 using SHA-256 diff --git a/src/main.rs b/src/main.rs index d9b22a64..27c20128 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ mod footer; mod header; mod jwt; mod not_found; +pub mod serde; mod url_query_params; mod utils; diff --git a/src/crypto_helper/serde.rs b/src/serde.rs similarity index 100% rename from src/crypto_helper/serde.rs rename to src/serde.rs diff --git a/src/url_query_params.rs b/src/url_query_params.rs index 7d7d6df6..60aba0a9 100644 --- a/src/url_query_params.rs +++ b/src/url_query_params.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; -use crate::crypto_helper::serde::{deserialize_bytes, serialize_bytes}; use crate::crypto_helper::Algorithm; +use crate::serde::{deserialize_bytes, serialize_bytes}; const APP_HOST: &str = env!("APP_HOST"); From 2b14b04faad5ed697d6b7625b535ab433a63c8db Mon Sep 17 00:00:00 2001 From: Pavlo Myroniuk Date: Fri, 1 Mar 2024 23:54:52 +0200 Subject: [PATCH 4/4] feat(crypto-helper): asn1: implement local storage support; --- src/asn1.rs | 32 ++++++++++++++++++++++++++++---- src/common/mod.rs | 2 +- src/jwt.rs | 2 +- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/asn1.rs b/src/asn1.rs index f4dec698..0db67ab6 100644 --- a/src/asn1.rs +++ b/src/asn1.rs @@ -8,15 +8,15 @@ mod scheme; use std::rc::Rc; -use asn1_parser::{Asn1, Asn1Decoder}; +use asn1_parser::{Asn1, Asn1Decoder, Asn1Encoder}; use web_sys::KeyboardEvent; use yew::{classes, function_component, html, use_effect_with_deps, use_reducer, use_state, Callback, Html, Reducible}; -use yew_hooks::{use_clipboard, use_location}; +use yew_hooks::{use_clipboard, use_local_storage, use_location}; use yew_notifications::{use_notification, Notification, NotificationType}; use crate::asn1::asn1_viewer::Asn1Viewer; use crate::asn1::hex_view::HexViewer; -use crate::common::ByteInput; +use crate::common::{encode_bytes, ByteInput, BytesFormat}; use crate::url_query_params; use crate::url_query_params::generate_asn1_link; @@ -26,6 +26,7 @@ pub const TEST_ASN1: &[u8] = &[ 48, 30, 160, 2, 5, 0, 161, 24, 30, 22, 0, 67, 0, 101, 0, 114, 0, 116, 0, 105, 0, 102, 0, 105, 0, 99, 0, 97, 0, 116, 0, 101, ]; +const ASN1_LOCAL_STORAGE_KEY: &str = "ASN1_DATA"; pub fn compare_ids(asn1_node_id: u64, cur_node: &Option) -> bool { matches!(cur_node, Some(node_id) if *node_id == asn1_node_id) @@ -109,11 +110,25 @@ pub fn asn1_parser_page() -> Html { let notifications = notification_manager.clone(); let raw_asn1_setter = raw_asn1.setter(); let asn1_setter = parsed_asn1.setter(); + let local_storage = use_local_storage::(ASN1_LOCAL_STORAGE_KEY.to_owned()); use_effect_with_deps( move |_: &[(); 0]| { let query = &location.search; if query.len() < 2 { + // URL query params is empty. We try to load ASN1 from local storage. + if let Some(raw_asn1) = (*local_storage).as_ref() { + if let Ok(bytes) = hex::decode(raw_asn1) { + match Asn1::decode_buff(&bytes) { + Ok(asn1) => { + asn1_setter.set(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned())); + } + Err(err) => { + error!("Can not decode asn1: {:?}", err); + } + } + } + } return; } @@ -122,7 +137,6 @@ pub fn asn1_parser_page() -> Html { let url_query_params::Asn1 { asn1: asn1_data } = asn1; match Asn1::decode_buff(&asn1_data) { Ok(asn1) => { - debug!("parsed!"); asn1_setter.set(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned())); } Err(error) => notifications.spawn(Notification::new( @@ -145,6 +159,16 @@ pub fn asn1_parser_page() -> Html { [], ); + let local_storage = use_local_storage::(ASN1_LOCAL_STORAGE_KEY.to_owned()); + use_effect_with_deps( + move |asn1| { + let mut encoded = vec![0; asn1.needed_buf_size()]; + asn1.encode_buff(&mut encoded).expect("ASN1 encoding should not fail"); + local_storage.set(encode_bytes(encoded, BytesFormat::Hex)); + }, + parsed_asn1.clone(), + ); + let clipboard = use_clipboard(); let raw_asn1_data = (*raw_asn1).clone(); let share_by_link = Callback::from(move |_| { diff --git a/src/common/mod.rs b/src/common/mod.rs index 891251b8..bec9513d 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -63,7 +63,7 @@ pub const BYTES_FORMATS: [BytesFormat; 5] = [ BytesFormat::Binary, ]; -fn encode_bytes(bytes: impl AsRef<[u8]>, format: BytesFormat) -> String { +pub fn encode_bytes(bytes: impl AsRef<[u8]>, format: BytesFormat) -> String { match format { BytesFormat::Hex => hex::encode(bytes), BytesFormat::Base64 => base64::encode(bytes), diff --git a/src/jwt.rs b/src/jwt.rs index 34993b28..7ba0bece 100644 --- a/src/jwt.rs +++ b/src/jwt.rs @@ -93,7 +93,7 @@ pub fn jwt() -> Html { let query = &location.search; if query.len() < 2 { - // URL query params is empty. We try to local JWT from local storage. + // URL query params is empty. We try to load JWT from local storage. if let Some(raw_jwt) = (*local_storage).as_ref() { match serde_json::from_str(raw_jwt.as_str()) { Ok(jwt) => {