From 33d2c21f083133f16f63fada52a634638d87608b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Mon, 19 Jun 2023 13:35:50 +0800 Subject: [PATCH 01/35] support wasm --- Cargo.toml | 10 +++- src/crypto/mod.rs | 80 +++++++++++++++++++++++++++-- src/crypto/rsa.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++- src/errors.rs | 7 ++- 4 files changed, 212 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8bb727e3..591f6446 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,12 +15,18 @@ rust-version = "1.56.0" [dependencies] serde_json = "1.0" serde = {version = "1.0", features = ["derive"] } -ring = { version = "0.16.5", features = ["std"] } base64 = "0.21.0" # For PEM decoding pem = {version = "2", optional = true} simple_asn1 = {version = "0.6", optional = true} - +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +ring = { version = "0.16.5", features = ["std"] } +[target.'cfg(target_arch="wasm32")'.dependencies] +hmac = "0.12.1" +rsa = "0.9.2" +sha2 = { version = "0.10.7", features = ["oid"] } +getrandom = { version = "0.2.10", features = ["js"] } +rand = { version = "0.8.5", features = ["std"], default-features = false } [dev-dependencies] # For the custom time example time = "0.3" diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index c2957dc8..ad929d0e 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,22 +1,60 @@ +#[cfg(not(target_arch = "wasm32"))] use ring::constant_time::verify_slices_are_equal; +#[cfg(not(target_arch = "wasm32"))] use ring::{hmac, signature}; +#[cfg(target_arch = "wasm32")] +use sha2::{Sha256,Sha384,Sha512}; +#[cfg(target_arch = "wasm32")] +use hmac::{Hmac, Mac}; use crate::algorithms::Algorithm; use crate::decoding::{DecodingKey, DecodingKeyKind}; use crate::encoding::EncodingKey; use crate::errors::Result; use crate::serialization::{b64_decode, b64_encode}; - +#[cfg(not(target_arch = "wasm32"))] pub(crate) mod ecdsa; +#[cfg(not(target_arch = "wasm32"))] pub(crate) mod eddsa; pub(crate) mod rsa; /// The actual HS signing + encoding /// Could be in its own file to match RSA/EC but it's 2 lines... +#[cfg(not(target_arch = "wasm32"))] pub(crate) fn sign_hmac(alg: hmac::Algorithm, key: &[u8], message: &[u8]) -> String { let digest = hmac::sign(&hmac::Key::new(alg, key), message); b64_encode(digest) } +#[cfg(target_arch = "wasm32")] +type HmacSha256 = Hmac; +#[cfg(target_arch = "wasm32")] +type HmacSha384 = Hmac; +#[cfg(target_arch = "wasm32")] +type HmacSha512 = Hmac; +#[cfg(target_arch = "wasm32")] +pub(crate) fn sign_hmac(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { + let digest= match alg { + Algorithm::HS256 => { + let mut mac = HmacSha256::new_from_slice(key).map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?; + mac.update(message); + mac.finalize().into_bytes().to_vec() + } + Algorithm::HS384 => { + let mut mac = HmacSha384::new_from_slice(key).map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?; + mac.update(message); + mac.finalize().into_bytes().to_vec() + } + Algorithm::HS512 => { + let mut mac = HmacSha512::new_from_slice(key).map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?; + mac.update(message); + mac.finalize().into_bytes().to_vec() + } + _=>{ + return Err(crate::errors::new_error(crate::errors::ErrorKind::InvalidAlgorithm)); + } + }; + Ok(b64_encode(&digest)) +} /// Take the payload of a JWT, sign it using the algorithm given and return /// the base64 url safe encoded of the result. @@ -24,25 +62,43 @@ pub(crate) fn sign_hmac(alg: hmac::Algorithm, key: &[u8], message: &[u8]) -> Str /// If you just want to encode a JWT, use `encode` instead. pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result { match algorithm { + #[cfg(not(target_arch = "wasm32"))] Algorithm::HS256 => Ok(sign_hmac(hmac::HMAC_SHA256, key.inner(), message)), + #[cfg(not(target_arch = "wasm32"))] Algorithm::HS384 => Ok(sign_hmac(hmac::HMAC_SHA384, key.inner(), message)), + #[cfg(not(target_arch = "wasm32"))] Algorithm::HS512 => Ok(sign_hmac(hmac::HMAC_SHA512, key.inner(), message)), - + #[cfg(not(target_arch = "wasm32"))] Algorithm::ES256 | Algorithm::ES384 => { ecdsa::sign(ecdsa::alg_to_ec_signing(algorithm), key.inner(), message) } - + #[cfg(not(target_arch = "wasm32"))] Algorithm::EdDSA => eddsa::sign(key.inner(), message), - + #[cfg(not(target_arch = "wasm32"))] Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 | Algorithm::PS512 => rsa::sign(rsa::alg_to_rsa_signing(algorithm), key.inner(), message), + #[cfg(target_arch = "wasm32")] + Algorithm::HS256 + | Algorithm::HS384 + | Algorithm::HS512 => sign_hmac(algorithm, key.inner(), message), + #[cfg(target_arch = "wasm32")] + Algorithm::RS256 + | Algorithm::RS384 + | Algorithm::RS512 + | Algorithm::PS256 + | Algorithm::PS384 + | Algorithm::PS512 => { + rsa::sign(algorithm, key.inner(), message) + }, + #[cfg(target_arch = "wasm32")] + _ => Err(crate::errors::new_error(crate::errors::ErrorKind::InvalidAlgorithm)), } } - +#[cfg(not(target_arch = "wasm32"))] /// See Ring docs for more details fn verify_ring( alg: &'static dyn signature::VerificationAlgorithm, @@ -72,17 +128,20 @@ pub fn verify( algorithm: Algorithm, ) -> Result { match algorithm { + #[cfg(not(target_arch = "wasm32"))] Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { // we just re-sign the message with the key and compare if they are equal let signed = sign(message, &EncodingKey::from_secret(key.as_bytes()), algorithm)?; Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok()) } + #[cfg(not(target_arch = "wasm32"))] Algorithm::ES256 | Algorithm::ES384 => verify_ring( ecdsa::alg_to_ec_verification(algorithm), signature, message, key.as_bytes(), ), + #[cfg(not(target_arch = "wasm32"))] Algorithm::EdDSA => verify_ring( eddsa::alg_to_ec_verification(algorithm), signature, @@ -95,13 +154,24 @@ pub fn verify( | Algorithm::PS256 | Algorithm::PS384 | Algorithm::PS512 => { + #[cfg(not(target_arch = "wasm32"))] let alg = rsa::alg_to_rsa_parameters(algorithm); match &key.kind { + #[cfg(not(target_arch = "wasm32"))] DecodingKeyKind::SecretOrDer(bytes) => verify_ring(alg, signature, message, bytes), + #[cfg(not(target_arch = "wasm32"))] DecodingKeyKind::RsaModulusExponent { n, e } => { rsa::verify_from_components(alg, signature, message, (n, e)) } + #[cfg(target_arch = "wasm32")] + DecodingKeyKind::SecretOrDer(bytes) => rsa::verify_der(algorithm, signature, message, bytes), + #[cfg(target_arch = "wasm32")] + DecodingKeyKind::RsaModulusExponent { n, e } => { + rsa::verify_from_components(algorithm, signature, message, (n, e)) + } } } + #[cfg(target_arch = "wasm32")] + _ => Err(crate::errors::new_error(crate::errors::ErrorKind::InvalidAlgorithm)), } } diff --git a/src/crypto/rsa.rs b/src/crypto/rsa.rs index 34fce1ad..3a51b9ca 100644 --- a/src/crypto/rsa.rs +++ b/src/crypto/rsa.rs @@ -1,10 +1,16 @@ +#[cfg(not(target_arch = "wasm32"))] use ring::{rand, signature}; +#[cfg(target_arch = "wasm32")] +use rsa::{pss::Pss, Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey, traits::SignatureScheme, BigUint, pkcs1::DecodeRsaPrivateKey, pkcs1::DecodeRsaPublicKey}; +#[cfg(target_arch = "wasm32")] +use sha2::{Digest, Sha256, Sha384, Sha512}; use crate::algorithms::Algorithm; -use crate::errors::{ErrorKind, Result}; +use crate::errors::{ErrorKind, Result, new_error}; use crate::serialization::{b64_decode, b64_encode}; /// Only used internally when validating RSA, to map from our enum to the Ring param structs. +#[cfg(not(target_arch = "wasm32"))] pub(crate) fn alg_to_rsa_parameters(alg: Algorithm) -> &'static signature::RsaParameters { match alg { Algorithm::RS256 => &signature::RSA_PKCS1_2048_8192_SHA256, @@ -18,6 +24,7 @@ pub(crate) fn alg_to_rsa_parameters(alg: Algorithm) -> &'static signature::RsaPa } /// Only used internally when signing with RSA, to map from our enum to the Ring signing structs. +#[cfg(not(target_arch = "wasm32"))] pub(crate) fn alg_to_rsa_signing(alg: Algorithm) -> &'static dyn signature::RsaEncoding { match alg { Algorithm::RS256 => &signature::RSA_PKCS1_SHA256, @@ -33,13 +40,14 @@ pub(crate) fn alg_to_rsa_signing(alg: Algorithm) -> &'static dyn signature::RsaE /// The actual RSA signing + encoding /// The key needs to be in PKCS8 format /// Taken from Ring doc https://docs.rs/ring/latest/ring/signature/index.html +#[cfg(not(target_arch = "wasm32"))] pub(crate) fn sign( alg: &'static dyn signature::RsaEncoding, key: &[u8], message: &[u8], ) -> Result { let key_pair = signature::RsaKeyPair::from_der(key) - .map_err(|e| ErrorKind::InvalidRsaKey(e.description_()))?; + .map_err(|e| ErrorKind::InvalidRsaKey(e.description_().to_owned()))?; let mut signature = vec![0; key_pair.public_modulus_len()]; let rng = rand::SystemRandom::new(); @@ -49,6 +57,7 @@ pub(crate) fn sign( } /// Checks that a signature is valid based on the (n, e) RSA pubkey components +#[cfg(not(target_arch = "wasm32"))] pub(crate) fn verify_from_components( alg: &'static signature::RsaParameters, signature: &str, @@ -60,3 +69,115 @@ pub(crate) fn verify_from_components( let res = pubkey.verify(alg, message, &signature_bytes); Ok(res.is_ok()) } + +#[cfg(target_arch = "wasm32")] +fn alg_to_pss(alg: Algorithm, digest_len: usize) -> Option { + match alg { + Algorithm::PS256 => Some(Pss::new_with_salt::(digest_len)), + Algorithm::PS384 => Some(Pss::new_with_salt::(digest_len)), + Algorithm::PS512 => Some(Pss::new_with_salt::(digest_len)), + _ => None, + } +} + +#[cfg(target_arch = "wasm32")] +fn alg_to_pkcs1_v15(alg: Algorithm) -> Option { + match alg { + Algorithm::RS256 => Some(Pkcs1v15Sign::new::()), + Algorithm::RS384 => Some(Pkcs1v15Sign::new::()), + Algorithm::RS512 => Some(Pkcs1v15Sign::new::()), + _ => None, + } +} + +#[cfg(target_arch = "wasm32")] +fn message_digest(alg: Algorithm, message: &[u8]) -> Result> { + match alg { + Algorithm::RS256 | Algorithm::PS256 => { + let mut hasher = Sha256::new(); + hasher.update(message); + let d = hasher.finalize(); + Ok(d.as_slice().to_vec()) + } + Algorithm::RS384 | Algorithm::PS384 => { + let mut hasher = Sha384::new(); + hasher.update(message); + let d = hasher.finalize(); + Ok(d.as_slice().to_vec()) + } + Algorithm::RS512 | Algorithm::PS512 => { + let mut hasher = Sha512::new(); + hasher.update(message); + let d = hasher.finalize(); + Ok(d.as_slice().to_vec()) + } + _ => Err(new_error(ErrorKind::InvalidAlgorithm)), + } +} + +#[cfg(target_arch = "wasm32")] +pub(crate) fn sign(alg: Algorithm, + key: &[u8], + message: &[u8]) -> Result { + let digest = message_digest(alg, message)?; + let signatures_scheme_pkcs = alg_to_pkcs1_v15(alg); + let signatures_scheme_pss = alg_to_pss(alg, digest.len()); + let private_key = RsaPrivateKey::from_pkcs1_der(key) + .map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; + let mut rng = rand::thread_rng(); + let signature = if let Some(signatures_scheme) = signatures_scheme_pkcs { + signatures_scheme.sign(Some(&mut rng), &private_key, &digest) + .map_err(|e| ErrorKind::RsaFailedSigning)? + } else if let Some(signatures_scheme) = signatures_scheme_pss { + signatures_scheme.sign(Some(&mut rng), &private_key, &digest) + .map_err(|e| ErrorKind::RsaFailedSigning)? + } else { + return Err(new_error(ErrorKind::InvalidAlgorithmName)); + }; + Ok(b64_encode(&signature)) +} + +#[cfg(target_arch = "wasm32")] +pub(crate) fn verify_from_components( + alg: Algorithm, + signature: &str, + message: &[u8], + components: (&[u8], &[u8]), +) -> Result { + let n = BigUint::from_bytes_be(components.0); + let e = BigUint::from_bytes_be(components.1); + let pub_key = RsaPublicKey::new(n, e) + .map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; + + verify(alg, signature, message, &pub_key) +} + +#[cfg(target_arch = "wasm32")] +fn verify(alg: Algorithm, signature: &str, message: &[u8], pub_key: &RsaPublicKey) -> Result { + let signature_bytes = b64_decode(signature)?; + let digest = message_digest(alg, message)?; + let signatures_scheme_pkcs = alg_to_pkcs1_v15(alg); + let signatures_scheme_pss = alg_to_pss(alg, digest.len()); + if let Some(signatures_scheme) = signatures_scheme_pkcs { + signatures_scheme.verify(&pub_key, &digest, &signature_bytes) + .map_err(|e| ErrorKind::InvalidSignature)?; + } else if let Some(signatures_scheme) = signatures_scheme_pss { + signatures_scheme + .verify(&pub_key, &digest, &signature_bytes) + .map_err(|e| ErrorKind::InvalidSignature)?; + } else { + return Err(new_error(ErrorKind::InvalidAlgorithmName)); + }; + Ok(true) +} + +#[cfg(target_arch = "wasm32")] +pub(crate) fn verify_der( + alg: Algorithm, + signature: &str, + message: &[u8], + bytes: &[u8], +) -> Result { + let pub_key = RsaPublicKey::from_pkcs1_der(bytes).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; + verify(alg, signature, message, &pub_key) +} diff --git a/src/errors.rs b/src/errors.rs index c5d8041b..ff5f198c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -42,7 +42,7 @@ pub enum ErrorKind { /// When the secret given is not a valid ECDSA key InvalidEcdsaKey, /// When the secret given is not a valid RSA key - InvalidRsaKey(&'static str), + InvalidRsaKey(String), /// We could not sign with the given key RsaFailedSigning, /// When the algorithm from string doesn't match the one passed to `from_str` @@ -77,6 +77,7 @@ pub enum ErrorKind { /// Some of the text was invalid UTF-8 Utf8(::std::string::FromUtf8Error), /// Something unspecified went wrong with crypto + #[cfg(not(target_arch = "wasm32"))] Crypto(::ring::error::Unspecified), } @@ -101,6 +102,7 @@ impl StdError for Error { ErrorKind::Base64(err) => Some(err), ErrorKind::Json(err) => Some(err.as_ref()), ErrorKind::Utf8(err) => Some(err), + #[cfg(not(target_arch = "wasm32"))] ErrorKind::Crypto(err) => Some(err), } } @@ -126,6 +128,7 @@ impl fmt::Display for Error { ErrorKind::InvalidRsaKey(msg) => write!(f, "RSA key invalid: {}", msg), ErrorKind::Json(err) => write!(f, "JSON error: {}", err), ErrorKind::Utf8(err) => write!(f, "UTF-8 error: {}", err), + #[cfg(not(target_arch = "wasm32"))] ErrorKind::Crypto(err) => write!(f, "Crypto error: {}", err), ErrorKind::Base64(err) => write!(f, "Base64 error: {}", err), } @@ -159,12 +162,14 @@ impl From<::std::string::FromUtf8Error> for Error { } } +#[cfg(not(target_arch = "wasm32"))] impl From<::ring::error::Unspecified> for Error { fn from(err: ::ring::error::Unspecified) -> Error { new_error(ErrorKind::Crypto(err)) } } +#[cfg(not(target_arch = "wasm32"))] impl From<::ring::error::KeyRejected> for Error { fn from(_err: ::ring::error::KeyRejected) -> Error { new_error(ErrorKind::InvalidEcdsaKey) From b116e4429912cadcf652a911edccb425bde8b119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Wed, 13 Mar 2024 14:50:01 +0800 Subject: [PATCH 02/35] upgrade dependency --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 591f6446..849208ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ simple_asn1 = {version = "0.6", optional = true} ring = { version = "0.16.5", features = ["std"] } [target.'cfg(target_arch="wasm32")'.dependencies] hmac = "0.12.1" -rsa = "0.9.2" +rsa = "0.9.6" sha2 = { version = "0.10.7", features = ["oid"] } getrandom = { version = "0.2.10", features = ["js"] } rand = { version = "0.8.5", features = ["std"], default-features = false } From e2a203f54f0acf8567f6dc818c875cbeae6e5ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Fri, 15 Mar 2024 13:19:17 +0800 Subject: [PATCH 03/35] remove ring --- Cargo.toml | 17 +++--- examples/custom_header.rs | 2 +- examples/ed25519.rs | 31 ++++++---- examples/validation.rs | 2 +- src/crypto/ecdsa.rs | 84 ++++++++++++++++++-------- src/crypto/eddsa.rs | 34 ++++++----- src/crypto/hmac.rs | 124 ++++++++++++++++++++++++++++++++++++++ src/crypto/mod.rs | 124 +++----------------------------------- src/crypto/rsa.rs | 98 +++++------------------------- src/errors.rs | 25 ++------ src/jwk.rs | 10 +-- src/pem/decoder.rs | 2 +- src/validation.rs | 2 +- tests/hmac.rs | 2 +- 14 files changed, 269 insertions(+), 288 deletions(-) create mode 100644 src/crypto/hmac.rs diff --git a/Cargo.toml b/Cargo.toml index 849208ef..80eaa889 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,27 +14,28 @@ rust-version = "1.56.0" [dependencies] serde_json = "1.0" -serde = {version = "1.0", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } base64 = "0.21.0" # For PEM decoding -pem = {version = "2", optional = true} -simple_asn1 = {version = "0.6", optional = true} -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -ring = { version = "0.16.5", features = ["std"] } -[target.'cfg(target_arch="wasm32")'.dependencies] +pem = { version = "2", optional = true } +simple_asn1 = { version = "0.6", optional = true } hmac = "0.12.1" rsa = "0.9.6" sha2 = { version = "0.10.7", features = ["oid"] } getrandom = { version = "0.2.10", features = ["js"] } rand = { version = "0.8.5", features = ["std"], default-features = false } +ed25519-dalek = "2.1.1" +p256 = { version = "0.13.2", features = ["ecdsa"] } +p384 = { version = "0.13.0", features = ["ecdsa"] } +rand_core = "0.6.4" [dev-dependencies] # For the custom time example time = "0.3" -criterion = "0.4" +criterion = { version = "0.4", default-features = false } [features] default = ["use_pem"] -use_pem = ["pem", "simple_asn1"] +use_pem = ["pem", "simple_asn1", 'p256/pem', 'p384/pem'] [[bench]] name = "jwt" diff --git a/examples/custom_header.rs b/examples/custom_header.rs index 9bda7d39..850da438 100644 --- a/examples/custom_header.rs +++ b/examples/custom_header.rs @@ -7,7 +7,7 @@ use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, struct Claims { sub: String, company: String, - exp: usize, + exp: i64, } fn main() { diff --git a/examples/ed25519.rs b/examples/ed25519.rs index 21a8f217..a2ab1dc5 100644 --- a/examples/ed25519.rs +++ b/examples/ed25519.rs @@ -1,8 +1,11 @@ +use p256::pkcs8::{EncodePrivateKey, EncodePublicKey}; +use p256::SecretKey; +use rand_core::OsRng; +use serde::{Deserialize, Serialize}; + use jsonwebtoken::{ - decode, encode, get_current_timestamp, Algorithm, DecodingKey, EncodingKey, Validation, + Algorithm, decode, DecodingKey, encode, EncodingKey, get_current_timestamp, Validation, }; -use ring::signature::{Ed25519KeyPair, KeyPair}; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct Claims { @@ -11,11 +14,14 @@ pub struct Claims { } fn main() { - let doc = Ed25519KeyPair::generate_pkcs8(&ring::rand::SystemRandom::new()).unwrap(); - let encoding_key = EncodingKey::from_ed_der(doc.as_ref()); + let secret_key = SecretKey::random(&mut OsRng); + let pkcs8 = secret_key.to_pkcs8_pem(Default::default()).unwrap(); + let pkcs8= pkcs8.as_bytes(); - let pair = Ed25519KeyPair::from_pkcs8(doc.as_ref()).unwrap(); - let decoding_key = DecodingKey::from_ed_der(pair.public_key().as_ref()); + let encoding_key = EncodingKey::from_ed_der(pkcs8); + + let public_key_der = secret_key.public_key().to_public_key_der().unwrap(); + let decoding_key = DecodingKey::from_ed_der(public_key_der.as_bytes()); let claims = Claims { sub: "test".to_string(), exp: get_current_timestamp() }; @@ -37,11 +43,14 @@ mod tests { impl Jot { fn new() -> Jot { - let doc = Ed25519KeyPair::generate_pkcs8(&ring::rand::SystemRandom::new()).unwrap(); - let encoding_key = EncodingKey::from_ed_der(doc.as_ref()); + let secret_key = SecretKey::random(&mut OsRng); + let pkcs8 = secret_key.to_pkcs8_pem(Default::default()).unwrap(); + let pkcs8= pkcs8.as_bytes(); + let encoding_key = EncodingKey::from_ed_der(pkcs8); + + let public_key_der = secret_key.public_key().to_public_key_der().unwrap(); + let decoding_key = DecodingKey::from_ed_der(public_key_der.as_bytes()); - let pair = Ed25519KeyPair::from_pkcs8(doc.as_ref()).unwrap(); - let decoding_key = DecodingKey::from_ed_der(pair.public_key().as_ref()); Jot { encoding_key, decoding_key } } } diff --git a/examples/validation.rs b/examples/validation.rs index fdd0c638..991f53de 100644 --- a/examples/validation.rs +++ b/examples/validation.rs @@ -7,7 +7,7 @@ struct Claims { aud: String, sub: String, company: String, - exp: usize, + exp: i64, } fn main() { diff --git a/src/crypto/ecdsa.rs b/src/crypto/ecdsa.rs index f1c3626d..2819e014 100644 --- a/src/crypto/ecdsa.rs +++ b/src/crypto/ecdsa.rs @@ -1,38 +1,74 @@ -use ring::{rand, signature}; - use crate::algorithms::Algorithm; use crate::errors::Result; -use crate::serialization::b64_encode; +use crate::serialization::{b64_decode, b64_encode}; -/// Only used internally when validating EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs. -pub(crate) fn alg_to_ec_verification( - alg: Algorithm, -) -> &'static signature::EcdsaVerificationAlgorithm { +/// The actual ECDSA signing + encoding +/// The key needs to be in PKCS8 format +pub(crate) fn sign(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { match alg { - Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED, - Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED, + Algorithm::ES256 => es256_sign(key, message), + Algorithm::ES384 => es384_sign(key, message), + _ => unreachable!("Tried to get EC alg for a non-EC algorithm"), } } -/// Only used internally when signing EC, to map from our enum to the Ring EcdsaVerificationAlgorithm structs. -pub(crate) fn alg_to_ec_signing(alg: Algorithm) -> &'static signature::EcdsaSigningAlgorithm { +fn es256_sign(key: &[u8], message: &[u8]) -> Result { + use p256::ecdsa::signature::Signer; + use p256::ecdsa::{Signature, SigningKey}; + use p256::pkcs8::DecodePrivateKey; + use p256::SecretKey; + let secret_key = SecretKey::from_pkcs8_der(key) + .map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; + let signing_key: SigningKey = secret_key.into(); + + let signature: Signature = signing_key.sign(message); + let bytes = signature.to_bytes(); + Ok(b64_encode(bytes)) +} + +fn es384_sign(key: &[u8], message: &[u8]) -> Result { + use p384::ecdsa::signature::Signer; + use p384::ecdsa::{Signature, SigningKey}; + use p384::pkcs8::DecodePrivateKey; + use p384::SecretKey; + let secret_key = SecretKey::from_pkcs8_der(key) + .map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; + let signing_key: SigningKey = secret_key.into(); + let signature: Signature = signing_key.sign(message); + let bytes = signature.to_bytes(); + Ok(b64_encode(bytes)) +} + +pub(crate) fn verify(alg: Algorithm, signature: &str, message: &[u8], key: &[u8]) -> Result { match alg { - Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED_SIGNING, - Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED_SIGNING, + Algorithm::ES256 => es256_verify(signature, message, key), + Algorithm::ES384 => es384_verify(signature, message, key), _ => unreachable!("Tried to get EC alg for a non-EC algorithm"), } } -/// The actual ECDSA signing + encoding -/// The key needs to be in PKCS8 format -pub fn sign( - alg: &'static signature::EcdsaSigningAlgorithm, - key: &[u8], - message: &[u8], -) -> Result { - let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, key)?; - let rng = rand::SystemRandom::new(); - let out = signing_key.sign(&rng, message)?; - Ok(b64_encode(out)) +fn es384_verify(signature: &str, message: &[u8], key: &[u8]) -> Result { + use p384::ecdsa::signature::Verifier; + use p384::ecdsa::{Signature, VerifyingKey}; + use p384::PublicKey; + + let public_key = PublicKey::from_sec1_bytes(key) + .map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; + let verifying_key: VerifyingKey = public_key.into(); + let signature = Signature::from_slice(&b64_decode(signature)?) + .map_err(|_e| crate::errors::ErrorKind::InvalidSignature)?; + Ok(verifying_key.verify(message, &signature).is_ok()) } + +fn es256_verify(signature: &str, message: &[u8], key: &[u8]) -> Result { + use p256::ecdsa::signature::Verifier; + use p256::ecdsa::{Signature, VerifyingKey}; + use p256::PublicKey; + let public_key = PublicKey::from_sec1_bytes(key) + .map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; + let verifying_key: VerifyingKey = public_key.into(); + let signature = Signature::from_slice(&b64_decode(signature)?) + .map_err(|_e| crate::errors::ErrorKind::InvalidSignature)?; + Ok(verifying_key.verify(message, &signature).is_ok()) +} \ No newline at end of file diff --git a/src/crypto/eddsa.rs b/src/crypto/eddsa.rs index 7c185347..1fd1ec4d 100644 --- a/src/crypto/eddsa.rs +++ b/src/crypto/eddsa.rs @@ -1,23 +1,29 @@ -use ring::signature; +use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; -use crate::algorithms::Algorithm; -use crate::errors::Result; -use crate::serialization::b64_encode; +use crate::errors::{ErrorKind, new_error, Result}; +use crate::serialization::{b64_decode, b64_encode}; -/// Only used internally when signing or validating EdDSA, to map from our enum to the Ring EdDSAParameters structs. -pub(crate) fn alg_to_ec_verification(alg: Algorithm) -> &'static signature::EdDSAParameters { - // To support additional key subtypes, like Ed448, we would need to match on the JWK's ("crv") - // parameter. - match alg { - Algorithm::EdDSA => &signature::ED25519, - _ => unreachable!("Tried to get EdDSA alg for a non-EdDSA algorithm"), - } +fn parse_key(key: &[u8]) -> Result { + let key = key.try_into() + .map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?; + let signing_key = SigningKey::from_bytes(key); + Ok(signing_key) +} + +pub(crate) fn verify(signature: &str, message: &[u8], key: &[u8]) -> Result { + let signature = b64_decode(signature)?; + let signature = + Signature::from_slice(&signature).map_err(|_e| new_error(ErrorKind::InvalidSignature))?; + let key = key.try_into().map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?; + let verifying_key = VerifyingKey::from_bytes(key).map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?; + Ok(verifying_key.verify(message, &signature).is_ok()) } /// The actual EdDSA signing + encoding /// The key needs to be in PKCS8 format pub fn sign(key: &[u8], message: &[u8]) -> Result { - let signing_key = signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(key)?; + let key = key[16..].into(); + let signing_key = parse_key(key)?; let out = signing_key.sign(message); - Ok(b64_encode(out)) + Ok(b64_encode(out.to_bytes())) } diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs new file mode 100644 index 00000000..604577a9 --- /dev/null +++ b/src/crypto/hmac.rs @@ -0,0 +1,124 @@ + +use hmac::{Hmac, Mac}; +use sha2::{Sha256, Sha384, Sha512}; + +use crate::Algorithm; +use crate::errors::Result; +use crate::serialization::{b64_decode, b64_encode}; + +type HmacSha256 = Hmac; +type HmacSha384 = Hmac; +type HmacSha512 = Hmac; + +pub(crate) fn sign_hmac(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { + let mut hmac = create_hmac(alg, key)?; + let digest = hmac.sign(message); + Ok(b64_encode(digest)) +} + + +pub(crate) fn hmac_verify( + alg: Algorithm, + signature: &str, + key: &[u8], + message: &[u8]) -> Result { + let mut hmac = create_hmac(alg, key)?; + let signature = b64_decode(signature)?; + Ok(hmac.verify(&signature, message)) +} + +fn create_hmac(alg: Algorithm, key: &[u8]) -> Result> { + let hmac: Box = match alg { + Algorithm::HS256 => { + let sha256 = HmacSha256::new_from_slice(key) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + Box::new(sha256) + } + Algorithm::HS384 => { + let sha384 = HmacSha384::new_from_slice(key) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + Box::new(sha384) + } + Algorithm::HS512 => { + let sha512 = HmacSha512::new_from_slice(key) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + Box::new(sha512) + } + _ => { + return Err(crate::errors::new_error(crate::errors::ErrorKind::InvalidAlgorithm)); + } + }; + Ok(hmac) +} + +trait HmacAlgorithm { + fn sign(&mut self, message: &[u8]) -> Vec; + fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool; +} + +impl HmacAlgorithm for Box { + fn sign(&mut self, message: &[u8]) -> Vec { + (**self).sign(message) + } + + fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool { + (**self).verify(signature, message) + } +} + +impl HmacAlgorithm for HmacSha256 { + // type S = Self; + // fn new_hmac_from_slice(key: &[u8]) -> Result { + // Ok(HmacSha256::new_from_slice(key) + // .map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?) + // } + + fn sign(&mut self, message: &[u8]) -> Vec { + self.reset(); + self.update(message); + self.clone().finalize().into_bytes().to_vec() + } + fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool { + self.reset(); + self.update(message); + self.clone().verify_slice(signature).is_ok() + } +} + +impl HmacAlgorithm for HmacSha384 { + // type S = Self; + // fn new_hmac_from_slice(key: &[u8]) -> Result { + // Ok(HmacSha384::new_from_slice(key) + // .map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?) + // } + + fn sign(&mut self, message: &[u8]) -> Vec { + self.reset(); + self.update(message); + self.clone().finalize().into_bytes().to_vec() + } + fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool { + self.reset(); + self.update(message); + self.clone().verify_slice(signature).is_ok() + } +} + +impl HmacAlgorithm for HmacSha512 { + // type S = Self; + // fn new_hmac_from_slice(key: &[u8]) -> Result { + // Ok(HmacSha512::new_from_slice(key) + // .map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?) + // } + + fn sign(&mut self, message: &[u8]) -> Vec { + self.reset(); + self.update(message); + self.clone().finalize().into_bytes().to_vec() + } + fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool { + self.reset(); + self.update(message); + self.clone().verify_slice(signature).is_ok() + } +} \ No newline at end of file diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index ad929d0e..f4de10b1 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,60 +1,14 @@ -#[cfg(not(target_arch = "wasm32"))] -use ring::constant_time::verify_slices_are_equal; -#[cfg(not(target_arch = "wasm32"))] -use ring::{hmac, signature}; -#[cfg(target_arch = "wasm32")] -use sha2::{Sha256,Sha384,Sha512}; -#[cfg(target_arch = "wasm32")] -use hmac::{Hmac, Mac}; use crate::algorithms::Algorithm; use crate::decoding::{DecodingKey, DecodingKeyKind}; use crate::encoding::EncodingKey; use crate::errors::Result; -use crate::serialization::{b64_decode, b64_encode}; -#[cfg(not(target_arch = "wasm32"))] + pub(crate) mod ecdsa; -#[cfg(not(target_arch = "wasm32"))] pub(crate) mod eddsa; pub(crate) mod rsa; +pub(crate) mod hmac; -/// The actual HS signing + encoding -/// Could be in its own file to match RSA/EC but it's 2 lines... -#[cfg(not(target_arch = "wasm32"))] -pub(crate) fn sign_hmac(alg: hmac::Algorithm, key: &[u8], message: &[u8]) -> String { - let digest = hmac::sign(&hmac::Key::new(alg, key), message); - b64_encode(digest) -} -#[cfg(target_arch = "wasm32")] -type HmacSha256 = Hmac; -#[cfg(target_arch = "wasm32")] -type HmacSha384 = Hmac; -#[cfg(target_arch = "wasm32")] -type HmacSha512 = Hmac; -#[cfg(target_arch = "wasm32")] -pub(crate) fn sign_hmac(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { - let digest= match alg { - Algorithm::HS256 => { - let mut mac = HmacSha256::new_from_slice(key).map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?; - mac.update(message); - mac.finalize().into_bytes().to_vec() - } - Algorithm::HS384 => { - let mut mac = HmacSha384::new_from_slice(key).map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?; - mac.update(message); - mac.finalize().into_bytes().to_vec() - } - Algorithm::HS512 => { - let mut mac = HmacSha512::new_from_slice(key).map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?; - mac.update(message); - mac.finalize().into_bytes().to_vec() - } - _=>{ - return Err(crate::errors::new_error(crate::errors::ErrorKind::InvalidAlgorithm)); - } - }; - Ok(b64_encode(&digest)) -} /// Take the payload of a JWT, sign it using the algorithm given and return /// the base64 url safe encoded of the result. @@ -62,30 +16,13 @@ pub(crate) fn sign_hmac(alg: Algorithm, key: &[u8], message: &[u8]) -> Result Result { match algorithm { - #[cfg(not(target_arch = "wasm32"))] - Algorithm::HS256 => Ok(sign_hmac(hmac::HMAC_SHA256, key.inner(), message)), - #[cfg(not(target_arch = "wasm32"))] - Algorithm::HS384 => Ok(sign_hmac(hmac::HMAC_SHA384, key.inner(), message)), - #[cfg(not(target_arch = "wasm32"))] - Algorithm::HS512 => Ok(sign_hmac(hmac::HMAC_SHA512, key.inner(), message)), - #[cfg(not(target_arch = "wasm32"))] Algorithm::ES256 | Algorithm::ES384 => { - ecdsa::sign(ecdsa::alg_to_ec_signing(algorithm), key.inner(), message) + ecdsa::sign(algorithm, key.inner(), message) } - #[cfg(not(target_arch = "wasm32"))] Algorithm::EdDSA => eddsa::sign(key.inner(), message), - #[cfg(not(target_arch = "wasm32"))] - Algorithm::RS256 - | Algorithm::RS384 - | Algorithm::RS512 - | Algorithm::PS256 - | Algorithm::PS384 - | Algorithm::PS512 => rsa::sign(rsa::alg_to_rsa_signing(algorithm), key.inner(), message), - #[cfg(target_arch = "wasm32")] Algorithm::HS256 | Algorithm::HS384 - | Algorithm::HS512 => sign_hmac(algorithm, key.inner(), message), - #[cfg(target_arch = "wasm32")] + | Algorithm::HS512 => hmac::sign_hmac(algorithm, key.inner(), message), Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 @@ -93,25 +30,9 @@ pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result { rsa::sign(algorithm, key.inner(), message) - }, - #[cfg(target_arch = "wasm32")] - _ => Err(crate::errors::new_error(crate::errors::ErrorKind::InvalidAlgorithm)), + } } } -#[cfg(not(target_arch = "wasm32"))] -/// See Ring docs for more details -fn verify_ring( - alg: &'static dyn signature::VerificationAlgorithm, - signature: &str, - message: &[u8], - key: &[u8], -) -> Result { - let signature_bytes = b64_decode(signature)?; - let public_key = signature::UnparsedPublicKey::new(alg, key); - let res = public_key.verify(message, &signature_bytes); - - Ok(res.is_ok()) -} /// Compares the signature given with a re-computed signature for HMAC or using the public key /// for RSA/EC. @@ -128,50 +49,21 @@ pub fn verify( algorithm: Algorithm, ) -> Result { match algorithm { - #[cfg(not(target_arch = "wasm32"))] - Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { - // we just re-sign the message with the key and compare if they are equal - let signed = sign(message, &EncodingKey::from_secret(key.as_bytes()), algorithm)?; - Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok()) - } - #[cfg(not(target_arch = "wasm32"))] - Algorithm::ES256 | Algorithm::ES384 => verify_ring( - ecdsa::alg_to_ec_verification(algorithm), - signature, - message, - key.as_bytes(), - ), - #[cfg(not(target_arch = "wasm32"))] - Algorithm::EdDSA => verify_ring( - eddsa::alg_to_ec_verification(algorithm), - signature, - message, - key.as_bytes(), - ), + Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => hmac::hmac_verify(algorithm, signature, key.as_bytes(), message), + Algorithm::ES256 | Algorithm::ES384 => ecdsa::verify(algorithm, signature, message, key.as_bytes()), + Algorithm::EdDSA => eddsa::verify(signature, message, key.as_bytes()), Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 | Algorithm::PS512 => { - #[cfg(not(target_arch = "wasm32"))] - let alg = rsa::alg_to_rsa_parameters(algorithm); match &key.kind { - #[cfg(not(target_arch = "wasm32"))] - DecodingKeyKind::SecretOrDer(bytes) => verify_ring(alg, signature, message, bytes), - #[cfg(not(target_arch = "wasm32"))] - DecodingKeyKind::RsaModulusExponent { n, e } => { - rsa::verify_from_components(alg, signature, message, (n, e)) - } - #[cfg(target_arch = "wasm32")] DecodingKeyKind::SecretOrDer(bytes) => rsa::verify_der(algorithm, signature, message, bytes), - #[cfg(target_arch = "wasm32")] DecodingKeyKind::RsaModulusExponent { n, e } => { rsa::verify_from_components(algorithm, signature, message, (n, e)) } } } - #[cfg(target_arch = "wasm32")] - _ => Err(crate::errors::new_error(crate::errors::ErrorKind::InvalidAlgorithm)), } } diff --git a/src/crypto/rsa.rs b/src/crypto/rsa.rs index 3a51b9ca..05427686 100644 --- a/src/crypto/rsa.rs +++ b/src/crypto/rsa.rs @@ -1,76 +1,10 @@ -#[cfg(not(target_arch = "wasm32"))] -use ring::{rand, signature}; -#[cfg(target_arch = "wasm32")] -use rsa::{pss::Pss, Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey, traits::SignatureScheme, BigUint, pkcs1::DecodeRsaPrivateKey, pkcs1::DecodeRsaPublicKey}; -#[cfg(target_arch = "wasm32")] +use rsa::{BigUint, pkcs1::DecodeRsaPrivateKey, pkcs1::DecodeRsaPublicKey, Pkcs1v15Sign, pss::Pss, RsaPrivateKey, RsaPublicKey, traits::SignatureScheme}; use sha2::{Digest, Sha256, Sha384, Sha512}; use crate::algorithms::Algorithm; -use crate::errors::{ErrorKind, Result, new_error}; +use crate::errors::{ErrorKind, new_error, Result}; use crate::serialization::{b64_decode, b64_encode}; -/// Only used internally when validating RSA, to map from our enum to the Ring param structs. -#[cfg(not(target_arch = "wasm32"))] -pub(crate) fn alg_to_rsa_parameters(alg: Algorithm) -> &'static signature::RsaParameters { - match alg { - Algorithm::RS256 => &signature::RSA_PKCS1_2048_8192_SHA256, - Algorithm::RS384 => &signature::RSA_PKCS1_2048_8192_SHA384, - Algorithm::RS512 => &signature::RSA_PKCS1_2048_8192_SHA512, - Algorithm::PS256 => &signature::RSA_PSS_2048_8192_SHA256, - Algorithm::PS384 => &signature::RSA_PSS_2048_8192_SHA384, - Algorithm::PS512 => &signature::RSA_PSS_2048_8192_SHA512, - _ => unreachable!("Tried to get RSA signature for a non-rsa algorithm"), - } -} - -/// Only used internally when signing with RSA, to map from our enum to the Ring signing structs. -#[cfg(not(target_arch = "wasm32"))] -pub(crate) fn alg_to_rsa_signing(alg: Algorithm) -> &'static dyn signature::RsaEncoding { - match alg { - Algorithm::RS256 => &signature::RSA_PKCS1_SHA256, - Algorithm::RS384 => &signature::RSA_PKCS1_SHA384, - Algorithm::RS512 => &signature::RSA_PKCS1_SHA512, - Algorithm::PS256 => &signature::RSA_PSS_SHA256, - Algorithm::PS384 => &signature::RSA_PSS_SHA384, - Algorithm::PS512 => &signature::RSA_PSS_SHA512, - _ => unreachable!("Tried to get RSA signature for a non-rsa algorithm"), - } -} - -/// The actual RSA signing + encoding -/// The key needs to be in PKCS8 format -/// Taken from Ring doc https://docs.rs/ring/latest/ring/signature/index.html -#[cfg(not(target_arch = "wasm32"))] -pub(crate) fn sign( - alg: &'static dyn signature::RsaEncoding, - key: &[u8], - message: &[u8], -) -> Result { - let key_pair = signature::RsaKeyPair::from_der(key) - .map_err(|e| ErrorKind::InvalidRsaKey(e.description_().to_owned()))?; - - let mut signature = vec![0; key_pair.public_modulus_len()]; - let rng = rand::SystemRandom::new(); - key_pair.sign(alg, &rng, message, &mut signature).map_err(|_| ErrorKind::RsaFailedSigning)?; - - Ok(b64_encode(signature)) -} - -/// Checks that a signature is valid based on the (n, e) RSA pubkey components -#[cfg(not(target_arch = "wasm32"))] -pub(crate) fn verify_from_components( - alg: &'static signature::RsaParameters, - signature: &str, - message: &[u8], - components: (&[u8], &[u8]), -) -> Result { - let signature_bytes = b64_decode(signature)?; - let pubkey = signature::RsaPublicKeyComponents { n: components.0, e: components.1 }; - let res = pubkey.verify(alg, message, &signature_bytes); - Ok(res.is_ok()) -} - -#[cfg(target_arch = "wasm32")] fn alg_to_pss(alg: Algorithm, digest_len: usize) -> Option { match alg { Algorithm::PS256 => Some(Pss::new_with_salt::(digest_len)), @@ -80,7 +14,6 @@ fn alg_to_pss(alg: Algorithm, digest_len: usize) -> Option { } } -#[cfg(target_arch = "wasm32")] fn alg_to_pkcs1_v15(alg: Algorithm) -> Option { match alg { Algorithm::RS256 => Some(Pkcs1v15Sign::new::()), @@ -90,7 +23,6 @@ fn alg_to_pkcs1_v15(alg: Algorithm) -> Option { } } -#[cfg(target_arch = "wasm32")] fn message_digest(alg: Algorithm, message: &[u8]) -> Result> { match alg { Algorithm::RS256 | Algorithm::PS256 => { @@ -115,7 +47,6 @@ fn message_digest(alg: Algorithm, message: &[u8]) -> Result> { } } -#[cfg(target_arch = "wasm32")] pub(crate) fn sign(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { @@ -127,17 +58,16 @@ pub(crate) fn sign(alg: Algorithm, let mut rng = rand::thread_rng(); let signature = if let Some(signatures_scheme) = signatures_scheme_pkcs { signatures_scheme.sign(Some(&mut rng), &private_key, &digest) - .map_err(|e| ErrorKind::RsaFailedSigning)? + .map_err(|_e| ErrorKind::RsaFailedSigning)? } else if let Some(signatures_scheme) = signatures_scheme_pss { signatures_scheme.sign(Some(&mut rng), &private_key, &digest) - .map_err(|e| ErrorKind::RsaFailedSigning)? + .map_err(|_e| ErrorKind::RsaFailedSigning)? } else { return Err(new_error(ErrorKind::InvalidAlgorithmName)); }; - Ok(b64_encode(&signature)) + Ok(b64_encode(signature)) } -#[cfg(target_arch = "wasm32")] pub(crate) fn verify_from_components( alg: Algorithm, signature: &str, @@ -146,38 +76,38 @@ pub(crate) fn verify_from_components( ) -> Result { let n = BigUint::from_bytes_be(components.0); let e = BigUint::from_bytes_be(components.1); - let pub_key = RsaPublicKey::new(n, e) - .map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; + let pub_key = + RsaPublicKey::new(n, e).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; verify(alg, signature, message, &pub_key) } -#[cfg(target_arch = "wasm32")] fn verify(alg: Algorithm, signature: &str, message: &[u8], pub_key: &RsaPublicKey) -> Result { let signature_bytes = b64_decode(signature)?; let digest = message_digest(alg, message)?; let signatures_scheme_pkcs = alg_to_pkcs1_v15(alg); let signatures_scheme_pss = alg_to_pss(alg, digest.len()); if let Some(signatures_scheme) = signatures_scheme_pkcs { - signatures_scheme.verify(&pub_key, &digest, &signature_bytes) - .map_err(|e| ErrorKind::InvalidSignature)?; + signatures_scheme + .verify(pub_key, &digest, &signature_bytes) + .map_err(|_e| ErrorKind::InvalidSignature)?; } else if let Some(signatures_scheme) = signatures_scheme_pss { signatures_scheme - .verify(&pub_key, &digest, &signature_bytes) - .map_err(|e| ErrorKind::InvalidSignature)?; + .verify(pub_key, &digest, &signature_bytes) + .map_err(|_e| ErrorKind::InvalidSignature)?; } else { return Err(new_error(ErrorKind::InvalidAlgorithmName)); }; Ok(true) } -#[cfg(target_arch = "wasm32")] pub(crate) fn verify_der( alg: Algorithm, signature: &str, message: &[u8], bytes: &[u8], ) -> Result { - let pub_key = RsaPublicKey::from_pkcs1_der(bytes).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; + let pub_key = + RsaPublicKey::from_pkcs1_der(bytes).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; verify(alg, signature, message, &pub_key) } diff --git a/src/errors.rs b/src/errors.rs index ff5f198c..4648ff5a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -41,6 +41,8 @@ pub enum ErrorKind { InvalidSignature, /// When the secret given is not a valid ECDSA key InvalidEcdsaKey, + /// When the secret given is not a valid EDDSA key + InvalidEddsaKey, /// When the secret given is not a valid RSA key InvalidRsaKey(String), /// We could not sign with the given key @@ -76,9 +78,6 @@ pub enum ErrorKind { Json(Arc), /// Some of the text was invalid UTF-8 Utf8(::std::string::FromUtf8Error), - /// Something unspecified went wrong with crypto - #[cfg(not(target_arch = "wasm32"))] - Crypto(::ring::error::Unspecified), } impl StdError for Error { @@ -102,8 +101,7 @@ impl StdError for Error { ErrorKind::Base64(err) => Some(err), ErrorKind::Json(err) => Some(err.as_ref()), ErrorKind::Utf8(err) => Some(err), - #[cfg(not(target_arch = "wasm32"))] - ErrorKind::Crypto(err) => Some(err), + ErrorKind::InvalidEddsaKey => None, } } } @@ -123,13 +121,12 @@ impl fmt::Display for Error { | ErrorKind::ImmatureSignature | ErrorKind::InvalidAlgorithm | ErrorKind::InvalidKeyFormat + | ErrorKind::InvalidEddsaKey | ErrorKind::InvalidAlgorithmName => write!(f, "{:?}", self.0), ErrorKind::MissingRequiredClaim(c) => write!(f, "Missing required claim: {}", c), ErrorKind::InvalidRsaKey(msg) => write!(f, "RSA key invalid: {}", msg), ErrorKind::Json(err) => write!(f, "JSON error: {}", err), ErrorKind::Utf8(err) => write!(f, "UTF-8 error: {}", err), - #[cfg(not(target_arch = "wasm32"))] - ErrorKind::Crypto(err) => write!(f, "Crypto error: {}", err), ErrorKind::Base64(err) => write!(f, "Base64 error: {}", err), } } @@ -162,20 +159,6 @@ impl From<::std::string::FromUtf8Error> for Error { } } -#[cfg(not(target_arch = "wasm32"))] -impl From<::ring::error::Unspecified> for Error { - fn from(err: ::ring::error::Unspecified) -> Error { - new_error(ErrorKind::Crypto(err)) - } -} - -#[cfg(not(target_arch = "wasm32"))] -impl From<::ring::error::KeyRejected> for Error { - fn from(_err: ::ring::error::KeyRejected) -> Error { - new_error(ErrorKind::InvalidEcdsaKey) - } -} - impl From for Error { fn from(kind: ErrorKind) -> Error { new_error(kind) diff --git a/src/jwk.rs b/src/jwk.rs index 627ff71c..f9ca828e 100644 --- a/src/jwk.rs +++ b/src/jwk.rs @@ -1,9 +1,9 @@ #![allow(missing_docs)] -///! This crate contains types only for working JWK and JWK Sets -///! This is only meant to be used to deal with public JWK, not generate ones. -///! Most of the code in this file is taken from https://github.com/lawliet89/biscuit but -/// tweaked to remove the private bits as it's not the goal for this crate currently. -///! +//! This crate contains types only for working JWK and JWK Sets +//! This is only meant to be used to deal with public JWK, not generate ones. +//! Most of the code in this file is taken from https://github.com/lawliet89/biscuit but +// tweaked to remove the private bits as it's not the goal for this crate currently. +//! use crate::Algorithm; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; diff --git a/src/pem/decoder.rs b/src/pem/decoder.rs index e4c24d23..94ff8821 100644 --- a/src/pem/decoder.rs +++ b/src/pem/decoder.rs @@ -58,7 +58,7 @@ impl PemEncodedKey { Err(_) => return Err(ErrorKind::InvalidKeyFormat.into()), }; - match content.tag().as_ref() { + match content.tag() { // This handles a PKCS#1 RSA Private key "RSA PRIVATE KEY" => Ok(PemEncodedKey { content: content.into_contents(), diff --git a/src/validation.rs b/src/validation.rs index 399a38a1..195c9161 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -228,7 +228,7 @@ pub(crate) fn validate(claims: ClaimsForValidation, options: &Validation) -> Res } } - if (options.validate_exp || options.validate_nbf) { + if options.validate_exp || options.validate_nbf { let now = get_current_timestamp(); if matches!(claims.exp, TryParse::Parsed(exp) if options.validate_exp && exp < now - options.leeway) diff --git a/tests/hmac.rs b/tests/hmac.rs index a6d44642..17a56e60 100644 --- a/tests/hmac.rs +++ b/tests/hmac.rs @@ -96,7 +96,7 @@ fn decode_token_missing_parts() { #[should_panic(expected = "InvalidSignature")] fn decode_token_invalid_signature() { let token = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.wrong"; + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.Hm0yvKH25TavFPz7J_coST9lZFYH1hQo0tvhvImmaks"; let claims = decode::( token, &DecodingKey::from_secret(b"secret"), From d80832c608ab45bbc4a7c17924e3b37b66385928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Sun, 17 Mar 2024 15:54:03 +0800 Subject: [PATCH 04/35] fix wasm test failed --- Cargo.toml | 3 +++ examples/ed25519.rs | 2 +- src/crypto/ecdsa.rs | 4 ++-- src/crypto/eddsa.rs | 8 ++++---- src/crypto/hmac.rs | 21 +++------------------ src/crypto/mod.rs | 40 +++++++++++++++++++--------------------- src/crypto/rsa.rs | 24 +++++++++++++----------- tests/hmac.rs | 9 +++------ 8 files changed, 48 insertions(+), 63 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 79a5af64..4cc3754b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,9 @@ ed25519-dalek = "2.1.1" p256 = { version = "0.13.2", features = ["ecdsa"] } p384 = { version = "0.13.0", features = ["ecdsa"] } rand_core = "0.6.4" +[target.'cfg(target_arch = "wasm32")'.dependencies] +js-sys = "0.3" + [dev-dependencies] wasm-bindgen-test = "0.3.1" diff --git a/examples/ed25519.rs b/examples/ed25519.rs index a2ab1dc5..90596b69 100644 --- a/examples/ed25519.rs +++ b/examples/ed25519.rs @@ -4,7 +4,7 @@ use rand_core::OsRng; use serde::{Deserialize, Serialize}; use jsonwebtoken::{ - Algorithm, decode, DecodingKey, encode, EncodingKey, get_current_timestamp, Validation, + decode, encode, get_current_timestamp, Algorithm, DecodingKey, EncodingKey, Validation, }; #[derive(Debug, Serialize, Deserialize)] diff --git a/src/crypto/ecdsa.rs b/src/crypto/ecdsa.rs index 2819e014..c29abef0 100644 --- a/src/crypto/ecdsa.rs +++ b/src/crypto/ecdsa.rs @@ -65,8 +65,8 @@ fn es256_verify(signature: &str, message: &[u8], key: &[u8]) -> Result { use p256::ecdsa::signature::Verifier; use p256::ecdsa::{Signature, VerifyingKey}; use p256::PublicKey; - let public_key = PublicKey::from_sec1_bytes(key) - .map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; + let public_key = + PublicKey::from_sec1_bytes(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; let verifying_key: VerifyingKey = public_key.into(); let signature = Signature::from_slice(&b64_decode(signature)?) .map_err(|_e| crate::errors::ErrorKind::InvalidSignature)?; diff --git a/src/crypto/eddsa.rs b/src/crypto/eddsa.rs index 1fd1ec4d..3da4fb5a 100644 --- a/src/crypto/eddsa.rs +++ b/src/crypto/eddsa.rs @@ -1,11 +1,10 @@ use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; -use crate::errors::{ErrorKind, new_error, Result}; +use crate::errors::{new_error, ErrorKind, Result}; use crate::serialization::{b64_decode, b64_encode}; fn parse_key(key: &[u8]) -> Result { - let key = key.try_into() - .map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?; + let key = key.try_into().map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?; let signing_key = SigningKey::from_bytes(key); Ok(signing_key) } @@ -15,7 +14,8 @@ pub(crate) fn verify(signature: &str, message: &[u8], key: &[u8]) -> Result Result Result { + message: &[u8], +) -> Result { let mut hmac = create_hmac(alg, key)?; let signature = b64_decode(signature)?; Ok(hmac.verify(&signature, message)) @@ -67,11 +66,6 @@ impl HmacAlgorithm for Box { } impl HmacAlgorithm for HmacSha256 { - // type S = Self; - // fn new_hmac_from_slice(key: &[u8]) -> Result { - // Ok(HmacSha256::new_from_slice(key) - // .map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?) - // } fn sign(&mut self, message: &[u8]) -> Vec { self.reset(); @@ -86,11 +80,6 @@ impl HmacAlgorithm for HmacSha256 { } impl HmacAlgorithm for HmacSha384 { - // type S = Self; - // fn new_hmac_from_slice(key: &[u8]) -> Result { - // Ok(HmacSha384::new_from_slice(key) - // .map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?) - // } fn sign(&mut self, message: &[u8]) -> Vec { self.reset(); @@ -105,17 +94,13 @@ impl HmacAlgorithm for HmacSha384 { } impl HmacAlgorithm for HmacSha512 { - // type S = Self; - // fn new_hmac_from_slice(key: &[u8]) -> Result { - // Ok(HmacSha512::new_from_slice(key) - // .map_err(|e| crate::errors::ErrorKind::InvalidKeyFormat)?) - // } fn sign(&mut self, message: &[u8]) -> Vec { self.reset(); self.update(message); self.clone().finalize().into_bytes().to_vec() } + fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool { self.reset(); self.update(message); diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index f4de10b1..4f5328eb 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,4 +1,3 @@ - use crate::algorithms::Algorithm; use crate::decoding::{DecodingKey, DecodingKeyKind}; use crate::encoding::EncodingKey; @@ -6,9 +5,8 @@ use crate::errors::Result; pub(crate) mod ecdsa; pub(crate) mod eddsa; -pub(crate) mod rsa; pub(crate) mod hmac; - +pub(crate) mod rsa; /// Take the payload of a JWT, sign it using the algorithm given and return /// the base64 url safe encoded of the result. @@ -16,21 +14,17 @@ pub(crate) mod hmac; /// If you just want to encode a JWT, use `encode` instead. pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result { match algorithm { - Algorithm::ES256 | Algorithm::ES384 => { - ecdsa::sign(algorithm, key.inner(), message) - } + Algorithm::ES256 | Algorithm::ES384 => ecdsa::sign(algorithm, key.inner(), message), Algorithm::EdDSA => eddsa::sign(key.inner(), message), - Algorithm::HS256 - | Algorithm::HS384 - | Algorithm::HS512 => hmac::sign_hmac(algorithm, key.inner(), message), + Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { + hmac::sign_hmac(algorithm, key.inner(), message) + } Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 - | Algorithm::PS512 => { - rsa::sign(algorithm, key.inner(), message) - } + | Algorithm::PS512 => rsa::sign(algorithm, key.inner(), message), } } @@ -49,21 +43,25 @@ pub fn verify( algorithm: Algorithm, ) -> Result { match algorithm { - Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => hmac::hmac_verify(algorithm, signature, key.as_bytes(), message), - Algorithm::ES256 | Algorithm::ES384 => ecdsa::verify(algorithm, signature, message, key.as_bytes()), + Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { + hmac::hmac_verify(algorithm, signature, key.as_bytes(), message) + } + Algorithm::ES256 | Algorithm::ES384 => { + ecdsa::verify(algorithm, signature, message, key.as_bytes()) + } Algorithm::EdDSA => eddsa::verify(signature, message, key.as_bytes()), Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 | Algorithm::PS256 | Algorithm::PS384 - | Algorithm::PS512 => { - match &key.kind { - DecodingKeyKind::SecretOrDer(bytes) => rsa::verify_der(algorithm, signature, message, bytes), - DecodingKeyKind::RsaModulusExponent { n, e } => { - rsa::verify_from_components(algorithm, signature, message, (n, e)) - } + | Algorithm::PS512 => match &key.kind { + DecodingKeyKind::SecretOrDer(bytes) => { + rsa::verify_der(algorithm, signature, message, bytes) } - } + DecodingKeyKind::RsaModulusExponent { n, e } => { + rsa::verify_from_components(algorithm, signature, message, (n, e)) + } + }, } } diff --git a/src/crypto/rsa.rs b/src/crypto/rsa.rs index 05427686..7d13411e 100644 --- a/src/crypto/rsa.rs +++ b/src/crypto/rsa.rs @@ -1,8 +1,11 @@ -use rsa::{BigUint, pkcs1::DecodeRsaPrivateKey, pkcs1::DecodeRsaPublicKey, Pkcs1v15Sign, pss::Pss, RsaPrivateKey, RsaPublicKey, traits::SignatureScheme}; +use rsa::{ + pkcs1::DecodeRsaPrivateKey, pkcs1::DecodeRsaPublicKey, pss::Pss, traits::SignatureScheme, + BigUint, Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey, +}; use sha2::{Digest, Sha256, Sha384, Sha512}; use crate::algorithms::Algorithm; -use crate::errors::{ErrorKind, new_error, Result}; +use crate::errors::{new_error, ErrorKind, Result}; use crate::serialization::{b64_decode, b64_encode}; fn alg_to_pss(alg: Algorithm, digest_len: usize) -> Option { @@ -47,20 +50,20 @@ fn message_digest(alg: Algorithm, message: &[u8]) -> Result> { } } -pub(crate) fn sign(alg: Algorithm, - key: &[u8], - message: &[u8]) -> Result { +pub(crate) fn sign(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { let digest = message_digest(alg, message)?; let signatures_scheme_pkcs = alg_to_pkcs1_v15(alg); let signatures_scheme_pss = alg_to_pss(alg, digest.len()); - let private_key = RsaPrivateKey::from_pkcs1_der(key) - .map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; + let private_key = + RsaPrivateKey::from_pkcs1_der(key).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; let mut rng = rand::thread_rng(); let signature = if let Some(signatures_scheme) = signatures_scheme_pkcs { - signatures_scheme.sign(Some(&mut rng), &private_key, &digest) + signatures_scheme + .sign(Some(&mut rng), &private_key, &digest) .map_err(|_e| ErrorKind::RsaFailedSigning)? } else if let Some(signatures_scheme) = signatures_scheme_pss { - signatures_scheme.sign(Some(&mut rng), &private_key, &digest) + signatures_scheme + .sign(Some(&mut rng), &private_key, &digest) .map_err(|_e| ErrorKind::RsaFailedSigning)? } else { return Err(new_error(ErrorKind::InvalidAlgorithmName)); @@ -76,8 +79,7 @@ pub(crate) fn verify_from_components( ) -> Result { let n = BigUint::from_bytes_be(components.0); let e = BigUint::from_bytes_be(components.1); - let pub_key = - RsaPublicKey::new(n, e).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; + let pub_key = RsaPublicKey::new(n, e).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; verify(alg, signature, message, &pub_key) } diff --git a/tests/hmac.rs b/tests/hmac.rs index ea1a458a..e8b56b6a 100644 --- a/tests/hmac.rs +++ b/tests/hmac.rs @@ -101,7 +101,6 @@ fn decode_token_missing_parts() { #[test] #[wasm_bindgen_test] -#[should_panic(expected = "InvalidSignature")] fn decode_token_invalid_signature() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.Hm0yvKH25TavFPz7J_coST9lZFYH1hQo0tvhvImmaks"; @@ -110,12 +109,11 @@ fn decode_token_invalid_signature() { &DecodingKey::from_secret(b"secret"), &Validation::new(Algorithm::HS256), ); - claims.unwrap(); + assert_eq!(claims.unwrap_err().into_kind(), ErrorKind::InvalidSignature); } #[test] #[wasm_bindgen_test] -#[should_panic(expected = "InvalidAlgorithm")] fn decode_token_wrong_algorithm() { let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.I1BvFoHe94AFf09O6tDbcSB8-jp8w6xZqmyHIwPeSdY"; let claims = decode::( @@ -123,12 +121,11 @@ fn decode_token_wrong_algorithm() { &DecodingKey::from_secret(b"secret"), &Validation::new(Algorithm::RS512), ); - claims.unwrap(); + assert_eq!(claims.unwrap_err().into_kind(), ErrorKind::InvalidAlgorithm); } #[test] #[wasm_bindgen_test] -#[should_panic(expected = "InvalidAlgorithm")] fn encode_wrong_alg_family() { let my_claims = Claims { sub: "b@b.com".to_string(), @@ -136,7 +133,7 @@ fn encode_wrong_alg_family() { exp: OffsetDateTime::now_utc().unix_timestamp() + 10000, }; let claims = encode(&Header::default(), &my_claims, &EncodingKey::from_rsa_der(b"secret")); - claims.unwrap(); + assert_eq!(claims.unwrap_err().into_kind(), ErrorKind::InvalidAlgorithm); } #[test] From 69cbfa37fed50efc39540fb72871e10de0f46220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Sun, 17 Mar 2024 16:31:45 +0800 Subject: [PATCH 05/35] fix cargo format --- examples/custom_time.rs | 11 +++++++---- examples/ed25519.rs | 4 ++-- examples/validation.rs | 3 ++- src/algorithms.rs | 6 ++++-- src/crypto/ecdsa.rs | 14 +++++++------- src/crypto/hmac.rs | 7 ++----- src/jwk.rs | 11 +++++++---- src/lib.rs | 12 ++++++------ src/validation.rs | 11 ++++++++--- tests/ecdsa/mod.rs | 10 +++++----- tests/eddsa/mod.rs | 10 +++++----- tests/header/mod.rs | 3 ++- tests/hmac.rs | 7 ++++--- tests/rsa/mod.rs | 10 +++++----- 14 files changed, 66 insertions(+), 53 deletions(-) diff --git a/examples/custom_time.rs b/examples/custom_time.rs index d502a0f4..fbf92b96 100644 --- a/examples/custom_time.rs +++ b/examples/custom_time.rs @@ -1,7 +1,8 @@ -use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation}; use serde::{Deserialize, Serialize}; use time::{Duration, OffsetDateTime}; +use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation}; + const SECRET: &str = "some-secret"; #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -60,13 +61,15 @@ mod jwt_numeric_date { #[cfg(test)] mod tests { - const EXPECTED_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gT2Zmc2V0RGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjMyNTAzNjgwMDAwfQ.BcPipupP9oIV6uFRI6Acn7FMLws_wA3oo6CrfeFF3Gg"; + use time::{Duration, OffsetDateTime}; - use super::super::{Claims, SECRET}; use jsonwebtoken::{ decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation, }; - use time::{Duration, OffsetDateTime}; + + use super::super::{Claims, SECRET}; + + const EXPECTED_TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJDdXN0b20gT2Zmc2V0RGF0ZVRpbWUgc2VyL2RlIiwiaWF0IjowLCJleHAiOjMyNTAzNjgwMDAwfQ.BcPipupP9oIV6uFRI6Acn7FMLws_wA3oo6CrfeFF3Gg"; #[test] fn round_trip() { diff --git a/examples/ed25519.rs b/examples/ed25519.rs index 90596b69..59b29c22 100644 --- a/examples/ed25519.rs +++ b/examples/ed25519.rs @@ -16,7 +16,7 @@ pub struct Claims { fn main() { let secret_key = SecretKey::random(&mut OsRng); let pkcs8 = secret_key.to_pkcs8_pem(Default::default()).unwrap(); - let pkcs8= pkcs8.as_bytes(); + let pkcs8 = pkcs8.as_bytes(); let encoding_key = EncodingKey::from_ed_der(pkcs8); @@ -45,7 +45,7 @@ mod tests { fn new() -> Jot { let secret_key = SecretKey::random(&mut OsRng); let pkcs8 = secret_key.to_pkcs8_pem(Default::default()).unwrap(); - let pkcs8= pkcs8.as_bytes(); + let pkcs8 = pkcs8.as_bytes(); let encoding_key = EncodingKey::from_ed_der(pkcs8); let public_key_der = secret_key.public_key().to_public_key_der().unwrap(); diff --git a/examples/validation.rs b/examples/validation.rs index 392b6b83..8129465d 100644 --- a/examples/validation.rs +++ b/examples/validation.rs @@ -1,6 +1,7 @@ +use serde::{Deserialize, Serialize}; + use jsonwebtoken::errors::ErrorKind; use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation}; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] struct Claims { diff --git a/src/algorithms.rs b/src/algorithms.rs index e162bab2..ee17c54b 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -1,7 +1,9 @@ -use crate::errors::{Error, ErrorKind, Result}; -use serde::{Deserialize, Serialize}; use std::str::FromStr; +use serde::{Deserialize, Serialize}; + +use crate::errors::{Error, ErrorKind, Result}; + #[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)] pub(crate) enum AlgorithmFamily { Hmac, diff --git a/src/crypto/ecdsa.rs b/src/crypto/ecdsa.rs index c29abef0..df7190c1 100644 --- a/src/crypto/ecdsa.rs +++ b/src/crypto/ecdsa.rs @@ -18,8 +18,8 @@ fn es256_sign(key: &[u8], message: &[u8]) -> Result { use p256::ecdsa::{Signature, SigningKey}; use p256::pkcs8::DecodePrivateKey; use p256::SecretKey; - let secret_key = SecretKey::from_pkcs8_der(key) - .map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; + let secret_key = + SecretKey::from_pkcs8_der(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; let signing_key: SigningKey = secret_key.into(); let signature: Signature = signing_key.sign(message); @@ -32,8 +32,8 @@ fn es384_sign(key: &[u8], message: &[u8]) -> Result { use p384::ecdsa::{Signature, SigningKey}; use p384::pkcs8::DecodePrivateKey; use p384::SecretKey; - let secret_key = SecretKey::from_pkcs8_der(key) - .map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; + let secret_key = + SecretKey::from_pkcs8_der(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; let signing_key: SigningKey = secret_key.into(); let signature: Signature = signing_key.sign(message); let bytes = signature.to_bytes(); @@ -53,8 +53,8 @@ fn es384_verify(signature: &str, message: &[u8], key: &[u8]) -> Result { use p384::ecdsa::{Signature, VerifyingKey}; use p384::PublicKey; - let public_key = PublicKey::from_sec1_bytes(key) - .map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; + let public_key = + PublicKey::from_sec1_bytes(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; let verifying_key: VerifyingKey = public_key.into(); let signature = Signature::from_slice(&b64_decode(signature)?) .map_err(|_e| crate::errors::ErrorKind::InvalidSignature)?; @@ -71,4 +71,4 @@ fn es256_verify(signature: &str, message: &[u8], key: &[u8]) -> Result { let signature = Signature::from_slice(&b64_decode(signature)?) .map_err(|_e| crate::errors::ErrorKind::InvalidSignature)?; Ok(verifying_key.verify(message, &signature).is_ok()) -} \ No newline at end of file +} diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs index 25976780..7e4e2b21 100644 --- a/src/crypto/hmac.rs +++ b/src/crypto/hmac.rs @@ -1,9 +1,9 @@ use hmac::{Hmac, Mac}; use sha2::{Sha256, Sha384, Sha512}; -use crate::Algorithm; use crate::errors::Result; use crate::serialization::{b64_decode, b64_encode}; +use crate::Algorithm; type HmacSha256 = Hmac; type HmacSha384 = Hmac; @@ -66,7 +66,6 @@ impl HmacAlgorithm for Box { } impl HmacAlgorithm for HmacSha256 { - fn sign(&mut self, message: &[u8]) -> Vec { self.reset(); self.update(message); @@ -80,7 +79,6 @@ impl HmacAlgorithm for HmacSha256 { } impl HmacAlgorithm for HmacSha384 { - fn sign(&mut self, message: &[u8]) -> Vec { self.reset(); self.update(message); @@ -94,7 +92,6 @@ impl HmacAlgorithm for HmacSha384 { } impl HmacAlgorithm for HmacSha512 { - fn sign(&mut self, message: &[u8]) -> Vec { self.reset(); self.update(message); @@ -106,4 +103,4 @@ impl HmacAlgorithm for HmacSha512 { self.update(message); self.clone().verify_slice(signature).is_ok() } -} \ No newline at end of file +} diff --git a/src/jwk.rs b/src/jwk.rs index 49c58003..9100fe0c 100644 --- a/src/jwk.rs +++ b/src/jwk.rs @@ -4,12 +4,14 @@ //! Most of the code in this file is taken from https://github.com/lawliet89/biscuit but //! tweaked to remove the private bits as it's not the goal for this crate currently. +use std::{fmt, str::FromStr}; + +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + use crate::{ errors::{self, Error, ErrorKind}, Algorithm, }; -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use std::{fmt, str::FromStr}; /// The intended usage of the public `KeyType`. This enum is serialized `untagged` #[derive(Clone, Debug, Eq, PartialEq, Hash)] @@ -435,11 +437,12 @@ impl JwkSet { #[cfg(test)] mod tests { + use serde_json::json; + use wasm_bindgen_test::wasm_bindgen_test; + use crate::jwk::{AlgorithmParameters, JwkSet, OctetKeyType}; use crate::serialization::b64_encode; use crate::Algorithm; - use serde_json::json; - use wasm_bindgen_test::wasm_bindgen_test; #[test] #[wasm_bindgen_test] diff --git a/src/lib.rs b/src/lib.rs index 0c8664bf..2f936c8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,12 @@ //! Documentation: [stable](https://docs.rs/jsonwebtoken/) #![deny(missing_docs)] +pub use algorithms::Algorithm; +pub use decoding::{decode, decode_header, DecodingKey, TokenData}; +pub use encoding::{encode, EncodingKey}; +pub use header::Header; +pub use validation::{get_current_timestamp, Validation}; + mod algorithms; /// Lower level functions, if you want to do something other than JWTs pub mod crypto; @@ -16,9 +22,3 @@ pub mod jwk; mod pem; mod serialization; mod validation; - -pub use algorithms::Algorithm; -pub use decoding::{decode, decode_header, DecodingKey, TokenData}; -pub use encoding::{encode, EncodingKey}; -pub use header::Header; -pub use validation::{get_current_timestamp, Validation}; diff --git a/src/validation.rs b/src/validation.rs index e0d64a7c..e85c5e40 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -170,12 +170,14 @@ pub(crate) struct ClaimsForValidation<'a> { #[serde(borrow)] aud: TryParse>, } + #[derive(Debug)] enum TryParse { Parsed(T), FailedToParse, NotPresent, } + impl<'de, T: Deserialize<'de>> Deserialize<'de> for TryParse { fn deserialize>( deserializer: D, @@ -187,6 +189,7 @@ impl<'de, T: Deserialize<'de>> Deserialize<'de> for TryParse { }) } } + impl Default for TryParse { fn default() -> Self { Self::NotPresent @@ -212,6 +215,7 @@ enum Issuer<'a> { /// We use this struct in this case. #[derive(Deserialize, PartialEq, Eq, Hash)] struct BorrowedCowIfPossible<'a>(#[serde(borrow)] Cow<'a, str>); + impl std::borrow::Borrow for BorrowedCowIfPossible<'_> { fn borrow(&self) -> &str { &self.0 @@ -345,14 +349,15 @@ where #[cfg(test)] mod tests { + use std::collections::HashSet; + use serde_json::json; use wasm_bindgen_test::wasm_bindgen_test; - use super::{get_current_timestamp, validate, ClaimsForValidation, Validation}; - use crate::errors::ErrorKind; use crate::Algorithm; - use std::collections::HashSet; + + use super::{get_current_timestamp, validate, ClaimsForValidation, Validation}; fn deserialize_claims(claims: &serde_json::Value) -> ClaimsForValidation { serde::Deserialize::deserialize(claims).unwrap() diff --git a/tests/ecdsa/mod.rs b/tests/ecdsa/mod.rs index 8c06910f..fe2fbb00 100644 --- a/tests/ecdsa/mod.rs +++ b/tests/ecdsa/mod.rs @@ -1,14 +1,14 @@ +use serde::{Deserialize, Serialize}; +#[cfg(feature = "use_pem")] +use time::OffsetDateTime; +use wasm_bindgen_test::wasm_bindgen_test; + use jsonwebtoken::{ crypto::{sign, verify}, Algorithm, DecodingKey, EncodingKey, }; -use serde::{Deserialize, Serialize}; - #[cfg(feature = "use_pem")] use jsonwebtoken::{decode, encode, Header, Validation}; -#[cfg(feature = "use_pem")] -use time::OffsetDateTime; -use wasm_bindgen_test::wasm_bindgen_test; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { diff --git a/tests/eddsa/mod.rs b/tests/eddsa/mod.rs index 85dd0245..61cc209b 100644 --- a/tests/eddsa/mod.rs +++ b/tests/eddsa/mod.rs @@ -1,14 +1,14 @@ +use serde::{Deserialize, Serialize}; +#[cfg(feature = "use_pem")] +use time::OffsetDateTime; +use wasm_bindgen_test::wasm_bindgen_test; + use jsonwebtoken::{ crypto::{sign, verify}, Algorithm, DecodingKey, EncodingKey, }; -use serde::{Deserialize, Serialize}; -use wasm_bindgen_test::wasm_bindgen_test; - #[cfg(feature = "use_pem")] use jsonwebtoken::{decode, encode, Header, Validation}; -#[cfg(feature = "use_pem")] -use time::OffsetDateTime; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { diff --git a/tests/header/mod.rs b/tests/header/mod.rs index 997a6cb7..f50e6336 100644 --- a/tests/header/mod.rs +++ b/tests/header/mod.rs @@ -1,7 +1,8 @@ use base64::{engine::general_purpose::STANDARD, Engine}; -use jsonwebtoken::Header; use wasm_bindgen_test::wasm_bindgen_test; +use jsonwebtoken::Header; + static CERT_CHAIN: [&str; 3] = include!("cert_chain.json"); #[test] diff --git a/tests/hmac.rs b/tests/hmac.rs index e8b56b6a..ec24e6e7 100644 --- a/tests/hmac.rs +++ b/tests/hmac.rs @@ -1,12 +1,13 @@ +use serde::{Deserialize, Serialize}; +use time::OffsetDateTime; +use wasm_bindgen_test::wasm_bindgen_test; + use jsonwebtoken::errors::ErrorKind; use jsonwebtoken::jwk::Jwk; use jsonwebtoken::{ crypto::{sign, verify}, decode, decode_header, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation, }; -use serde::{Deserialize, Serialize}; -use time::OffsetDateTime; -use wasm_bindgen_test::wasm_bindgen_test; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { diff --git a/tests/rsa/mod.rs b/tests/rsa/mod.rs index 3297149f..9c679e88 100644 --- a/tests/rsa/mod.rs +++ b/tests/rsa/mod.rs @@ -1,14 +1,14 @@ +use serde::{Deserialize, Serialize}; +#[cfg(feature = "use_pem")] +use time::OffsetDateTime; +use wasm_bindgen_test::wasm_bindgen_test; + use jsonwebtoken::{ crypto::{sign, verify}, Algorithm, DecodingKey, EncodingKey, }; -use serde::{Deserialize, Serialize}; -use wasm_bindgen_test::wasm_bindgen_test; - #[cfg(feature = "use_pem")] use jsonwebtoken::{decode, encode, Header, Validation}; -#[cfg(feature = "use_pem")] -use time::OffsetDateTime; const RSA_ALGORITHMS: &[Algorithm] = &[ Algorithm::RS256, From a66864ff278a327e8c85ef400509227b60cc89e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Sun, 17 Mar 2024 16:33:19 +0800 Subject: [PATCH 06/35] change ci branch --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6de223a4..8a36b838 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ on: push: branches: - master + - wasm_support pull_request: jobs: From 485fb9f2feb3dd3e1d0e53c86610287308fce536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Sun, 17 Mar 2024 16:39:39 +0800 Subject: [PATCH 07/35] fix ci --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a36b838..0b9e1177 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,10 @@ jobs: - name: Build System Info run: rustc --version + # bumpalo 3.15.4 is not compatible with rust 1.67.0 + - name: Downgrade bumpalo to 3.14.0 + run: cargo update -p bumpalo@3.15.4 --precise 3.14.0 + - name: Run tests default features run: cargo test From 490564c77a8bbf8ef4dcc44ccf3778145fbb4bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Sun, 17 Mar 2024 16:47:34 +0800 Subject: [PATCH 08/35] fix ci --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b9e1177..16b5eeff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,10 +55,12 @@ jobs: - name: Build System Info run: rustc --version - # bumpalo 3.15.4 is not compatible with rust 1.67.0 - - name: Downgrade bumpalo to 3.14.0 + - name: Downgrade bumpalo run: cargo update -p bumpalo@3.15.4 --precise 3.14.0 + - name: Downgrade half + run: cargo update -p half@2.4.0 --precise 2.3.1 + - name: Run tests default features run: cargo test From a672130c647227da15ae55350f323066feae051e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Sun, 17 Mar 2024 16:53:44 +0800 Subject: [PATCH 09/35] fix ci --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16b5eeff..9ab07cba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: run: cargo update -p bumpalo@3.15.4 --precise 3.14.0 - name: Downgrade half - run: cargo update -p half@2.4.0 --precise 2.3.1 + run: cargo update -p half@2.4.0 --precise 2.2.1 - name: Run tests default features run: cargo test From 73ea86994b1101aa04174aea7b21f3bdf070427e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Sun, 17 Mar 2024 16:57:03 +0800 Subject: [PATCH 10/35] remove ci branch --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ab07cba..cfc6a0db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,6 @@ on: push: branches: - master - - wasm_support pull_request: jobs: From a41c817ecffe261609d06d0ec52800debd1334e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=9C=B3=E4=BD=B3?= Date: Sat, 6 Apr 2024 20:39:26 +0800 Subject: [PATCH 11/35] fix examples ed25519 test failed --- Cargo.toml | 4 ++-- examples/ed25519.rs | 29 +++++++++++++++++------------ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4cc3754b..9eea5bf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ rsa = "0.9.6" sha2 = { version = "0.10.7", features = ["oid"] } getrandom = { version = "0.2.10", features = ["js"] } rand = { version = "0.8.5", features = ["std"], default-features = false } -ed25519-dalek = "2.1.1" +ed25519-dalek = { version = "2.1.1" } p256 = { version = "0.13.2", features = ["ecdsa"] } p384 = { version = "0.13.0", features = ["ecdsa"] } rand_core = "0.6.4" @@ -41,7 +41,7 @@ js-sys = "0.3" [dev-dependencies] wasm-bindgen-test = "0.3.1" - +ed25519-dalek = { version = "2.1.1", features = ["pkcs8", "rand_core"] } [target.'cfg(not(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi")))))'.dev-dependencies] # For the custom time example time = "0.3" diff --git a/examples/ed25519.rs b/examples/ed25519.rs index 59b29c22..8ee8b3fa 100644 --- a/examples/ed25519.rs +++ b/examples/ed25519.rs @@ -1,5 +1,5 @@ -use p256::pkcs8::{EncodePrivateKey, EncodePublicKey}; -use p256::SecretKey; +use ed25519_dalek::pkcs8::EncodePrivateKey; +use ed25519_dalek::SigningKey; use rand_core::OsRng; use serde::{Deserialize, Serialize}; @@ -14,14 +14,16 @@ pub struct Claims { } fn main() { - let secret_key = SecretKey::random(&mut OsRng); - let pkcs8 = secret_key.to_pkcs8_pem(Default::default()).unwrap(); + let signing_key = SigningKey::generate(&mut OsRng); + let pkcs8 = signing_key.to_pkcs8_der().unwrap(); let pkcs8 = pkcs8.as_bytes(); - + // The `to_pkcs8_der` includes the public key, the first 48 bits are the private key. + let pkcs8 = &pkcs8[..48]; let encoding_key = EncodingKey::from_ed_der(pkcs8); - let public_key_der = secret_key.public_key().to_public_key_der().unwrap(); - let decoding_key = DecodingKey::from_ed_der(public_key_der.as_bytes()); + let verifying_key = signing_key.verifying_key(); + let public_key = verifying_key.as_bytes(); + let decoding_key = DecodingKey::from_ed_der(public_key); let claims = Claims { sub: "test".to_string(), exp: get_current_timestamp() }; @@ -43,13 +45,16 @@ mod tests { impl Jot { fn new() -> Jot { - let secret_key = SecretKey::random(&mut OsRng); - let pkcs8 = secret_key.to_pkcs8_pem(Default::default()).unwrap(); + let signing_key = SigningKey::generate(&mut OsRng); + let pkcs8 = signing_key.to_pkcs8_der().unwrap(); let pkcs8 = pkcs8.as_bytes(); - let encoding_key = EncodingKey::from_ed_der(pkcs8); + // The `to_pkcs8_der` includes the public key, the first 48 bits are the private key. + let pkcs8 = &pkcs8[..48]; + let encoding_key = EncodingKey::from_ed_der(&pkcs8); - let public_key_der = secret_key.public_key().to_public_key_der().unwrap(); - let decoding_key = DecodingKey::from_ed_der(public_key_der.as_bytes()); + let verifying_key = signing_key.verifying_key(); + let public_key = verifying_key.as_bytes(); + let decoding_key = DecodingKey::from_ed_der(public_key); Jot { encoding_key, decoding_key } } From 6f9b55320b3751b6c71eeccfbc2790dcb236fcef Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Thu, 29 Aug 2024 13:08:49 +0200 Subject: [PATCH 12/35] Next version --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ea1920..ad70c695 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 10.0.0 (unreleased) + +## 9.3.0 (2024-03-12) + +- Add `Validation.reject_tokens_expiring_in_less_than`, the opposite of leeway + ## 9.2.0 (2023-12-01) - Add an option to not validate `aud` in the Validation struct From e06188274e22d6f60c116f678768f3c6aba5080f Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Mon, 2 Sep 2024 12:57:11 +0200 Subject: [PATCH 13/35] Remove downgrade steps --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfc6a0db..6de223a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,12 +54,6 @@ jobs: - name: Build System Info run: rustc --version - - name: Downgrade bumpalo - run: cargo update -p bumpalo@3.15.4 --precise 3.14.0 - - - name: Downgrade half - run: cargo update -p half@2.4.0 --precise 2.2.1 - - name: Run tests default features run: cargo test From 502faa491099c8ed5d8806dae9e31c6618e94f9b Mon Sep 17 00:00:00 2001 From: sidrubs <52378223+sidrubs@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:26:43 -0300 Subject: [PATCH 14/35] feat(encoder): Add encoder builder --- Cargo.toml | 1 + src/builder.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++ src/crypto/hmac.rs | 28 +++++++++++++++ src/crypto/mod.rs | 18 ++++++++++ src/lib.rs | 1 + 5 files changed, 138 insertions(+) create mode 100644 src/builder.rs diff --git a/Cargo.toml b/Cargo.toml index 7d438c0f..b31f72ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ ed25519-dalek = { version = "2.1.1" } p256 = { version = "0.13.2", features = ["ecdsa"] } p384 = { version = "0.13.0", features = ["ecdsa"] } rand_core = "0.6.4" +signature = "2.2.0" [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = "0.3" diff --git a/src/builder.rs b/src/builder.rs new file mode 100644 index 00000000..456a3232 --- /dev/null +++ b/src/builder.rs @@ -0,0 +1,90 @@ +//! # Todo +//! +//! - Documentation + +use serde::Serialize; + +use crate::{ + crypto::JwtSigner, + errors::{new_error, Result}, + serialization::{b64_encode, b64_encode_part}, + Header, +}; + +/// # Todo +/// +/// - Documentation +pub struct JwtEncoder { + signing_provider: C, + header: Header, +} + +impl JwtEncoder { + /// Create a new [`JwtEncoder`] with any crypto provider that implements the [`CryptoProvider`] trait. + pub fn new(signing_provider: C) -> Self { + // Determine a default header + let mut header = Header::new(signing_provider.algorithm()); + header.typ = Some("JWT".to_owned()); + + Self { signing_provider, header } + } + + /// Provide a custom header. + /// + /// This would be used in the rare cases that fields other than `algorithm` and `type` need to be populated. + /// + /// # Todo + /// + /// - Test the the error checking works + pub fn with_header(mut self, header: Header) -> Result { + // Check that the header makes use of the correct algorithm + if header.alg != self.signing_provider.algorithm() { + return Err(new_error(crate::errors::ErrorKind::InvalidAlgorithm)); + } + + self.header = header; + Ok(self) + } + + /// Encode and sign the `claims` as a JWT. + /// + /// # Todo + /// + /// - Put in example usage. + pub fn encode(&self, claims: &T) -> Result { + let encoded_header = b64_encode_part(&self.header)?; + let encoded_claims = b64_encode_part(claims)?; + let message = [encoded_header, encoded_claims].join("."); + + let signature = b64_encode(&self.signing_provider.sign(message.as_bytes())); + + Ok([message, signature].join(".")) + } +} + +#[cfg(test)] +mod builder_tests { + use crate::crypto::hmac::HmacSha256Trait; + + use super::*; + + #[derive(Debug, Serialize)] + struct Claims { + sub: String, + age: u32, + } + + #[test] + fn test_builder() { + // Arrange + let claims = Claims { sub: "123345".to_owned(), age: 25 }; + let signer = HmacSha256Trait::new("k3XTGsWiuO0stzhwPkuF2R6FdFY2crfyAVDjSBX34bW41ektItjp340PNXz1UvLkaq4CcT6ZMl7GXzfTvCvpkFXJbMni1wj40g423FbUxI7ZclVyzIrVFywrB5trt94Rv9AkTpShXzpnEWKGhZdD0MIOrQlg".as_ref()).unwrap(); + + // Act + let jwt = JwtEncoder::new(signer).encode(&claims).unwrap(); + + dbg!(&jwt); + + // Assert + } +} diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs index 7e4e2b21..e9d6c526 100644 --- a/src/crypto/hmac.rs +++ b/src/crypto/hmac.rs @@ -1,14 +1,42 @@ use hmac::{Hmac, Mac}; use sha2::{Sha256, Sha384, Sha512}; +use signature::Signer; use crate::errors::Result; use crate::serialization::{b64_decode, b64_encode}; use crate::Algorithm; +use super::JwtSigner; + type HmacSha256 = Hmac; type HmacSha384 = Hmac; type HmacSha512 = Hmac; +pub(crate) struct HmacSha256Trait(HmacSha256); + +impl HmacSha256Trait { + pub(crate) fn new(key: &[u8]) -> Result { + let inner = HmacSha256::new_from_slice(key) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + + Ok(Self(inner)) + } +} + +impl Signer> for HmacSha256Trait { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + let mut signer = self.0.clone(); + + Ok(signer.sign(msg)) + } +} + +impl JwtSigner for HmacSha256Trait { + fn algorithm(&self) -> Algorithm { + Algorithm::HS256 + } +} + pub(crate) fn sign_hmac(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { let mut hmac = create_hmac(alg, key)?; let digest = hmac.sign(message); diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 4f5328eb..da347600 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -8,6 +8,24 @@ pub(crate) mod eddsa; pub(crate) mod hmac; pub(crate) mod rsa; +use signature::{Signer, Verifier}; + +/// Trait providing the functionality to sign a JWT. +/// +/// Allows an arbitrary crypto backend to be provided. +pub trait JwtSigner: Signer> { + /// Return the [`Algorithm`] corresponding to the signing module. + fn algorithm(&self) -> Algorithm; +} + +/// Trait providing the functionality to verify a JWT. +/// +/// Allows an arbitrary crypto backend to be provided. +pub trait JwtVerifier: Verifier> { + /// Return the [`Algorithm`] corresponding to the signing module. + fn algorithm(&self) -> Algorithm; +} + /// Take the payload of a JWT, sign it using the algorithm given and return /// the base64 url safe encoded of the result. /// diff --git a/src/lib.rs b/src/lib.rs index 2f936c8d..0eb22777 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ pub use header::Header; pub use validation::{get_current_timestamp, Validation}; mod algorithms; +pub mod builder; /// Lower level functions, if you want to do something other than JWTs pub mod crypto; mod decoding; From a701547b39fea62c4a32a00319bb3d41a1144c71 Mon Sep 17 00:00:00 2001 From: sidrubs <52378223+sidrubs@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:25:34 -0300 Subject: [PATCH 15/35] feat(encoder): Convert to dynamic dispatch --- src/builder.rs | 37 ++++++++--- src/crypto/hmac.rs | 149 +++++++++++++++++++-------------------------- src/crypto/mod.rs | 118 +++++++++++++++++------------------ src/encoding.rs | 32 ++++++++-- src/lib.rs | 4 +- 5 files changed, 180 insertions(+), 160 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 456a3232..d6b7ad94 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -5,7 +5,10 @@ use serde::Serialize; use crate::{ - crypto::JwtSigner, + crypto::{ + hmac::{HmacSecret, Hs256, Hs384}, + JwtSigner, + }, errors::{new_error, Result}, serialization::{b64_encode, b64_encode_part}, Header, @@ -14,14 +17,19 @@ use crate::{ /// # Todo /// /// - Documentation -pub struct JwtEncoder { - signing_provider: C, +pub struct JwtEncoder { + signing_provider: Box, header: Header, } -impl JwtEncoder { +impl JwtEncoder { + /// Todo + pub fn from_signer(signing_provider: S) -> Self { + Self::from_boxed_signer(Box::new(signing_provider)) + } + /// Create a new [`JwtEncoder`] with any crypto provider that implements the [`CryptoProvider`] trait. - pub fn new(signing_provider: C) -> Self { + pub fn from_boxed_signer(signing_provider: Box) -> Self { // Determine a default header let mut header = Header::new(signing_provider.algorithm()); header.typ = Some("JWT".to_owned()); @@ -60,11 +68,24 @@ impl JwtEncoder { Ok([message, signature].join(".")) } + + /// Create new [`JwtEncoder`] with the `HS256` algorithm. + pub fn hs_256(secret: HmacSecret) -> Result { + let signing_provider = Box::new(Hs256::new(secret)?); + + Ok(JwtEncoder::from_boxed_signer(signing_provider)) + } + + /// Create new [`JwtEncoder`] with the `HS384` algorithm. + pub fn hs_384(secret: HmacSecret) -> Result { + let signing_provider = Box::new(Hs384::new(secret)?); + + Ok(JwtEncoder::from_boxed_signer(signing_provider)) + } } #[cfg(test)] mod builder_tests { - use crate::crypto::hmac::HmacSha256Trait; use super::*; @@ -78,10 +99,10 @@ mod builder_tests { fn test_builder() { // Arrange let claims = Claims { sub: "123345".to_owned(), age: 25 }; - let signer = HmacSha256Trait::new("k3XTGsWiuO0stzhwPkuF2R6FdFY2crfyAVDjSBX34bW41ektItjp340PNXz1UvLkaq4CcT6ZMl7GXzfTvCvpkFXJbMni1wj40g423FbUxI7ZclVyzIrVFywrB5trt94Rv9AkTpShXzpnEWKGhZdD0MIOrQlg".as_ref()).unwrap(); + let secret = HmacSecret::from_secret("test".as_ref()); // Act - let jwt = JwtEncoder::new(signer).encode(&claims).unwrap(); + let jwt = JwtEncoder::hs_256(secret).unwrap().encode(&claims).unwrap(); dbg!(&jwt); diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs index e9d6c526..11a5144a 100644 --- a/src/crypto/hmac.rs +++ b/src/crypto/hmac.rs @@ -1,134 +1,113 @@ +use base64::{engine::general_purpose::STANDARD, Engine}; use hmac::{Hmac, Mac}; use sha2::{Sha256, Sha384, Sha512}; -use signature::Signer; +use signature::{Signer, Verifier}; use crate::errors::Result; -use crate::serialization::{b64_decode, b64_encode}; use crate::Algorithm; -use super::JwtSigner; +use super::{JwtSigner, JwtVerifier}; type HmacSha256 = Hmac; type HmacSha384 = Hmac; type HmacSha512 = Hmac; -pub(crate) struct HmacSha256Trait(HmacSha256); +pub(crate) struct HmacSecret(Vec); -impl HmacSha256Trait { - pub(crate) fn new(key: &[u8]) -> Result { - let inner = HmacSha256::new_from_slice(key) +impl HmacSecret { + /// If you're using an HMAC secret that is not base64, use that. + pub fn from_secret(secret: &[u8]) -> Self { + Self(secret.to_vec()) + } + + /// If you have a base64 HMAC secret, use that. + pub fn from_base64_secret(secret: &str) -> Result { + Ok(Self(STANDARD.decode(secret)?)) + } +} + +pub struct Hs256(HmacSha256); + +impl Hs256 { + pub(crate) fn new(secret: HmacSecret) -> Result { + let inner = HmacSha256::new_from_slice(&secret.0) .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; Ok(Self(inner)) } } -impl Signer> for HmacSha256Trait { +impl Signer> for Hs256 { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { let mut signer = self.0.clone(); + signer.reset(); + signer.update(msg); - Ok(signer.sign(msg)) + Ok(signer.finalize().into_bytes().to_vec()) } } -impl JwtSigner for HmacSha256Trait { +impl JwtSigner for Hs256 { fn algorithm(&self) -> Algorithm { Algorithm::HS256 } } -pub(crate) fn sign_hmac(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { - let mut hmac = create_hmac(alg, key)?; - let digest = hmac.sign(message); - Ok(b64_encode(digest)) -} +impl Verifier> for Hs256 { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + let mut verifier = self.0.clone(); + verifier.reset(); + verifier.update(msg); -pub(crate) fn hmac_verify( - alg: Algorithm, - signature: &str, - key: &[u8], - message: &[u8], -) -> Result { - let mut hmac = create_hmac(alg, key)?; - let signature = b64_decode(signature)?; - Ok(hmac.verify(&signature, message)) + verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) + } } -fn create_hmac(alg: Algorithm, key: &[u8]) -> Result> { - let hmac: Box = match alg { - Algorithm::HS256 => { - let sha256 = HmacSha256::new_from_slice(key) - .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; - Box::new(sha256) - } - Algorithm::HS384 => { - let sha384 = HmacSha384::new_from_slice(key) - .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; - Box::new(sha384) - } - Algorithm::HS512 => { - let sha512 = HmacSha512::new_from_slice(key) - .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; - Box::new(sha512) - } - _ => { - return Err(crate::errors::new_error(crate::errors::ErrorKind::InvalidAlgorithm)); - } - }; - Ok(hmac) +impl JwtVerifier for Hs256 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS256 + } } -trait HmacAlgorithm { - fn sign(&mut self, message: &[u8]) -> Vec; - fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool; -} +pub(crate) struct Hs384(HmacSha384); -impl HmacAlgorithm for Box { - fn sign(&mut self, message: &[u8]) -> Vec { - (**self).sign(message) - } +impl Hs384 { + pub(crate) fn new(secret: HmacSecret) -> Result { + let inner = HmacSha384::new_from_slice(&secret.0) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; - fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool { - (**self).verify(signature, message) + Ok(Self(inner)) } } -impl HmacAlgorithm for HmacSha256 { - fn sign(&mut self, message: &[u8]) -> Vec { - self.reset(); - self.update(message); - self.clone().finalize().into_bytes().to_vec() - } - fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool { - self.reset(); - self.update(message); - self.clone().verify_slice(signature).is_ok() +impl Signer> for Hs384 { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + let mut signer = self.0.clone(); + signer.reset(); + signer.update(msg); + + Ok(signer.finalize().into_bytes().to_vec()) } } -impl HmacAlgorithm for HmacSha384 { - fn sign(&mut self, message: &[u8]) -> Vec { - self.reset(); - self.update(message); - self.clone().finalize().into_bytes().to_vec() - } - fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool { - self.reset(); - self.update(message); - self.clone().verify_slice(signature).is_ok() +impl JwtSigner for Hs384 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS384 } } -impl HmacAlgorithm for HmacSha512 { - fn sign(&mut self, message: &[u8]) -> Vec { - self.reset(); - self.update(message); - self.clone().finalize().into_bytes().to_vec() +impl Verifier> for Hs384 { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + let mut verifier = self.0.clone(); + verifier.reset(); + verifier.update(msg); + + verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) } +} - fn verify(&mut self, signature: &[u8], message: &[u8]) -> bool { - self.reset(); - self.update(message); - self.clone().verify_slice(signature).is_ok() +impl JwtVerifier for Hs384 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS384 } } diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index da347600..4317a780 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,7 +1,7 @@ use crate::algorithms::Algorithm; -use crate::decoding::{DecodingKey, DecodingKeyKind}; -use crate::encoding::EncodingKey; -use crate::errors::Result; +// use crate::decoding::{DecodingKey, DecodingKeyKind}; +// use crate::encoding::EncodingKey; +// use crate::errors::Result; pub(crate) mod ecdsa; pub(crate) mod eddsa; @@ -26,60 +26,60 @@ pub trait JwtVerifier: Verifier> { fn algorithm(&self) -> Algorithm; } -/// Take the payload of a JWT, sign it using the algorithm given and return -/// the base64 url safe encoded of the result. -/// -/// If you just want to encode a JWT, use `encode` instead. -pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result { - match algorithm { - Algorithm::ES256 | Algorithm::ES384 => ecdsa::sign(algorithm, key.inner(), message), - Algorithm::EdDSA => eddsa::sign(key.inner(), message), - Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { - hmac::sign_hmac(algorithm, key.inner(), message) - } - Algorithm::RS256 - | Algorithm::RS384 - | Algorithm::RS512 - | Algorithm::PS256 - | Algorithm::PS384 - | Algorithm::PS512 => rsa::sign(algorithm, key.inner(), message), - } -} +// /// Take the payload of a JWT, sign it using the algorithm given and return +// /// the base64 url safe encoded of the result. +// /// +// /// If you just want to encode a JWT, use `encode` instead. +// pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result { +// match algorithm { +// Algorithm::ES256 | Algorithm::ES384 => ecdsa::sign(algorithm, key.inner(), message), +// Algorithm::EdDSA => eddsa::sign(key.inner(), message), +// Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { +// hmac::sign_hmac(algorithm, key.inner(), message) +// } +// Algorithm::RS256 +// | Algorithm::RS384 +// | Algorithm::RS512 +// | Algorithm::PS256 +// | Algorithm::PS384 +// | Algorithm::PS512 => rsa::sign(algorithm, key.inner(), message), +// } +// } -/// Compares the signature given with a re-computed signature for HMAC or using the public key -/// for RSA/EC. -/// -/// If you just want to decode a JWT, use `decode` instead. -/// -/// `signature` is the signature part of a jwt (text after the second '.') -/// -/// `message` is base64(header) + "." + base64(claims) -pub fn verify( - signature: &str, - message: &[u8], - key: &DecodingKey, - algorithm: Algorithm, -) -> Result { - match algorithm { - Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { - hmac::hmac_verify(algorithm, signature, key.as_bytes(), message) - } - Algorithm::ES256 | Algorithm::ES384 => { - ecdsa::verify(algorithm, signature, message, key.as_bytes()) - } - Algorithm::EdDSA => eddsa::verify(signature, message, key.as_bytes()), - Algorithm::RS256 - | Algorithm::RS384 - | Algorithm::RS512 - | Algorithm::PS256 - | Algorithm::PS384 - | Algorithm::PS512 => match &key.kind { - DecodingKeyKind::SecretOrDer(bytes) => { - rsa::verify_der(algorithm, signature, message, bytes) - } - DecodingKeyKind::RsaModulusExponent { n, e } => { - rsa::verify_from_components(algorithm, signature, message, (n, e)) - } - }, - } -} +// /// Compares the signature given with a re-computed signature for HMAC or using the public key +// /// for RSA/EC. +// /// +// /// If you just want to decode a JWT, use `decode` instead. +// /// +// /// `signature` is the signature part of a jwt (text after the second '.') +// /// +// /// `message` is base64(header) + "." + base64(claims) +// pub fn verify( +// signature: &str, +// message: &[u8], +// key: &DecodingKey, +// algorithm: Algorithm, +// ) -> Result { +// match algorithm { +// Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { +// hmac::hmac_verify(algorithm, signature, key.as_bytes(), message) +// } +// Algorithm::ES256 | Algorithm::ES384 => { +// ecdsa::verify(algorithm, signature, message, key.as_bytes()) +// } +// Algorithm::EdDSA => eddsa::verify(signature, message, key.as_bytes()), +// Algorithm::RS256 +// | Algorithm::RS384 +// | Algorithm::RS512 +// | Algorithm::PS256 +// | Algorithm::PS384 +// | Algorithm::PS512 => match &key.kind { +// DecodingKeyKind::SecretOrDer(bytes) => { +// rsa::verify_der(algorithm, signature, message, bytes) +// } +// DecodingKeyKind::RsaModulusExponent { n, e } => { +// rsa::verify_from_components(algorithm, signature, message, (n, e)) +// } +// }, +// } +// } diff --git a/src/encoding.rs b/src/encoding.rs index 26f5c4c3..626cc138 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,12 +2,15 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::ser::Serialize; use crate::algorithms::AlgorithmFamily; -use crate::crypto; +use crate::builder::JwtEncoder; +use crate::crypto::hmac::{HmacSecret, Hs256, Hs384}; +use crate::crypto::JwtSigner; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; use crate::serialization::b64_encode_part; +use crate::{crypto, Algorithm}; /// A key to encode a JWT with. Can be a secret, a PEM-encoded key or a DER-encoded key. /// This key can be re-used so make sure you only initialize it once if you can for better performance. @@ -122,10 +125,27 @@ pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> R if key.family != header.alg.family() { return Err(new_error(ErrorKind::InvalidAlgorithm)); } - let encoded_header = b64_encode_part(header)?; - let encoded_claims = b64_encode_part(claims)?; - let message = [encoded_header, encoded_claims].join("."); - let signature = crypto::sign(message.as_bytes(), key, header.alg)?; - Ok([message, signature].join(".")) + let jwt_encoder = encoder_factory(&header.alg, key)?; + + jwt_encoder.encode(claims) +} + +fn encoder_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result { + let jwt_encoder = match algorithm { + Algorithm::HS256 => JwtEncoder::hs_256(HmacSecret::from_secret(&key.content))?, + Algorithm::HS384 => JwtEncoder::hs_384(HmacSecret::from_secret(&key.content))?, + Algorithm::HS512 => todo!(), + Algorithm::ES256 => todo!(), + Algorithm::ES384 => todo!(), + Algorithm::RS256 => todo!(), + Algorithm::RS384 => todo!(), + Algorithm::RS512 => todo!(), + Algorithm::PS256 => todo!(), + Algorithm::PS384 => todo!(), + Algorithm::PS512 => todo!(), + Algorithm::EdDSA => todo!(), + }; + + Ok(jwt_encoder) } diff --git a/src/lib.rs b/src/lib.rs index 0eb22777..34daafee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ #![deny(missing_docs)] pub use algorithms::Algorithm; -pub use decoding::{decode, decode_header, DecodingKey, TokenData}; +// pub use decoding::{decode, decode_header, DecodingKey, TokenData}; pub use encoding::{encode, EncodingKey}; pub use header::Header; pub use validation::{get_current_timestamp, Validation}; @@ -13,7 +13,7 @@ mod algorithms; pub mod builder; /// Lower level functions, if you want to do something other than JWTs pub mod crypto; -mod decoding; +// mod decoding; mod encoding; /// All the errors that can be encountered while encoding/decoding JWTs pub mod errors; From 6a41ba749bc8dee7921838dfa917fd0629d516ff Mon Sep 17 00:00:00 2001 From: sidrubs <52378223+sidrubs@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:56:41 -0300 Subject: [PATCH 16/35] feat(decoder): Create decoder --- src/builder.rs | 120 +++++++++++++++++++++++++++++++++++++++++++++--- src/decoding.rs | 20 ++++++++ src/encoding.rs | 2 +- src/lib.rs | 4 +- 4 files changed, 136 insertions(+), 10 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index d6b7ad94..da2c8c4a 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2,16 +2,17 @@ //! //! - Documentation -use serde::Serialize; +use serde::{de::DeserializeOwned, Serialize}; use crate::{ crypto::{ hmac::{HmacSecret, Hs256, Hs384}, - JwtSigner, + JwtSigner, JwtVerifier, }, - errors::{new_error, Result}, - serialization::{b64_encode, b64_encode_part}, - Header, + errors::{new_error, ErrorKind, Result}, + serialization::{b64_encode, b64_encode_part, DecodedJwtPartClaims}, + validation::validate, + Header, TokenData, Validation, }; /// # Todo @@ -44,13 +45,13 @@ impl JwtEncoder { /// # Todo /// /// - Test the the error checking works - pub fn with_header(mut self, header: Header) -> Result { + pub fn with_header(mut self, header: &Header) -> Result { // Check that the header makes use of the correct algorithm if header.alg != self.signing_provider.algorithm() { return Err(new_error(crate::errors::ErrorKind::InvalidAlgorithm)); } - self.header = header; + self.header = header.clone(); Ok(self) } @@ -84,6 +85,111 @@ impl JwtEncoder { } } +/// Takes the result of a rsplit and ensure we only get 2 parts +/// Errors if we don't +macro_rules! expect_two { + ($iter:expr) => {{ + let mut i = $iter; + match (i.next(), i.next(), i.next()) { + (Some(first), Some(second), None) => (first, second), + _ => return Err(new_error(ErrorKind::InvalidToken)), + } + }}; +} + +/// Todo +pub struct JwtDecoder { + verifying_provider: Box, + validation: Validation, +} + +impl JwtDecoder { + /// Todo + pub fn from_verifier(verifying_provider: V) -> Self { + Self::from_boxed_verifiyer(Box::new(verifying_provider)) + } + + /// Todo + pub fn from_boxed_verifiyer(verifying_provider: Box) -> Self { + let validation = Validation::new(verifying_provider.algorithm()); + + Self { verifying_provider, validation } + } + + /// Todo + pub fn with_validation(mut self, validation: &Validation) -> Result { + // Check that the validation contains the correct algorithm + if !validation.algorithms.contains(&self.verifying_provider.algorithm()) { + return Err(new_error(crate::errors::ErrorKind::InvalidAlgorithm)); + } + + self.validation = validation.clone(); + Ok(self) + } + + /// Todo + pub fn decode(&self, token: &str) -> Result> { + let (header, claims) = self.verify_signature(token)?; + + let decoded_claims = DecodedJwtPartClaims::from_jwt_part_claims(claims)?; + let claims = decoded_claims.deserialize()?; + validate(decoded_claims.deserialize()?, &self.validation)?; + + Ok(TokenData { header, claims }) + } + + /// Verify signature of a JWT, and return header object and raw payload + /// + /// If the token or its signature is invalid, it will return an error. + fn verify_signature<'a>(&self, token: &'a str) -> Result<(Header, &'a str)> { + if self.validation.validate_signature && self.validation.algorithms.is_empty() { + return Err(new_error(ErrorKind::MissingAlgorithm)); + } + + // Todo: This behaviour is currently not captured anywhere. + // if validation.validate_signature { + // for alg in &validation.algorithms { + // if key.family != alg.family() { + // return Err(new_error(ErrorKind::InvalidAlgorithm)); + // } + // } + // } + + let (signature, message) = expect_two!(token.rsplitn(2, '.')); + let (payload, header) = expect_two!(message.rsplitn(2, '.')); + let header = Header::from_encoded(header)?; + + if self.validation.validate_signature && !self.validation.algorithms.contains(&header.alg) { + return Err(new_error(ErrorKind::InvalidAlgorithm)); + } + + if self.validation.validate_signature + && self + .verifying_provider + .verify(message.as_bytes(), &signature.as_bytes().to_vec()) + .is_err() + { + return Err(new_error(ErrorKind::InvalidSignature)); + } + + Ok((header, payload)) + } + + /// Create new [`JwtDecoder`] with the `HS256` algorithm. + pub fn hs_256(secret: HmacSecret) -> Result { + let verifying_provider = Box::new(Hs256::new(secret)?); + + Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) + } + + /// Create new [`JwtDecoder`] with the `HS384` algorithm. + pub fn hs_384(secret: HmacSecret) -> Result { + let verifying_provider = Box::new(Hs384::new(secret)?); + + Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) + } +} + #[cfg(test)] mod builder_tests { diff --git a/src/decoding.rs b/src/decoding.rs index 8d87f03d..3b041201 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -2,6 +2,7 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::de::DeserializeOwned; use crate::algorithms::AlgorithmFamily; +use crate::builder::JwtDecoder; use crate::crypto::verify; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; @@ -271,6 +272,25 @@ pub fn decode( } } +fn decoder_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result { + let jwt_encoder = match algorithm { + Algorithm::HS256 => JwtDecoder::hs_256(HmacSecret::from_secret(&key.content))?, + Algorithm::HS384 => JwtDecoder::hs_384(HmacSecret::from_secret(&key.content))?, + Algorithm::HS512 => todo!(), + Algorithm::ES256 => todo!(), + Algorithm::ES384 => todo!(), + Algorithm::RS256 => todo!(), + Algorithm::RS384 => todo!(), + Algorithm::RS512 => todo!(), + Algorithm::PS256 => todo!(), + Algorithm::PS384 => todo!(), + Algorithm::PS512 => todo!(), + Algorithm::EdDSA => todo!(), + }; + + Ok(jwt_encoder) +} + /// Decode a JWT without any signature verification/validations and return its [Header](struct.Header.html). /// /// If the token has an invalid format (ie 3 parts separated by a `.`), it will return an error. diff --git a/src/encoding.rs b/src/encoding.rs index 626cc138..9443c5e1 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -126,7 +126,7 @@ pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> R return Err(new_error(ErrorKind::InvalidAlgorithm)); } - let jwt_encoder = encoder_factory(&header.alg, key)?; + let jwt_encoder = encoder_factory(&header.alg, key)?.with_header(header)?; jwt_encoder.encode(claims) } diff --git a/src/lib.rs b/src/lib.rs index 34daafee..0eb22777 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ #![deny(missing_docs)] pub use algorithms::Algorithm; -// pub use decoding::{decode, decode_header, DecodingKey, TokenData}; +pub use decoding::{decode, decode_header, DecodingKey, TokenData}; pub use encoding::{encode, EncodingKey}; pub use header::Header; pub use validation::{get_current_timestamp, Validation}; @@ -13,7 +13,7 @@ mod algorithms; pub mod builder; /// Lower level functions, if you want to do something other than JWTs pub mod crypto; -// mod decoding; +mod decoding; mod encoding; /// All the errors that can be encountered while encoding/decoding JWTs pub mod errors; From 66d54be7aa277612db6fef741871ff39a5d4cf9f Mon Sep 17 00:00:00 2001 From: sidrubs <52378223+sidrubs@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:26:12 -0300 Subject: [PATCH 17/35] test: Get HMAC tests passing --- src/builder.rs | 217 --------------------------------------------- src/crypto/hmac.rs | 44 +++++++++ src/decoding.rs | 179 ++++++++++++++++++++++++++----------- src/encoding.rs | 76 +++++++++++++++- src/lib.rs | 1 - tests/hmac.rs | 44 +++++---- tests/lib.rs | 8 +- 7 files changed, 276 insertions(+), 293 deletions(-) delete mode 100644 src/builder.rs diff --git a/src/builder.rs b/src/builder.rs deleted file mode 100644 index da2c8c4a..00000000 --- a/src/builder.rs +++ /dev/null @@ -1,217 +0,0 @@ -//! # Todo -//! -//! - Documentation - -use serde::{de::DeserializeOwned, Serialize}; - -use crate::{ - crypto::{ - hmac::{HmacSecret, Hs256, Hs384}, - JwtSigner, JwtVerifier, - }, - errors::{new_error, ErrorKind, Result}, - serialization::{b64_encode, b64_encode_part, DecodedJwtPartClaims}, - validation::validate, - Header, TokenData, Validation, -}; - -/// # Todo -/// -/// - Documentation -pub struct JwtEncoder { - signing_provider: Box, - header: Header, -} - -impl JwtEncoder { - /// Todo - pub fn from_signer(signing_provider: S) -> Self { - Self::from_boxed_signer(Box::new(signing_provider)) - } - - /// Create a new [`JwtEncoder`] with any crypto provider that implements the [`CryptoProvider`] trait. - pub fn from_boxed_signer(signing_provider: Box) -> Self { - // Determine a default header - let mut header = Header::new(signing_provider.algorithm()); - header.typ = Some("JWT".to_owned()); - - Self { signing_provider, header } - } - - /// Provide a custom header. - /// - /// This would be used in the rare cases that fields other than `algorithm` and `type` need to be populated. - /// - /// # Todo - /// - /// - Test the the error checking works - pub fn with_header(mut self, header: &Header) -> Result { - // Check that the header makes use of the correct algorithm - if header.alg != self.signing_provider.algorithm() { - return Err(new_error(crate::errors::ErrorKind::InvalidAlgorithm)); - } - - self.header = header.clone(); - Ok(self) - } - - /// Encode and sign the `claims` as a JWT. - /// - /// # Todo - /// - /// - Put in example usage. - pub fn encode(&self, claims: &T) -> Result { - let encoded_header = b64_encode_part(&self.header)?; - let encoded_claims = b64_encode_part(claims)?; - let message = [encoded_header, encoded_claims].join("."); - - let signature = b64_encode(&self.signing_provider.sign(message.as_bytes())); - - Ok([message, signature].join(".")) - } - - /// Create new [`JwtEncoder`] with the `HS256` algorithm. - pub fn hs_256(secret: HmacSecret) -> Result { - let signing_provider = Box::new(Hs256::new(secret)?); - - Ok(JwtEncoder::from_boxed_signer(signing_provider)) - } - - /// Create new [`JwtEncoder`] with the `HS384` algorithm. - pub fn hs_384(secret: HmacSecret) -> Result { - let signing_provider = Box::new(Hs384::new(secret)?); - - Ok(JwtEncoder::from_boxed_signer(signing_provider)) - } -} - -/// Takes the result of a rsplit and ensure we only get 2 parts -/// Errors if we don't -macro_rules! expect_two { - ($iter:expr) => {{ - let mut i = $iter; - match (i.next(), i.next(), i.next()) { - (Some(first), Some(second), None) => (first, second), - _ => return Err(new_error(ErrorKind::InvalidToken)), - } - }}; -} - -/// Todo -pub struct JwtDecoder { - verifying_provider: Box, - validation: Validation, -} - -impl JwtDecoder { - /// Todo - pub fn from_verifier(verifying_provider: V) -> Self { - Self::from_boxed_verifiyer(Box::new(verifying_provider)) - } - - /// Todo - pub fn from_boxed_verifiyer(verifying_provider: Box) -> Self { - let validation = Validation::new(verifying_provider.algorithm()); - - Self { verifying_provider, validation } - } - - /// Todo - pub fn with_validation(mut self, validation: &Validation) -> Result { - // Check that the validation contains the correct algorithm - if !validation.algorithms.contains(&self.verifying_provider.algorithm()) { - return Err(new_error(crate::errors::ErrorKind::InvalidAlgorithm)); - } - - self.validation = validation.clone(); - Ok(self) - } - - /// Todo - pub fn decode(&self, token: &str) -> Result> { - let (header, claims) = self.verify_signature(token)?; - - let decoded_claims = DecodedJwtPartClaims::from_jwt_part_claims(claims)?; - let claims = decoded_claims.deserialize()?; - validate(decoded_claims.deserialize()?, &self.validation)?; - - Ok(TokenData { header, claims }) - } - - /// Verify signature of a JWT, and return header object and raw payload - /// - /// If the token or its signature is invalid, it will return an error. - fn verify_signature<'a>(&self, token: &'a str) -> Result<(Header, &'a str)> { - if self.validation.validate_signature && self.validation.algorithms.is_empty() { - return Err(new_error(ErrorKind::MissingAlgorithm)); - } - - // Todo: This behaviour is currently not captured anywhere. - // if validation.validate_signature { - // for alg in &validation.algorithms { - // if key.family != alg.family() { - // return Err(new_error(ErrorKind::InvalidAlgorithm)); - // } - // } - // } - - let (signature, message) = expect_two!(token.rsplitn(2, '.')); - let (payload, header) = expect_two!(message.rsplitn(2, '.')); - let header = Header::from_encoded(header)?; - - if self.validation.validate_signature && !self.validation.algorithms.contains(&header.alg) { - return Err(new_error(ErrorKind::InvalidAlgorithm)); - } - - if self.validation.validate_signature - && self - .verifying_provider - .verify(message.as_bytes(), &signature.as_bytes().to_vec()) - .is_err() - { - return Err(new_error(ErrorKind::InvalidSignature)); - } - - Ok((header, payload)) - } - - /// Create new [`JwtDecoder`] with the `HS256` algorithm. - pub fn hs_256(secret: HmacSecret) -> Result { - let verifying_provider = Box::new(Hs256::new(secret)?); - - Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) - } - - /// Create new [`JwtDecoder`] with the `HS384` algorithm. - pub fn hs_384(secret: HmacSecret) -> Result { - let verifying_provider = Box::new(Hs384::new(secret)?); - - Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) - } -} - -#[cfg(test)] -mod builder_tests { - - use super::*; - - #[derive(Debug, Serialize)] - struct Claims { - sub: String, - age: u32, - } - - #[test] - fn test_builder() { - // Arrange - let claims = Claims { sub: "123345".to_owned(), age: 25 }; - let secret = HmacSecret::from_secret("test".as_ref()); - - // Act - let jwt = JwtEncoder::hs_256(secret).unwrap().encode(&claims).unwrap(); - - dbg!(&jwt); - - // Assert - } -} diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs index 11a5144a..9ad3046a 100644 --- a/src/crypto/hmac.rs +++ b/src/crypto/hmac.rs @@ -12,6 +12,7 @@ type HmacSha256 = Hmac; type HmacSha384 = Hmac; type HmacSha512 = Hmac; +#[derive(Debug)] pub(crate) struct HmacSecret(Vec); impl HmacSecret { @@ -111,3 +112,46 @@ impl JwtVerifier for Hs384 { Algorithm::HS384 } } + +pub struct Hs512(HmacSha512); + +impl Hs512 { + pub(crate) fn new(secret: HmacSecret) -> Result { + let inner = HmacSha512::new_from_slice(&secret.0) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + + Ok(Self(inner)) + } +} + +impl Signer> for Hs512 { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + let mut signer = self.0.clone(); + signer.reset(); + signer.update(msg); + + Ok(signer.finalize().into_bytes().to_vec()) + } +} + +impl JwtSigner for Hs512 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS512 + } +} + +impl Verifier> for Hs512 { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + let mut verifier = self.0.clone(); + verifier.reset(); + verifier.update(msg); + + verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) + } +} + +impl JwtVerifier for Hs512 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS512 + } +} diff --git a/src/decoding.rs b/src/decoding.rs index 3b041201..90c19b24 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -2,15 +2,16 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::de::DeserializeOwned; use crate::algorithms::AlgorithmFamily; -use crate::builder::JwtDecoder; -use crate::crypto::verify; -use crate::errors::{new_error, ErrorKind, Result}; +use crate::crypto::hmac::{HmacSecret, Hs256, Hs384, Hs512}; +use crate::crypto::JwtVerifier; +use crate::errors::{new_error, Error, ErrorKind, Result}; use crate::header::Header; use crate::jwk::{AlgorithmParameters, Jwk}; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_decode, DecodedJwtPartClaims}; use crate::validation::{validate, Validation}; +use crate::Algorithm; /// The return type of a successful call to [decode](fn.decode.html). #[derive(Debug)] @@ -202,41 +203,6 @@ impl DecodingKey { } } -/// Verify signature of a JWT, and return header object and raw payload -/// -/// If the token or its signature is invalid, it will return an error. -fn verify_signature<'a>( - token: &'a str, - key: &DecodingKey, - validation: &Validation, -) -> Result<(Header, &'a str)> { - if validation.validate_signature && validation.algorithms.is_empty() { - return Err(new_error(ErrorKind::MissingAlgorithm)); - } - - if validation.validate_signature { - for alg in &validation.algorithms { - if key.family != alg.family() { - return Err(new_error(ErrorKind::InvalidAlgorithm)); - } - } - } - - let (signature, message) = expect_two!(token.rsplitn(2, '.')); - let (payload, header) = expect_two!(message.rsplitn(2, '.')); - let header = Header::from_encoded(header)?; - - if validation.validate_signature && !validation.algorithms.contains(&header.alg) { - return Err(new_error(ErrorKind::InvalidAlgorithm)); - } - - if validation.validate_signature && !verify(signature, message.as_bytes(), key, header.alg)? { - return Err(new_error(ErrorKind::InvalidSignature)); - } - - Ok((header, payload)) -} - /// Decode and validate a JWT /// /// If the token or its signature is invalid or the claims fail validation, it will return an error. @@ -260,23 +226,23 @@ pub fn decode( key: &DecodingKey, validation: &Validation, ) -> Result> { - match verify_signature(token, key, validation) { - Err(e) => Err(e), - Ok((header, claims)) => { - let decoded_claims = DecodedJwtPartClaims::from_jwt_part_claims(claims)?; - let claims = decoded_claims.deserialize()?; - validate(decoded_claims.deserialize()?, validation)?; - - Ok(TokenData { header, claims }) - } + // The decoding step is unfortunately a little clunky but that seems to be how I need to do it to fit it into the `decode` function + let header = decode_header(token)?; + + if validation.validate_signature && !(validation.algorithms.contains(&header.alg)) { + return Err(new_error(ErrorKind::InvalidAlgorithm)); } + + let decoder = decoder_factory(&header.alg, key)?.with_validation(validation)?; + + decoder.decode(token) } fn decoder_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result { let jwt_encoder = match algorithm { - Algorithm::HS256 => JwtDecoder::hs_256(HmacSecret::from_secret(&key.content))?, - Algorithm::HS384 => JwtDecoder::hs_384(HmacSecret::from_secret(&key.content))?, - Algorithm::HS512 => todo!(), + Algorithm::HS256 => JwtDecoder::hs_256(key.try_into()?)?, + Algorithm::HS384 => JwtDecoder::hs_384(key.try_into()?)?, + Algorithm::HS512 => JwtDecoder::hs_512(key.try_into()?)?, Algorithm::ES256 => todo!(), Algorithm::ES384 => todo!(), Algorithm::RS256 => todo!(), @@ -291,6 +257,20 @@ fn decoder_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result for &DecodingKey { + type Error = Error; + + fn try_into(self) -> std::result::Result { + match self.kind.clone() { + DecodingKeyKind::SecretOrDer(vec) => Ok(HmacSecret::from_secret(&vec)), + DecodingKeyKind::RsaModulusExponent { .. } => { + Err(new_error(crate::errors::ErrorKind::InvalidKeyFormat)) + } + } + } +} + /// Decode a JWT without any signature verification/validations and return its [Header](struct.Header.html). /// /// If the token has an invalid format (ie 3 parts separated by a `.`), it will return an error. @@ -306,3 +286,102 @@ pub fn decode_header(token: &str) -> Result
{ let (_, header) = expect_two!(message.rsplitn(2, '.')); Header::from_encoded(header) } + +/// Todo +pub struct JwtDecoder { + verifying_provider: Box, + validation: Validation, +} + +impl JwtDecoder { + /// Todo + pub fn from_verifier(verifying_provider: V) -> Self { + Self::from_boxed_verifiyer(Box::new(verifying_provider)) + } + + /// Todo + pub fn from_boxed_verifiyer(verifying_provider: Box) -> Self { + let validation = Validation::new(verifying_provider.algorithm()); + + Self { verifying_provider, validation } + } + + /// Todo + pub fn with_validation(mut self, validation: &Validation) -> Result { + // Check that the validation contains the correct algorithm + if validation.validate_signature + && !validation.algorithms.contains(&self.verifying_provider.algorithm()) + { + return Err(new_error(crate::errors::ErrorKind::InvalidAlgorithm)); + } + + self.validation = validation.clone(); + Ok(self) + } + + /// Todo + pub fn decode(&self, token: &str) -> Result> { + let (header, claims) = self.verify_signature(token)?; + + let decoded_claims = DecodedJwtPartClaims::from_jwt_part_claims(claims)?; + let claims = decoded_claims.deserialize()?; + validate(decoded_claims.deserialize()?, &self.validation)?; + + Ok(TokenData { header, claims }) + } + + /// Verify signature of a JWT, and return header object and raw payload + /// + /// If the token or its signature is invalid, it will return an error. + fn verify_signature<'a>(&self, token: &'a str) -> Result<(Header, &'a str)> { + if self.validation.validate_signature && self.validation.algorithms.is_empty() { + return Err(new_error(ErrorKind::MissingAlgorithm)); + } + + // Todo: This behaviour is currently not captured anywhere. + // if validation.validate_signature { + // for alg in &validation.algorithms { + // if key.family != alg.family() { + // return Err(new_error(ErrorKind::InvalidAlgorithm)); + // } + // } + // } + + let (signature, message) = expect_two!(token.rsplitn(2, '.')); + let (payload, header) = expect_two!(message.rsplitn(2, '.')); + let header = Header::from_encoded(header)?; + + if self.validation.validate_signature && !self.validation.algorithms.contains(&header.alg) { + return Err(new_error(ErrorKind::InvalidAlgorithm)); + } + + if self.validation.validate_signature + && self.verifying_provider.verify(message.as_bytes(), &b64_decode(signature)?).is_err() + { + return Err(new_error(ErrorKind::InvalidSignature)); + } + + Ok((header, payload)) + } + + /// Create new [`JwtDecoder`] with the `HS256` algorithm. + pub fn hs_256(secret: HmacSecret) -> Result { + let verifying_provider = Box::new(Hs256::new(secret)?); + + Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) + } + + /// Create new [`JwtDecoder`] with the `HS384` algorithm. + pub fn hs_384(secret: HmacSecret) -> Result { + let verifying_provider = Box::new(Hs384::new(secret)?); + + Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) + } + + /// Create new [`JwtDecoder`] with the `HS512` algorithm. + pub fn hs_512(secret: HmacSecret) -> Result { + let verifying_provider = Box::new(Hs512::new(secret)?); + + Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) + } +} diff --git a/src/encoding.rs b/src/encoding.rs index 9443c5e1..41f21488 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,15 +2,14 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::ser::Serialize; use crate::algorithms::AlgorithmFamily; -use crate::builder::JwtEncoder; use crate::crypto::hmac::{HmacSecret, Hs256, Hs384}; use crate::crypto::JwtSigner; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; -use crate::serialization::b64_encode_part; -use crate::{crypto, Algorithm}; +use crate::serialization::{b64_encode, b64_encode_part}; +use crate::Algorithm; /// A key to encode a JWT with. Can be a secret, a PEM-encoded key or a DER-encoded key. /// This key can be re-used so make sure you only initialize it once if you can for better performance. @@ -133,6 +132,7 @@ pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> R fn encoder_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result { let jwt_encoder = match algorithm { + // Todo: Need to implement `TryInto for &EncodingKey` Algorithm::HS256 => JwtEncoder::hs_256(HmacSecret::from_secret(&key.content))?, Algorithm::HS384 => JwtEncoder::hs_384(HmacSecret::from_secret(&key.content))?, Algorithm::HS512 => todo!(), @@ -149,3 +149,73 @@ fn encoder_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result, + header: Header, +} + +impl JwtEncoder { + /// Todo + pub fn from_signer(signing_provider: S) -> Self { + Self::from_boxed_signer(Box::new(signing_provider)) + } + + /// Create a new [`JwtEncoder`] with any crypto provider that implements the [`CryptoProvider`] trait. + pub fn from_boxed_signer(signing_provider: Box) -> Self { + // Determine a default header + let mut header = Header::new(signing_provider.algorithm()); + header.typ = Some("JWT".to_owned()); + + Self { signing_provider, header } + } + + /// Provide a custom header. + /// + /// This would be used in the rare cases that fields other than `algorithm` and `type` need to be populated. + /// + /// # Todo + /// + /// - Test the the error checking works + pub fn with_header(mut self, header: &Header) -> Result { + // Check that the header makes use of the correct algorithm + if header.alg != self.signing_provider.algorithm() { + return Err(new_error(crate::errors::ErrorKind::InvalidAlgorithm)); + } + + self.header = header.clone(); + Ok(self) + } + + /// Encode and sign the `claims` as a JWT. + /// + /// # Todo + /// + /// - Put in example usage. + pub fn encode(&self, claims: &T) -> Result { + let encoded_header = b64_encode_part(&self.header)?; + let encoded_claims = b64_encode_part(claims)?; + let message = [encoded_header, encoded_claims].join("."); + + let signature = b64_encode(&self.signing_provider.sign(message.as_bytes())); + + Ok([message, signature].join(".")) + } + + /// Create new [`JwtEncoder`] with the `HS256` algorithm. + pub fn hs_256(secret: HmacSecret) -> Result { + let signing_provider = Box::new(Hs256::new(secret)?); + + Ok(JwtEncoder::from_boxed_signer(signing_provider)) + } + + /// Create new [`JwtEncoder`] with the `HS384` algorithm. + pub fn hs_384(secret: HmacSecret) -> Result { + let signing_provider = Box::new(Hs384::new(secret)?); + + Ok(JwtEncoder::from_boxed_signer(signing_provider)) + } +} diff --git a/src/lib.rs b/src/lib.rs index 0eb22777..2f936c8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,6 @@ pub use header::Header; pub use validation::{get_current_timestamp, Validation}; mod algorithms; -pub mod builder; /// Lower level functions, if you want to do something other than JWTs pub mod crypto; mod decoding; diff --git a/tests/hmac.rs b/tests/hmac.rs index ec24e6e7..8d02c997 100644 --- a/tests/hmac.rs +++ b/tests/hmac.rs @@ -5,8 +5,15 @@ use wasm_bindgen_test::wasm_bindgen_test; use jsonwebtoken::errors::ErrorKind; use jsonwebtoken::jwk::Jwk; use jsonwebtoken::{ - crypto::{sign, verify}, - decode, decode_header, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation, + // crypto::{sign, verify}, + decode, + decode_header, + encode, + Algorithm, + DecodingKey, + EncodingKey, + Header, + Validation, }; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] @@ -16,23 +23,23 @@ pub struct Claims { exp: i64, } -#[test] -#[wasm_bindgen_test] -fn sign_hs256() { - let result = - sign(b"hello world", &EncodingKey::from_secret(b"secret"), Algorithm::HS256).unwrap(); - let expected = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo"; - assert_eq!(result, expected); -} +// #[test] +// #[wasm_bindgen_test] +// fn sign_hs256() { +// let result = +// sign(b"hello world", &EncodingKey::from_secret(b"secret"), Algorithm::HS256).unwrap(); +// let expected = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo"; +// assert_eq!(result, expected); +// } -#[test] -#[wasm_bindgen_test] -fn verify_hs256() { - let sig = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo"; - let valid = verify(sig, b"hello world", &DecodingKey::from_secret(b"secret"), Algorithm::HS256) - .unwrap(); - assert!(valid); -} +// #[test] +// #[wasm_bindgen_test] +// fn verify_hs256() { +// let sig = "c0zGLzKEFWj0VxWuufTXiRMk5tlI5MbGDAYhzaxIYjo"; +// let valid = verify(sig, b"hello world", &DecodingKey::from_secret(b"secret"), Algorithm::HS256) +// .unwrap(); +// assert!(valid); +// } #[test] #[wasm_bindgen_test] @@ -44,6 +51,7 @@ fn encode_with_custom_header() { }; let header = Header { kid: Some("kid".to_string()), ..Default::default() }; let token = encode(&header, &my_claims, &EncodingKey::from_secret(b"secret")).unwrap(); + let token_data = decode::( &token, &DecodingKey::from_secret(b"secret"), diff --git a/tests/lib.rs b/tests/lib.rs index 89661df9..c5374c72 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,4 +1,4 @@ -mod ecdsa; -mod eddsa; -mod header; -mod rsa; +// mod ecdsa; +// mod eddsa; +// mod header; +// mod rsa; From 088606440e6c346c4ce4a3b1f00fb4ff4997c847 Mon Sep 17 00:00:00 2001 From: sidrubs <52378223+sidrubs@users.noreply.github.com> Date: Wed, 2 Oct 2024 22:14:22 -0300 Subject: [PATCH 18/35] docs: Neaten up docstrings --- src/crypto/hmac.rs | 6 +++- src/crypto/mod.rs | 71 ++++-------------------------------- src/decoding.rs | 56 ++++++++++++++++++++++------- src/encoding.rs | 90 +++++++++++++++++++++++++++++++++++++--------- src/lib.rs | 5 +-- 5 files changed, 133 insertions(+), 95 deletions(-) diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs index 9ad3046a..824c5b41 100644 --- a/src/crypto/hmac.rs +++ b/src/crypto/hmac.rs @@ -1,3 +1,6 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the +//! HMAC family of algorithms. + use base64::{engine::general_purpose::STANDARD, Engine}; use hmac::{Hmac, Mac}; use sha2::{Sha256, Sha384, Sha512}; @@ -12,8 +15,9 @@ type HmacSha256 = Hmac; type HmacSha384 = Hmac; type HmacSha512 = Hmac; +/// The shared secret used for the HMAC family of algorithms. #[derive(Debug)] -pub(crate) struct HmacSecret(Vec); +pub struct HmacSecret(Vec); impl HmacSecret { /// If you're using an HMAC secret that is not base64, use that. diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 4317a780..f4b456b4 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,12 +1,13 @@ +//! The cryptography of the `jsonwebtoken` crate is decoupled behind +//! [`JwtSigner`] and [`JwtVerifier`] traits. These make use of `RustCrypto`'s +//! [`Signer`] and [`Verifier`] traits respectively. + use crate::algorithms::Algorithm; -// use crate::decoding::{DecodingKey, DecodingKeyKind}; -// use crate::encoding::EncodingKey; -// use crate::errors::Result; -pub(crate) mod ecdsa; -pub(crate) mod eddsa; +// pub(crate) mod ecdsa; +// pub(crate) mod eddsa; pub(crate) mod hmac; -pub(crate) mod rsa; +// pub(crate) mod rsa; use signature::{Signer, Verifier}; @@ -25,61 +26,3 @@ pub trait JwtVerifier: Verifier> { /// Return the [`Algorithm`] corresponding to the signing module. fn algorithm(&self) -> Algorithm; } - -// /// Take the payload of a JWT, sign it using the algorithm given and return -// /// the base64 url safe encoded of the result. -// /// -// /// If you just want to encode a JWT, use `encode` instead. -// pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result { -// match algorithm { -// Algorithm::ES256 | Algorithm::ES384 => ecdsa::sign(algorithm, key.inner(), message), -// Algorithm::EdDSA => eddsa::sign(key.inner(), message), -// Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { -// hmac::sign_hmac(algorithm, key.inner(), message) -// } -// Algorithm::RS256 -// | Algorithm::RS384 -// | Algorithm::RS512 -// | Algorithm::PS256 -// | Algorithm::PS384 -// | Algorithm::PS512 => rsa::sign(algorithm, key.inner(), message), -// } -// } - -// /// Compares the signature given with a re-computed signature for HMAC or using the public key -// /// for RSA/EC. -// /// -// /// If you just want to decode a JWT, use `decode` instead. -// /// -// /// `signature` is the signature part of a jwt (text after the second '.') -// /// -// /// `message` is base64(header) + "." + base64(claims) -// pub fn verify( -// signature: &str, -// message: &[u8], -// key: &DecodingKey, -// algorithm: Algorithm, -// ) -> Result { -// match algorithm { -// Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { -// hmac::hmac_verify(algorithm, signature, key.as_bytes(), message) -// } -// Algorithm::ES256 | Algorithm::ES384 => { -// ecdsa::verify(algorithm, signature, message, key.as_bytes()) -// } -// Algorithm::EdDSA => eddsa::verify(signature, message, key.as_bytes()), -// Algorithm::RS256 -// | Algorithm::RS384 -// | Algorithm::RS512 -// | Algorithm::PS256 -// | Algorithm::PS384 -// | Algorithm::PS512 => match &key.kind { -// DecodingKeyKind::SecretOrDer(bytes) => { -// rsa::verify_der(algorithm, signature, message, bytes) -// } -// DecodingKeyKind::RsaModulusExponent { n, e } => { -// rsa::verify_from_components(algorithm, signature, message, (n, e)) -// } -// }, -// } -// } diff --git a/src/decoding.rs b/src/decoding.rs index 90c19b24..9e9d7648 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -194,13 +194,6 @@ impl DecodingKey { } } } - - pub(crate) fn as_bytes(&self) -> &[u8] { - match &self.kind { - DecodingKeyKind::SecretOrDer(b) => b, - DecodingKeyKind::RsaModulusExponent { .. } => unreachable!(), - } - } } /// Decode and validate a JWT @@ -238,6 +231,7 @@ pub fn decode( decoder.decode(token) } +/// Return the correct decoder based on the `algorithm`. fn decoder_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result { let jwt_encoder = match algorithm { Algorithm::HS256 => JwtDecoder::hs_256(key.try_into()?)?, @@ -287,26 +281,64 @@ pub fn decode_header(token: &str) -> Result
{ Header::from_encoded(header) } -/// Todo +/// A builder style JWT decoder +/// +/// # Examples +/// +/// ``` +/// use jsonwebtoken::{JwtDecoder, HmacSecret}; +/// use serde::{Serialize, Deserialize}; +/// +/// #[derive(Debug, Serialize, Deserialize)] +/// struct Claims { +/// sub: String, +/// company: String, +/// exp: usize, +/// } +/// +/// let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98"; +/// +/// let hmac_secret = HmacSecret::from_secret(b"secret"); +/// +/// let claims = JwtDecoder::hs_256(hmac_secret) +/// .unwrap() +/// .decode::(&token) +/// .unwrap(); +/// ``` pub struct JwtDecoder { verifying_provider: Box, validation: Validation, } impl JwtDecoder { - /// Todo + /// Create a new [`JwtDecoder`] with any `verifying_provider` that implements the [`JwtVerifier`] trait. pub fn from_verifier(verifying_provider: V) -> Self { Self::from_boxed_verifiyer(Box::new(verifying_provider)) } - /// Todo + /// Create a new [`JwtDecoder`] with any `verifying_provider` implements the [`JwtVerifier`] trait. pub fn from_boxed_verifiyer(verifying_provider: Box) -> Self { let validation = Validation::new(verifying_provider.algorithm()); Self { verifying_provider, validation } } - /// Todo + /// Provide custom a custom validation configuration. + /// + /// # Examples + /// + /// ``` + /// use jsonwebtoken::{JwtDecoder, HmacSecret, Validation, Algorithm}; + /// + /// let hmac_secret = HmacSecret::from_secret(b"secret"); + /// let mut validation = Validation::new(Algorithm::HS256); + /// validation.leeway = 5; + /// + /// let jwt_decoder = JwtDecoder::hs_256(hmac_secret) + /// .unwrap() + /// .with_validation(&validation) + /// .unwrap(); + /// ``` pub fn with_validation(mut self, validation: &Validation) -> Result { // Check that the validation contains the correct algorithm if validation.validate_signature @@ -319,7 +351,7 @@ impl JwtDecoder { Ok(self) } - /// Todo + /// Decode and verify a JWT `token` using the `verifying_provider` and `validation` of the [`JwtDecoder`] pub fn decode(&self, token: &str) -> Result> { let (header, claims) = self.verify_signature(token)?; diff --git a/src/encoding.rs b/src/encoding.rs index 41f21488..9e7558d7 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,7 +2,7 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::ser::Serialize; use crate::algorithms::AlgorithmFamily; -use crate::crypto::hmac::{HmacSecret, Hs256, Hs384}; +use crate::crypto::hmac::{HmacSecret, Hs256, Hs384, Hs512}; use crate::crypto::JwtSigner; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; @@ -130,12 +130,12 @@ pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> R jwt_encoder.encode(claims) } +/// Return the correct [`JwtEncoder`] based on the `algorithm`. fn encoder_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result { let jwt_encoder = match algorithm { - // Todo: Need to implement `TryInto for &EncodingKey` - Algorithm::HS256 => JwtEncoder::hs_256(HmacSecret::from_secret(&key.content))?, - Algorithm::HS384 => JwtEncoder::hs_384(HmacSecret::from_secret(&key.content))?, - Algorithm::HS512 => todo!(), + Algorithm::HS256 => JwtEncoder::hs_256(key.into())?, + Algorithm::HS384 => JwtEncoder::hs_384(key.into())?, + Algorithm::HS512 => JwtEncoder::hs_512(key.into())?, Algorithm::ES256 => todo!(), Algorithm::ES384 => todo!(), Algorithm::RS256 => todo!(), @@ -150,21 +150,51 @@ fn encoder_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result for HmacSecret { + fn from(key: &EncodingKey) -> Self { + HmacSecret::from_secret(&key.content) + } +} + +/// A builder style JWT encoder. +/// +/// # Examples +/// +/// ``` +/// use serde::{Deserialize, Serialize}; +/// use jsonwebtoken::{JwtEncoder, HmacSecret}; +/// +/// #[derive(Debug, Serialize, Deserialize)] +/// struct Claims { +/// sub: String, +/// company: String +/// } +/// +/// let my_claims = Claims { +/// sub: "b@b.com".to_owned(), +/// company: "ACME".to_owned() +/// }; /// -/// - Documentation +/// let hmac_secret = HmacSecret::from_secret(b"secret"); +/// +/// let token = JwtEncoder::hs_256(hmac_secret) +/// .unwrap() +/// .encode(&my_claims) +/// .unwrap(); +/// ``` pub struct JwtEncoder { signing_provider: Box, header: Header, } impl JwtEncoder { - /// Todo + /// Create a new [`JwtEncoder`] with any `signing_provider` that implements the [`JwtSigner`] trait. pub fn from_signer(signing_provider: S) -> Self { Self::from_boxed_signer(Box::new(signing_provider)) } - /// Create a new [`JwtEncoder`] with any crypto provider that implements the [`CryptoProvider`] trait. + /// Create a new [`JwtEncoder`] with any `signing_provider` that implements the [`JwtSigner`] trait. pub fn from_boxed_signer(signing_provider: Box) -> Self { // Determine a default header let mut header = Header::new(signing_provider.algorithm()); @@ -177,9 +207,34 @@ impl JwtEncoder { /// /// This would be used in the rare cases that fields other than `algorithm` and `type` need to be populated. /// - /// # Todo + /// # Examples + /// + /// ``` + /// use serde::{Deserialize, Serialize}; + /// use jsonwebtoken::{JwtEncoder, HmacSecret, Header, Algorithm}; + /// + /// #[derive(Debug, Serialize, Deserialize)] + /// struct Claims { + /// sub: String, + /// company: String + /// } + /// + /// let my_claims = Claims { + /// sub: "b@b.com".to_owned(), + /// company: "ACME".to_owned() + /// }; /// - /// - Test the the error checking works + /// let hmac_secret = HmacSecret::from_secret(b"secret"); + /// let mut header = Header::new(Algorithm::HS256); + /// header.cty = Some("content-type".to_owned()); + /// + /// let token = JwtEncoder::hs_256(hmac_secret) + /// .unwrap() + /// .with_header(&header) + /// .unwrap() + /// .encode(&my_claims) + /// .unwrap(); + /// ``` pub fn with_header(mut self, header: &Header) -> Result { // Check that the header makes use of the correct algorithm if header.alg != self.signing_provider.algorithm() { @@ -190,11 +245,7 @@ impl JwtEncoder { Ok(self) } - /// Encode and sign the `claims` as a JWT. - /// - /// # Todo - /// - /// - Put in example usage. + /// Encode and sign the `claims` as a JWT using the `signing_provider` of the [`JwtEncoder`]. pub fn encode(&self, claims: &T) -> Result { let encoded_header = b64_encode_part(&self.header)?; let encoded_claims = b64_encode_part(claims)?; @@ -218,4 +269,11 @@ impl JwtEncoder { Ok(JwtEncoder::from_boxed_signer(signing_provider)) } + + /// Create new [`JwtEncoder`] with the `HS512` algorithm. + pub fn hs_512(secret: HmacSecret) -> Result { + let signing_provider = Box::new(Hs512::new(secret)?); + + Ok(JwtEncoder::from_boxed_signer(signing_provider)) + } } diff --git a/src/lib.rs b/src/lib.rs index 2f936c8d..0d6b3be7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,9 @@ #![deny(missing_docs)] pub use algorithms::Algorithm; -pub use decoding::{decode, decode_header, DecodingKey, TokenData}; -pub use encoding::{encode, EncodingKey}; +pub use crypto::hmac::HmacSecret; +pub use decoding::{decode, decode_header, DecodingKey, JwtDecoder, TokenData}; +pub use encoding::{encode, EncodingKey, JwtEncoder}; pub use header::Header; pub use validation::{get_current_timestamp, Validation}; From 337f9ed5d6cf1e0e2f3d059aa34c9f6a3b55a609 Mon Sep 17 00:00:00 2001 From: sidrubs <52378223+sidrubs@users.noreply.github.com> Date: Sat, 5 Oct 2024 10:04:05 -0300 Subject: [PATCH 19/35] feat(crypto): Implement JwtSigner and JwtVerifier for aws-lc-rs --- Cargo.toml | 12 ++- src/crypto/aws_lc/hmac.rs | 105 ++++++++++++++++++++++++ src/crypto/aws_lc/mod.rs | 1 + src/crypto/ecdsa.rs | 74 ----------------- src/crypto/eddsa.rs | 29 ------- src/crypto/hmac.rs | 144 ++------------------------------- src/crypto/mod.rs | 7 +- src/crypto/rsa.rs | 115 -------------------------- src/crypto/rust_crypto/hmac.rs | 143 ++++++++++++++++++++++++++++++++ src/crypto/rust_crypto/mod.rs | 1 + src/decoding.rs | 8 +- src/encoding.rs | 8 +- src/lib.rs | 5 ++ 13 files changed, 290 insertions(+), 362 deletions(-) create mode 100644 src/crypto/aws_lc/hmac.rs create mode 100644 src/crypto/aws_lc/mod.rs delete mode 100644 src/crypto/ecdsa.rs delete mode 100644 src/crypto/eddsa.rs delete mode 100644 src/crypto/rsa.rs create mode 100644 src/crypto/rust_crypto/hmac.rs create mode 100644 src/crypto/rust_crypto/mod.rs diff --git a/Cargo.toml b/Cargo.toml index b31f72ff..7d228aec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ base64 = "0.22" pem = { version = "3", optional = true } simple_asn1 = { version = "0.6", optional = true } -hmac = "0.12.1" rsa = "0.9.6" sha2 = { version = "0.10.7", features = ["oid"] } getrandom = { version = "0.2.10", features = ["js"] } @@ -37,6 +36,13 @@ p256 = { version = "0.13.2", features = ["ecdsa"] } p384 = { version = "0.13.0", features = ["ecdsa"] } rand_core = "0.6.4" signature = "2.2.0" + +# "rust_crypto" feature +hmac = { version = "0.12.1", optional = true } + +# "aws_lc_rs" feature +aws-lc-rs = { version = "1.10.0", optional = true } + [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = "0.3" @@ -54,8 +60,10 @@ time = { version = "0.3", features = ["wasm-bindgen"] } criterion = { version = "0.4", default-features = false } [features] -default = ["use_pem"] +default = ["use_pem", "rust_crypto"] use_pem = ["pem", "simple_asn1", 'p256/pem', 'p384/pem'] +rust_crypto = ["hmac"] +aws_lc_rs = ["aws-lc-rs"] [[bench]] name = "jwt" diff --git a/src/crypto/aws_lc/hmac.rs b/src/crypto/aws_lc/hmac.rs new file mode 100644 index 00000000..00b3570b --- /dev/null +++ b/src/crypto/aws_lc/hmac.rs @@ -0,0 +1,105 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the +//! HMAC family of algorithms using [`aws_lc_rs`] + +use aws_lc_rs::hmac; +use signature::{Signer, Verifier}; + +use crate::crypto::{JwtSigner, JwtVerifier}; +use crate::errors::Result; +use crate::{Algorithm, HmacSecret}; + +pub struct Hs256(hmac::Key); + +impl Hs256 { + pub(crate) fn new(secret: HmacSecret) -> Result { + Ok(Self(hmac::Key::new(hmac::HMAC_SHA256, &secret))) + } +} + +impl Signer> for Hs256 { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + Ok(hmac::sign(&self.0, msg).as_ref().to_vec()) + } +} + +impl JwtSigner for Hs256 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS256 + } +} + +impl Verifier> for Hs256 { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + hmac::verify(&self.0, msg, &signature).map_err(|err| signature::Error::from_source(err)) + } +} + +impl JwtVerifier for Hs256 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS256 + } +} + +pub struct Hs384(hmac::Key); + +impl Hs384 { + pub(crate) fn new(secret: HmacSecret) -> Result { + Ok(Self(hmac::Key::new(hmac::HMAC_SHA384, &secret))) + } +} + +impl Signer> for Hs384 { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + Ok(hmac::sign(&self.0, msg).as_ref().to_vec()) + } +} + +impl JwtSigner for Hs384 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS384 + } +} + +impl Verifier> for Hs384 { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + hmac::verify(&self.0, msg, &signature).map_err(|err| signature::Error::from_source(err)) + } +} + +impl JwtVerifier for Hs384 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS384 + } +} + +pub struct Hs512(hmac::Key); + +impl Hs512 { + pub(crate) fn new(secret: HmacSecret) -> Result { + Ok(Self(hmac::Key::new(hmac::HMAC_SHA512, &secret))) + } +} + +impl Signer> for Hs512 { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + Ok(hmac::sign(&self.0, msg).as_ref().to_vec()) + } +} + +impl JwtSigner for Hs512 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS512 + } +} + +impl Verifier> for Hs512 { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + hmac::verify(&self.0, msg, &signature).map_err(|err| signature::Error::from_source(err)) + } +} + +impl JwtVerifier for Hs512 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS512 + } +} diff --git a/src/crypto/aws_lc/mod.rs b/src/crypto/aws_lc/mod.rs new file mode 100644 index 00000000..3a812e97 --- /dev/null +++ b/src/crypto/aws_lc/mod.rs @@ -0,0 +1 @@ +pub(crate) mod hmac; diff --git a/src/crypto/ecdsa.rs b/src/crypto/ecdsa.rs deleted file mode 100644 index df7190c1..00000000 --- a/src/crypto/ecdsa.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::algorithms::Algorithm; -use crate::errors::Result; -use crate::serialization::{b64_decode, b64_encode}; - -/// The actual ECDSA signing + encoding -/// The key needs to be in PKCS8 format -pub(crate) fn sign(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { - match alg { - Algorithm::ES256 => es256_sign(key, message), - Algorithm::ES384 => es384_sign(key, message), - - _ => unreachable!("Tried to get EC alg for a non-EC algorithm"), - } -} - -fn es256_sign(key: &[u8], message: &[u8]) -> Result { - use p256::ecdsa::signature::Signer; - use p256::ecdsa::{Signature, SigningKey}; - use p256::pkcs8::DecodePrivateKey; - use p256::SecretKey; - let secret_key = - SecretKey::from_pkcs8_der(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; - let signing_key: SigningKey = secret_key.into(); - - let signature: Signature = signing_key.sign(message); - let bytes = signature.to_bytes(); - Ok(b64_encode(bytes)) -} - -fn es384_sign(key: &[u8], message: &[u8]) -> Result { - use p384::ecdsa::signature::Signer; - use p384::ecdsa::{Signature, SigningKey}; - use p384::pkcs8::DecodePrivateKey; - use p384::SecretKey; - let secret_key = - SecretKey::from_pkcs8_der(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; - let signing_key: SigningKey = secret_key.into(); - let signature: Signature = signing_key.sign(message); - let bytes = signature.to_bytes(); - Ok(b64_encode(bytes)) -} - -pub(crate) fn verify(alg: Algorithm, signature: &str, message: &[u8], key: &[u8]) -> Result { - match alg { - Algorithm::ES256 => es256_verify(signature, message, key), - Algorithm::ES384 => es384_verify(signature, message, key), - _ => unreachable!("Tried to get EC alg for a non-EC algorithm"), - } -} - -fn es384_verify(signature: &str, message: &[u8], key: &[u8]) -> Result { - use p384::ecdsa::signature::Verifier; - use p384::ecdsa::{Signature, VerifyingKey}; - use p384::PublicKey; - - let public_key = - PublicKey::from_sec1_bytes(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; - let verifying_key: VerifyingKey = public_key.into(); - let signature = Signature::from_slice(&b64_decode(signature)?) - .map_err(|_e| crate::errors::ErrorKind::InvalidSignature)?; - Ok(verifying_key.verify(message, &signature).is_ok()) -} - -fn es256_verify(signature: &str, message: &[u8], key: &[u8]) -> Result { - use p256::ecdsa::signature::Verifier; - use p256::ecdsa::{Signature, VerifyingKey}; - use p256::PublicKey; - let public_key = - PublicKey::from_sec1_bytes(key).map_err(|_e| crate::errors::ErrorKind::InvalidEcdsaKey)?; - let verifying_key: VerifyingKey = public_key.into(); - let signature = Signature::from_slice(&b64_decode(signature)?) - .map_err(|_e| crate::errors::ErrorKind::InvalidSignature)?; - Ok(verifying_key.verify(message, &signature).is_ok()) -} diff --git a/src/crypto/eddsa.rs b/src/crypto/eddsa.rs deleted file mode 100644 index 3da4fb5a..00000000 --- a/src/crypto/eddsa.rs +++ /dev/null @@ -1,29 +0,0 @@ -use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; - -use crate::errors::{new_error, ErrorKind, Result}; -use crate::serialization::{b64_decode, b64_encode}; - -fn parse_key(key: &[u8]) -> Result { - let key = key.try_into().map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?; - let signing_key = SigningKey::from_bytes(key); - Ok(signing_key) -} - -pub(crate) fn verify(signature: &str, message: &[u8], key: &[u8]) -> Result { - let signature = b64_decode(signature)?; - let signature = - Signature::from_slice(&signature).map_err(|_e| new_error(ErrorKind::InvalidSignature))?; - let key = key.try_into().map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?; - let verifying_key = - VerifyingKey::from_bytes(key).map_err(|_| new_error(ErrorKind::InvalidEddsaKey))?; - Ok(verifying_key.verify(message, &signature).is_ok()) -} - -/// The actual EdDSA signing + encoding -/// The key needs to be in PKCS8 format -pub fn sign(key: &[u8], message: &[u8]) -> Result { - let key = key[16..].into(); - let signing_key = parse_key(key)?; - let out = signing_key.sign(message); - Ok(b64_encode(out.to_bytes())) -} diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs index 824c5b41..8467f2dd 100644 --- a/src/crypto/hmac.rs +++ b/src/crypto/hmac.rs @@ -1,19 +1,10 @@ -//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the -//! HMAC family of algorithms. +//! Common HMAC related functionality. + +use std::ops::Deref; use base64::{engine::general_purpose::STANDARD, Engine}; -use hmac::{Hmac, Mac}; -use sha2::{Sha256, Sha384, Sha512}; -use signature::{Signer, Verifier}; use crate::errors::Result; -use crate::Algorithm; - -use super::{JwtSigner, JwtVerifier}; - -type HmacSha256 = Hmac; -type HmacSha384 = Hmac; -type HmacSha512 = Hmac; /// The shared secret used for the HMAC family of algorithms. #[derive(Debug)] @@ -31,131 +22,10 @@ impl HmacSecret { } } -pub struct Hs256(HmacSha256); - -impl Hs256 { - pub(crate) fn new(secret: HmacSecret) -> Result { - let inner = HmacSha256::new_from_slice(&secret.0) - .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; - - Ok(Self(inner)) - } -} - -impl Signer> for Hs256 { - fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { - let mut signer = self.0.clone(); - signer.reset(); - signer.update(msg); - - Ok(signer.finalize().into_bytes().to_vec()) - } -} - -impl JwtSigner for Hs256 { - fn algorithm(&self) -> Algorithm { - Algorithm::HS256 - } -} - -impl Verifier> for Hs256 { - fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - let mut verifier = self.0.clone(); - verifier.reset(); - verifier.update(msg); - - verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) - } -} - -impl JwtVerifier for Hs256 { - fn algorithm(&self) -> Algorithm { - Algorithm::HS256 - } -} - -pub(crate) struct Hs384(HmacSha384); - -impl Hs384 { - pub(crate) fn new(secret: HmacSecret) -> Result { - let inner = HmacSha384::new_from_slice(&secret.0) - .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; - - Ok(Self(inner)) - } -} - -impl Signer> for Hs384 { - fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { - let mut signer = self.0.clone(); - signer.reset(); - signer.update(msg); - - Ok(signer.finalize().into_bytes().to_vec()) - } -} - -impl JwtSigner for Hs384 { - fn algorithm(&self) -> Algorithm { - Algorithm::HS384 - } -} - -impl Verifier> for Hs384 { - fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - let mut verifier = self.0.clone(); - verifier.reset(); - verifier.update(msg); - - verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) - } -} - -impl JwtVerifier for Hs384 { - fn algorithm(&self) -> Algorithm { - Algorithm::HS384 - } -} - -pub struct Hs512(HmacSha512); - -impl Hs512 { - pub(crate) fn new(secret: HmacSecret) -> Result { - let inner = HmacSha512::new_from_slice(&secret.0) - .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; - - Ok(Self(inner)) - } -} - -impl Signer> for Hs512 { - fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { - let mut signer = self.0.clone(); - signer.reset(); - signer.update(msg); - - Ok(signer.finalize().into_bytes().to_vec()) - } -} - -impl JwtSigner for Hs512 { - fn algorithm(&self) -> Algorithm { - Algorithm::HS512 - } -} - -impl Verifier> for Hs512 { - fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - let mut verifier = self.0.clone(); - verifier.reset(); - verifier.update(msg); - - verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) - } -} +impl Deref for HmacSecret { + type Target = Vec; -impl JwtVerifier for Hs512 { - fn algorithm(&self) -> Algorithm { - Algorithm::HS512 + fn deref(&self) -> &Self::Target { + &self.0 } } diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index f4b456b4..15d9f479 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -4,10 +4,11 @@ use crate::algorithms::Algorithm; -// pub(crate) mod ecdsa; -// pub(crate) mod eddsa; +#[cfg(feature = "aws_lc_rs")] +pub(crate) mod aws_lc; pub(crate) mod hmac; -// pub(crate) mod rsa; +#[cfg(feature = "rust_crypto")] +pub(crate) mod rust_crypto; use signature::{Signer, Verifier}; diff --git a/src/crypto/rsa.rs b/src/crypto/rsa.rs deleted file mode 100644 index 7d13411e..00000000 --- a/src/crypto/rsa.rs +++ /dev/null @@ -1,115 +0,0 @@ -use rsa::{ - pkcs1::DecodeRsaPrivateKey, pkcs1::DecodeRsaPublicKey, pss::Pss, traits::SignatureScheme, - BigUint, Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey, -}; -use sha2::{Digest, Sha256, Sha384, Sha512}; - -use crate::algorithms::Algorithm; -use crate::errors::{new_error, ErrorKind, Result}; -use crate::serialization::{b64_decode, b64_encode}; - -fn alg_to_pss(alg: Algorithm, digest_len: usize) -> Option { - match alg { - Algorithm::PS256 => Some(Pss::new_with_salt::(digest_len)), - Algorithm::PS384 => Some(Pss::new_with_salt::(digest_len)), - Algorithm::PS512 => Some(Pss::new_with_salt::(digest_len)), - _ => None, - } -} - -fn alg_to_pkcs1_v15(alg: Algorithm) -> Option { - match alg { - Algorithm::RS256 => Some(Pkcs1v15Sign::new::()), - Algorithm::RS384 => Some(Pkcs1v15Sign::new::()), - Algorithm::RS512 => Some(Pkcs1v15Sign::new::()), - _ => None, - } -} - -fn message_digest(alg: Algorithm, message: &[u8]) -> Result> { - match alg { - Algorithm::RS256 | Algorithm::PS256 => { - let mut hasher = Sha256::new(); - hasher.update(message); - let d = hasher.finalize(); - Ok(d.as_slice().to_vec()) - } - Algorithm::RS384 | Algorithm::PS384 => { - let mut hasher = Sha384::new(); - hasher.update(message); - let d = hasher.finalize(); - Ok(d.as_slice().to_vec()) - } - Algorithm::RS512 | Algorithm::PS512 => { - let mut hasher = Sha512::new(); - hasher.update(message); - let d = hasher.finalize(); - Ok(d.as_slice().to_vec()) - } - _ => Err(new_error(ErrorKind::InvalidAlgorithm)), - } -} - -pub(crate) fn sign(alg: Algorithm, key: &[u8], message: &[u8]) -> Result { - let digest = message_digest(alg, message)?; - let signatures_scheme_pkcs = alg_to_pkcs1_v15(alg); - let signatures_scheme_pss = alg_to_pss(alg, digest.len()); - let private_key = - RsaPrivateKey::from_pkcs1_der(key).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; - let mut rng = rand::thread_rng(); - let signature = if let Some(signatures_scheme) = signatures_scheme_pkcs { - signatures_scheme - .sign(Some(&mut rng), &private_key, &digest) - .map_err(|_e| ErrorKind::RsaFailedSigning)? - } else if let Some(signatures_scheme) = signatures_scheme_pss { - signatures_scheme - .sign(Some(&mut rng), &private_key, &digest) - .map_err(|_e| ErrorKind::RsaFailedSigning)? - } else { - return Err(new_error(ErrorKind::InvalidAlgorithmName)); - }; - Ok(b64_encode(signature)) -} - -pub(crate) fn verify_from_components( - alg: Algorithm, - signature: &str, - message: &[u8], - components: (&[u8], &[u8]), -) -> Result { - let n = BigUint::from_bytes_be(components.0); - let e = BigUint::from_bytes_be(components.1); - let pub_key = RsaPublicKey::new(n, e).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; - - verify(alg, signature, message, &pub_key) -} - -fn verify(alg: Algorithm, signature: &str, message: &[u8], pub_key: &RsaPublicKey) -> Result { - let signature_bytes = b64_decode(signature)?; - let digest = message_digest(alg, message)?; - let signatures_scheme_pkcs = alg_to_pkcs1_v15(alg); - let signatures_scheme_pss = alg_to_pss(alg, digest.len()); - if let Some(signatures_scheme) = signatures_scheme_pkcs { - signatures_scheme - .verify(pub_key, &digest, &signature_bytes) - .map_err(|_e| ErrorKind::InvalidSignature)?; - } else if let Some(signatures_scheme) = signatures_scheme_pss { - signatures_scheme - .verify(pub_key, &digest, &signature_bytes) - .map_err(|_e| ErrorKind::InvalidSignature)?; - } else { - return Err(new_error(ErrorKind::InvalidAlgorithmName)); - }; - Ok(true) -} - -pub(crate) fn verify_der( - alg: Algorithm, - signature: &str, - message: &[u8], - bytes: &[u8], -) -> Result { - let pub_key = - RsaPublicKey::from_pkcs1_der(bytes).map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; - verify(alg, signature, message, &pub_key) -} diff --git a/src/crypto/rust_crypto/hmac.rs b/src/crypto/rust_crypto/hmac.rs new file mode 100644 index 00000000..a29f5c68 --- /dev/null +++ b/src/crypto/rust_crypto/hmac.rs @@ -0,0 +1,143 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the +//! HMAC family of algorithms using `RustCtypto`'s [`hmac`]. + +use hmac::{Hmac, Mac}; +use sha2::{Sha256, Sha384, Sha512}; +use signature::{Signer, Verifier}; + +use crate::crypto::{JwtSigner, JwtVerifier}; +use crate::errors::Result; +use crate::{Algorithm, HmacSecret}; + +type HmacSha256 = Hmac; +type HmacSha384 = Hmac; +type HmacSha512 = Hmac; + +pub struct Hs256(HmacSha256); + +impl Hs256 { + pub(crate) fn new(secret: HmacSecret) -> Result { + let inner = HmacSha256::new_from_slice(&secret) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + + Ok(Self(inner)) + } +} + +impl Signer> for Hs256 { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + let mut signer = self.0.clone(); + signer.reset(); + signer.update(msg); + + Ok(signer.finalize().into_bytes().to_vec()) + } +} + +impl JwtSigner for Hs256 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS256 + } +} + +impl Verifier> for Hs256 { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + let mut verifier = self.0.clone(); + verifier.reset(); + verifier.update(msg); + + verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) + } +} + +impl JwtVerifier for Hs256 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS256 + } +} + +pub(crate) struct Hs384(HmacSha384); + +impl Hs384 { + pub(crate) fn new(secret: HmacSecret) -> Result { + let inner = HmacSha384::new_from_slice(&secret) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + + Ok(Self(inner)) + } +} + +impl Signer> for Hs384 { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + let mut signer = self.0.clone(); + signer.reset(); + signer.update(msg); + + Ok(signer.finalize().into_bytes().to_vec()) + } +} + +impl JwtSigner for Hs384 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS384 + } +} + +impl Verifier> for Hs384 { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + let mut verifier = self.0.clone(); + verifier.reset(); + verifier.update(msg); + + verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) + } +} + +impl JwtVerifier for Hs384 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS384 + } +} + +pub struct Hs512(HmacSha512); + +impl Hs512 { + pub(crate) fn new(secret: HmacSecret) -> Result { + let inner = HmacSha512::new_from_slice(&secret) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + + Ok(Self(inner)) + } +} + +impl Signer> for Hs512 { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + let mut signer = self.0.clone(); + signer.reset(); + signer.update(msg); + + Ok(signer.finalize().into_bytes().to_vec()) + } +} + +impl JwtSigner for Hs512 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS512 + } +} + +impl Verifier> for Hs512 { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + let mut verifier = self.0.clone(); + verifier.reset(); + verifier.update(msg); + + verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) + } +} + +impl JwtVerifier for Hs512 { + fn algorithm(&self) -> Algorithm { + Algorithm::HS512 + } +} diff --git a/src/crypto/rust_crypto/mod.rs b/src/crypto/rust_crypto/mod.rs new file mode 100644 index 00000000..3a812e97 --- /dev/null +++ b/src/crypto/rust_crypto/mod.rs @@ -0,0 +1 @@ +pub(crate) mod hmac; diff --git a/src/decoding.rs b/src/decoding.rs index 9e9d7648..5f62d520 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -2,7 +2,7 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::de::DeserializeOwned; use crate::algorithms::AlgorithmFamily; -use crate::crypto::hmac::{HmacSecret, Hs256, Hs384, Hs512}; +use crate::crypto::hmac::HmacSecret; use crate::crypto::JwtVerifier; use crate::errors::{new_error, Error, ErrorKind, Result}; use crate::header::Header; @@ -13,6 +13,12 @@ use crate::serialization::{b64_decode, DecodedJwtPartClaims}; use crate::validation::{validate, Validation}; use crate::Algorithm; +// Crypto +#[cfg(feature = "aws_lc_rs")] +use crate::crypto::aws_lc::hmac::{Hs256, Hs384, Hs512}; +#[cfg(feature = "rust_crypto")] +use crate::crypto::rust_crypto::hmac::{Hs256, Hs384, Hs512}; + /// The return type of a successful call to [decode](fn.decode.html). #[derive(Debug)] pub struct TokenData { diff --git a/src/encoding.rs b/src/encoding.rs index 9e7558d7..96dc6dce 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,7 +2,7 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::ser::Serialize; use crate::algorithms::AlgorithmFamily; -use crate::crypto::hmac::{HmacSecret, Hs256, Hs384, Hs512}; +use crate::crypto::hmac::HmacSecret; use crate::crypto::JwtSigner; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; @@ -11,6 +11,12 @@ use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_encode, b64_encode_part}; use crate::Algorithm; +// Crypto +#[cfg(feature = "aws_lc_rs")] +use crate::crypto::aws_lc::hmac::{Hs256, Hs384, Hs512}; +#[cfg(feature = "rust_crypto")] +use crate::crypto::rust_crypto::hmac::{Hs256, Hs384, Hs512}; + /// A key to encode a JWT with. Can be a secret, a PEM-encoded key or a DER-encoded key. /// This key can be re-used so make sure you only initialize it once if you can for better performance. #[derive(Clone)] diff --git a/src/lib.rs b/src/lib.rs index 0d6b3be7..4d18c704 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,11 @@ //! Documentation: [stable](https://docs.rs/jsonwebtoken/) #![deny(missing_docs)] +#[cfg(all(feature = "rust_crypto", feature = "aws_lc_rs"))] +compile_error!( + "feature \"rust_crypto\" and feature \"aws_lc_rs\" cannot be enabled at the same time" +); + pub use algorithms::Algorithm; pub use crypto::hmac::HmacSecret; pub use decoding::{decode, decode_header, DecodingKey, JwtDecoder, TokenData}; From a0431d898d32a629743393c74222b1f7b5a57d9b Mon Sep 17 00:00:00 2001 From: sidrubs <52378223+sidrubs@users.noreply.github.com> Date: Sat, 12 Oct 2024 12:12:48 -0300 Subject: [PATCH 20/35] feat: Remove builder style implementation --- src/decoding.rs | 182 +++++++++++++----------------------------------- src/encoding.rs | 160 ++++++++---------------------------------- src/lib.rs | 4 +- 3 files changed, 82 insertions(+), 264 deletions(-) diff --git a/src/decoding.rs b/src/decoding.rs index 5f62d520..debec3b8 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -232,17 +232,34 @@ pub fn decode( return Err(new_error(ErrorKind::InvalidAlgorithm)); } - let decoder = decoder_factory(&header.alg, key)?.with_validation(validation)?; + let verifying_provider = jwt_verifier_factory(&header.alg, key)?; - decoder.decode(token) + _decode(token, validation, verifying_provider) } -/// Return the correct decoder based on the `algorithm`. -fn decoder_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result { +/// # Todo +/// +/// - Documentation +pub fn _decode( + token: &str, + validation: &Validation, + verifying_provider: Box, +) -> Result> { + let (header, claims) = verify_signature(token, validation, verifying_provider)?; + + let decoded_claims = DecodedJwtPartClaims::from_jwt_part_claims(claims)?; + let claims = decoded_claims.deserialize()?; + validate(decoded_claims.deserialize()?, validation)?; + + Ok(TokenData { header, claims }) +} + +/// Return the correct [`JwtVerifier`] based on the `algorithm`. +fn jwt_verifier_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result> { let jwt_encoder = match algorithm { - Algorithm::HS256 => JwtDecoder::hs_256(key.try_into()?)?, - Algorithm::HS384 => JwtDecoder::hs_384(key.try_into()?)?, - Algorithm::HS512 => JwtDecoder::hs_512(key.try_into()?)?, + Algorithm::HS256 => Box::new(Hs256::new(key.try_into()?)?) as Box, + Algorithm::HS384 => Box::new(Hs384::new(key.try_into()?)?) as Box, + Algorithm::HS512 => Box::new(Hs512::new(key.try_into()?)?) as Box, Algorithm::ES256 => todo!(), Algorithm::ES384 => todo!(), Algorithm::RS256 => todo!(), @@ -287,139 +304,40 @@ pub fn decode_header(token: &str) -> Result
{ Header::from_encoded(header) } -/// A builder style JWT decoder -/// -/// # Examples -/// -/// ``` -/// use jsonwebtoken::{JwtDecoder, HmacSecret}; -/// use serde::{Serialize, Deserialize}; +/// Verify signature of a JWT, and return header object and raw payload /// -/// #[derive(Debug, Serialize, Deserialize)] -/// struct Claims { -/// sub: String, -/// company: String, -/// exp: usize, -/// } -/// -/// let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjI1MzI1MjQ4OTF9.9r56oF7ZliOBlOAyiOFperTGxBtPykRQiWNFxhDCW98"; -/// -/// let hmac_secret = HmacSecret::from_secret(b"secret"); -/// -/// let claims = JwtDecoder::hs_256(hmac_secret) -/// .unwrap() -/// .decode::(&token) -/// .unwrap(); -/// ``` -pub struct JwtDecoder { +/// If the token or its signature is invalid, it will return an error. +fn verify_signature<'a>( + token: &'a str, + validation: &Validation, verifying_provider: Box, - validation: Validation, -} - -impl JwtDecoder { - /// Create a new [`JwtDecoder`] with any `verifying_provider` that implements the [`JwtVerifier`] trait. - pub fn from_verifier(verifying_provider: V) -> Self { - Self::from_boxed_verifiyer(Box::new(verifying_provider)) +) -> Result<(Header, &'a str)> { + if validation.validate_signature && validation.algorithms.is_empty() { + return Err(new_error(ErrorKind::MissingAlgorithm)); } - /// Create a new [`JwtDecoder`] with any `verifying_provider` implements the [`JwtVerifier`] trait. - pub fn from_boxed_verifiyer(verifying_provider: Box) -> Self { - let validation = Validation::new(verifying_provider.algorithm()); + // Todo: This behaviour is currently not captured anywhere. + // if validation.validate_signature { + // for alg in &validation.algorithms { + // if key.family != alg.family() { + // return Err(new_error(ErrorKind::InvalidAlgorithm)); + // } + // } + // } - Self { verifying_provider, validation } - } - - /// Provide custom a custom validation configuration. - /// - /// # Examples - /// - /// ``` - /// use jsonwebtoken::{JwtDecoder, HmacSecret, Validation, Algorithm}; - /// - /// let hmac_secret = HmacSecret::from_secret(b"secret"); - /// let mut validation = Validation::new(Algorithm::HS256); - /// validation.leeway = 5; - /// - /// let jwt_decoder = JwtDecoder::hs_256(hmac_secret) - /// .unwrap() - /// .with_validation(&validation) - /// .unwrap(); - /// ``` - pub fn with_validation(mut self, validation: &Validation) -> Result { - // Check that the validation contains the correct algorithm - if validation.validate_signature - && !validation.algorithms.contains(&self.verifying_provider.algorithm()) - { - return Err(new_error(crate::errors::ErrorKind::InvalidAlgorithm)); - } - - self.validation = validation.clone(); - Ok(self) - } - - /// Decode and verify a JWT `token` using the `verifying_provider` and `validation` of the [`JwtDecoder`] - pub fn decode(&self, token: &str) -> Result> { - let (header, claims) = self.verify_signature(token)?; - - let decoded_claims = DecodedJwtPartClaims::from_jwt_part_claims(claims)?; - let claims = decoded_claims.deserialize()?; - validate(decoded_claims.deserialize()?, &self.validation)?; - - Ok(TokenData { header, claims }) - } - - /// Verify signature of a JWT, and return header object and raw payload - /// - /// If the token or its signature is invalid, it will return an error. - fn verify_signature<'a>(&self, token: &'a str) -> Result<(Header, &'a str)> { - if self.validation.validate_signature && self.validation.algorithms.is_empty() { - return Err(new_error(ErrorKind::MissingAlgorithm)); - } + let (signature, message) = expect_two!(token.rsplitn(2, '.')); + let (payload, header) = expect_two!(message.rsplitn(2, '.')); + let header = Header::from_encoded(header)?; - // Todo: This behaviour is currently not captured anywhere. - // if validation.validate_signature { - // for alg in &validation.algorithms { - // if key.family != alg.family() { - // return Err(new_error(ErrorKind::InvalidAlgorithm)); - // } - // } - // } - - let (signature, message) = expect_two!(token.rsplitn(2, '.')); - let (payload, header) = expect_two!(message.rsplitn(2, '.')); - let header = Header::from_encoded(header)?; - - if self.validation.validate_signature && !self.validation.algorithms.contains(&header.alg) { - return Err(new_error(ErrorKind::InvalidAlgorithm)); - } - - if self.validation.validate_signature - && self.verifying_provider.verify(message.as_bytes(), &b64_decode(signature)?).is_err() - { - return Err(new_error(ErrorKind::InvalidSignature)); - } - - Ok((header, payload)) - } - - /// Create new [`JwtDecoder`] with the `HS256` algorithm. - pub fn hs_256(secret: HmacSecret) -> Result { - let verifying_provider = Box::new(Hs256::new(secret)?); - - Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) + if validation.validate_signature && !validation.algorithms.contains(&header.alg) { + return Err(new_error(ErrorKind::InvalidAlgorithm)); } - /// Create new [`JwtDecoder`] with the `HS384` algorithm. - pub fn hs_384(secret: HmacSecret) -> Result { - let verifying_provider = Box::new(Hs384::new(secret)?); - - Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) + if validation.validate_signature + && verifying_provider.verify(message.as_bytes(), &b64_decode(signature)?).is_err() + { + return Err(new_error(ErrorKind::InvalidSignature)); } - /// Create new [`JwtDecoder`] with the `HS512` algorithm. - pub fn hs_512(secret: HmacSecret) -> Result { - let verifying_provider = Box::new(Hs512::new(secret)?); - - Ok(JwtDecoder::from_boxed_verifiyer(verifying_provider)) - } + Ok((header, payload)) } diff --git a/src/encoding.rs b/src/encoding.rs index 96dc6dce..e045d185 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -131,17 +131,38 @@ pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> R return Err(new_error(ErrorKind::InvalidAlgorithm)); } - let jwt_encoder = encoder_factory(&header.alg, key)?.with_header(header)?; + let signing_provider = jwt_signer_factory(&header.alg, key)?; - jwt_encoder.encode(claims) + _encode(header, claims, signing_provider) } -/// Return the correct [`JwtEncoder`] based on the `algorithm`. -fn encoder_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result { - let jwt_encoder = match algorithm { - Algorithm::HS256 => JwtEncoder::hs_256(key.into())?, - Algorithm::HS384 => JwtEncoder::hs_384(key.into())?, - Algorithm::HS512 => JwtEncoder::hs_512(key.into())?, +/// # Todo +/// +/// - Documentation +pub fn _encode( + header: &Header, + claims: &T, + signing_provider: Box, +) -> Result { + if signing_provider.algorithm() != header.alg { + return Err(new_error(ErrorKind::InvalidAlgorithm)); + } + + let encoded_header = b64_encode_part(&header)?; + let encoded_claims = b64_encode_part(claims)?; + let message = [encoded_header, encoded_claims].join("."); + + let signature = b64_encode(&signing_provider.sign(message.as_bytes())); + + Ok([message, signature].join(".")) +} + +/// Return the correct [`JwtSigner`] based on the `algorithm`. +fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result> { + let jwt_signer = match algorithm { + Algorithm::HS256 => Box::new(Hs256::new(key.into())?) as Box, + Algorithm::HS384 => Box::new(Hs384::new(key.into())?) as Box, + Algorithm::HS512 => Box::new(Hs512::new(key.into())?) as Box, Algorithm::ES256 => todo!(), Algorithm::ES384 => todo!(), Algorithm::RS256 => todo!(), @@ -153,7 +174,7 @@ fn encoder_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result todo!(), }; - Ok(jwt_encoder) + Ok(jwt_signer) } /// Convert an [`&EncodingKey`] to an [`HmacSecret`]. @@ -162,124 +183,3 @@ impl From<&EncodingKey> for HmacSecret { HmacSecret::from_secret(&key.content) } } - -/// A builder style JWT encoder. -/// -/// # Examples -/// -/// ``` -/// use serde::{Deserialize, Serialize}; -/// use jsonwebtoken::{JwtEncoder, HmacSecret}; -/// -/// #[derive(Debug, Serialize, Deserialize)] -/// struct Claims { -/// sub: String, -/// company: String -/// } -/// -/// let my_claims = Claims { -/// sub: "b@b.com".to_owned(), -/// company: "ACME".to_owned() -/// }; -/// -/// let hmac_secret = HmacSecret::from_secret(b"secret"); -/// -/// let token = JwtEncoder::hs_256(hmac_secret) -/// .unwrap() -/// .encode(&my_claims) -/// .unwrap(); -/// ``` -pub struct JwtEncoder { - signing_provider: Box, - header: Header, -} - -impl JwtEncoder { - /// Create a new [`JwtEncoder`] with any `signing_provider` that implements the [`JwtSigner`] trait. - pub fn from_signer(signing_provider: S) -> Self { - Self::from_boxed_signer(Box::new(signing_provider)) - } - - /// Create a new [`JwtEncoder`] with any `signing_provider` that implements the [`JwtSigner`] trait. - pub fn from_boxed_signer(signing_provider: Box) -> Self { - // Determine a default header - let mut header = Header::new(signing_provider.algorithm()); - header.typ = Some("JWT".to_owned()); - - Self { signing_provider, header } - } - - /// Provide a custom header. - /// - /// This would be used in the rare cases that fields other than `algorithm` and `type` need to be populated. - /// - /// # Examples - /// - /// ``` - /// use serde::{Deserialize, Serialize}; - /// use jsonwebtoken::{JwtEncoder, HmacSecret, Header, Algorithm}; - /// - /// #[derive(Debug, Serialize, Deserialize)] - /// struct Claims { - /// sub: String, - /// company: String - /// } - /// - /// let my_claims = Claims { - /// sub: "b@b.com".to_owned(), - /// company: "ACME".to_owned() - /// }; - /// - /// let hmac_secret = HmacSecret::from_secret(b"secret"); - /// let mut header = Header::new(Algorithm::HS256); - /// header.cty = Some("content-type".to_owned()); - /// - /// let token = JwtEncoder::hs_256(hmac_secret) - /// .unwrap() - /// .with_header(&header) - /// .unwrap() - /// .encode(&my_claims) - /// .unwrap(); - /// ``` - pub fn with_header(mut self, header: &Header) -> Result { - // Check that the header makes use of the correct algorithm - if header.alg != self.signing_provider.algorithm() { - return Err(new_error(crate::errors::ErrorKind::InvalidAlgorithm)); - } - - self.header = header.clone(); - Ok(self) - } - - /// Encode and sign the `claims` as a JWT using the `signing_provider` of the [`JwtEncoder`]. - pub fn encode(&self, claims: &T) -> Result { - let encoded_header = b64_encode_part(&self.header)?; - let encoded_claims = b64_encode_part(claims)?; - let message = [encoded_header, encoded_claims].join("."); - - let signature = b64_encode(&self.signing_provider.sign(message.as_bytes())); - - Ok([message, signature].join(".")) - } - - /// Create new [`JwtEncoder`] with the `HS256` algorithm. - pub fn hs_256(secret: HmacSecret) -> Result { - let signing_provider = Box::new(Hs256::new(secret)?); - - Ok(JwtEncoder::from_boxed_signer(signing_provider)) - } - - /// Create new [`JwtEncoder`] with the `HS384` algorithm. - pub fn hs_384(secret: HmacSecret) -> Result { - let signing_provider = Box::new(Hs384::new(secret)?); - - Ok(JwtEncoder::from_boxed_signer(signing_provider)) - } - - /// Create new [`JwtEncoder`] with the `HS512` algorithm. - pub fn hs_512(secret: HmacSecret) -> Result { - let signing_provider = Box::new(Hs512::new(secret)?); - - Ok(JwtEncoder::from_boxed_signer(signing_provider)) - } -} diff --git a/src/lib.rs b/src/lib.rs index 4d18c704..3de6f9a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,8 @@ compile_error!( pub use algorithms::Algorithm; pub use crypto::hmac::HmacSecret; -pub use decoding::{decode, decode_header, DecodingKey, JwtDecoder, TokenData}; -pub use encoding::{encode, EncodingKey, JwtEncoder}; +pub use decoding::{decode, decode_header, DecodingKey, TokenData, _decode}; +pub use encoding::{encode, EncodingKey, _encode}; pub use header::Header; pub use validation::{get_current_timestamp, Validation}; From 4225e1f1be29ca4867befb69ebde19be6cdf26b0 Mon Sep 17 00:00:00 2001 From: sidrubs <52378223+sidrubs@users.noreply.github.com> Date: Sat, 12 Oct 2024 21:22:06 -0300 Subject: [PATCH 21/35] feat: Use original encoding and decoding key structs --- src/crypto/aws_lc/mod.rs | 1 + src/crypto/hmac.rs | 31 ----------- src/crypto/mod.rs | 1 - src/crypto/rust_crypto/hmac.rs | 97 ++++++++++++++++++++++++---------- src/decoding.rs | 39 +++++++------- src/encoding.rs | 22 ++++---- src/lib.rs | 1 - 7 files changed, 102 insertions(+), 90 deletions(-) delete mode 100644 src/crypto/hmac.rs diff --git a/src/crypto/aws_lc/mod.rs b/src/crypto/aws_lc/mod.rs index 3a812e97..dccbe7b3 100644 --- a/src/crypto/aws_lc/mod.rs +++ b/src/crypto/aws_lc/mod.rs @@ -1 +1,2 @@ pub(crate) mod hmac; +// pub(crate) mod rsa; diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs deleted file mode 100644 index 8467f2dd..00000000 --- a/src/crypto/hmac.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! Common HMAC related functionality. - -use std::ops::Deref; - -use base64::{engine::general_purpose::STANDARD, Engine}; - -use crate::errors::Result; - -/// The shared secret used for the HMAC family of algorithms. -#[derive(Debug)] -pub struct HmacSecret(Vec); - -impl HmacSecret { - /// If you're using an HMAC secret that is not base64, use that. - pub fn from_secret(secret: &[u8]) -> Self { - Self(secret.to_vec()) - } - - /// If you have a base64 HMAC secret, use that. - pub fn from_base64_secret(secret: &str) -> Result { - Ok(Self(STANDARD.decode(secret)?)) - } -} - -impl Deref for HmacSecret { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 15d9f479..0ee0fcb9 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -6,7 +6,6 @@ use crate::algorithms::Algorithm; #[cfg(feature = "aws_lc_rs")] pub(crate) mod aws_lc; -pub(crate) mod hmac; #[cfg(feature = "rust_crypto")] pub(crate) mod rust_crypto; diff --git a/src/crypto/rust_crypto/hmac.rs b/src/crypto/rust_crypto/hmac.rs index a29f5c68..5de983c6 100644 --- a/src/crypto/rust_crypto/hmac.rs +++ b/src/crypto/rust_crypto/hmac.rs @@ -6,25 +6,28 @@ use sha2::{Sha256, Sha384, Sha512}; use signature::{Signer, Verifier}; use crate::crypto::{JwtSigner, JwtVerifier}; +use crate::decoding::try_get_hmac_secret_from_decoding_key; +use crate::encoding::try_get_hmac_secret_from_encoding_key; use crate::errors::Result; -use crate::{Algorithm, HmacSecret}; +use crate::{Algorithm, DecodingKey, EncodingKey}; type HmacSha256 = Hmac; type HmacSha384 = Hmac; type HmacSha512 = Hmac; -pub struct Hs256(HmacSha256); +pub struct Hs256Signer(HmacSha256); -impl Hs256 { - pub(crate) fn new(secret: HmacSecret) -> Result { - let inner = HmacSha256::new_from_slice(&secret) - .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; +impl Hs256Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + let inner = + HmacSha256::new_from_slice(try_get_hmac_secret_from_encoding_key(encoding_key)?) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; Ok(Self(inner)) } } -impl Signer> for Hs256 { +impl Signer> for Hs256Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { let mut signer = self.0.clone(); signer.reset(); @@ -34,13 +37,25 @@ impl Signer> for Hs256 { } } -impl JwtSigner for Hs256 { +impl JwtSigner for Hs256Signer { fn algorithm(&self) -> Algorithm { Algorithm::HS256 } } -impl Verifier> for Hs256 { +pub struct Hs256Verifier(HmacSha256); + +impl Hs256Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + let inner = + HmacSha256::new_from_slice(&try_get_hmac_secret_from_decoding_key(decoding_key)?) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + + Ok(Self(inner)) + } +} + +impl Verifier> for Hs256Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { let mut verifier = self.0.clone(); verifier.reset(); @@ -50,24 +65,25 @@ impl Verifier> for Hs256 { } } -impl JwtVerifier for Hs256 { +impl JwtVerifier for Hs256Verifier { fn algorithm(&self) -> Algorithm { Algorithm::HS256 } } -pub(crate) struct Hs384(HmacSha384); +pub struct Hs384Signer(HmacSha384); -impl Hs384 { - pub(crate) fn new(secret: HmacSecret) -> Result { - let inner = HmacSha384::new_from_slice(&secret) - .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; +impl Hs384Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + let inner = + HmacSha384::new_from_slice(try_get_hmac_secret_from_encoding_key(encoding_key)?) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; Ok(Self(inner)) } } -impl Signer> for Hs384 { +impl Signer> for Hs384Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { let mut signer = self.0.clone(); signer.reset(); @@ -77,13 +93,25 @@ impl Signer> for Hs384 { } } -impl JwtSigner for Hs384 { +impl JwtSigner for Hs384Signer { fn algorithm(&self) -> Algorithm { Algorithm::HS384 } } -impl Verifier> for Hs384 { +pub struct Hs384Verifier(HmacSha384); + +impl Hs384Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + let inner = + HmacSha384::new_from_slice(&try_get_hmac_secret_from_decoding_key(decoding_key)?) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + + Ok(Self(inner)) + } +} + +impl Verifier> for Hs384Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { let mut verifier = self.0.clone(); verifier.reset(); @@ -93,24 +121,25 @@ impl Verifier> for Hs384 { } } -impl JwtVerifier for Hs384 { +impl JwtVerifier for Hs384Verifier { fn algorithm(&self) -> Algorithm { Algorithm::HS384 } } -pub struct Hs512(HmacSha512); +pub struct Hs512Signer(HmacSha512); -impl Hs512 { - pub(crate) fn new(secret: HmacSecret) -> Result { - let inner = HmacSha512::new_from_slice(&secret) - .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; +impl Hs512Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + let inner = + HmacSha512::new_from_slice(try_get_hmac_secret_from_encoding_key(encoding_key)?) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; Ok(Self(inner)) } } -impl Signer> for Hs512 { +impl Signer> for Hs512Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { let mut signer = self.0.clone(); signer.reset(); @@ -120,13 +149,25 @@ impl Signer> for Hs512 { } } -impl JwtSigner for Hs512 { +impl JwtSigner for Hs512Signer { fn algorithm(&self) -> Algorithm { Algorithm::HS512 } } -impl Verifier> for Hs512 { +pub struct Hs512Verifier(HmacSha512); + +impl Hs512Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + let inner = + HmacSha512::new_from_slice(&try_get_hmac_secret_from_decoding_key(decoding_key)?) + .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; + + Ok(Self(inner)) + } +} + +impl Verifier> for Hs512Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { let mut verifier = self.0.clone(); verifier.reset(); @@ -136,7 +177,7 @@ impl Verifier> for Hs512 { } } -impl JwtVerifier for Hs512 { +impl JwtVerifier for Hs512Verifier { fn algorithm(&self) -> Algorithm { Algorithm::HS512 } diff --git a/src/decoding.rs b/src/decoding.rs index debec3b8..ba2780db 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -2,7 +2,6 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::de::DeserializeOwned; use crate::algorithms::AlgorithmFamily; -use crate::crypto::hmac::HmacSecret; use crate::crypto::JwtVerifier; use crate::errors::{new_error, Error, ErrorKind, Result}; use crate::header::Header; @@ -17,7 +16,7 @@ use crate::Algorithm; #[cfg(feature = "aws_lc_rs")] use crate::crypto::aws_lc::hmac::{Hs256, Hs384, Hs512}; #[cfg(feature = "rust_crypto")] -use crate::crypto::rust_crypto::hmac::{Hs256, Hs384, Hs512}; +use crate::crypto::rust_crypto::hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}; /// The return type of a successful call to [decode](fn.decode.html). #[derive(Debug)] @@ -257,9 +256,9 @@ pub fn _decode( /// Return the correct [`JwtVerifier`] based on the `algorithm`. fn jwt_verifier_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result> { let jwt_encoder = match algorithm { - Algorithm::HS256 => Box::new(Hs256::new(key.try_into()?)?) as Box, - Algorithm::HS384 => Box::new(Hs384::new(key.try_into()?)?) as Box, - Algorithm::HS512 => Box::new(Hs512::new(key.try_into()?)?) as Box, + Algorithm::HS256 => Box::new(Hs256Verifier::new(key)?) as Box, + Algorithm::HS384 => Box::new(Hs384Verifier::new(key)?) as Box, + Algorithm::HS512 => Box::new(Hs512Verifier::new(key)?) as Box, Algorithm::ES256 => todo!(), Algorithm::ES384 => todo!(), Algorithm::RS256 => todo!(), @@ -274,20 +273,6 @@ fn jwt_verifier_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result for &DecodingKey { - type Error = Error; - - fn try_into(self) -> std::result::Result { - match self.kind.clone() { - DecodingKeyKind::SecretOrDer(vec) => Ok(HmacSecret::from_secret(&vec)), - DecodingKeyKind::RsaModulusExponent { .. } => { - Err(new_error(crate::errors::ErrorKind::InvalidKeyFormat)) - } - } - } -} - /// Decode a JWT without any signature verification/validations and return its [Header](struct.Header.html). /// /// If the token has an invalid format (ie 3 parts separated by a `.`), it will return an error. @@ -341,3 +326,19 @@ fn verify_signature<'a>( Ok((header, payload)) } + +/// # Todo +/// +/// - Try return a reference. +pub(crate) fn try_get_hmac_secret_from_decoding_key(decoding_key: &DecodingKey) -> Result> { + if decoding_key.family != AlgorithmFamily::Hmac { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + match decoding_key.kind.clone() { + DecodingKeyKind::SecretOrDer(vec) => Ok(vec), + DecodingKeyKind::RsaModulusExponent { .. } => { + Err(new_error(crate::errors::ErrorKind::InvalidKeyFormat)) + } + } +} diff --git a/src/encoding.rs b/src/encoding.rs index e045d185..3de6c11c 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,20 +2,19 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::ser::Serialize; use crate::algorithms::AlgorithmFamily; -use crate::crypto::hmac::HmacSecret; use crate::crypto::JwtSigner; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_encode, b64_encode_part}; -use crate::Algorithm; +use crate::{Algorithm, DecodingKey}; // Crypto #[cfg(feature = "aws_lc_rs")] use crate::crypto::aws_lc::hmac::{Hs256, Hs384, Hs512}; #[cfg(feature = "rust_crypto")] -use crate::crypto::rust_crypto::hmac::{Hs256, Hs384, Hs512}; +use crate::crypto::rust_crypto::hmac::{Hs256Signer, Hs384Signer, Hs512Signer}; /// A key to encode a JWT with. Can be a secret, a PEM-encoded key or a DER-encoded key. /// This key can be re-used so make sure you only initialize it once if you can for better performance. @@ -160,9 +159,9 @@ pub fn _encode( /// Return the correct [`JwtSigner`] based on the `algorithm`. fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result> { let jwt_signer = match algorithm { - Algorithm::HS256 => Box::new(Hs256::new(key.into())?) as Box, - Algorithm::HS384 => Box::new(Hs384::new(key.into())?) as Box, - Algorithm::HS512 => Box::new(Hs512::new(key.into())?) as Box, + Algorithm::HS256 => Box::new(Hs256Signer::new(key)?) as Box, + Algorithm::HS384 => Box::new(Hs384Signer::new(key)?) as Box, + Algorithm::HS512 => Box::new(Hs512Signer::new(key)?) as Box, Algorithm::ES256 => todo!(), Algorithm::ES384 => todo!(), Algorithm::RS256 => todo!(), @@ -177,9 +176,12 @@ fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result for HmacSecret { - fn from(key: &EncodingKey) -> Self { - HmacSecret::from_secret(&key.content) +pub(crate) fn try_get_hmac_secret_from_encoding_key( + encoding_key: &EncodingKey, +) -> Result<&Vec> { + if encoding_key.family == AlgorithmFamily::Hmac { + Ok(&encoding_key.content) + } else { + Err(new_error(ErrorKind::InvalidKeyFormat)) } } diff --git a/src/lib.rs b/src/lib.rs index 3de6f9a3..0fc4404e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,6 @@ compile_error!( ); pub use algorithms::Algorithm; -pub use crypto::hmac::HmacSecret; pub use decoding::{decode, decode_header, DecodingKey, TokenData, _decode}; pub use encoding::{encode, EncodingKey, _encode}; pub use header::Header; From 78e84c1f57a25ed1000d455cf22da955a01e05d8 Mon Sep 17 00:00:00 2001 From: sidrubs <52378223+sidrubs@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:15:56 -0300 Subject: [PATCH 22/35] feat(crypto): Add RSA family --- Cargo.toml | 2 +- src/crypto/aws_lc/hmac.rs | 95 +++++++++++----- src/crypto/aws_lc/mod.rs | 2 +- src/crypto/aws_lc/rsa.rs | 200 +++++++++++++++++++++++++++++++++ src/crypto/mod.rs | 1 + src/crypto/rust_crypto/hmac.rs | 11 +- src/crypto/utils.rs | 26 +++++ src/decoding.rs | 32 ++---- src/encoding.rs | 23 ++-- tests/lib.rs | 2 +- tests/rsa/mod.rs | 168 +++++++++++++-------------- 11 files changed, 412 insertions(+), 150 deletions(-) create mode 100644 src/crypto/aws_lc/rsa.rs create mode 100644 src/crypto/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 7d228aec..ab274b0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ time = { version = "0.3", features = ["wasm-bindgen"] } criterion = { version = "0.4", default-features = false } [features] -default = ["use_pem", "rust_crypto"] +default = ["use_pem", "aws_lc_rs"] use_pem = ["pem", "simple_asn1", 'p256/pem', 'p384/pem'] rust_crypto = ["hmac"] aws_lc_rs = ["aws-lc-rs"] diff --git a/src/crypto/aws_lc/hmac.rs b/src/crypto/aws_lc/hmac.rs index 00b3570b..73413e2d 100644 --- a/src/crypto/aws_lc/hmac.rs +++ b/src/crypto/aws_lc/hmac.rs @@ -4,101 +4,146 @@ use aws_lc_rs::hmac; use signature::{Signer, Verifier}; +use crate::crypto::utils::{ + try_get_hmac_secret_from_decoding_key, try_get_hmac_secret_from_encoding_key, +}; use crate::crypto::{JwtSigner, JwtVerifier}; use crate::errors::Result; -use crate::{Algorithm, HmacSecret}; +use crate::{Algorithm, DecodingKey, EncodingKey}; -pub struct Hs256(hmac::Key); +pub struct Hs256Signer(hmac::Key); -impl Hs256 { - pub(crate) fn new(secret: HmacSecret) -> Result { - Ok(Self(hmac::Key::new(hmac::HMAC_SHA256, &secret))) +impl Hs256Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + Ok(Self(hmac::Key::new( + hmac::HMAC_SHA256, + try_get_hmac_secret_from_encoding_key(encoding_key)?, + ))) } } -impl Signer> for Hs256 { +impl Signer> for Hs256Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { Ok(hmac::sign(&self.0, msg).as_ref().to_vec()) } } -impl JwtSigner for Hs256 { +impl JwtSigner for Hs256Signer { fn algorithm(&self) -> Algorithm { Algorithm::HS256 } } -impl Verifier> for Hs256 { +pub struct Hs256Verifier(hmac::Key); + +impl Hs256Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + Ok(Self(hmac::Key::new( + hmac::HMAC_SHA256, + try_get_hmac_secret_from_decoding_key(decoding_key)?, + ))) + } +} + +impl Verifier> for Hs256Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { hmac::verify(&self.0, msg, &signature).map_err(|err| signature::Error::from_source(err)) } } -impl JwtVerifier for Hs256 { +impl JwtVerifier for Hs256Verifier { fn algorithm(&self) -> Algorithm { Algorithm::HS256 } } -pub struct Hs384(hmac::Key); +pub struct Hs384Signer(hmac::Key); -impl Hs384 { - pub(crate) fn new(secret: HmacSecret) -> Result { - Ok(Self(hmac::Key::new(hmac::HMAC_SHA384, &secret))) +impl Hs384Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + Ok(Self(hmac::Key::new( + hmac::HMAC_SHA384, + try_get_hmac_secret_from_encoding_key(encoding_key)?, + ))) } } -impl Signer> for Hs384 { +impl Signer> for Hs384Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { Ok(hmac::sign(&self.0, msg).as_ref().to_vec()) } } -impl JwtSigner for Hs384 { +impl JwtSigner for Hs384Signer { fn algorithm(&self) -> Algorithm { Algorithm::HS384 } } -impl Verifier> for Hs384 { +pub struct Hs384Verifier(hmac::Key); + +impl Hs384Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + Ok(Self(hmac::Key::new( + hmac::HMAC_SHA384, + try_get_hmac_secret_from_decoding_key(decoding_key)?, + ))) + } +} + +impl Verifier> for Hs384Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { hmac::verify(&self.0, msg, &signature).map_err(|err| signature::Error::from_source(err)) } } -impl JwtVerifier for Hs384 { +impl JwtVerifier for Hs384Verifier { fn algorithm(&self) -> Algorithm { Algorithm::HS384 } } -pub struct Hs512(hmac::Key); +pub struct Hs512Signer(hmac::Key); -impl Hs512 { - pub(crate) fn new(secret: HmacSecret) -> Result { - Ok(Self(hmac::Key::new(hmac::HMAC_SHA512, &secret))) +impl Hs512Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + Ok(Self(hmac::Key::new( + hmac::HMAC_SHA512, + try_get_hmac_secret_from_encoding_key(encoding_key)?, + ))) } } -impl Signer> for Hs512 { +impl Signer> for Hs512Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { Ok(hmac::sign(&self.0, msg).as_ref().to_vec()) } } -impl JwtSigner for Hs512 { +impl JwtSigner for Hs512Signer { fn algorithm(&self) -> Algorithm { Algorithm::HS512 } } -impl Verifier> for Hs512 { +pub struct Hs512Verifier(hmac::Key); + +impl Hs512Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + Ok(Self(hmac::Key::new( + hmac::HMAC_SHA512, + try_get_hmac_secret_from_decoding_key(decoding_key)?, + ))) + } +} + +impl Verifier> for Hs512Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { hmac::verify(&self.0, msg, &signature).map_err(|err| signature::Error::from_source(err)) } } -impl JwtVerifier for Hs512 { +impl JwtVerifier for Hs512Verifier { fn algorithm(&self) -> Algorithm { Algorithm::HS512 } diff --git a/src/crypto/aws_lc/mod.rs b/src/crypto/aws_lc/mod.rs index dccbe7b3..5461dfa2 100644 --- a/src/crypto/aws_lc/mod.rs +++ b/src/crypto/aws_lc/mod.rs @@ -1,2 +1,2 @@ pub(crate) mod hmac; -// pub(crate) mod rsa; +pub(crate) mod rsa; diff --git a/src/crypto/aws_lc/rsa.rs b/src/crypto/aws_lc/rsa.rs new file mode 100644 index 00000000..2d9ece56 --- /dev/null +++ b/src/crypto/aws_lc/rsa.rs @@ -0,0 +1,200 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the +//! RSA family of algorithms using [`aws_lc_rs`] + +use aws_lc_rs::{rand, signature as crypto_sig}; +use signature::{Signer, Verifier}; + +use crate::algorithms::AlgorithmFamily; +use crate::crypto::{JwtSigner, JwtVerifier}; +use crate::decoding::DecodingKeyKind; +use crate::errors::{new_error, ErrorKind, Result}; +use crate::{Algorithm, DecodingKey, EncodingKey}; + +/// Try to sign the `message` using an `RSA` `algorithm`. +fn try_sign_rsa( + algorithm: &'static dyn crypto_sig::RsaEncoding, + encoding_key: &EncodingKey, + msg: &[u8], +) -> std::result::Result, signature::Error> { + let key_pair = crypto_sig::RsaKeyPair::from_der(encoding_key.inner()) + .map_err(|err| signature::Error::from_source(err))?; + + let mut signature = vec![0; key_pair.public_modulus_len()]; + let rng = rand::SystemRandom::new(); + key_pair + .sign(algorithm, &rng, msg, &mut signature) + .map_err(|err| signature::Error::from_source(err))?; + + Ok(signature) +} + +/// Return a `aws_lc_rs` RSA public key from a [`DecodingKey`] +/// +/// # Errors +/// +/// - If `decoding_key` is not from the RSA family. +fn verify_rsa( + algorithm: &'static crypto_sig::RsaParameters, + decoding_key: &DecodingKey, + msg: &[u8], + signature: &[u8], +) -> std::result::Result<(), signature::Error> { + match &decoding_key.kind { + DecodingKeyKind::SecretOrDer(bytes) => { + let public_key = crypto_sig::UnparsedPublicKey::new(algorithm, bytes); + public_key.verify(msg, signature).map_err(|err| signature::Error::from_source(err))?; + } + DecodingKeyKind::RsaModulusExponent { n, e } => { + let public_key = crypto_sig::RsaPublicKeyComponents { n, e }; + public_key + .verify(algorithm, msg, &signature) + .map_err(|err| signature::Error::from_source(err))?; + } + }; + + Ok(()) +} + +pub struct Rsa256Signer(EncodingKey); + +impl Rsa256Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for Rsa256Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa(&crypto_sig::RSA_PKCS1_SHA256, &self.0, msg) + } +} + +impl JwtSigner for Rsa256Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::RS256 + } +} + +pub struct Rsa256Verifier(DecodingKey); + +impl Rsa256Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for Rsa256Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(&crypto_sig::RSA_PKCS1_2048_8192_SHA256, &self.0, msg, signature) + } +} + +impl JwtVerifier for Rsa256Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::RS256 + } +} + +pub struct Rsa384Signer(EncodingKey); + +impl Rsa384Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for Rsa384Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa(&crypto_sig::RSA_PKCS1_SHA384, &self.0, msg) + } +} + +impl JwtSigner for Rsa384Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::RS384 + } +} + +pub struct Rsa384Verifier(DecodingKey); + +impl Rsa384Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for Rsa384Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(&crypto_sig::RSA_PKCS1_2048_8192_SHA384, &self.0, msg, signature) + } +} + +impl JwtVerifier for Rsa384Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::RS384 + } +} + +pub struct Rsa512Signer(EncodingKey); + +impl Rsa512Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for Rsa512Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa(&crypto_sig::RSA_PKCS1_SHA512, &self.0, msg) + } +} + +impl JwtSigner for Rsa512Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::RS512 + } +} + +pub struct Rsa512Verifier(DecodingKey); + +impl Rsa512Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for Rsa512Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(&crypto_sig::RSA_PKCS1_2048_8192_SHA512, &self.0, msg, signature) + } +} + +impl JwtVerifier for Rsa512Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::RS512 + } +} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 0ee0fcb9..5acca716 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -8,6 +8,7 @@ use crate::algorithms::Algorithm; pub(crate) mod aws_lc; #[cfg(feature = "rust_crypto")] pub(crate) mod rust_crypto; +pub(crate) mod utils; use signature::{Signer, Verifier}; diff --git a/src/crypto/rust_crypto/hmac.rs b/src/crypto/rust_crypto/hmac.rs index 5de983c6..374bfa68 100644 --- a/src/crypto/rust_crypto/hmac.rs +++ b/src/crypto/rust_crypto/hmac.rs @@ -5,9 +5,10 @@ use hmac::{Hmac, Mac}; use sha2::{Sha256, Sha384, Sha512}; use signature::{Signer, Verifier}; +use crate::crypto::utils::{ + try_get_hmac_secret_from_decoding_key, try_get_hmac_secret_from_encoding_key, +}; use crate::crypto::{JwtSigner, JwtVerifier}; -use crate::decoding::try_get_hmac_secret_from_decoding_key; -use crate::encoding::try_get_hmac_secret_from_encoding_key; use crate::errors::Result; use crate::{Algorithm, DecodingKey, EncodingKey}; @@ -48,7 +49,7 @@ pub struct Hs256Verifier(HmacSha256); impl Hs256Verifier { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { let inner = - HmacSha256::new_from_slice(&try_get_hmac_secret_from_decoding_key(decoding_key)?) + HmacSha256::new_from_slice(try_get_hmac_secret_from_decoding_key(decoding_key)?) .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; Ok(Self(inner)) @@ -104,7 +105,7 @@ pub struct Hs384Verifier(HmacSha384); impl Hs384Verifier { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { let inner = - HmacSha384::new_from_slice(&try_get_hmac_secret_from_decoding_key(decoding_key)?) + HmacSha384::new_from_slice(try_get_hmac_secret_from_decoding_key(decoding_key)?) .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; Ok(Self(inner)) @@ -160,7 +161,7 @@ pub struct Hs512Verifier(HmacSha512); impl Hs512Verifier { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { let inner = - HmacSha512::new_from_slice(&try_get_hmac_secret_from_decoding_key(decoding_key)?) + HmacSha512::new_from_slice(try_get_hmac_secret_from_decoding_key(decoding_key)?) .map_err(|_e| crate::errors::ErrorKind::InvalidKeyFormat)?; Ok(Self(inner)) diff --git a/src/crypto/utils.rs b/src/crypto/utils.rs new file mode 100644 index 00000000..2044a7a4 --- /dev/null +++ b/src/crypto/utils.rs @@ -0,0 +1,26 @@ +//! # Todo +//! +//! - Put in documentation + +use crate::{ + algorithms::AlgorithmFamily, + decoding::DecodingKeyKind, + errors::{self, new_error, ErrorKind, Result}, + DecodingKey, EncodingKey, +}; + +pub(crate) fn try_get_hmac_secret_from_encoding_key(encoding_key: &EncodingKey) -> Result<&[u8]> { + if encoding_key.family == AlgorithmFamily::Hmac { + Ok(encoding_key.inner()) + } else { + Err(new_error(ErrorKind::InvalidKeyFormat)) + } +} + +pub(crate) fn try_get_hmac_secret_from_decoding_key(decoding_key: &DecodingKey) -> Result<&[u8]> { + if decoding_key.family != AlgorithmFamily::Hmac { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(decoding_key.as_bytes()) +} diff --git a/src/decoding.rs b/src/decoding.rs index ba2780db..ac0e6314 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -2,6 +2,7 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::de::DeserializeOwned; use crate::algorithms::AlgorithmFamily; +use crate::crypto::aws_lc::rsa::{Rsa256Verifier, Rsa384Verifier, Rsa512Verifier}; use crate::crypto::JwtVerifier; use crate::errors::{new_error, Error, ErrorKind, Result}; use crate::header::Header; @@ -14,7 +15,7 @@ use crate::Algorithm; // Crypto #[cfg(feature = "aws_lc_rs")] -use crate::crypto::aws_lc::hmac::{Hs256, Hs384, Hs512}; +use crate::crypto::aws_lc::hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}; #[cfg(feature = "rust_crypto")] use crate::crypto::rust_crypto::hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}; @@ -199,6 +200,13 @@ impl DecodingKey { } } } + + pub(crate) fn as_bytes(&self) -> &[u8] { + match &self.kind { + DecodingKeyKind::SecretOrDer(b) => b, + DecodingKeyKind::RsaModulusExponent { .. } => unreachable!(), + } + } } /// Decode and validate a JWT @@ -261,9 +269,9 @@ fn jwt_verifier_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result Box::new(Hs512Verifier::new(key)?) as Box, Algorithm::ES256 => todo!(), Algorithm::ES384 => todo!(), - Algorithm::RS256 => todo!(), - Algorithm::RS384 => todo!(), - Algorithm::RS512 => todo!(), + Algorithm::RS256 => Box::new(Rsa256Verifier::new(key)?) as Box, + Algorithm::RS384 => Box::new(Rsa384Verifier::new(key)?) as Box, + Algorithm::RS512 => Box::new(Rsa512Verifier::new(key)?) as Box, Algorithm::PS256 => todo!(), Algorithm::PS384 => todo!(), Algorithm::PS512 => todo!(), @@ -326,19 +334,3 @@ fn verify_signature<'a>( Ok((header, payload)) } - -/// # Todo -/// -/// - Try return a reference. -pub(crate) fn try_get_hmac_secret_from_decoding_key(decoding_key: &DecodingKey) -> Result> { - if decoding_key.family != AlgorithmFamily::Hmac { - return Err(new_error(ErrorKind::InvalidKeyFormat)); - } - - match decoding_key.kind.clone() { - DecodingKeyKind::SecretOrDer(vec) => Ok(vec), - DecodingKeyKind::RsaModulusExponent { .. } => { - Err(new_error(crate::errors::ErrorKind::InvalidKeyFormat)) - } - } -} diff --git a/src/encoding.rs b/src/encoding.rs index 3de6c11c..7b744a42 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,17 +2,20 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::ser::Serialize; use crate::algorithms::AlgorithmFamily; +use crate::crypto::aws_lc::rsa::Rsa512Signer; use crate::crypto::JwtSigner; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_encode, b64_encode_part}; -use crate::{Algorithm, DecodingKey}; +use crate::Algorithm; // Crypto #[cfg(feature = "aws_lc_rs")] -use crate::crypto::aws_lc::hmac::{Hs256, Hs384, Hs512}; +use crate::crypto::aws_lc::hmac::{Hs256Signer, Hs384Signer, Hs512Signer}; +#[cfg(feature = "aws_lc_rs")] +use crate::crypto::aws_lc::rsa::{Rsa256Signer, Rsa384Signer}; #[cfg(feature = "rust_crypto")] use crate::crypto::rust_crypto::hmac::{Hs256Signer, Hs384Signer, Hs512Signer}; @@ -164,9 +167,9 @@ fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result Box::new(Hs512Signer::new(key)?) as Box, Algorithm::ES256 => todo!(), Algorithm::ES384 => todo!(), - Algorithm::RS256 => todo!(), - Algorithm::RS384 => todo!(), - Algorithm::RS512 => todo!(), + Algorithm::RS256 => Box::new(Rsa256Signer::new(key)?) as Box, + Algorithm::RS384 => Box::new(Rsa384Signer::new(key)?) as Box, + Algorithm::RS512 => Box::new(Rsa512Signer::new(key)?) as Box, Algorithm::PS256 => todo!(), Algorithm::PS384 => todo!(), Algorithm::PS512 => todo!(), @@ -175,13 +178,3 @@ fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result Result<&Vec> { - if encoding_key.family == AlgorithmFamily::Hmac { - Ok(&encoding_key.content) - } else { - Err(new_error(ErrorKind::InvalidKeyFormat)) - } -} diff --git a/tests/lib.rs b/tests/lib.rs index c5374c72..cd8729e3 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,4 +1,4 @@ // mod ecdsa; // mod eddsa; // mod header; -// mod rsa; +mod rsa; diff --git a/tests/rsa/mod.rs b/tests/rsa/mod.rs index 9c679e88..74e03291 100644 --- a/tests/rsa/mod.rs +++ b/tests/rsa/mod.rs @@ -3,12 +3,14 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use wasm_bindgen_test::wasm_bindgen_test; -use jsonwebtoken::{ - crypto::{sign, verify}, - Algorithm, DecodingKey, EncodingKey, -}; #[cfg(feature = "use_pem")] use jsonwebtoken::{decode, encode, Header, Validation}; +use jsonwebtoken::{ + // crypto::{sign, verify}, + Algorithm, + DecodingKey, + EncodingKey, +}; const RSA_ALGORITHMS: &[Algorithm] = &[ Algorithm::RS256, @@ -26,84 +28,86 @@ pub struct Claims { exp: i64, } -#[cfg(feature = "use_pem")] -#[test] -#[wasm_bindgen_test] -fn round_trip_sign_verification_pem_pkcs1() { - let privkey_pem = include_bytes!("private_rsa_key_pkcs1.pem"); - let pubkey_pem = include_bytes!("public_rsa_key_pkcs1.pem"); - let certificate_pem = include_bytes!("certificate_rsa_key_pkcs1.crt"); - - for &alg in RSA_ALGORITHMS { - let encrypted = - sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); - - let is_valid = verify( - &encrypted, - b"hello world", - &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), - alg, - ) - .unwrap(); - assert!(is_valid); - - let cert_is_valid = verify( - &encrypted, - b"hello world", - &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), - alg, - ) - .unwrap(); - assert!(cert_is_valid); - } -} - -#[cfg(feature = "use_pem")] -#[test] -#[wasm_bindgen_test] -fn round_trip_sign_verification_pem_pkcs8() { - let privkey_pem = include_bytes!("private_rsa_key_pkcs8.pem"); - let pubkey_pem = include_bytes!("public_rsa_key_pkcs8.pem"); - let certificate_pem = include_bytes!("certificate_rsa_key_pkcs8.crt"); - - for &alg in RSA_ALGORITHMS { - let encrypted = - sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); - - let is_valid = verify( - &encrypted, - b"hello world", - &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), - alg, - ) - .unwrap(); - assert!(is_valid); - - let cert_is_valid = verify( - &encrypted, - b"hello world", - &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), - alg, - ) - .unwrap(); - assert!(cert_is_valid); - } -} - -#[test] -#[wasm_bindgen_test] -fn round_trip_sign_verification_der() { - let privkey_der = include_bytes!("private_rsa_key.der"); - let pubkey_der = include_bytes!("public_rsa_key.der"); - - for &alg in RSA_ALGORITHMS { - let encrypted = sign(b"hello world", &EncodingKey::from_rsa_der(privkey_der), alg).unwrap(); - let is_valid = - verify(&encrypted, b"hello world", &DecodingKey::from_rsa_der(pubkey_der), alg) - .unwrap(); - assert!(is_valid); - } -} +// Todo: These no longer apply because `verify` does not exist, would probably need to convert it to test the factory for getting signers and verifiers. But I would rather this not be part of the public facing API. + +// #[cfg(feature = "use_pem")] +// #[test] +// #[wasm_bindgen_test] +// fn round_trip_sign_verification_pem_pkcs1() { +// let privkey_pem = include_bytes!("private_rsa_key_pkcs1.pem"); +// let pubkey_pem = include_bytes!("public_rsa_key_pkcs1.pem"); +// let certificate_pem = include_bytes!("certificate_rsa_key_pkcs1.crt"); + +// for &alg in RSA_ALGORITHMS { +// let encrypted = +// sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); + +// let is_valid = verify( +// &encrypted, +// b"hello world", +// &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), +// alg, +// ) +// .unwrap(); +// assert!(is_valid); + +// let cert_is_valid = verify( +// &encrypted, +// b"hello world", +// &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), +// alg, +// ) +// .unwrap(); +// assert!(cert_is_valid); +// } +// } + +// #[cfg(feature = "use_pem")] +// #[test] +// #[wasm_bindgen_test] +// fn round_trip_sign_verification_pem_pkcs8() { +// let privkey_pem = include_bytes!("private_rsa_key_pkcs8.pem"); +// let pubkey_pem = include_bytes!("public_rsa_key_pkcs8.pem"); +// let certificate_pem = include_bytes!("certificate_rsa_key_pkcs8.crt"); + +// for &alg in RSA_ALGORITHMS { +// let encrypted = +// sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); + +// let is_valid = verify( +// &encrypted, +// b"hello world", +// &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), +// alg, +// ) +// .unwrap(); +// assert!(is_valid); + +// let cert_is_valid = verify( +// &encrypted, +// b"hello world", +// &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), +// alg, +// ) +// .unwrap(); +// assert!(cert_is_valid); +// } +// } + +// #[test] +// #[wasm_bindgen_test] +// fn round_trip_sign_verification_der() { +// let privkey_der = include_bytes!("private_rsa_key.der"); +// let pubkey_der = include_bytes!("public_rsa_key.der"); + +// for &alg in RSA_ALGORITHMS { +// let encrypted = sign(b"hello world", &EncodingKey::from_rsa_der(privkey_der), alg).unwrap(); +// let is_valid = +// verify(&encrypted, b"hello world", &DecodingKey::from_rsa_der(pubkey_der), alg) +// .unwrap(); +// assert!(is_valid); +// } +// } #[cfg(feature = "use_pem")] #[test] From 2a50e0dcf97030346e2b0a93c23006ad1d4a819e Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 14:08:19 +0900 Subject: [PATCH 23/35] Add ECDSA via AWS-LC This is following the existing scheme established in hmac & rsa. --- src/crypto/aws_lc/ecdsa.rs | 108 +++++++++++++++++++++++++++++++++++++ src/crypto/aws_lc/mod.rs | 1 + src/decoding.rs | 12 +++-- src/encoding.rs | 14 ++--- tests/ecdsa/mod.rs | 67 +++++++++++------------ tests/lib.rs | 2 +- 6 files changed, 156 insertions(+), 48 deletions(-) create mode 100644 src/crypto/aws_lc/ecdsa.rs diff --git a/src/crypto/aws_lc/ecdsa.rs b/src/crypto/aws_lc/ecdsa.rs new file mode 100644 index 00000000..c313a5a9 --- /dev/null +++ b/src/crypto/aws_lc/ecdsa.rs @@ -0,0 +1,108 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the +//! ECDSA family of algorithms using [`aws_lc_rs`] + +use crate::crypto::{JwtSigner, JwtVerifier}; +use crate::errors::Result; +use crate::{Algorithm, DecodingKey, EncodingKey}; +use aws_lc_rs::rand::SystemRandom; +use aws_lc_rs::signature::{ + EcdsaKeyPair, VerificationAlgorithm, ECDSA_P256_SHA256_FIXED, ECDSA_P256_SHA256_FIXED_SIGNING, + ECDSA_P384_SHA384_FIXED, ECDSA_P384_SHA384_FIXED_SIGNING, +}; +use signature::{Error, Signer, Verifier}; + +pub struct Es256Signer(EcdsaKeyPair); + +impl Es256Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + Ok(Self( + EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, encoding_key.inner()) + .map_err(|_| crate::errors::ErrorKind::InvalidEcdsaKey)?, + )) + } +} + +impl Signer> for Es256Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, Error> { + let rng = SystemRandom::new(); + let signature = self.0.sign(&rng, msg).map_err(Error::from_source)?; + Ok(signature.as_ref().to_vec()) + } +} + +impl JwtSigner for Es256Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::ES256 + } +} + +pub struct Es256Verifier(DecodingKey); + +impl Es256Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for Es256Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), Error> { + ECDSA_P256_SHA256_FIXED + .verify_sig(self.0.as_bytes(), msg, signature) + .map_err(Error::from_source)?; + Ok(()) + } +} + +impl JwtVerifier for Es256Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::ES256 + } +} + +pub struct Es384Signer(EcdsaKeyPair); + +impl Es384Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + Ok(Self( + EcdsaKeyPair::from_pkcs8(&ECDSA_P384_SHA384_FIXED_SIGNING, encoding_key.inner()) + .map_err(|_| crate::errors::ErrorKind::InvalidEcdsaKey)?, + )) + } +} + +impl Signer> for Es384Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, Error> { + let rng = SystemRandom::new(); + let signature = self.0.sign(&rng, msg).map_err(Error::from_source)?; + Ok(signature.as_ref().to_vec()) + } +} + +impl JwtSigner for Es384Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::ES384 + } +} + +pub struct Es384Verifier(DecodingKey); + +impl Es384Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for Es384Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), Error> { + ECDSA_P384_SHA384_FIXED + .verify_sig(self.0.as_bytes(), msg, signature).map_err(Error::from_source)?; + + Ok(()) + } +} + +impl JwtVerifier for Es384Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::ES384 + } +} diff --git a/src/crypto/aws_lc/mod.rs b/src/crypto/aws_lc/mod.rs index 5461dfa2..0a79d935 100644 --- a/src/crypto/aws_lc/mod.rs +++ b/src/crypto/aws_lc/mod.rs @@ -1,2 +1,3 @@ +pub(crate) mod ecdsa; pub(crate) mod hmac; pub(crate) mod rsa; diff --git a/src/decoding.rs b/src/decoding.rs index ac0e6314..6a9ef71a 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -2,7 +2,6 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::de::DeserializeOwned; use crate::algorithms::AlgorithmFamily; -use crate::crypto::aws_lc::rsa::{Rsa256Verifier, Rsa384Verifier, Rsa512Verifier}; use crate::crypto::JwtVerifier; use crate::errors::{new_error, Error, ErrorKind, Result}; use crate::header::Header; @@ -12,10 +11,13 @@ use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_decode, DecodedJwtPartClaims}; use crate::validation::{validate, Validation}; use crate::Algorithm; - // Crypto #[cfg(feature = "aws_lc_rs")] -use crate::crypto::aws_lc::hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}; +use crate::crypto::aws_lc::{ + ecdsa::{Es256Verifier, Es384Verifier}, + hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}, + rsa::{Rsa256Verifier, Rsa384Verifier, Rsa512Verifier}, +}; #[cfg(feature = "rust_crypto")] use crate::crypto::rust_crypto::hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}; @@ -267,8 +269,8 @@ fn jwt_verifier_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result Box::new(Hs256Verifier::new(key)?) as Box, Algorithm::HS384 => Box::new(Hs384Verifier::new(key)?) as Box, Algorithm::HS512 => Box::new(Hs512Verifier::new(key)?) as Box, - Algorithm::ES256 => todo!(), - Algorithm::ES384 => todo!(), + Algorithm::ES256 => Box::new(Es256Verifier::new(key)?) as Box, + Algorithm::ES384 => Box::new(Es384Verifier::new(key)?) as Box, Algorithm::RS256 => Box::new(Rsa256Verifier::new(key)?) as Box, Algorithm::RS384 => Box::new(Rsa384Verifier::new(key)?) as Box, Algorithm::RS512 => Box::new(Rsa512Verifier::new(key)?) as Box, diff --git a/src/encoding.rs b/src/encoding.rs index 7b744a42..28677d8a 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,7 +2,6 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::ser::Serialize; use crate::algorithms::AlgorithmFamily; -use crate::crypto::aws_lc::rsa::Rsa512Signer; use crate::crypto::JwtSigner; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; @@ -10,12 +9,13 @@ use crate::header::Header; use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_encode, b64_encode_part}; use crate::Algorithm; - // Crypto #[cfg(feature = "aws_lc_rs")] -use crate::crypto::aws_lc::hmac::{Hs256Signer, Hs384Signer, Hs512Signer}; -#[cfg(feature = "aws_lc_rs")] -use crate::crypto::aws_lc::rsa::{Rsa256Signer, Rsa384Signer}; +use crate::crypto::aws_lc::{ + ecdsa::{Es256Signer, Es384Signer}, + hmac::{Hs256Signer, Hs384Signer, Hs512Signer}, + rsa::{Rsa256Signer, Rsa384Signer, Rsa512Signer}, +}; #[cfg(feature = "rust_crypto")] use crate::crypto::rust_crypto::hmac::{Hs256Signer, Hs384Signer, Hs512Signer}; @@ -165,8 +165,8 @@ fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result Box::new(Hs256Signer::new(key)?) as Box, Algorithm::HS384 => Box::new(Hs384Signer::new(key)?) as Box, Algorithm::HS512 => Box::new(Hs512Signer::new(key)?) as Box, - Algorithm::ES256 => todo!(), - Algorithm::ES384 => todo!(), + Algorithm::ES256 => Box::new(Es256Signer::new(key)?) as Box, + Algorithm::ES384 => Box::new(Es384Signer::new(key)?) as Box, Algorithm::RS256 => Box::new(Rsa256Signer::new(key)?) as Box, Algorithm::RS384 => Box::new(Rsa384Signer::new(key)?) as Box, Algorithm::RS512 => Box::new(Rsa512Signer::new(key)?) as Box, diff --git a/tests/ecdsa/mod.rs b/tests/ecdsa/mod.rs index fe2fbb00..637433b0 100644 --- a/tests/ecdsa/mod.rs +++ b/tests/ecdsa/mod.rs @@ -3,12 +3,9 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use wasm_bindgen_test::wasm_bindgen_test; -use jsonwebtoken::{ - crypto::{sign, verify}, - Algorithm, DecodingKey, EncodingKey, -}; #[cfg(feature = "use_pem")] use jsonwebtoken::{decode, encode, Header, Validation}; +use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey}; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { @@ -17,38 +14,38 @@ pub struct Claims { exp: i64, } -#[test] -#[wasm_bindgen_test] -fn round_trip_sign_verification_pk8() { - let privkey = include_bytes!("private_ecdsa_key.pk8"); - let pubkey = include_bytes!("public_ecdsa_key.pk8"); - - let encrypted = - sign(b"hello world", &EncodingKey::from_ec_der(privkey), Algorithm::ES256).unwrap(); - let is_valid = - verify(&encrypted, b"hello world", &DecodingKey::from_ec_der(pubkey), Algorithm::ES256) - .unwrap(); - assert!(is_valid); -} +// #[test] +// #[wasm_bindgen_test] +// fn round_trip_sign_verification_pk8() { +// let privkey = include_bytes!("private_ecdsa_key.pk8"); +// let pubkey = include_bytes!("public_ecdsa_key.pk8"); +// +// let encrypted = +// sign(b"hello world", &EncodingKey::from_ec_der(privkey), Algorithm::ES256).unwrap(); +// let is_valid = +// verify(&encrypted, b"hello world", &DecodingKey::from_ec_der(pubkey), Algorithm::ES256) +// .unwrap(); +// assert!(is_valid); +// } -#[cfg(feature = "use_pem")] -#[test] -#[wasm_bindgen_test] -fn round_trip_sign_verification_pem() { - let privkey_pem = include_bytes!("private_ecdsa_key.pem"); - let pubkey_pem = include_bytes!("public_ecdsa_key.pem"); - let encrypted = - sign(b"hello world", &EncodingKey::from_ec_pem(privkey_pem).unwrap(), Algorithm::ES256) - .unwrap(); - let is_valid = verify( - &encrypted, - b"hello world", - &DecodingKey::from_ec_pem(pubkey_pem).unwrap(), - Algorithm::ES256, - ) - .unwrap(); - assert!(is_valid); -} +// #[cfg(feature = "use_pem")] +// #[test] +// #[wasm_bindgen_test] +// fn round_trip_sign_verification_pem() { +// let privkey_pem = include_bytes!("private_ecdsa_key.pem"); +// let pubkey_pem = include_bytes!("public_ecdsa_key.pem"); +// let encrypted = +// sign(b"hello world", &EncodingKey::from_ec_pem(privkey_pem).unwrap(), Algorithm::ES256) +// .unwrap(); +// let is_valid = verify( +// &encrypted, +// b"hello world", +// &DecodingKey::from_ec_pem(pubkey_pem).unwrap(), +// Algorithm::ES256, +// ) +// .unwrap(); +// assert!(is_valid); +// } #[cfg(feature = "use_pem")] #[test] diff --git a/tests/lib.rs b/tests/lib.rs index cd8729e3..0dc88ba5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,4 +1,4 @@ -// mod ecdsa; +mod ecdsa; // mod eddsa; // mod header; mod rsa; From 19ee8f37220cb84b66135e85242c3a5e3ce60dc2 Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 14:28:36 +0900 Subject: [PATCH 24/35] Implement EdDSA through AWS-LC --- src/crypto/aws_lc/eddsa.rs | 52 +++++++++++++++++++++++++++++ src/crypto/aws_lc/mod.rs | 1 + src/decoding.rs | 3 +- src/encoding.rs | 3 +- src/errors.rs | 4 +-- tests/eddsa/mod.rs | 67 ++++++++++++++++++-------------------- tests/lib.rs | 2 +- 7 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 src/crypto/aws_lc/eddsa.rs diff --git a/src/crypto/aws_lc/eddsa.rs b/src/crypto/aws_lc/eddsa.rs new file mode 100644 index 00000000..33faecc5 --- /dev/null +++ b/src/crypto/aws_lc/eddsa.rs @@ -0,0 +1,52 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the +//! EdDSA family of algorithms using [`aws_lc_rs`] + +use crate::crypto::{JwtSigner, JwtVerifier}; +use crate::errors::Result; +use crate::{Algorithm, DecodingKey, EncodingKey}; +use aws_lc_rs::signature::{Ed25519KeyPair, VerificationAlgorithm, ED25519}; +use signature::{Error, Signer, Verifier}; + +pub struct EdDSASigner(Ed25519KeyPair); + +impl EdDSASigner { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + Ok(Self( + Ed25519KeyPair::from_pkcs8(encoding_key.inner()) + .map_err(|_| crate::errors::ErrorKind::InvalidEddsaKey)?, + )) + } +} + +impl Signer> for EdDSASigner { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, Error> { + Ok(self.0.sign(msg).as_ref().to_vec()) + } +} + +impl JwtSigner for EdDSASigner { + fn algorithm(&self) -> Algorithm { + Algorithm::EdDSA + } +} + +pub struct EdDSAVerifier(DecodingKey); + +impl EdDSAVerifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for EdDSAVerifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), Error> { + ED25519.verify_sig(self.0.as_bytes(), msg, signature).map_err(Error::from_source)?; + Ok(()) + } +} + +impl JwtVerifier for EdDSAVerifier { + fn algorithm(&self) -> Algorithm { + Algorithm::EdDSA + } +} diff --git a/src/crypto/aws_lc/mod.rs b/src/crypto/aws_lc/mod.rs index 0a79d935..16f66b5f 100644 --- a/src/crypto/aws_lc/mod.rs +++ b/src/crypto/aws_lc/mod.rs @@ -1,3 +1,4 @@ pub(crate) mod ecdsa; +pub(crate) mod eddsa; pub(crate) mod hmac; pub(crate) mod rsa; diff --git a/src/decoding.rs b/src/decoding.rs index 6a9ef71a..af424781 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -15,6 +15,7 @@ use crate::Algorithm; #[cfg(feature = "aws_lc_rs")] use crate::crypto::aws_lc::{ ecdsa::{Es256Verifier, Es384Verifier}, + eddsa::EdDSAVerifier, hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}, rsa::{Rsa256Verifier, Rsa384Verifier, Rsa512Verifier}, }; @@ -277,7 +278,7 @@ fn jwt_verifier_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result todo!(), Algorithm::PS384 => todo!(), Algorithm::PS512 => todo!(), - Algorithm::EdDSA => todo!(), + Algorithm::EdDSA => Box::new(EdDSAVerifier::new(key)?) as Box, }; Ok(jwt_encoder) diff --git a/src/encoding.rs b/src/encoding.rs index 28677d8a..e26a1e02 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -13,6 +13,7 @@ use crate::Algorithm; #[cfg(feature = "aws_lc_rs")] use crate::crypto::aws_lc::{ ecdsa::{Es256Signer, Es384Signer}, + eddsa::EdDSASigner, hmac::{Hs256Signer, Hs384Signer, Hs512Signer}, rsa::{Rsa256Signer, Rsa384Signer, Rsa512Signer}, }; @@ -173,7 +174,7 @@ fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result todo!(), Algorithm::PS384 => todo!(), Algorithm::PS512 => todo!(), - Algorithm::EdDSA => todo!(), + Algorithm::EdDSA => Box::new(EdDSASigner::new(key)?) as Box, }; Ok(jwt_signer) diff --git a/src/errors.rs b/src/errors.rs index e9ee1dbd..7ad913f3 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -41,7 +41,7 @@ pub enum ErrorKind { InvalidSignature, /// When the secret given is not a valid ECDSA key InvalidEcdsaKey, - /// When the secret given is not a valid EDDSA key + /// When the secret given is not a valid EdDSA key InvalidEddsaKey, /// When the secret given is not a valid RSA key InvalidRsaKey(String), @@ -86,6 +86,7 @@ impl StdError for Error { ErrorKind::InvalidToken => None, ErrorKind::InvalidSignature => None, ErrorKind::InvalidEcdsaKey => None, + ErrorKind::InvalidEddsaKey => None, ErrorKind::RsaFailedSigning => None, ErrorKind::InvalidRsaKey(_) => None, ErrorKind::ExpiredSignature => None, @@ -101,7 +102,6 @@ impl StdError for Error { ErrorKind::Base64(err) => Some(err), ErrorKind::Json(err) => Some(err.as_ref()), ErrorKind::Utf8(err) => Some(err), - ErrorKind::InvalidEddsaKey => None, } } } diff --git a/tests/eddsa/mod.rs b/tests/eddsa/mod.rs index 61cc209b..38b71dc1 100644 --- a/tests/eddsa/mod.rs +++ b/tests/eddsa/mod.rs @@ -3,12 +3,9 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use wasm_bindgen_test::wasm_bindgen_test; -use jsonwebtoken::{ - crypto::{sign, verify}, - Algorithm, DecodingKey, EncodingKey, -}; #[cfg(feature = "use_pem")] use jsonwebtoken::{decode, encode, Header, Validation}; +use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey}; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { @@ -17,38 +14,38 @@ pub struct Claims { exp: i64, } -#[test] -#[wasm_bindgen_test] -fn round_trip_sign_verification_pk8() { - let privkey = include_bytes!("private_ed25519_key.pk8"); - let pubkey = include_bytes!("public_ed25519_key.pk8"); - - let encrypted = - sign(b"hello world", &EncodingKey::from_ed_der(privkey), Algorithm::EdDSA).unwrap(); - let is_valid = - verify(&encrypted, b"hello world", &DecodingKey::from_ed_der(pubkey), Algorithm::EdDSA) - .unwrap(); - assert!(is_valid); -} +// #[test] +// #[wasm_bindgen_test] +// fn round_trip_sign_verification_pk8() { +// let privkey = include_bytes!("private_ed25519_key.pk8"); +// let pubkey = include_bytes!("public_ed25519_key.pk8"); +// +// let encrypted = +// sign(b"hello world", &EncodingKey::from_ed_der(privkey), Algorithm::EdDSA).unwrap(); +// let is_valid = +// verify(&encrypted, b"hello world", &DecodingKey::from_ed_der(pubkey), Algorithm::EdDSA) +// .unwrap(); +// assert!(is_valid); +// } -#[cfg(feature = "use_pem")] -#[test] -#[wasm_bindgen_test] -fn round_trip_sign_verification_pem() { - let privkey_pem = include_bytes!("private_ed25519_key.pem"); - let pubkey_pem = include_bytes!("public_ed25519_key.pem"); - let encrypted = - sign(b"hello world", &EncodingKey::from_ed_pem(privkey_pem).unwrap(), Algorithm::EdDSA) - .unwrap(); - let is_valid = verify( - &encrypted, - b"hello world", - &DecodingKey::from_ed_pem(pubkey_pem).unwrap(), - Algorithm::EdDSA, - ) - .unwrap(); - assert!(is_valid); -} +// #[cfg(feature = "use_pem")] +// #[test] +// #[wasm_bindgen_test] +// fn round_trip_sign_verification_pem() { +// let privkey_pem = include_bytes!("private_ed25519_key.pem"); +// let pubkey_pem = include_bytes!("public_ed25519_key.pem"); +// let encrypted = +// sign(b"hello world", &EncodingKey::from_ed_pem(privkey_pem).unwrap(), Algorithm::EdDSA) +// .unwrap(); +// let is_valid = verify( +// &encrypted, +// b"hello world", +// &DecodingKey::from_ed_pem(pubkey_pem).unwrap(), +// Algorithm::EdDSA, +// ) +// .unwrap(); +// assert!(is_valid); +// } #[cfg(feature = "use_pem")] #[test] diff --git a/tests/lib.rs b/tests/lib.rs index 0dc88ba5..2eadda2c 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,4 +1,4 @@ mod ecdsa; -// mod eddsa; +mod eddsa; // mod header; mod rsa; From 765c04c953752699bda1d699a1cece5dda11e575 Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 14:36:07 +0900 Subject: [PATCH 25/35] Verify ES and ED keys are of the right type --- src/crypto/aws_lc/ecdsa.rs | 22 ++++++++++++++++++++-- src/crypto/aws_lc/eddsa.rs | 11 ++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/crypto/aws_lc/ecdsa.rs b/src/crypto/aws_lc/ecdsa.rs index c313a5a9..f71e1213 100644 --- a/src/crypto/aws_lc/ecdsa.rs +++ b/src/crypto/aws_lc/ecdsa.rs @@ -1,8 +1,9 @@ //! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the //! ECDSA family of algorithms using [`aws_lc_rs`] +use crate::algorithms::AlgorithmFamily; use crate::crypto::{JwtSigner, JwtVerifier}; -use crate::errors::Result; +use crate::errors::{new_error, ErrorKind, Result}; use crate::{Algorithm, DecodingKey, EncodingKey}; use aws_lc_rs::rand::SystemRandom; use aws_lc_rs::signature::{ @@ -15,6 +16,10 @@ pub struct Es256Signer(EcdsaKeyPair); impl Es256Signer { pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + Ok(Self( EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, encoding_key.inner()) .map_err(|_| crate::errors::ErrorKind::InvalidEcdsaKey)?, @@ -40,6 +45,10 @@ pub struct Es256Verifier(DecodingKey); impl Es256Verifier { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + Ok(Self(decoding_key.clone())) } } @@ -63,6 +72,10 @@ pub struct Es384Signer(EcdsaKeyPair); impl Es384Signer { pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + Ok(Self( EcdsaKeyPair::from_pkcs8(&ECDSA_P384_SHA384_FIXED_SIGNING, encoding_key.inner()) .map_err(|_| crate::errors::ErrorKind::InvalidEcdsaKey)?, @@ -88,6 +101,10 @@ pub struct Es384Verifier(DecodingKey); impl Es384Verifier { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + Ok(Self(decoding_key.clone())) } } @@ -95,7 +112,8 @@ impl Es384Verifier { impl Verifier> for Es384Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), Error> { ECDSA_P384_SHA384_FIXED - .verify_sig(self.0.as_bytes(), msg, signature).map_err(Error::from_source)?; + .verify_sig(self.0.as_bytes(), msg, signature) + .map_err(Error::from_source)?; Ok(()) } diff --git a/src/crypto/aws_lc/eddsa.rs b/src/crypto/aws_lc/eddsa.rs index 33faecc5..4a3d1f2b 100644 --- a/src/crypto/aws_lc/eddsa.rs +++ b/src/crypto/aws_lc/eddsa.rs @@ -1,8 +1,9 @@ //! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the //! EdDSA family of algorithms using [`aws_lc_rs`] +use crate::algorithms::AlgorithmFamily; use crate::crypto::{JwtSigner, JwtVerifier}; -use crate::errors::Result; +use crate::errors::{new_error, ErrorKind, Result}; use crate::{Algorithm, DecodingKey, EncodingKey}; use aws_lc_rs::signature::{Ed25519KeyPair, VerificationAlgorithm, ED25519}; use signature::{Error, Signer, Verifier}; @@ -11,6 +12,10 @@ pub struct EdDSASigner(Ed25519KeyPair); impl EdDSASigner { pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Ed { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + Ok(Self( Ed25519KeyPair::from_pkcs8(encoding_key.inner()) .map_err(|_| crate::errors::ErrorKind::InvalidEddsaKey)?, @@ -34,6 +39,10 @@ pub struct EdDSAVerifier(DecodingKey); impl EdDSAVerifier { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Ed { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + Ok(Self(decoding_key.clone())) } } From e1305ef64c272745a88d093c8cf01600bbf7ab09 Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 14:46:04 +0900 Subject: [PATCH 26/35] Implement RSA-PSS via AWS-LC --- src/crypto/aws_lc/rsa.rs | 144 +++++++++++++++++++++++++++++++++++++++ src/crypto/utils.rs | 1 - src/decoding.rs | 13 ++-- src/encoding.rs | 8 +-- 4 files changed, 156 insertions(+), 10 deletions(-) diff --git a/src/crypto/aws_lc/rsa.rs b/src/crypto/aws_lc/rsa.rs index 2d9ece56..bf47bfc3 100644 --- a/src/crypto/aws_lc/rsa.rs +++ b/src/crypto/aws_lc/rsa.rs @@ -198,3 +198,147 @@ impl JwtVerifier for Rsa512Verifier { Algorithm::RS512 } } + +pub struct RsaPss256Signer(EncodingKey); + +impl RsaPss256Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for RsaPss256Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa(&crypto_sig::RSA_PSS_SHA256, &self.0, msg) + } +} + +impl JwtSigner for RsaPss256Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::PS256 + } +} + +pub struct RsaPss256Verifier(DecodingKey); + +impl RsaPss256Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for RsaPss256Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(&crypto_sig::RSA_PSS_2048_8192_SHA256, &self.0, msg, signature) + } +} + +impl JwtVerifier for RsaPss256Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::PS256 + } +} + +pub struct RsaPss384Signer(EncodingKey); + +impl RsaPss384Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for RsaPss384Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa(&crypto_sig::RSA_PSS_SHA384, &self.0, msg) + } +} + +impl JwtSigner for RsaPss384Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::PS384 + } +} + +pub struct RsaPss384Verifier(DecodingKey); + +impl RsaPss384Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for RsaPss384Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(&crypto_sig::RSA_PSS_2048_8192_SHA384, &self.0, msg, signature) + } +} + +impl JwtVerifier for RsaPss384Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::PS384 + } +} + +pub struct RsaPss512Signer(EncodingKey); + +impl RsaPss512Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for RsaPss512Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa(&crypto_sig::RSA_PSS_SHA512, &self.0, msg) + } +} + +impl JwtSigner for RsaPss512Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::PS512 + } +} + +pub struct RsaPss512Verifier(DecodingKey); + +impl RsaPss512Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for RsaPss512Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(&crypto_sig::RSA_PSS_2048_8192_SHA512, &self.0, msg, signature) + } +} + +impl JwtVerifier for RsaPss512Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::PS512 + } +} diff --git a/src/crypto/utils.rs b/src/crypto/utils.rs index 2044a7a4..0dc7ab08 100644 --- a/src/crypto/utils.rs +++ b/src/crypto/utils.rs @@ -4,7 +4,6 @@ use crate::{ algorithms::AlgorithmFamily, - decoding::DecodingKeyKind, errors::{self, new_error, ErrorKind, Result}, DecodingKey, EncodingKey, }; diff --git a/src/decoding.rs b/src/decoding.rs index af424781..f0496f51 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -3,7 +3,7 @@ use serde::de::DeserializeOwned; use crate::algorithms::AlgorithmFamily; use crate::crypto::JwtVerifier; -use crate::errors::{new_error, Error, ErrorKind, Result}; +use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; use crate::jwk::{AlgorithmParameters, Jwk}; #[cfg(feature = "use_pem")] @@ -17,7 +17,10 @@ use crate::crypto::aws_lc::{ ecdsa::{Es256Verifier, Es384Verifier}, eddsa::EdDSAVerifier, hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}, - rsa::{Rsa256Verifier, Rsa384Verifier, Rsa512Verifier}, + rsa::{ + Rsa256Verifier, Rsa384Verifier, Rsa512Verifier, RsaPss256Verifier, RsaPss384Verifier, + RsaPss512Verifier, + }, }; #[cfg(feature = "rust_crypto")] use crate::crypto::rust_crypto::hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}; @@ -275,9 +278,9 @@ fn jwt_verifier_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result Box::new(Rsa256Verifier::new(key)?) as Box, Algorithm::RS384 => Box::new(Rsa384Verifier::new(key)?) as Box, Algorithm::RS512 => Box::new(Rsa512Verifier::new(key)?) as Box, - Algorithm::PS256 => todo!(), - Algorithm::PS384 => todo!(), - Algorithm::PS512 => todo!(), + Algorithm::PS256 => Box::new(RsaPss256Verifier::new(key)?) as Box, + Algorithm::PS384 => Box::new(RsaPss384Verifier::new(key)?) as Box, + Algorithm::PS512 => Box::new(RsaPss512Verifier::new(key)?) as Box, Algorithm::EdDSA => Box::new(EdDSAVerifier::new(key)?) as Box, }; diff --git a/src/encoding.rs b/src/encoding.rs index e26a1e02..28d37daa 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -15,7 +15,7 @@ use crate::crypto::aws_lc::{ ecdsa::{Es256Signer, Es384Signer}, eddsa::EdDSASigner, hmac::{Hs256Signer, Hs384Signer, Hs512Signer}, - rsa::{Rsa256Signer, Rsa384Signer, Rsa512Signer}, + rsa::{Rsa256Signer, Rsa384Signer, Rsa512Signer, RsaPss256Signer, RsaPss384Signer, RsaPss512Signer}, }; #[cfg(feature = "rust_crypto")] use crate::crypto::rust_crypto::hmac::{Hs256Signer, Hs384Signer, Hs512Signer}; @@ -171,9 +171,9 @@ fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result Box::new(Rsa256Signer::new(key)?) as Box, Algorithm::RS384 => Box::new(Rsa384Signer::new(key)?) as Box, Algorithm::RS512 => Box::new(Rsa512Signer::new(key)?) as Box, - Algorithm::PS256 => todo!(), - Algorithm::PS384 => todo!(), - Algorithm::PS512 => todo!(), + Algorithm::PS256 => Box::new(RsaPss256Signer::new(key)?) as Box, + Algorithm::PS384 => Box::new(RsaPss384Signer::new(key)?) as Box, + Algorithm::PS512 => Box::new(RsaPss512Signer::new(key)?) as Box, Algorithm::EdDSA => Box::new(EdDSASigner::new(key)?) as Box, }; From e37ebee87d087d51742c8a195b5e5ddf16ae88b3 Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 16:33:04 +0900 Subject: [PATCH 27/35] Implement EdDSA via RustCrypto/Dalek --- src/crypto/aws_lc/eddsa.rs | 5 +-- src/crypto/rust_crypto/eddsa.rs | 69 +++++++++++++++++++++++++++++++++ src/crypto/rust_crypto/hmac.rs | 2 +- src/crypto/rust_crypto/mod.rs | 1 + src/decoding.rs | 10 ++++- src/encoding.rs | 11 +++++- 6 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 src/crypto/rust_crypto/eddsa.rs diff --git a/src/crypto/aws_lc/eddsa.rs b/src/crypto/aws_lc/eddsa.rs index 4a3d1f2b..0e7994a9 100644 --- a/src/crypto/aws_lc/eddsa.rs +++ b/src/crypto/aws_lc/eddsa.rs @@ -1,5 +1,4 @@ -//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the -//! EdDSA family of algorithms using [`aws_lc_rs`] +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for EdDSA using AWS-LC-RS. use crate::algorithms::AlgorithmFamily; use crate::crypto::{JwtSigner, JwtVerifier}; @@ -18,7 +17,7 @@ impl EdDSASigner { Ok(Self( Ed25519KeyPair::from_pkcs8(encoding_key.inner()) - .map_err(|_| crate::errors::ErrorKind::InvalidEddsaKey)?, + .map_err(|_| ErrorKind::InvalidEddsaKey)?, )) } } diff --git a/src/crypto/rust_crypto/eddsa.rs b/src/crypto/rust_crypto/eddsa.rs new file mode 100644 index 00000000..2e1c52c4 --- /dev/null +++ b/src/crypto/rust_crypto/eddsa.rs @@ -0,0 +1,69 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for EdDSA using RustCrypto. + +use crate::algorithms::AlgorithmFamily; +use crate::crypto::{JwtSigner, JwtVerifier}; +use crate::errors::{new_error, ErrorKind, Result}; +use crate::{Algorithm, DecodingKey, EncodingKey}; +use ed25519_dalek::{Signature, SigningKey}; +use signature::{Error, Signer, Verifier}; + +pub struct EdDSASigner(SigningKey); + +impl EdDSASigner { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Ed { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self( + SigningKey::from_keypair_bytes( + <&[u8; 64]>::try_from(encoding_key.inner()) + .map_err(|_| ErrorKind::InvalidEddsaKey)?, + ) + .map_err(|_| ErrorKind::InvalidEddsaKey)?, + )) + } +} + +impl Signer> for EdDSASigner { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, Error> { + Ok(self.0.sign(msg).to_bytes().to_vec()) + } +} + +impl JwtSigner for EdDSASigner { + fn algorithm(&self) -> Algorithm { + Algorithm::EdDSA + } +} + +pub struct EdDSAVerifier(SigningKey); + +impl EdDSAVerifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Ed { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self( + SigningKey::from_keypair_bytes( + <&[u8; 64]>::try_from(decoding_key.as_bytes()) + .map_err(|_| ErrorKind::InvalidEddsaKey)?, + ) + .map_err(|_| ErrorKind::InvalidEddsaKey)?, + )) + } +} + +impl Verifier> for EdDSAVerifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), Error> { + self.0.verify(msg, &Signature::from_slice(signature)?)?; + Ok(()) + } +} + +impl JwtVerifier for EdDSAVerifier { + fn algorithm(&self) -> Algorithm { + Algorithm::EdDSA + } +} diff --git a/src/crypto/rust_crypto/hmac.rs b/src/crypto/rust_crypto/hmac.rs index 374bfa68..90af0877 100644 --- a/src/crypto/rust_crypto/hmac.rs +++ b/src/crypto/rust_crypto/hmac.rs @@ -1,5 +1,5 @@ //! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the -//! HMAC family of algorithms using `RustCtypto`'s [`hmac`]. +//! HMAC family of algorithms using `RustCrypto`'s [`hmac`]. use hmac::{Hmac, Mac}; use sha2::{Sha256, Sha384, Sha512}; diff --git a/src/crypto/rust_crypto/mod.rs b/src/crypto/rust_crypto/mod.rs index 3a812e97..d9fd0b3c 100644 --- a/src/crypto/rust_crypto/mod.rs +++ b/src/crypto/rust_crypto/mod.rs @@ -1 +1,2 @@ pub(crate) mod hmac; +pub(crate) mod eddsa; diff --git a/src/decoding.rs b/src/decoding.rs index f0496f51..0ea44068 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -23,7 +23,15 @@ use crate::crypto::aws_lc::{ }, }; #[cfg(feature = "rust_crypto")] -use crate::crypto::rust_crypto::hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}; +use crate::crypto::rust_crypto::{ + // ecdsa::{Es256Verifier, Es384Verifier}, + eddsa::EdDSAVerifier, + hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}, + // rsa::{ + // Rsa256Verifier, Rsa384Verifier, Rsa512Verifier, RsaPss256Verifier, RsaPss384Verifier, + // RsaPss512Verifier, + // }, +}; /// The return type of a successful call to [decode](fn.decode.html). #[derive(Debug)] diff --git a/src/encoding.rs b/src/encoding.rs index 28d37daa..4b9a6efe 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -15,10 +15,17 @@ use crate::crypto::aws_lc::{ ecdsa::{Es256Signer, Es384Signer}, eddsa::EdDSASigner, hmac::{Hs256Signer, Hs384Signer, Hs512Signer}, - rsa::{Rsa256Signer, Rsa384Signer, Rsa512Signer, RsaPss256Signer, RsaPss384Signer, RsaPss512Signer}, + rsa::{ + Rsa256Signer, Rsa384Signer, Rsa512Signer, RsaPss256Signer, RsaPss384Signer, RsaPss512Signer, + }, }; #[cfg(feature = "rust_crypto")] -use crate::crypto::rust_crypto::hmac::{Hs256Signer, Hs384Signer, Hs512Signer}; +use crate::crypto::rust_crypto::{ + // ecdsa::{Es256Signer, Es384Signer}, + eddsa::EdDSASigner, + hmac::{Hs256Signer, Hs384Signer, Hs512Signer}, + // rsa::{Rsa256Signer, Rsa384Signer, Rsa512Signer, RsaPss256Signer, RsaPss384Signer, RsaPss512Signer}, +}; /// A key to encode a JWT with. Can be a secret, a PEM-encoded key or a DER-encoded key. /// This key can be re-used so make sure you only initialize it once if you can for better performance. From 25f84c35d24af6028f4ba7eab743f031133e33db Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 17:05:18 +0900 Subject: [PATCH 28/35] Implement EcDSA through RustCrypto --- src/crypto/aws_lc/ecdsa.rs | 4 +- src/crypto/rust_crypto/ecdsa.rs | 131 ++++++++++++++++++++++++++++++++ src/crypto/rust_crypto/mod.rs | 3 +- src/decoding.rs | 2 +- src/encoding.rs | 2 +- 5 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 src/crypto/rust_crypto/ecdsa.rs diff --git a/src/crypto/aws_lc/ecdsa.rs b/src/crypto/aws_lc/ecdsa.rs index f71e1213..2a149727 100644 --- a/src/crypto/aws_lc/ecdsa.rs +++ b/src/crypto/aws_lc/ecdsa.rs @@ -21,8 +21,8 @@ impl Es256Signer { } Ok(Self( - EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, encoding_key.inner()) - .map_err(|_| crate::errors::ErrorKind::InvalidEcdsaKey)?, + EcdsaKeyPair::from_pcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, encoding_key.inner()) + .map_err(|_| ErrorKind::InvalidEcdsaKey)?, )) } } diff --git a/src/crypto/rust_crypto/ecdsa.rs b/src/crypto/rust_crypto/ecdsa.rs new file mode 100644 index 00000000..7f4d0e8a --- /dev/null +++ b/src/crypto/rust_crypto/ecdsa.rs @@ -0,0 +1,131 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the +//! ECDSA family of algorithms using RustCrypto + +use crate::algorithms::AlgorithmFamily; +use crate::crypto::{JwtSigner, JwtVerifier}; +use crate::errors::{new_error, ErrorKind, Result}; +use crate::{Algorithm, DecodingKey, EncodingKey}; +use p256::ecdsa::{ + Signature as Signature256, SigningKey as SigningKey256, VerifyingKey as VerifyingKey256, +}; +use p384::ecdsa::{ + Signature as Signature384, SigningKey as SigningKey384, VerifyingKey as VerifyingKey384, +}; +use signature::{Error, Signer, Verifier}; + +pub struct Es256Signer(SigningKey256); + +impl Es256Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self( + SigningKey256::from_slice(encoding_key.inner()) + .map_err(|_| ErrorKind::InvalidEcdsaKey)?, + )) + } +} + +impl Signer> for Es256Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, Error> { + let signature = self.0.sign_recoverable(msg).map_err(Error::from_source)?.0; + Ok(signature.to_vec()) + } +} + +impl JwtSigner for Es256Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::ES256 + } +} + +pub struct Es256Verifier(VerifyingKey256); + +impl Es256Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self( + VerifyingKey256::from_sec1_bytes(decoding_key.as_bytes()) + .map_err(|_| ErrorKind::InvalidEcdsaKey)?, + )) + } +} + +impl Verifier> for Es256Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), Error> { + self.0 + .verify(msg, &Signature256::from_slice(signature).map_err(Error::from_source)?) + .map_err(Error::from_source)?; + Ok(()) + } +} + +impl JwtVerifier for Es256Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::ES256 + } +} + +pub struct Es384Signer(SigningKey384); + +impl Es384Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self( + SigningKey384::from_slice(encoding_key.inner()) + .map_err(|_| ErrorKind::InvalidEcdsaKey)?, + )) + } +} + +impl Signer> for Es384Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, Error> { + let signature = self.0.sign_recoverable(msg).map_err(Error::from_source)?.0; + Ok(signature.to_vec()) + } +} + +impl JwtSigner for Es384Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::ES384 + } +} + +pub struct Es384Verifier(VerifyingKey384); + +impl Es384Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self( + VerifyingKey384::from_sec1_bytes(decoding_key.as_bytes()) + .map_err(|_| ErrorKind::InvalidEcdsaKey)?, + )) + } +} + +impl Verifier> for Es384Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), Error> { + self.0 + .verify(msg, &Signature384::from_slice(signature).map_err(Error::from_source)?) + .map_err(Error::from_source)?; + Ok(()) + } +} + +impl JwtVerifier for Es384Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::ES384 + } +} + diff --git a/src/crypto/rust_crypto/mod.rs b/src/crypto/rust_crypto/mod.rs index d9fd0b3c..731e7ea5 100644 --- a/src/crypto/rust_crypto/mod.rs +++ b/src/crypto/rust_crypto/mod.rs @@ -1,2 +1,3 @@ -pub(crate) mod hmac; +pub(crate) mod ecdsa; pub(crate) mod eddsa; +pub(crate) mod hmac; diff --git a/src/decoding.rs b/src/decoding.rs index 0ea44068..70ed0084 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -24,7 +24,7 @@ use crate::crypto::aws_lc::{ }; #[cfg(feature = "rust_crypto")] use crate::crypto::rust_crypto::{ - // ecdsa::{Es256Verifier, Es384Verifier}, + ecdsa::{Es256Verifier, Es384Verifier}, eddsa::EdDSAVerifier, hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}, // rsa::{ diff --git a/src/encoding.rs b/src/encoding.rs index 4b9a6efe..6adfe6c9 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -21,7 +21,7 @@ use crate::crypto::aws_lc::{ }; #[cfg(feature = "rust_crypto")] use crate::crypto::rust_crypto::{ - // ecdsa::{Es256Signer, Es384Signer}, + ecdsa::{Es256Signer, Es384Signer}, eddsa::EdDSASigner, hmac::{Hs256Signer, Hs384Signer, Hs512Signer}, // rsa::{Rsa256Signer, Rsa384Signer, Rsa512Signer, RsaPss256Signer, RsaPss384Signer, RsaPss512Signer}, From 4b7fdfb30bed111062b881169e4bf940b53c7bcf Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 19:15:06 +0900 Subject: [PATCH 29/35] Implement RSA via RustCrypto --- src/crypto/aws_lc/ecdsa.rs | 4 +- src/crypto/rust_crypto/mod.rs | 1 + src/crypto/rust_crypto/rsa.rs | 344 ++++++++++++++++++++++++++++++++++ src/decoding.rs | 8 +- src/encoding.rs | 4 +- 5 files changed, 354 insertions(+), 7 deletions(-) create mode 100644 src/crypto/rust_crypto/rsa.rs diff --git a/src/crypto/aws_lc/ecdsa.rs b/src/crypto/aws_lc/ecdsa.rs index 2a149727..de8b6aba 100644 --- a/src/crypto/aws_lc/ecdsa.rs +++ b/src/crypto/aws_lc/ecdsa.rs @@ -21,7 +21,7 @@ impl Es256Signer { } Ok(Self( - EcdsaKeyPair::from_pcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, encoding_key.inner()) + EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, encoding_key.inner()) .map_err(|_| ErrorKind::InvalidEcdsaKey)?, )) } @@ -75,7 +75,7 @@ impl Es384Signer { if encoding_key.family != AlgorithmFamily::Ec { return Err(new_error(ErrorKind::InvalidKeyFormat)); } - + Ok(Self( EcdsaKeyPair::from_pkcs8(&ECDSA_P384_SHA384_FIXED_SIGNING, encoding_key.inner()) .map_err(|_| crate::errors::ErrorKind::InvalidEcdsaKey)?, diff --git a/src/crypto/rust_crypto/mod.rs b/src/crypto/rust_crypto/mod.rs index 731e7ea5..16f66b5f 100644 --- a/src/crypto/rust_crypto/mod.rs +++ b/src/crypto/rust_crypto/mod.rs @@ -1,3 +1,4 @@ pub(crate) mod ecdsa; pub(crate) mod eddsa; pub(crate) mod hmac; +pub(crate) mod rsa; diff --git a/src/crypto/rust_crypto/rsa.rs b/src/crypto/rust_crypto/rsa.rs new file mode 100644 index 00000000..2dea3ff2 --- /dev/null +++ b/src/crypto/rust_crypto/rsa.rs @@ -0,0 +1,344 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the +//! RSA family of algorithms using [`aws_lc_rs`] + +use rsa::{ + pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey}, + pkcs1v15::SigningKey, + pkcs8::AssociatedOid, + traits::SignatureScheme, + BigUint, Pkcs1v15Sign, Pss, RsaPublicKey, +}; +use sha2::{Digest, Sha256, Sha384, Sha512}; +use signature::{SignatureEncoding, Signer, Verifier}; + +use crate::algorithms::AlgorithmFamily; +use crate::crypto::{JwtSigner, JwtVerifier}; +use crate::decoding::DecodingKeyKind; +use crate::errors::{new_error, ErrorKind, Result}; +use crate::{Algorithm, DecodingKey, EncodingKey}; + +fn try_sign_rsa( + encoding_key: &EncodingKey, + msg: &[u8], +) -> std::result::Result, signature::Error> +where + H: Digest + AssociatedOid, +{ + let private_key = rsa::RsaPrivateKey::from_pkcs1_der(encoding_key.inner()) + .map_err(signature::Error::from_source)?; + let signing_key = SigningKey::::new(private_key); + let signature = signing_key.sign(msg).to_vec(); + + Ok(signature) +} + +fn verify_rsa( + scheme: S, + decoding_key: &DecodingKey, + msg: &[u8], + signature: &[u8], +) -> std::result::Result<(), signature::Error> { + match &decoding_key.kind { + DecodingKeyKind::SecretOrDer(bytes) => { + RsaPublicKey::from_pkcs1_der(bytes) + .map_err(signature::Error::from_source)? + .verify(scheme, msg, signature) + .map_err(signature::Error::from_source)?; + } + DecodingKeyKind::RsaModulusExponent { n, e } => { + RsaPublicKey::new(BigUint::from_bytes_be(n), BigUint::from_bytes_be(e))? + .verify(scheme, msg, signature) + .map_err(signature::Error::from_source)?; + } + }; + + Ok(()) +} + +pub struct Rsa256Signer(EncodingKey); + +impl Rsa256Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for Rsa256Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa::(&self.0, msg) + } +} + +impl JwtSigner for Rsa256Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::RS256 + } +} + +pub struct Rsa256Verifier(DecodingKey); + +impl Rsa256Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for Rsa256Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(Pkcs1v15Sign::new::(), &self.0, msg, signature) + } +} + +impl JwtVerifier for Rsa256Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::RS256 + } +} + +pub struct Rsa384Signer(EncodingKey); + +impl Rsa384Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for Rsa384Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa::(&self.0, msg) + } +} + +impl JwtSigner for Rsa384Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::RS384 + } +} + +pub struct Rsa384Verifier(DecodingKey); + +impl Rsa384Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for Rsa384Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(Pkcs1v15Sign::new::(), &self.0, msg, signature) + } +} + +impl JwtVerifier for Rsa384Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::RS384 + } +} + +pub struct Rsa512Signer(EncodingKey); + +impl Rsa512Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for Rsa512Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa::(&self.0, msg) + } +} + +impl JwtSigner for Rsa512Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::RS512 + } +} + +pub struct Rsa512Verifier(DecodingKey); + +impl Rsa512Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for Rsa512Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(Pkcs1v15Sign::new::(), &self.0, msg, signature) + } +} + +impl JwtVerifier for Rsa512Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::RS512 + } +} + +pub struct RsaPss256Signer(EncodingKey); + +impl RsaPss256Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for RsaPss256Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa::(&self.0, msg) + } +} + +impl JwtSigner for RsaPss256Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::PS256 + } +} + +pub struct RsaPss256Verifier(DecodingKey); + +impl RsaPss256Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for RsaPss256Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(Pss::new::(), &self.0, msg, signature) + } +} + +impl JwtVerifier for RsaPss256Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::PS256 + } +} + +pub struct RsaPss384Signer(EncodingKey); + +impl RsaPss384Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for RsaPss384Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa::(&self.0, msg) + } +} + +impl JwtSigner for RsaPss384Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::PS384 + } +} + +pub struct RsaPss384Verifier(DecodingKey); + +impl RsaPss384Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for RsaPss384Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(Pss::new::(), &self.0, msg, signature) + } +} + +impl JwtVerifier for RsaPss384Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::PS384 + } +} + +pub struct RsaPss512Signer(EncodingKey); + +impl RsaPss512Signer { + pub(crate) fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(encoding_key.clone())) + } +} + +impl Signer> for RsaPss512Signer { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { + try_sign_rsa::(&self.0, msg) + } +} + +impl JwtSigner for RsaPss512Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::PS512 + } +} + +pub struct RsaPss512Verifier(DecodingKey); + +impl RsaPss512Verifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Rsa { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self(decoding_key.clone())) + } +} + +impl Verifier> for RsaPss512Verifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { + verify_rsa(Pss::new::(), &self.0, msg, signature) + } +} + +impl JwtVerifier for RsaPss512Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::PS512 + } +} diff --git a/src/decoding.rs b/src/decoding.rs index 70ed0084..407a8e96 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -27,10 +27,10 @@ use crate::crypto::rust_crypto::{ ecdsa::{Es256Verifier, Es384Verifier}, eddsa::EdDSAVerifier, hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}, - // rsa::{ - // Rsa256Verifier, Rsa384Verifier, Rsa512Verifier, RsaPss256Verifier, RsaPss384Verifier, - // RsaPss512Verifier, - // }, + rsa::{ + Rsa256Verifier, Rsa384Verifier, Rsa512Verifier, RsaPss256Verifier, RsaPss384Verifier, + RsaPss512Verifier, + }, }; /// The return type of a successful call to [decode](fn.decode.html). diff --git a/src/encoding.rs b/src/encoding.rs index 6adfe6c9..b23fc185 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -24,7 +24,9 @@ use crate::crypto::rust_crypto::{ ecdsa::{Es256Signer, Es384Signer}, eddsa::EdDSASigner, hmac::{Hs256Signer, Hs384Signer, Hs512Signer}, - // rsa::{Rsa256Signer, Rsa384Signer, Rsa512Signer, RsaPss256Signer, RsaPss384Signer, RsaPss512Signer}, + rsa::{ + Rsa256Signer, Rsa384Signer, Rsa512Signer, RsaPss256Signer, RsaPss384Signer, RsaPss512Signer, + }, }; /// A key to encode a JWT with. Can be a secret, a PEM-encoded key or a DER-encoded key. From 1f49f435ff5ef4a01bc1d2af49112bc6c0b19156 Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 19:15:23 +0900 Subject: [PATCH 30/35] Clean up optional dependencies --- Cargo.toml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab274b0b..f9b58400 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,28 +20,26 @@ include = [ rust-version = "1.73.0" [dependencies] -serde_json = "1.0" -serde = { version = "1.0", features = ["derive"] } base64 = "0.22" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +signature = "2.2.0" + # For PEM decoding pem = { version = "3", optional = true } simple_asn1 = { version = "0.6", optional = true } -rsa = "0.9.6" -sha2 = { version = "0.10.7", features = ["oid"] } -getrandom = { version = "0.2.10", features = ["js"] } -rand = { version = "0.8.5", features = ["std"], default-features = false } -ed25519-dalek = { version = "2.1.1" } -p256 = { version = "0.13.2", features = ["ecdsa"] } -p384 = { version = "0.13.0", features = ["ecdsa"] } -rand_core = "0.6.4" -signature = "2.2.0" +# "aws_lc_rs" feature +aws-lc-rs = { version = "1.10.0", optional = true } # "rust_crypto" feature +ed25519-dalek = { version = "2.1.1", optional = true } hmac = { version = "0.12.1", optional = true } - -# "aws_lc_rs" feature -aws-lc-rs = { version = "1.10.0", optional = true } +p256 = { version = "0.13.2", optional = true, features = ["ecdsa"] } +p384 = { version = "0.13.0", optional = true, features = ["ecdsa"] } +rand = { version = "0.8.5", optional = true, features = ["std"], default-features = false } +rsa = { version = "0.9.6", optional = true } +sha2 = { version = "0.10.7", optional = true, features = ["oid"] } [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = "0.3" @@ -49,6 +47,8 @@ js-sys = "0.3" [dev-dependencies] wasm-bindgen-test = "0.3.1" ed25519-dalek = { version = "2.1.1", features = ["pkcs8", "rand_core"] } +rand = { version = "0.8.5", features = ["std"], default-features = false } +rand_core = "0.6.4" [target.'cfg(not(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi")))))'.dev-dependencies] # For the custom time example time = "0.3" @@ -60,9 +60,9 @@ time = { version = "0.3", features = ["wasm-bindgen"] } criterion = { version = "0.4", default-features = false } [features] -default = ["use_pem", "aws_lc_rs"] -use_pem = ["pem", "simple_asn1", 'p256/pem', 'p384/pem'] -rust_crypto = ["hmac"] +default = ["use_pem", "rust_crypto"] +use_pem = ["pem", "simple_asn1"] +rust_crypto = ["ed25519-dalek", "hmac", "p256", "p384", "rand", "rsa", "sha2"] aws_lc_rs = ["aws-lc-rs"] [[bench]] From 6062c9656bf7b49c03aeef7deabfc6d6bd67dbec Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 21:10:09 +0900 Subject: [PATCH 31/35] Fix all test-breaking issues with the RustCrypto versions --- Cargo.toml | 2 +- src/crypto/rust_crypto/ecdsa.rs | 6 ++-- src/crypto/rust_crypto/eddsa.rs | 16 ++++----- src/crypto/rust_crypto/rsa.rs | 57 ++++++++++++++++++++------------- src/crypto/utils.rs | 2 +- 5 files changed, 46 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f9b58400..dba3d690 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ time = { version = "0.3", features = ["wasm-bindgen"] } criterion = { version = "0.4", default-features = false } [features] -default = ["use_pem", "rust_crypto"] +default = ["use_pem", "aws_lc_rs"] use_pem = ["pem", "simple_asn1"] rust_crypto = ["ed25519-dalek", "hmac", "p256", "p384", "rand", "rsa", "sha2"] aws_lc_rs = ["aws-lc-rs"] diff --git a/src/crypto/rust_crypto/ecdsa.rs b/src/crypto/rust_crypto/ecdsa.rs index 7f4d0e8a..790aa39c 100644 --- a/src/crypto/rust_crypto/ecdsa.rs +++ b/src/crypto/rust_crypto/ecdsa.rs @@ -11,6 +11,7 @@ use p256::ecdsa::{ use p384::ecdsa::{ Signature as Signature384, SigningKey as SigningKey384, VerifyingKey as VerifyingKey384, }; +use rsa::pkcs8::DecodePrivateKey; use signature::{Error, Signer, Verifier}; pub struct Es256Signer(SigningKey256); @@ -22,7 +23,7 @@ impl Es256Signer { } Ok(Self( - SigningKey256::from_slice(encoding_key.inner()) + SigningKey256::from_pkcs8_der(encoding_key.inner()) .map_err(|_| ErrorKind::InvalidEcdsaKey)?, )) } @@ -80,7 +81,7 @@ impl Es384Signer { } Ok(Self( - SigningKey384::from_slice(encoding_key.inner()) + SigningKey384::from_pkcs8_der(encoding_key.inner()) .map_err(|_| ErrorKind::InvalidEcdsaKey)?, )) } @@ -128,4 +129,3 @@ impl JwtVerifier for Es384Verifier { Algorithm::ES384 } } - diff --git a/src/crypto/rust_crypto/eddsa.rs b/src/crypto/rust_crypto/eddsa.rs index 2e1c52c4..2d59b535 100644 --- a/src/crypto/rust_crypto/eddsa.rs +++ b/src/crypto/rust_crypto/eddsa.rs @@ -4,7 +4,8 @@ use crate::algorithms::AlgorithmFamily; use crate::crypto::{JwtSigner, JwtVerifier}; use crate::errors::{new_error, ErrorKind, Result}; use crate::{Algorithm, DecodingKey, EncodingKey}; -use ed25519_dalek::{Signature, SigningKey}; +use ed25519_dalek::pkcs8::DecodePrivateKey; +use ed25519_dalek::{Signature, SigningKey, VerifyingKey}; use signature::{Error, Signer, Verifier}; pub struct EdDSASigner(SigningKey); @@ -16,11 +17,8 @@ impl EdDSASigner { } Ok(Self( - SigningKey::from_keypair_bytes( - <&[u8; 64]>::try_from(encoding_key.inner()) - .map_err(|_| ErrorKind::InvalidEddsaKey)?, - ) - .map_err(|_| ErrorKind::InvalidEddsaKey)?, + SigningKey::from_pkcs8_der(encoding_key.inner()) + .map_err(|_| ErrorKind::InvalidEddsaKey)?, )) } } @@ -37,7 +35,7 @@ impl JwtSigner for EdDSASigner { } } -pub struct EdDSAVerifier(SigningKey); +pub struct EdDSAVerifier(VerifyingKey); impl EdDSAVerifier { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { @@ -46,8 +44,8 @@ impl EdDSAVerifier { } Ok(Self( - SigningKey::from_keypair_bytes( - <&[u8; 64]>::try_from(decoding_key.as_bytes()) + VerifyingKey::from_bytes( + <&[u8; 32]>::try_from(&decoding_key.as_bytes()[..32]) .map_err(|_| ErrorKind::InvalidEddsaKey)?, ) .map_err(|_| ErrorKind::InvalidEddsaKey)?, diff --git a/src/crypto/rust_crypto/rsa.rs b/src/crypto/rust_crypto/rsa.rs index 2dea3ff2..c0d8de17 100644 --- a/src/crypto/rust_crypto/rsa.rs +++ b/src/crypto/rust_crypto/rsa.rs @@ -1,15 +1,17 @@ //! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the //! RSA family of algorithms using [`aws_lc_rs`] +use hmac::digest::FixedOutputReset; use rsa::{ pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey}, pkcs1v15::SigningKey, pkcs8::AssociatedOid, + pss::BlindedSigningKey, traits::SignatureScheme, BigUint, Pkcs1v15Sign, Pss, RsaPublicKey, }; use sha2::{Digest, Sha256, Sha384, Sha512}; -use signature::{SignatureEncoding, Signer, Verifier}; +use signature::{RandomizedSigner, SignatureEncoding, Signer, Verifier}; use crate::algorithms::AlgorithmFamily; use crate::crypto::{JwtSigner, JwtVerifier}; @@ -20,34 +22,43 @@ use crate::{Algorithm, DecodingKey, EncodingKey}; fn try_sign_rsa( encoding_key: &EncodingKey, msg: &[u8], + pss: bool, ) -> std::result::Result, signature::Error> where - H: Digest + AssociatedOid, + H: Digest + AssociatedOid + FixedOutputReset, { - let private_key = rsa::RsaPrivateKey::from_pkcs1_der(encoding_key.inner()) - .map_err(signature::Error::from_source)?; - let signing_key = SigningKey::::new(private_key); - let signature = signing_key.sign(msg).to_vec(); - - Ok(signature) + let mut rng = rand::thread_rng(); + if pss { + let private_key = rsa::RsaPrivateKey::from_pkcs1_der(encoding_key.inner()) + .map_err(signature::Error::from_source)?; + let signing_key = BlindedSigningKey::::new(private_key); + Ok(signing_key.sign_with_rng(&mut rng, msg).to_vec()) + } else { + let private_key = rsa::RsaPrivateKey::from_pkcs1_der(encoding_key.inner()) + .map_err(signature::Error::from_source)?; + let signing_key = SigningKey::::new(private_key); + Ok(signing_key.sign_with_rng(&mut rng, msg).to_vec()) + } } -fn verify_rsa( +fn verify_rsa( scheme: S, decoding_key: &DecodingKey, msg: &[u8], signature: &[u8], ) -> std::result::Result<(), signature::Error> { + let digest = H::digest(msg); + match &decoding_key.kind { DecodingKeyKind::SecretOrDer(bytes) => { RsaPublicKey::from_pkcs1_der(bytes) .map_err(signature::Error::from_source)? - .verify(scheme, msg, signature) + .verify(scheme, &digest, signature) .map_err(signature::Error::from_source)?; } DecodingKeyKind::RsaModulusExponent { n, e } => { RsaPublicKey::new(BigUint::from_bytes_be(n), BigUint::from_bytes_be(e))? - .verify(scheme, msg, signature) + .verify(scheme, &digest, signature) .map_err(signature::Error::from_source)?; } }; @@ -69,7 +80,7 @@ impl Rsa256Signer { impl Signer> for Rsa256Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { - try_sign_rsa::(&self.0, msg) + try_sign_rsa::(&self.0, msg, false) } } @@ -93,7 +104,7 @@ impl Rsa256Verifier { impl Verifier> for Rsa256Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - verify_rsa(Pkcs1v15Sign::new::(), &self.0, msg, signature) + verify_rsa::<_, Sha256>(Pkcs1v15Sign::new::(), &self.0, msg, signature) } } @@ -117,7 +128,7 @@ impl Rsa384Signer { impl Signer> for Rsa384Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { - try_sign_rsa::(&self.0, msg) + try_sign_rsa::(&self.0, msg, false) } } @@ -141,7 +152,7 @@ impl Rsa384Verifier { impl Verifier> for Rsa384Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - verify_rsa(Pkcs1v15Sign::new::(), &self.0, msg, signature) + verify_rsa::<_, Sha384>(Pkcs1v15Sign::new::(), &self.0, msg, signature) } } @@ -165,7 +176,7 @@ impl Rsa512Signer { impl Signer> for Rsa512Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { - try_sign_rsa::(&self.0, msg) + try_sign_rsa::(&self.0, msg, false) } } @@ -189,7 +200,7 @@ impl Rsa512Verifier { impl Verifier> for Rsa512Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - verify_rsa(Pkcs1v15Sign::new::(), &self.0, msg, signature) + verify_rsa::<_, Sha512>(Pkcs1v15Sign::new::(), &self.0, msg, signature) } } @@ -213,7 +224,7 @@ impl RsaPss256Signer { impl Signer> for RsaPss256Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { - try_sign_rsa::(&self.0, msg) + try_sign_rsa::(&self.0, msg, true) } } @@ -237,7 +248,7 @@ impl RsaPss256Verifier { impl Verifier> for RsaPss256Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - verify_rsa(Pss::new::(), &self.0, msg, signature) + verify_rsa::<_, Sha256>(Pss::new::(), &self.0, msg, signature) } } @@ -261,7 +272,7 @@ impl RsaPss384Signer { impl Signer> for RsaPss384Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { - try_sign_rsa::(&self.0, msg) + try_sign_rsa::(&self.0, msg, true) } } @@ -285,7 +296,7 @@ impl RsaPss384Verifier { impl Verifier> for RsaPss384Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - verify_rsa(Pss::new::(), &self.0, msg, signature) + verify_rsa::<_, Sha384>(Pss::new::(), &self.0, msg, signature) } } @@ -309,7 +320,7 @@ impl RsaPss512Signer { impl Signer> for RsaPss512Signer { fn try_sign(&self, msg: &[u8]) -> std::result::Result, signature::Error> { - try_sign_rsa::(&self.0, msg) + try_sign_rsa::(&self.0, msg, true) } } @@ -333,7 +344,7 @@ impl RsaPss512Verifier { impl Verifier> for RsaPss512Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - verify_rsa(Pss::new::(), &self.0, msg, signature) + verify_rsa::<_, Sha512>(Pss::new::(), &self.0, msg, signature) } } diff --git a/src/crypto/utils.rs b/src/crypto/utils.rs index 0dc7ab08..000e014e 100644 --- a/src/crypto/utils.rs +++ b/src/crypto/utils.rs @@ -4,7 +4,7 @@ use crate::{ algorithms::AlgorithmFamily, - errors::{self, new_error, ErrorKind, Result}, + errors::{new_error, ErrorKind, Result}, DecodingKey, EncodingKey, }; From 492760f5fda3cd7f7e2f56bfd539629d96ef7ff3 Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 21:45:33 +0900 Subject: [PATCH 32/35] Re-add the crypto::{sign, verify} convenience functions --- src/crypto/mod.rs | 30 ++++++ src/crypto/rust_crypto/hmac.rs | 2 +- src/decoding.rs | 6 +- src/encoding.rs | 2 +- tests/ecdsa/mod.rs | 67 ++++++------- tests/eddsa/mod.rs | 67 ++++++------- tests/lib.rs | 2 +- tests/rsa/mod.rs | 166 ++++++++++++++++----------------- 8 files changed, 189 insertions(+), 153 deletions(-) diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 5acca716..f6d38c28 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -3,6 +3,8 @@ //! [`Signer`] and [`Verifier`] traits respectively. use crate::algorithms::Algorithm; +use crate::errors::Result; +use crate::{DecodingKey, EncodingKey}; #[cfg(feature = "aws_lc_rs")] pub(crate) mod aws_lc; @@ -10,6 +12,7 @@ pub(crate) mod aws_lc; pub(crate) mod rust_crypto; pub(crate) mod utils; +use crate::serialization::{b64_decode, b64_encode}; use signature::{Signer, Verifier}; /// Trait providing the functionality to sign a JWT. @@ -27,3 +30,30 @@ pub trait JwtVerifier: Verifier> { /// Return the [`Algorithm`] corresponding to the signing module. fn algorithm(&self) -> Algorithm; } + +/// Take the payload of a JWT, sign it using the algorithm given and return +/// the base64 url safe encoded of the result. +/// +/// If you just want to encode a JWT, use `encode` instead. +pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result { + let provider = crate::encoding::jwt_signer_factory(&algorithm, key)?; + Ok(b64_encode(provider.sign(message))) +} + +/// Compares the signature given with a re-computed signature for HMAC or using the public key +/// for RSA/EC. +/// +/// If you just want to decode a JWT, use `decode` instead. +/// +/// `signature` is the signature part of a jwt (text after the second '.') +/// +/// `message` is base64(header) + "." + base64(claims) +pub fn verify( + signature: &str, + message: &[u8], + key: &DecodingKey, + algorithm: Algorithm, +) -> Result { + let provider = crate::decoding::jwt_verifier_factory(&algorithm, key)?; + Ok(provider.verify(message, &b64_decode(signature)?).is_ok()) +} diff --git a/src/crypto/rust_crypto/hmac.rs b/src/crypto/rust_crypto/hmac.rs index 90af0877..374bfa68 100644 --- a/src/crypto/rust_crypto/hmac.rs +++ b/src/crypto/rust_crypto/hmac.rs @@ -1,5 +1,5 @@ //! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for the -//! HMAC family of algorithms using `RustCrypto`'s [`hmac`]. +//! HMAC family of algorithms using `RustCtypto`'s [`hmac`]. use hmac::{Hmac, Mac}; use sha2::{Sha256, Sha384, Sha512}; diff --git a/src/decoding.rs b/src/decoding.rs index 407a8e96..ba7bafb1 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -246,7 +246,6 @@ pub fn decode( key: &DecodingKey, validation: &Validation, ) -> Result> { - // The decoding step is unfortunately a little clunky but that seems to be how I need to do it to fit it into the `decode` function let header = decode_header(token)?; if validation.validate_signature && !(validation.algorithms.contains(&header.alg)) { @@ -276,7 +275,10 @@ pub fn _decode( } /// Return the correct [`JwtVerifier`] based on the `algorithm`. -fn jwt_verifier_factory(algorithm: &Algorithm, key: &DecodingKey) -> Result> { +pub fn jwt_verifier_factory( + algorithm: &Algorithm, + key: &DecodingKey, +) -> Result> { let jwt_encoder = match algorithm { Algorithm::HS256 => Box::new(Hs256Verifier::new(key)?) as Box, Algorithm::HS384 => Box::new(Hs384Verifier::new(key)?) as Box, diff --git a/src/encoding.rs b/src/encoding.rs index b23fc185..bf03feb3 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -170,7 +170,7 @@ pub fn _encode( } /// Return the correct [`JwtSigner`] based on the `algorithm`. -fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result> { +pub(crate) fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result> { let jwt_signer = match algorithm { Algorithm::HS256 => Box::new(Hs256Signer::new(key)?) as Box, Algorithm::HS384 => Box::new(Hs384Signer::new(key)?) as Box, diff --git a/tests/ecdsa/mod.rs b/tests/ecdsa/mod.rs index 637433b0..e49af3a8 100644 --- a/tests/ecdsa/mod.rs +++ b/tests/ecdsa/mod.rs @@ -4,7 +4,10 @@ use time::OffsetDateTime; use wasm_bindgen_test::wasm_bindgen_test; #[cfg(feature = "use_pem")] -use jsonwebtoken::{decode, encode, Header, Validation}; +use jsonwebtoken::{ + crypto::{sign, verify}, + decode, encode, Header, Validation, +}; use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey}; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] @@ -14,38 +17,38 @@ pub struct Claims { exp: i64, } -// #[test] -// #[wasm_bindgen_test] -// fn round_trip_sign_verification_pk8() { -// let privkey = include_bytes!("private_ecdsa_key.pk8"); -// let pubkey = include_bytes!("public_ecdsa_key.pk8"); -// -// let encrypted = -// sign(b"hello world", &EncodingKey::from_ec_der(privkey), Algorithm::ES256).unwrap(); -// let is_valid = -// verify(&encrypted, b"hello world", &DecodingKey::from_ec_der(pubkey), Algorithm::ES256) -// .unwrap(); -// assert!(is_valid); -// } +#[test] +#[wasm_bindgen_test] +fn round_trip_sign_verification_pk8() { + let privkey = include_bytes!("private_ecdsa_key.pk8"); + let pubkey = include_bytes!("public_ecdsa_key.pk8"); + + let encrypted = + sign(b"hello world", &EncodingKey::from_ec_der(privkey), Algorithm::ES256).unwrap(); + let is_valid = + verify(&encrypted, b"hello world", &DecodingKey::from_ec_der(pubkey), Algorithm::ES256) + .unwrap(); + assert!(is_valid); +} -// #[cfg(feature = "use_pem")] -// #[test] -// #[wasm_bindgen_test] -// fn round_trip_sign_verification_pem() { -// let privkey_pem = include_bytes!("private_ecdsa_key.pem"); -// let pubkey_pem = include_bytes!("public_ecdsa_key.pem"); -// let encrypted = -// sign(b"hello world", &EncodingKey::from_ec_pem(privkey_pem).unwrap(), Algorithm::ES256) -// .unwrap(); -// let is_valid = verify( -// &encrypted, -// b"hello world", -// &DecodingKey::from_ec_pem(pubkey_pem).unwrap(), -// Algorithm::ES256, -// ) -// .unwrap(); -// assert!(is_valid); -// } +#[cfg(feature = "use_pem")] +#[test] +#[wasm_bindgen_test] +fn round_trip_sign_verification_pem() { + let privkey_pem = include_bytes!("private_ecdsa_key.pem"); + let pubkey_pem = include_bytes!("public_ecdsa_key.pem"); + let encrypted = + sign(b"hello world", &EncodingKey::from_ec_pem(privkey_pem).unwrap(), Algorithm::ES256) + .unwrap(); + let is_valid = verify( + &encrypted, + b"hello world", + &DecodingKey::from_ec_pem(pubkey_pem).unwrap(), + Algorithm::ES256, + ) + .unwrap(); + assert!(is_valid); +} #[cfg(feature = "use_pem")] #[test] diff --git a/tests/eddsa/mod.rs b/tests/eddsa/mod.rs index 38b71dc1..d568a122 100644 --- a/tests/eddsa/mod.rs +++ b/tests/eddsa/mod.rs @@ -4,7 +4,10 @@ use time::OffsetDateTime; use wasm_bindgen_test::wasm_bindgen_test; #[cfg(feature = "use_pem")] -use jsonwebtoken::{decode, encode, Header, Validation}; +use jsonwebtoken::{ + crypto::{sign, verify}, + decode, encode, Header, Validation, +}; use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey}; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] @@ -14,38 +17,38 @@ pub struct Claims { exp: i64, } -// #[test] -// #[wasm_bindgen_test] -// fn round_trip_sign_verification_pk8() { -// let privkey = include_bytes!("private_ed25519_key.pk8"); -// let pubkey = include_bytes!("public_ed25519_key.pk8"); -// -// let encrypted = -// sign(b"hello world", &EncodingKey::from_ed_der(privkey), Algorithm::EdDSA).unwrap(); -// let is_valid = -// verify(&encrypted, b"hello world", &DecodingKey::from_ed_der(pubkey), Algorithm::EdDSA) -// .unwrap(); -// assert!(is_valid); -// } +#[test] +#[wasm_bindgen_test] +fn round_trip_sign_verification_pk8() { + let privkey = include_bytes!("private_ed25519_key.pk8"); + let pubkey = include_bytes!("public_ed25519_key.pk8"); + + let encrypted = + sign(b"hello world", &EncodingKey::from_ed_der(privkey), Algorithm::EdDSA).unwrap(); + let is_valid = + verify(&encrypted, b"hello world", &DecodingKey::from_ed_der(pubkey), Algorithm::EdDSA) + .unwrap(); + assert!(is_valid); +} -// #[cfg(feature = "use_pem")] -// #[test] -// #[wasm_bindgen_test] -// fn round_trip_sign_verification_pem() { -// let privkey_pem = include_bytes!("private_ed25519_key.pem"); -// let pubkey_pem = include_bytes!("public_ed25519_key.pem"); -// let encrypted = -// sign(b"hello world", &EncodingKey::from_ed_pem(privkey_pem).unwrap(), Algorithm::EdDSA) -// .unwrap(); -// let is_valid = verify( -// &encrypted, -// b"hello world", -// &DecodingKey::from_ed_pem(pubkey_pem).unwrap(), -// Algorithm::EdDSA, -// ) -// .unwrap(); -// assert!(is_valid); -// } +#[cfg(feature = "use_pem")] +#[test] +#[wasm_bindgen_test] +fn round_trip_sign_verification_pem() { + let privkey_pem = include_bytes!("private_ed25519_key.pem"); + let pubkey_pem = include_bytes!("public_ed25519_key.pem"); + let encrypted = + sign(b"hello world", &EncodingKey::from_ed_pem(privkey_pem).unwrap(), Algorithm::EdDSA) + .unwrap(); + let is_valid = verify( + &encrypted, + b"hello world", + &DecodingKey::from_ed_pem(pubkey_pem).unwrap(), + Algorithm::EdDSA, + ) + .unwrap(); + assert!(is_valid); +} #[cfg(feature = "use_pem")] #[test] diff --git a/tests/lib.rs b/tests/lib.rs index 2eadda2c..89661df9 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,4 +1,4 @@ mod ecdsa; mod eddsa; -// mod header; +mod header; mod rsa; diff --git a/tests/rsa/mod.rs b/tests/rsa/mod.rs index 74e03291..5c00cca4 100644 --- a/tests/rsa/mod.rs +++ b/tests/rsa/mod.rs @@ -3,14 +3,12 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use wasm_bindgen_test::wasm_bindgen_test; -#[cfg(feature = "use_pem")] -use jsonwebtoken::{decode, encode, Header, Validation}; use jsonwebtoken::{ - // crypto::{sign, verify}, - Algorithm, - DecodingKey, - EncodingKey, + crypto::{sign, verify}, + Algorithm, DecodingKey, EncodingKey, }; +#[cfg(feature = "use_pem")] +use jsonwebtoken::{decode, encode, Header, Validation}; const RSA_ALGORITHMS: &[Algorithm] = &[ Algorithm::RS256, @@ -30,84 +28,84 @@ pub struct Claims { // Todo: These no longer apply because `verify` does not exist, would probably need to convert it to test the factory for getting signers and verifiers. But I would rather this not be part of the public facing API. -// #[cfg(feature = "use_pem")] -// #[test] -// #[wasm_bindgen_test] -// fn round_trip_sign_verification_pem_pkcs1() { -// let privkey_pem = include_bytes!("private_rsa_key_pkcs1.pem"); -// let pubkey_pem = include_bytes!("public_rsa_key_pkcs1.pem"); -// let certificate_pem = include_bytes!("certificate_rsa_key_pkcs1.crt"); - -// for &alg in RSA_ALGORITHMS { -// let encrypted = -// sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); - -// let is_valid = verify( -// &encrypted, -// b"hello world", -// &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), -// alg, -// ) -// .unwrap(); -// assert!(is_valid); - -// let cert_is_valid = verify( -// &encrypted, -// b"hello world", -// &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), -// alg, -// ) -// .unwrap(); -// assert!(cert_is_valid); -// } -// } - -// #[cfg(feature = "use_pem")] -// #[test] -// #[wasm_bindgen_test] -// fn round_trip_sign_verification_pem_pkcs8() { -// let privkey_pem = include_bytes!("private_rsa_key_pkcs8.pem"); -// let pubkey_pem = include_bytes!("public_rsa_key_pkcs8.pem"); -// let certificate_pem = include_bytes!("certificate_rsa_key_pkcs8.crt"); - -// for &alg in RSA_ALGORITHMS { -// let encrypted = -// sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); - -// let is_valid = verify( -// &encrypted, -// b"hello world", -// &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), -// alg, -// ) -// .unwrap(); -// assert!(is_valid); - -// let cert_is_valid = verify( -// &encrypted, -// b"hello world", -// &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), -// alg, -// ) -// .unwrap(); -// assert!(cert_is_valid); -// } -// } - -// #[test] -// #[wasm_bindgen_test] -// fn round_trip_sign_verification_der() { -// let privkey_der = include_bytes!("private_rsa_key.der"); -// let pubkey_der = include_bytes!("public_rsa_key.der"); - -// for &alg in RSA_ALGORITHMS { -// let encrypted = sign(b"hello world", &EncodingKey::from_rsa_der(privkey_der), alg).unwrap(); -// let is_valid = -// verify(&encrypted, b"hello world", &DecodingKey::from_rsa_der(pubkey_der), alg) -// .unwrap(); -// assert!(is_valid); -// } -// } +#[cfg(feature = "use_pem")] +#[test] +#[wasm_bindgen_test] +fn round_trip_sign_verification_pem_pkcs1() { + let privkey_pem = include_bytes!("private_rsa_key_pkcs1.pem"); + let pubkey_pem = include_bytes!("public_rsa_key_pkcs1.pem"); + let certificate_pem = include_bytes!("certificate_rsa_key_pkcs1.crt"); + + for &alg in RSA_ALGORITHMS { + let encrypted = + sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); + + let is_valid = verify( + &encrypted, + b"hello world", + &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), + alg, + ) + .unwrap(); + assert!(is_valid); + + let cert_is_valid = verify( + &encrypted, + b"hello world", + &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), + alg, + ) + .unwrap(); + assert!(cert_is_valid); + } +} + +#[cfg(feature = "use_pem")] +#[test] +#[wasm_bindgen_test] +fn round_trip_sign_verification_pem_pkcs8() { + let privkey_pem = include_bytes!("private_rsa_key_pkcs8.pem"); + let pubkey_pem = include_bytes!("public_rsa_key_pkcs8.pem"); + let certificate_pem = include_bytes!("certificate_rsa_key_pkcs8.crt"); + + for &alg in RSA_ALGORITHMS { + let encrypted = + sign(b"hello world", &EncodingKey::from_rsa_pem(privkey_pem).unwrap(), alg).unwrap(); + + let is_valid = verify( + &encrypted, + b"hello world", + &DecodingKey::from_rsa_pem(pubkey_pem).unwrap(), + alg, + ) + .unwrap(); + assert!(is_valid); + + let cert_is_valid = verify( + &encrypted, + b"hello world", + &DecodingKey::from_rsa_pem(certificate_pem).unwrap(), + alg, + ) + .unwrap(); + assert!(cert_is_valid); + } +} + +#[test] +#[wasm_bindgen_test] +fn round_trip_sign_verification_der() { + let privkey_der = include_bytes!("private_rsa_key.der"); + let pubkey_der = include_bytes!("public_rsa_key.der"); + + for &alg in RSA_ALGORITHMS { + let encrypted = sign(b"hello world", &EncodingKey::from_rsa_der(privkey_der), alg).unwrap(); + let is_valid = + verify(&encrypted, b"hello world", &DecodingKey::from_rsa_der(pubkey_der), alg) + .unwrap(); + assert!(is_valid); + } +} #[cfg(feature = "use_pem")] #[test] From eed1c1c3d3550086ca14731cb7182baee088f898 Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sat, 10 May 2025 21:49:14 +0900 Subject: [PATCH 33/35] Require at least one crypto backend to be enabled --- src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0fc4404e..6ed1fc82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,11 @@ compile_error!( "feature \"rust_crypto\" and feature \"aws_lc_rs\" cannot be enabled at the same time" ); +#[cfg(not(any(feature = "rust_crypto", feature = "aws_lc_rs")))] +compile_error!( + "at least one of the features \"rust_crypto\" or \"aws_lc_rs\" must be enabled" +); + pub use algorithms::Algorithm; pub use decoding::{decode, decode_header, DecodingKey, TokenData, _decode}; pub use encoding::{encode, EncodingKey, _encode}; @@ -26,4 +31,4 @@ pub mod jwk; #[cfg(feature = "use_pem")] mod pem; mod serialization; -mod validation; +mod validation; \ No newline at end of file From 660d89ec01cd0e4f569dd3be7a2732d0c014c4ef Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sun, 11 May 2025 11:55:26 +0900 Subject: [PATCH 34/35] Ensure tests pass without use_pem feature as well --- tests/ecdsa/mod.rs | 6 +++--- tests/eddsa/mod.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ecdsa/mod.rs b/tests/ecdsa/mod.rs index e49af3a8..fe2fbb00 100644 --- a/tests/ecdsa/mod.rs +++ b/tests/ecdsa/mod.rs @@ -3,12 +3,12 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use wasm_bindgen_test::wasm_bindgen_test; -#[cfg(feature = "use_pem")] use jsonwebtoken::{ crypto::{sign, verify}, - decode, encode, Header, Validation, + Algorithm, DecodingKey, EncodingKey, }; -use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey}; +#[cfg(feature = "use_pem")] +use jsonwebtoken::{decode, encode, Header, Validation}; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { diff --git a/tests/eddsa/mod.rs b/tests/eddsa/mod.rs index d568a122..61cc209b 100644 --- a/tests/eddsa/mod.rs +++ b/tests/eddsa/mod.rs @@ -3,12 +3,12 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use wasm_bindgen_test::wasm_bindgen_test; -#[cfg(feature = "use_pem")] use jsonwebtoken::{ crypto::{sign, verify}, - decode, encode, Header, Validation, + Algorithm, DecodingKey, EncodingKey, }; -use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey}; +#[cfg(feature = "use_pem")] +use jsonwebtoken::{decode, encode, Header, Validation}; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { From 9adb7da1e1394be4051d9b15d7bebbf3a4398315 Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Sun, 11 May 2025 14:00:57 +0900 Subject: [PATCH 35/35] Fix dependency features & clippy lints --- Cargo.toml | 4 ++-- src/crypto/aws_lc/hmac.rs | 6 +++--- src/crypto/aws_lc/rsa.rs | 12 ++++-------- src/crypto/rust_crypto/hmac.rs | 6 +++--- src/encoding.rs | 7 +++++-- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bc2dc017..2c4da9be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ rust-version = "1.73.0" base64 = "0.22" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -signature = "2.2.0" +signature = { version = "2.2.0", features = ["std"] } # For PEM decoding pem = { version = "3", optional = true } @@ -33,7 +33,7 @@ simple_asn1 = { version = "0.6", optional = true } aws-lc-rs = { version = "1.10.0", optional = true } # "rust_crypto" feature -ed25519-dalek = { version = "2.1.1", optional = true } +ed25519-dalek = { version = "2.1.1", optional = true, features = ["pkcs8"] } hmac = { version = "0.12.1", optional = true } p256 = { version = "0.13.2", optional = true, features = ["ecdsa"] } p384 = { version = "0.13.0", optional = true, features = ["ecdsa"] } diff --git a/src/crypto/aws_lc/hmac.rs b/src/crypto/aws_lc/hmac.rs index 73413e2d..d5b79337 100644 --- a/src/crypto/aws_lc/hmac.rs +++ b/src/crypto/aws_lc/hmac.rs @@ -47,7 +47,7 @@ impl Hs256Verifier { impl Verifier> for Hs256Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - hmac::verify(&self.0, msg, &signature).map_err(|err| signature::Error::from_source(err)) + hmac::verify(&self.0, msg, signature).map_err(signature::Error::from_source) } } @@ -93,7 +93,7 @@ impl Hs384Verifier { impl Verifier> for Hs384Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - hmac::verify(&self.0, msg, &signature).map_err(|err| signature::Error::from_source(err)) + hmac::verify(&self.0, msg, signature).map_err(signature::Error::from_source) } } @@ -139,7 +139,7 @@ impl Hs512Verifier { impl Verifier> for Hs512Verifier { fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), signature::Error> { - hmac::verify(&self.0, msg, &signature).map_err(|err| signature::Error::from_source(err)) + hmac::verify(&self.0, msg, signature).map_err(signature::Error::from_source) } } diff --git a/src/crypto/aws_lc/rsa.rs b/src/crypto/aws_lc/rsa.rs index bf47bfc3..6aada3ff 100644 --- a/src/crypto/aws_lc/rsa.rs +++ b/src/crypto/aws_lc/rsa.rs @@ -17,13 +17,11 @@ fn try_sign_rsa( msg: &[u8], ) -> std::result::Result, signature::Error> { let key_pair = crypto_sig::RsaKeyPair::from_der(encoding_key.inner()) - .map_err(|err| signature::Error::from_source(err))?; + .map_err(signature::Error::from_source)?; let mut signature = vec![0; key_pair.public_modulus_len()]; let rng = rand::SystemRandom::new(); - key_pair - .sign(algorithm, &rng, msg, &mut signature) - .map_err(|err| signature::Error::from_source(err))?; + key_pair.sign(algorithm, &rng, msg, &mut signature).map_err(signature::Error::from_source)?; Ok(signature) } @@ -42,13 +40,11 @@ fn verify_rsa( match &decoding_key.kind { DecodingKeyKind::SecretOrDer(bytes) => { let public_key = crypto_sig::UnparsedPublicKey::new(algorithm, bytes); - public_key.verify(msg, signature).map_err(|err| signature::Error::from_source(err))?; + public_key.verify(msg, signature).map_err(signature::Error::from_source)?; } DecodingKeyKind::RsaModulusExponent { n, e } => { let public_key = crypto_sig::RsaPublicKeyComponents { n, e }; - public_key - .verify(algorithm, msg, &signature) - .map_err(|err| signature::Error::from_source(err))?; + public_key.verify(algorithm, msg, signature).map_err(signature::Error::from_source)?; } }; diff --git a/src/crypto/rust_crypto/hmac.rs b/src/crypto/rust_crypto/hmac.rs index 374bfa68..2b19cf42 100644 --- a/src/crypto/rust_crypto/hmac.rs +++ b/src/crypto/rust_crypto/hmac.rs @@ -62,7 +62,7 @@ impl Verifier> for Hs256Verifier { verifier.reset(); verifier.update(msg); - verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) + verifier.verify_slice(signature).map_err(signature::Error::from_source) } } @@ -118,7 +118,7 @@ impl Verifier> for Hs384Verifier { verifier.reset(); verifier.update(msg); - verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) + verifier.verify_slice(signature).map_err(signature::Error::from_source) } } @@ -174,7 +174,7 @@ impl Verifier> for Hs512Verifier { verifier.reset(); verifier.update(msg); - verifier.verify_slice(signature).map_err(|e| signature::Error::from_source(e)) + verifier.verify_slice(signature).map_err(signature::Error::from_source) } } diff --git a/src/encoding.rs b/src/encoding.rs index bf03feb3..696ecfeb 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -164,13 +164,16 @@ pub fn _encode( let encoded_claims = b64_encode_part(claims)?; let message = [encoded_header, encoded_claims].join("."); - let signature = b64_encode(&signing_provider.sign(message.as_bytes())); + let signature = b64_encode(signing_provider.sign(message.as_bytes())); Ok([message, signature].join(".")) } /// Return the correct [`JwtSigner`] based on the `algorithm`. -pub(crate) fn jwt_signer_factory(algorithm: &Algorithm, key: &EncodingKey) -> Result> { +pub(crate) fn jwt_signer_factory( + algorithm: &Algorithm, + key: &EncodingKey, +) -> Result> { let jwt_signer = match algorithm { Algorithm::HS256 => Box::new(Hs256Signer::new(key)?) as Box, Algorithm::HS384 => Box::new(Hs384Signer::new(key)?) as Box,