Skip to content

Commit 0486f5e

Browse files
Merge pull request #59 from TheBestTvarynka/local-storage
Local storage support
2 parents b20bfc0 + 2b14b04 commit 0486f5e

12 files changed

+120
-28
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ members = [
1818
yew = { version = "0.20", features = ["csr"] }
1919
yew-router = "0.17.0"
2020
yew-notifications = { git = "https://github.com/TheBestTvarynka/yew-notifications.git", features = ["standard-notification"] }
21+
yew-hooks = "0.2.0"
2122

2223
# wasm
2324
js-sys = "0.3.60"
@@ -47,7 +48,6 @@ sha1 = "0.10.5"
4748
hmac-sha256 = "1.1.5"
4849
hmac-sha512 = { version = "1.1.2", features = ["sha384"] }
4950
rsa = "0.7.2"
50-
yew-hooks = "0.2.0"
5151
bcrypt = "0.14.0"
5252
flate2 = { version = "1.0.26", features = ["zlib"] }
5353

src/asn1.rs

+29-5
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ mod scheme;
88

99
use std::rc::Rc;
1010

11-
use asn1_parser::{Asn1, Asn1Decoder};
11+
use asn1_parser::{Asn1, Asn1Decoder, Asn1Encoder};
1212
use web_sys::KeyboardEvent;
1313
use yew::{classes, function_component, html, use_effect_with_deps, use_reducer, use_state, Callback, Html, Reducible};
14-
use yew_hooks::{use_clipboard, use_location};
14+
use yew_hooks::{use_clipboard, use_local_storage, use_location};
1515
use yew_notifications::{use_notification, Notification, NotificationType};
1616

1717
use crate::asn1::asn1_viewer::Asn1Viewer;
1818
use crate::asn1::hex_view::HexViewer;
19-
use crate::common::ByteInput;
19+
use crate::common::{encode_bytes, ByteInput, BytesFormat};
2020
use crate::url_query_params;
2121
use crate::url_query_params::generate_asn1_link;
2222

@@ -26,6 +26,7 @@ pub const TEST_ASN1: &[u8] = &[
2626
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,
2727
0, 101,
2828
];
29+
const ASN1_LOCAL_STORAGE_KEY: &str = "ASN1_DATA";
2930

3031
pub fn compare_ids(asn1_node_id: u64, cur_node: &Option<u64>) -> bool {
3132
matches!(cur_node, Some(node_id) if *node_id == asn1_node_id)
@@ -83,7 +84,7 @@ pub fn asn1_parser_page() -> Html {
8384
let raw_data = (*raw_asn1).clone();
8485
let parse_asn1 = Callback::from(move |_| match Asn1::decode_buff(&raw_data) {
8586
Ok(asn1) => {
86-
log::debug!("parsed!");
87+
debug!("parsed!");
8788
asn1_setter.set(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned()));
8889
}
8990
Err(error) => notifications.spawn(Notification::new(
@@ -109,11 +110,25 @@ pub fn asn1_parser_page() -> Html {
109110
let notifications = notification_manager.clone();
110111
let raw_asn1_setter = raw_asn1.setter();
111112
let asn1_setter = parsed_asn1.setter();
113+
let local_storage = use_local_storage::<String>(ASN1_LOCAL_STORAGE_KEY.to_owned());
112114
use_effect_with_deps(
113115
move |_: &[(); 0]| {
114116
let query = &location.search;
115117

116118
if query.len() < 2 {
119+
// URL query params is empty. We try to load ASN1 from local storage.
120+
if let Some(raw_asn1) = (*local_storage).as_ref() {
121+
if let Ok(bytes) = hex::decode(raw_asn1) {
122+
match Asn1::decode_buff(&bytes) {
123+
Ok(asn1) => {
124+
asn1_setter.set(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned()));
125+
}
126+
Err(err) => {
127+
error!("Can not decode asn1: {:?}", err);
128+
}
129+
}
130+
}
131+
}
117132
return;
118133
}
119134

@@ -122,7 +137,6 @@ pub fn asn1_parser_page() -> Html {
122137
let url_query_params::Asn1 { asn1: asn1_data } = asn1;
123138
match Asn1::decode_buff(&asn1_data) {
124139
Ok(asn1) => {
125-
log::debug!("parsed!");
126140
asn1_setter.set(asn1.to_owned_with_asn1(asn1.inner_asn1().to_owned()));
127141
}
128142
Err(error) => notifications.spawn(Notification::new(
@@ -145,6 +159,16 @@ pub fn asn1_parser_page() -> Html {
145159
[],
146160
);
147161

162+
let local_storage = use_local_storage::<String>(ASN1_LOCAL_STORAGE_KEY.to_owned());
163+
use_effect_with_deps(
164+
move |asn1| {
165+
let mut encoded = vec![0; asn1.needed_buf_size()];
166+
asn1.encode_buff(&mut encoded).expect("ASN1 encoding should not fail");
167+
local_storage.set(encode_bytes(encoded, BytesFormat::Hex));
168+
},
169+
parsed_asn1.clone(),
170+
);
171+
148172
let clipboard = use_clipboard();
149173
let raw_asn1_data = (*raw_asn1).clone();
150174
let share_by_link = Callback::from(move |_| {

src/common/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ pub const BYTES_FORMATS: [BytesFormat; 5] = [
6363
BytesFormat::Binary,
6464
];
6565

66-
fn encode_bytes(bytes: impl AsRef<[u8]>, format: BytesFormat) -> String {
66+
pub fn encode_bytes(bytes: impl AsRef<[u8]>, format: BytesFormat) -> String {
6767
match format {
6868
BytesFormat::Hex => hex::encode(bytes),
6969
BytesFormat::Base64 => base64::encode(bytes),

src/crypto_helper.rs

+46-15
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ mod info;
44
mod input;
55
mod macros;
66
mod output;
7-
pub mod serde;
87

98
pub use algorithm::Algorithm;
109
use info::Info;
@@ -14,13 +13,15 @@ use picky_krb::crypto::{ChecksumSuite, CipherSuite};
1413
use sha1::{Digest, Sha1};
1514
use web_sys::KeyboardEvent;
1615
use yew::{function_component, html, use_effect_with_deps, use_state, Callback, Html};
17-
use yew_hooks::{use_clipboard, use_location};
16+
use yew_hooks::{use_clipboard, use_local_storage, use_location};
1817
use yew_notifications::{use_notification, Notification, NotificationType};
1918

2019
use self::computations::{process_krb_cipher, process_krb_hmac, process_rsa, process_zlib};
2120
use crate::crypto_helper::computations::process_bcrypt;
2221
use crate::url_query_params::generate_crypto_helper_link;
2322

23+
const CRYPTO_HELPER_LOCAL_STORAGE_KEY: &str = "CRYPTO_HELPER_DATA";
24+
2425
fn convert(algrithm: &Algorithm) -> Result<Vec<u8>, String> {
2526
match algrithm {
2627
Algorithm::Md5(input) => Ok(md5::compute(input).to_vec()),
@@ -71,29 +72,59 @@ pub fn crypto_helper() -> Html {
7172
let algorithm_setter = algorithm.setter();
7273
let location = use_location();
7374
let notifications = notification_manager.clone();
75+
let local_storage = use_local_storage::<String>(CRYPTO_HELPER_LOCAL_STORAGE_KEY.to_owned());
7476
use_effect_with_deps(
7577
move |_: &[(); 0]| {
7678
let query = &location.search;
7779

78-
if query.len() < 2 {
79-
return;
80-
}
81-
82-
match serde_qs::from_str(&query[1..]) {
83-
Ok(algorithm) => {
84-
algorithm_setter.set(algorithm);
80+
// First, we try to load data from the url.
81+
// question mark + one any other char
82+
if query.len() >= 2 {
83+
match serde_qs::from_str(&query[1..]) {
84+
Ok(algorithm) => {
85+
algorithm_setter.set(algorithm);
86+
}
87+
Err(err) => notifications.spawn(Notification::new(
88+
NotificationType::Error,
89+
"Can not load data from url",
90+
err.to_string(),
91+
Notification::NOTIFICATION_LIFETIME,
92+
)),
93+
}
94+
} else {
95+
// Otherwise, we try to find a data in the local storage.
96+
let raw_data = if let Some(raw_data) = (*local_storage).as_ref() {
97+
raw_data.as_str()
98+
} else {
99+
return;
100+
};
101+
match serde_json::from_str(raw_data) {
102+
Ok(algorithm) => {
103+
algorithm_setter.set(algorithm);
104+
}
105+
Err(err) => notifications.spawn(Notification::new(
106+
NotificationType::Error,
107+
"Can not load data from the local storage",
108+
err.to_string(),
109+
Notification::NOTIFICATION_LIFETIME,
110+
)),
85111
}
86-
Err(err) => notifications.spawn(Notification::new(
87-
NotificationType::Error,
88-
"Can not load data from url",
89-
err.to_string(),
90-
Notification::NOTIFICATION_LIFETIME,
91-
)),
92112
}
93113
},
94114
[],
95115
);
96116

117+
let local_storage = use_local_storage::<String>(CRYPTO_HELPER_LOCAL_STORAGE_KEY.to_owned());
118+
use_effect_with_deps(
119+
move |algorithm| {
120+
let algorithm: &Algorithm = algorithm;
121+
local_storage.set(
122+
serde_json::to_string(algorithm).expect("algorithm serialization into json string should never fail"),
123+
);
124+
},
125+
algorithm.clone(),
126+
);
127+
97128
let algorithm_data = (*algorithm).clone();
98129
let clipboard = use_clipboard();
99130
let share_by_link = Callback::from(move |_| {

src/crypto_helper/algorithm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rsa::pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey};
44
use rsa::{RsaPrivateKey, RsaPublicKey};
55
use serde::{Deserialize, Serialize};
66

7-
use super::serde::*;
7+
use crate::serde::*;
88

99
pub const MD5: &str = "MD5";
1010
pub const SHA1: &str = "SHA1";

src/jwt.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::str::FromStr;
1010

1111
use web_sys::{HtmlInputElement, KeyboardEvent};
1212
use yew::{function_component, html, use_effect_with_deps, use_state, Callback, Html, TargetCast};
13-
use yew_hooks::use_location;
13+
use yew_hooks::{use_local_storage, use_location};
1414
use yew_notifications::{use_notification, Notification, NotificationType};
1515

1616
use crate::common::Checkbox;
@@ -20,6 +20,7 @@ use crate::jwt::jwt_utils::JwtUtils;
2020
use crate::jwt::jwte::Jwte;
2121
use crate::url_query_params;
2222

23+
const JWT_LOCAL_STORAGE_KEY: &str = "JWT_DATA";
2324
const TEST_JWT: &str = "eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZSIsImV4cCI6MTY3MDAwNDI1NCwiaWF0IjoxNjcwMDA0MjU0fQ.ZGsN42vr-bM4uxXowtlNl7xRerkdKu6i29VS8DFQ4Tw";
2425

2526
#[function_component(Jwt)]
@@ -86,11 +87,23 @@ pub fn jwt() -> Html {
8687
let jwt_setter = raw_jwt.setter();
8788
let jwte_setter = jwte.setter();
8889
let notifications = use_notification::<Notification>();
90+
let local_storage = use_local_storage::<String>(JWT_LOCAL_STORAGE_KEY.to_owned());
8991
use_effect_with_deps(
9092
move |_: &[(); 0]| {
9193
let query = &location.search;
9294

9395
if query.len() < 2 {
96+
// URL query params is empty. We try to load JWT from local storage.
97+
if let Some(raw_jwt) = (*local_storage).as_ref() {
98+
match serde_json::from_str(raw_jwt.as_str()) {
99+
Ok(jwt) => {
100+
jwte_setter.set(Some(Jwte::Jwt(jwt)));
101+
}
102+
Err(err) => {
103+
error!("Can not load JWT from local storage: {:?}", err);
104+
}
105+
}
106+
}
94107
return;
95108
}
96109

@@ -125,6 +138,17 @@ pub fn jwt() -> Html {
125138
[],
126139
);
127140

141+
let local_storage = use_local_storage::<String>(JWT_LOCAL_STORAGE_KEY.to_owned());
142+
use_effect_with_deps(
143+
move |jwte| {
144+
let jwte: &Option<Jwte> = jwte;
145+
if let Some(Jwte::Jwt(jwt)) = jwte {
146+
local_storage.set(serde_json::to_string(jwt).expect("JWT serialization should not fail"));
147+
}
148+
},
149+
jwte.clone(),
150+
);
151+
128152
let jwte_setter = jwte.setter();
129153
let set_jwt = Callback::from(move |jwt| {
130154
jwte_setter.set(Some(Jwte::Jwt(jwt)));

src/jwt/jwt.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
use serde::{Deserialize, Serialize};
12
use serde_json::Value;
23

34
use super::signature::JwtSignatureAlgorithm;
5+
use crate::serde::{deserialize_bytes, serialize_bytes};
46

57
pub mod editor;
68
pub mod viewer;
79

8-
#[derive(Debug, PartialEq, Eq, Default, Clone)]
10+
#[derive(Debug, PartialEq, Eq, Default, Clone, Serialize, Deserialize)]
911
pub struct Jwt {
1012
pub raw_header: String,
1113
pub parsed_header: String,
@@ -15,6 +17,7 @@ pub struct Jwt {
1517

1618
pub raw_signature: String,
1719
pub parsed_signature: String,
20+
#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
1821
pub signature: Vec<u8>,
1922
pub signature_algorithm: JwtSignatureAlgorithm,
2023

src/jwt/jwt_utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ fn validate_signature(jwt: &Jwt, spawn_notification: Callback<Notification>) ->
427427
Some(jwt.signature == calculated_signature)
428428
}
429429

430-
fn generate_jwt(jwt: &Jwt, spawn_notification: Callback<Notification>) -> Option<Vec<u8>> {
430+
pub fn generate_jwt(jwt: &Jwt, spawn_notification: Callback<Notification>) -> Option<Vec<u8>> {
431431
let signature = calculate_signature(jwt, spawn_notification)?;
432432

433433
let header = base64::encode_config(jwt.parsed_header.as_bytes(), base64::URL_SAFE_NO_PAD);

src/jwt/signature.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
use std::fmt::{self, Display};
22

3+
use serde::{Deserialize, Serialize};
34
use serde_json::Value;
45

6+
use crate::serde::{deserialize_bytes, serialize_bytes};
7+
58
const JWT_SIGNATURE_ALGORITHMS: [&str; 10] = [
69
"HS256", "HS512", "none", "RS256", "HS384", "RS384", "RS512", "ES256", "ES384", "ES512",
710
];
811

9-
#[derive(Debug, PartialEq, Eq, Clone)]
12+
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
1013
pub enum JwtSignatureAlgorithm {
1114
None,
1215

1316
/// HMAC using SHA-256
17+
#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
1418
Hs256(Vec<u8>),
1519

1620
/// HMAC using SHA-384
21+
#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
1722
Hs384(Vec<u8>),
1823

1924
/// HMAC using SHA-512
25+
#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
2026
Hs512(Vec<u8>),
2127

2228
/// RSASSA-PKCS1-v1_5 using SHA-256

src/main.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#[macro_use]
2+
extern crate log;
3+
14
mod about;
25
mod asn1;
36
mod common;
@@ -6,6 +9,7 @@ mod footer;
69
mod header;
710
mod jwt;
811
mod not_found;
12+
pub mod serde;
913
mod url_query_params;
1014
mod utils;
1115

File renamed without changes.

src/url_query_params.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serde::{Deserialize, Serialize};
22

3-
use crate::crypto_helper::serde::{deserialize_bytes, serialize_bytes};
43
use crate::crypto_helper::Algorithm;
4+
use crate::serde::{deserialize_bytes, serialize_bytes};
55

66
const APP_HOST: &str = env!("APP_HOST");
77

0 commit comments

Comments
 (0)