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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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 } }