From 98b2d3825ff06c60f51819742f8da8b26466301d Mon Sep 17 00:00:00 2001 From: Deepu Date: Fri, 12 Jan 2024 22:38:37 +0100 Subject: [PATCH] remove base64 dependency --- Cargo.lock | 1 - Cargo.toml | 1 - src/app/jwt_decoder.rs | 31 ++++++++++++------ src/app/jwt_encoder.rs | 24 +++++++++----- src/app/jwt_utils.rs | 74 +++++++++++++++++------------------------- src/ui/decoder.rs | 2 +- src/ui/encoder.rs | 2 +- 7 files changed, 69 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ffdc67..3f0905b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -504,7 +504,6 @@ name = "jwt-ui" version = "1.0.1" dependencies = [ "backtrace", - "base64", "cargo-husky", "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index 12cb55c..ebb24f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,6 @@ tui-textarea = { version = "0.4.0", default-features = false, features = [ ] } backtrace = "0.3" human-panic = "1.1" -base64 = "0.21" jsonwebtoken = "9.2.0" chrono = "0.4" diff --git a/src/app/jwt_decoder.rs b/src/app/jwt_decoder.rs index 9d04444..ae11890 100644 --- a/src/app/jwt_decoder.rs +++ b/src/app/jwt_decoder.rs @@ -8,7 +8,7 @@ use serde_derive::{Deserialize, Serialize}; use serde_json::{to_string_pretty, Value}; use super::{ - jwt_utils::{decoding_key_from_jwks_secret, get_secret, JWTError, JWTResult, SecretFileType}, + jwt_utils::{decoding_key_from_jwks_secret, get_secret, JWTError, JWTResult, SecretType}, models::{ScrollableTxt, TabRoute, TabsState}, ActiveBlock, App, Route, RouteId, TextInput, }; @@ -227,6 +227,7 @@ fn decode_token( let mut secret_validator = Validation::new(algorithm); secret_validator.leeway = 1000; + secret_validator.validate_aud = false; if arguments.ignore_exp { secret_validator @@ -255,31 +256,41 @@ fn decoding_key_from_secret( let (secret, file_type) = get_secret(alg, secret_string); let secret = secret?; match alg { - Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => Ok(DecodingKey::from_secret(&secret)), + Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => match file_type { + SecretType::Plain => Ok(DecodingKey::from_secret(&secret)), + SecretType::Jwks => decoding_key_from_jwks_secret(&secret, header), + SecretType::B64 => { + DecodingKey::from_base64_secret(std::str::from_utf8(&secret)?).map_err(Error::into) + } + _ => Err(JWTError::Internal(format!( + "Invalid secret file type for {alg:?}" + ))), + }, Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 | Algorithm::PS512 => match file_type { - SecretFileType::Pem => DecodingKey::from_rsa_pem(&secret).map_err(Error::into), - SecretFileType::Der => Ok(DecodingKey::from_rsa_der(&secret)), - SecretFileType::Jwks => decoding_key_from_jwks_secret(&secret, header), + SecretType::Pem => DecodingKey::from_rsa_pem(&secret).map_err(Error::into), + SecretType::Der => Ok(DecodingKey::from_rsa_der(&secret)), + SecretType::Jwks => decoding_key_from_jwks_secret(&secret, header), _ => Err(JWTError::Internal(format!( "Invalid secret file type for {alg:?}" ))), }, Algorithm::ES256 | Algorithm::ES384 => match file_type { - SecretFileType::Pem => DecodingKey::from_ec_pem(&secret).map_err(Error::into), - SecretFileType::Der => Ok(DecodingKey::from_ec_der(&secret)), - SecretFileType::Jwks => decoding_key_from_jwks_secret(&secret, header), + SecretType::Pem => DecodingKey::from_ec_pem(&secret).map_err(Error::into), + SecretType::Der => Ok(DecodingKey::from_ec_der(&secret)), + SecretType::Jwks => decoding_key_from_jwks_secret(&secret, header), _ => Err(JWTError::Internal(format!( "Invalid secret file type for {alg:?}" ))), }, Algorithm::EdDSA => match file_type { - SecretFileType::Pem => DecodingKey::from_ed_pem(&secret).map_err(Error::into), - SecretFileType::Der => Ok(DecodingKey::from_ed_der(&secret)), + SecretType::Pem => DecodingKey::from_ed_pem(&secret).map_err(Error::into), + SecretType::Der => Ok(DecodingKey::from_ed_der(&secret)), + SecretType::Jwks => decoding_key_from_jwks_secret(&secret, header), _ => Err(JWTError::Internal(format!( "Invalid secret file type for {alg:?}" ))), diff --git a/src/app/jwt_encoder.rs b/src/app/jwt_encoder.rs index 11086c8..5a29a47 100644 --- a/src/app/jwt_encoder.rs +++ b/src/app/jwt_encoder.rs @@ -2,7 +2,7 @@ use jsonwebtoken::{errors::Error, Algorithm, EncodingKey, Header}; use super::{ jwt_decoder::Payload, - jwt_utils::{get_secret, JWTError, JWTResult, SecretFileType}, + jwt_utils::{get_secret, JWTError, JWTResult, SecretType}, models::{ScrollableTxt, TabRoute, TabsState}, ActiveBlock, App, Route, RouteId, TextAreaInput, TextInput, }; @@ -112,29 +112,37 @@ pub fn encoding_key_from_secret(alg: &Algorithm, secret_string: &str) -> JWTResu let secret = secret?; match alg { - Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => Ok(EncodingKey::from_secret(&secret)), + Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => match file_type { + SecretType::Plain => Ok(EncodingKey::from_secret(&secret)), + SecretType::B64 => { + EncodingKey::from_base64_secret(std::str::from_utf8(&secret)?).map_err(Error::into) + } + _ => Err(JWTError::Internal(format!( + "Invalid secret file type for {alg:?}" + ))), + }, Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 | Algorithm::PS512 => match file_type { - SecretFileType::Pem => EncodingKey::from_rsa_pem(&secret).map_err(Error::into), - SecretFileType::Der => Ok(EncodingKey::from_rsa_der(&secret)), + SecretType::Pem => EncodingKey::from_rsa_pem(&secret).map_err(Error::into), + SecretType::Der => Ok(EncodingKey::from_rsa_der(&secret)), _ => Err(JWTError::Internal(format!( "Invalid secret file type for {alg:?}" ))), }, Algorithm::ES256 | Algorithm::ES384 => match file_type { - SecretFileType::Pem => EncodingKey::from_ec_pem(&secret).map_err(Error::into), - SecretFileType::Der => Ok(EncodingKey::from_ec_der(&secret)), + SecretType::Pem => EncodingKey::from_ec_pem(&secret).map_err(Error::into), + SecretType::Der => Ok(EncodingKey::from_ec_der(&secret)), _ => Err(JWTError::Internal(format!( "Invalid secret file type for {alg:?}" ))), }, Algorithm::EdDSA => match file_type { - SecretFileType::Pem => EncodingKey::from_ed_pem(&secret).map_err(Error::into), - SecretFileType::Der => Ok(EncodingKey::from_ed_der(&secret)), + SecretType::Pem => EncodingKey::from_ed_pem(&secret).map_err(Error::into), + SecretType::Der => Ok(EncodingKey::from_ed_der(&secret)), _ => Err(JWTError::Internal(format!( "Invalid secret file type for {alg:?}" ))), diff --git a/src/app/jwt_utils.rs b/src/app/jwt_utils.rs index 1d3c168..c7ef9ca 100644 --- a/src/app/jwt_utils.rs +++ b/src/app/jwt_utils.rs @@ -1,6 +1,5 @@ -use std::fmt; +use std::{fmt, str::Utf8Error}; -use base64::{engine::general_purpose::STANDARD as base64_engine, Engine as _}; use jsonwebtoken::{ errors::{Error, ErrorKind}, jwk, Algorithm, DecodingKey, Header, @@ -23,14 +22,14 @@ impl From for JWTError { } } -impl From for JWTError { - fn from(value: serde_json::Error) -> Self { +impl From for JWTError { + fn from(value: Utf8Error) -> Self { JWTError::Internal(value.to_string()) } } -impl From for JWTError { - fn from(value: base64::DecodeError) -> Self { +impl From for JWTError { + fn from(value: serde_json::Error) -> Self { JWTError::Internal(value.to_string()) } } @@ -56,46 +55,41 @@ impl fmt::Display for JWTError { } } -pub enum SecretFileType { +pub enum SecretType { Pem, Der, Jwks, - Na, + B64, + Plain, } -pub fn get_secret(alg: &Algorithm, secret_string: &str) -> (JWTResult>, SecretFileType) { +pub fn get_secret(alg: &Algorithm, secret_string: &str) -> (JWTResult>, SecretType) { return match alg { Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { if secret_string.starts_with('@') { ( slurp_file(&secret_string.chars().skip(1).collect::()).map_err(JWTError::from), - SecretFileType::Na, + if secret_string.ends_with(".json") { + SecretType::Jwks + } else { + SecretType::Plain + }, ) } else if secret_string.starts_with("b64:") { ( - base64_engine - .decode(secret_string.chars().skip(4).collect::()) - .map_err(JWTError::from), - SecretFileType::Na, + Ok( + secret_string + .chars() + .skip(4) + .collect::() + .as_bytes() + .to_owned(), + ), + SecretType::B64, ) } else { - (Ok(secret_string.as_bytes().to_owned()), SecretFileType::Na) - } - } - Algorithm::EdDSA => { - if !&secret_string.starts_with('@') { - return ( - Err(JWTError::Internal(format!( - "Secret for {alg:?} must be a file path starting with @", - ))), - SecretFileType::Na, - ); + (Ok(secret_string.as_bytes().to_owned()), SecretType::Plain) } - - ( - slurp_file(&secret_string.chars().skip(1).collect::()).map_err(JWTError::from), - get_secret_file_type(secret_string), - ) } _ => { if secret_string.starts_with('@') { @@ -105,7 +99,7 @@ pub fn get_secret(alg: &Algorithm, secret_string: &str) -> (JWTResult>, ) } else { // allows to read JWKS from argument (e.g. output of 'curl https://auth.domain.com/jwks.json') - (Ok(secret_string.as_bytes().to_vec()), SecretFileType::Jwks) + (Ok(secret_string.as_bytes().to_vec()), SecretType::Jwks) } } }; @@ -145,15 +139,7 @@ fn decoding_key_from_jwks(jwks: jwk::JwkSet, header: &Header) -> JWTResult { - DecodingKey::from_rsa_components(&rsa.n, &rsa.e).map_err(Error::into) - } - jwk::AlgorithmParameters::EllipticCurve(ec) => { - DecodingKey::from_ec_components(&ec.x, &ec.y).map_err(Error::into) - } - _ => Err(JWTError::Internal("Unsupported alg".to_string())), - } + DecodingKey::from_jwk(jwk).map_err(Error::into) } fn parse_jwks(secret: &[u8]) -> Option { @@ -163,13 +149,13 @@ fn parse_jwks(secret: &[u8]) -> Option { } } -fn get_secret_file_type(secret_string: &str) -> SecretFileType { +fn get_secret_file_type(secret_string: &str) -> SecretType { if secret_string.ends_with(".pem") { - SecretFileType::Pem + SecretType::Pem } else if secret_string.ends_with(".json") { - SecretFileType::Jwks + SecretType::Jwks } else { - SecretFileType::Der + SecretType::Der } } diff --git a/src/ui/decoder.rs b/src/ui/decoder.rs index 2df5935..51760a1 100644 --- a/src/ui/decoder.rs +++ b/src/ui/decoder.rs @@ -70,7 +70,7 @@ fn draw_secret_block(f: &mut Frame<'_>, app: &App, area: Rect) { vertical_chunks_with_margin(vec![Constraint::Length(1), Constraint::Min(2)], area, 1); let mut text = Text::from( - "Prepend 'b64:' for base64 encoded secret. Prepend '@' for file path (.pem, .pk8, .der)", + "Prepend 'b64:' for base64 encoded secret. Prepend '@' for file path (.pem, .pk8, .der, .json)", ); text.patch_style(style_default(app.light_theme)); let paragraph = Paragraph::new(text).block(Block::default()); diff --git a/src/ui/encoder.rs b/src/ui/encoder.rs index 88aeef4..d0dee62 100644 --- a/src/ui/encoder.rs +++ b/src/ui/encoder.rs @@ -82,7 +82,7 @@ fn draw_secret_block(f: &mut Frame<'_>, app: &App, area: Rect) { vertical_chunks_with_margin(vec![Constraint::Length(1), Constraint::Min(2)], area, 1); let mut text = Text::from( - "Prepend 'b64:' for base64 encoded secret. Prepend '@' for file path (.pem, .pk8, .der)", + "Prepend 'b64:' for base64 encoded secret. Prepend '@' for file path (.pem, .pk8, .der, .json)", ); text.patch_style(style_default(app.light_theme)); let paragraph = Paragraph::new(text).block(Block::default());