diff --git a/CHANGELOG.md b/CHANGELOG.md index 1adb0296..c5e65374 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 10.0.0 (unreleased) + ## 9.3.1 (2024-02-06) - Update base64 diff --git a/Cargo.toml b/Cargo.toml index aed9704c..2c4da9be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,23 +20,35 @@ 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 = { version = "2.2.0", features = ["std"] } + # For PEM decoding pem = { version = "3", optional = true } simple_asn1 = { version = "0.6", optional = true } -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -ring = { version = "0.17.4", features = ["std"] } +# "aws_lc_rs" feature +aws-lc-rs = { version = "1.10.0", optional = true } + +# "rust_crypto" feature +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"] } +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" -ring = { version = "0.17.4", features = ["std", "wasm32_unknown_unknown_js"] } [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" @@ -48,8 +60,10 @@ time = { version = "0.3", features = ["wasm-bindgen"] } criterion = { version = "0.4", default-features = false } [features] -default = ["use_pem"] +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"] [[bench]] name = "jwt" 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 21a8f217..8ee8b3fa 100644 --- a/examples/ed25519.rs +++ b/examples/ed25519.rs @@ -1,8 +1,11 @@ +use ed25519_dalek::pkcs8::EncodePrivateKey; +use ed25519_dalek::SigningKey; +use rand_core::OsRng; +use serde::{Deserialize, Serialize}; + use jsonwebtoken::{ decode, encode, get_current_timestamp, Algorithm, DecodingKey, EncodingKey, Validation, }; -use ring::signature::{Ed25519KeyPair, KeyPair}; -use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct Claims { @@ -11,11 +14,16 @@ 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 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 pair = Ed25519KeyPair::from_pkcs8(doc.as_ref()).unwrap(); - let decoding_key = DecodingKey::from_ed_der(pair.public_key().as_ref()); + 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() }; @@ -37,11 +45,17 @@ 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 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 verifying_key = signing_key.verifying_key(); + let public_key = verifying_key.as_bytes(); + let decoding_key = DecodingKey::from_ed_der(public_key); - 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 ffabc6f1..ed2a9a8c 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/aws_lc/ecdsa.rs b/src/crypto/aws_lc/ecdsa.rs new file mode 100644 index 00000000..de8b6aba --- /dev/null +++ b/src/crypto/aws_lc/ecdsa.rs @@ -0,0 +1,126 @@ +//! 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::{new_error, ErrorKind, 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 { + 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(|_| 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 { + if decoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + 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 { + 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)?, + )) + } +} + +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 { + if decoding_key.family != AlgorithmFamily::Ec { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + 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/eddsa.rs b/src/crypto/aws_lc/eddsa.rs new file mode 100644 index 00000000..0e7994a9 --- /dev/null +++ b/src/crypto/aws_lc/eddsa.rs @@ -0,0 +1,60 @@ +//! Implementations of the [`JwtSigner`] and [`JwtVerifier`] traits for EdDSA using AWS-LC-RS. + +use crate::algorithms::AlgorithmFamily; +use crate::crypto::{JwtSigner, JwtVerifier}; +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}; + +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(|_| 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 { + if decoding_key.family != AlgorithmFamily::Ed { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + 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/hmac.rs b/src/crypto/aws_lc/hmac.rs new file mode 100644 index 00000000..d5b79337 --- /dev/null +++ b/src/crypto/aws_lc/hmac.rs @@ -0,0 +1,150 @@ +//! 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::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, DecodingKey, EncodingKey}; + +pub struct Hs256Signer(hmac::Key); + +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 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 Hs256Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::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(signature::Error::from_source) + } +} + +impl JwtVerifier for Hs256Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::HS256 + } +} + +pub struct Hs384Signer(hmac::Key); + +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 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 Hs384Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::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(signature::Error::from_source) + } +} + +impl JwtVerifier for Hs384Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::HS384 + } +} + +pub struct Hs512Signer(hmac::Key); + +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 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 Hs512Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::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(signature::Error::from_source) + } +} + +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 new file mode 100644 index 00000000..16f66b5f --- /dev/null +++ b/src/crypto/aws_lc/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod ecdsa; +pub(crate) mod eddsa; +pub(crate) mod hmac; +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..6aada3ff --- /dev/null +++ b/src/crypto/aws_lc/rsa.rs @@ -0,0 +1,340 @@ +//! 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(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(signature::Error::from_source)?; + + 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(signature::Error::from_source)?; + } + DecodingKeyKind::RsaModulusExponent { n, e } => { + let public_key = crypto_sig::RsaPublicKeyComponents { n, e }; + public_key.verify(algorithm, 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(&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 + } +} + +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/ecdsa.rs b/src/crypto/ecdsa.rs deleted file mode 100644 index 99b92fc6..00000000 --- a/src/crypto/ecdsa.rs +++ /dev/null @@ -1,38 +0,0 @@ -use ring::{rand, signature}; - -use crate::algorithms::Algorithm; -use crate::errors::Result; -use crate::serialization::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 { - match alg { - Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED, - Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED, - _ => 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 { - match alg { - Algorithm::ES256 => &signature::ECDSA_P256_SHA256_FIXED_SIGNING, - Algorithm::ES384 => &signature::ECDSA_P384_SHA384_FIXED_SIGNING, - _ => 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 rng = rand::SystemRandom::new(); - let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, key, &rng)?; - let out = signing_key.sign(&rng, message)?; - Ok(b64_encode(out)) -} diff --git a/src/crypto/eddsa.rs b/src/crypto/eddsa.rs deleted file mode 100644 index 7c185347..00000000 --- a/src/crypto/eddsa.rs +++ /dev/null @@ -1,23 +0,0 @@ -use ring::signature; - -use crate::algorithms::Algorithm; -use crate::errors::Result; -use crate::serialization::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"), - } -} - -/// 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 out = signing_key.sign(message); - Ok(b64_encode(out)) -} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index c2957dc8..f6d38c28 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,21 +1,34 @@ -use ring::constant_time::verify_slices_are_equal; -use ring::{hmac, signature}; +//! 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; +use crate::{DecodingKey, EncodingKey}; + +#[cfg(feature = "aws_lc_rs")] +pub(crate) mod aws_lc; +#[cfg(feature = "rust_crypto")] +pub(crate) mod rust_crypto; +pub(crate) mod utils; + use crate::serialization::{b64_decode, b64_encode}; +use signature::{Signer, Verifier}; -pub(crate) mod ecdsa; -pub(crate) mod eddsa; -pub(crate) mod rsa; +/// 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; +} -/// The actual HS signing + encoding -/// Could be in its own file to match RSA/EC but it's 2 lines... -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) +/// 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 @@ -23,38 +36,8 @@ 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 { - Algorithm::HS256 => Ok(sign_hmac(hmac::HMAC_SHA256, key.inner(), message)), - Algorithm::HS384 => Ok(sign_hmac(hmac::HMAC_SHA384, key.inner(), message)), - Algorithm::HS512 => Ok(sign_hmac(hmac::HMAC_SHA512, key.inner(), message)), - - Algorithm::ES256 | Algorithm::ES384 => { - ecdsa::sign(ecdsa::alg_to_ec_signing(algorithm), key.inner(), message) - } - - Algorithm::EdDSA => eddsa::sign(key.inner(), message), - - Algorithm::RS256 - | Algorithm::RS384 - | Algorithm::RS512 - | Algorithm::PS256 - | Algorithm::PS384 - | Algorithm::PS512 => rsa::sign(rsa::alg_to_rsa_signing(algorithm), key.inner(), message), - } -} - -/// 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()) + 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 @@ -71,37 +54,6 @@ pub fn verify( key: &DecodingKey, algorithm: Algorithm, ) -> Result { - match algorithm { - 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()) - } - Algorithm::ES256 | Algorithm::ES384 => verify_ring( - ecdsa::alg_to_ec_verification(algorithm), - signature, - message, - key.as_bytes(), - ), - Algorithm::EdDSA => verify_ring( - eddsa::alg_to_ec_verification(algorithm), - signature, - message, - key.as_bytes(), - ), - Algorithm::RS256 - | Algorithm::RS384 - | Algorithm::RS512 - | Algorithm::PS256 - | Algorithm::PS384 - | Algorithm::PS512 => { - let alg = rsa::alg_to_rsa_parameters(algorithm); - match &key.kind { - DecodingKeyKind::SecretOrDer(bytes) => verify_ring(alg, signature, message, bytes), - DecodingKeyKind::RsaModulusExponent { n, e } => { - rsa::verify_from_components(alg, signature, message, (n, e)) - } - } - } - } + let provider = crate::decoding::jwt_verifier_factory(&algorithm, key)?; + Ok(provider.verify(message, &b64_decode(signature)?).is_ok()) } diff --git a/src/crypto/rsa.rs b/src/crypto/rsa.rs deleted file mode 100644 index 4c97db3c..00000000 --- a/src/crypto/rsa.rs +++ /dev/null @@ -1,62 +0,0 @@ -use ring::{rand, signature}; - -use crate::algorithms::Algorithm; -use crate::errors::{ErrorKind, Result}; -use crate::serialization::{b64_decode, b64_encode}; - -/// Only used internally when validating RSA, to map from our enum to the Ring param structs. -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. -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 -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.to_string()))?; - - 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 -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()) -} diff --git a/src/crypto/rust_crypto/ecdsa.rs b/src/crypto/rust_crypto/ecdsa.rs new file mode 100644 index 00000000..790aa39c --- /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 rsa::pkcs8::DecodePrivateKey; +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_pkcs8_der(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_pkcs8_der(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/eddsa.rs b/src/crypto/rust_crypto/eddsa.rs new file mode 100644 index 00000000..2d59b535 --- /dev/null +++ b/src/crypto/rust_crypto/eddsa.rs @@ -0,0 +1,67 @@ +//! 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::pkcs8::DecodePrivateKey; +use ed25519_dalek::{Signature, SigningKey, VerifyingKey}; +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_pkcs8_der(encoding_key.inner()) + .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(VerifyingKey); + +impl EdDSAVerifier { + pub(crate) fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family != AlgorithmFamily::Ed { + return Err(new_error(ErrorKind::InvalidKeyFormat)); + } + + Ok(Self( + VerifyingKey::from_bytes( + <&[u8; 32]>::try_from(&decoding_key.as_bytes()[..32]) + .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 new file mode 100644 index 00000000..2b19cf42 --- /dev/null +++ b/src/crypto/rust_crypto/hmac.rs @@ -0,0 +1,185 @@ +//! 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::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, DecodingKey, EncodingKey}; + +type HmacSha256 = Hmac; +type HmacSha384 = Hmac; +type HmacSha512 = Hmac; + +pub struct Hs256Signer(HmacSha256); + +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 Hs256Signer { + 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 Hs256Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::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(); + verifier.update(msg); + + verifier.verify_slice(signature).map_err(signature::Error::from_source) + } +} + +impl JwtVerifier for Hs256Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::HS256 + } +} + +pub struct Hs384Signer(HmacSha384); + +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 Hs384Signer { + 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 Hs384Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::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(); + verifier.update(msg); + + verifier.verify_slice(signature).map_err(signature::Error::from_source) + } +} + +impl JwtVerifier for Hs384Verifier { + fn algorithm(&self) -> Algorithm { + Algorithm::HS384 + } +} + +pub struct Hs512Signer(HmacSha512); + +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 Hs512Signer { + 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 Hs512Signer { + fn algorithm(&self) -> Algorithm { + Algorithm::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(); + verifier.update(msg); + + verifier.verify_slice(signature).map_err(signature::Error::from_source) + } +} + +impl JwtVerifier for Hs512Verifier { + 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..16f66b5f --- /dev/null +++ b/src/crypto/rust_crypto/mod.rs @@ -0,0 +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..c0d8de17 --- /dev/null +++ b/src/crypto/rust_crypto/rsa.rs @@ -0,0 +1,355 @@ +//! 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::{RandomizedSigner, 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], + pss: bool, +) -> std::result::Result, signature::Error> +where + H: Digest + AssociatedOid + FixedOutputReset, +{ + 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( + 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, &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, &digest, 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, false) + } +} + +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::<_, Sha256>(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, false) + } +} + +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::<_, Sha384>(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, false) + } +} + +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::<_, Sha512>(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, true) + } +} + +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::<_, Sha256>(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, true) + } +} + +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::<_, Sha384>(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, true) + } +} + +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::<_, Sha512>(Pss::new::(), &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 new file mode 100644 index 00000000..000e014e --- /dev/null +++ b/src/crypto/utils.rs @@ -0,0 +1,25 @@ +//! # Todo +//! +//! - Put in documentation + +use crate::{ + algorithms::AlgorithmFamily, + errors::{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 8d87f03d..ba7bafb1 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::verify; +use crate::crypto::JwtVerifier; use crate::errors::{new_error, ErrorKind, Result}; use crate::header::Header; use crate::jwk::{AlgorithmParameters, Jwk}; @@ -10,6 +10,28 @@ use crate::jwk::{AlgorithmParameters, Jwk}; 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::{ + ecdsa::{Es256Verifier, Es384Verifier}, + eddsa::EdDSAVerifier, + hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier}, + rsa::{ + Rsa256Verifier, Rsa384Verifier, Rsa512Verifier, RsaPss256Verifier, RsaPss384Verifier, + RsaPss512Verifier, + }, +}; +#[cfg(feature = "rust_crypto")] +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)] @@ -201,41 +223,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. @@ -259,16 +246,55 @@ 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 }) - } + let header = decode_header(token)?; + + if validation.validate_signature && !(validation.algorithms.contains(&header.alg)) { + return Err(new_error(ErrorKind::InvalidAlgorithm)); } + + let verifying_provider = jwt_verifier_factory(&header.alg, key)?; + + _decode(token, validation, verifying_provider) +} + +/// # 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`. +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, + Algorithm::HS512 => Box::new(Hs512Verifier::new(key)?) as Box, + 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, + 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, + }; + + Ok(jwt_encoder) } /// Decode a JWT without any signature verification/validations and return its [Header](struct.Header.html). @@ -286,3 +312,41 @@ pub fn decode_header(token: &str) -> Result
{ let (_, header) = expect_two!(message.rsplitn(2, '.')); Header::from_encoded(header) } + +/// 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, + validation: &Validation, + verifying_provider: Box, +) -> Result<(Header, &'a str)> { + if validation.validate_signature && 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 validation.validate_signature && !validation.algorithms.contains(&header.alg) { + return Err(new_error(ErrorKind::InvalidAlgorithm)); + } + + if validation.validate_signature + && verifying_provider.verify(message.as_bytes(), &b64_decode(signature)?).is_err() + { + return Err(new_error(ErrorKind::InvalidSignature)); + } + + Ok((header, payload)) +} diff --git a/src/encoding.rs b/src/encoding.rs index 26f5c4c3..696ecfeb 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -2,12 +2,32 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use serde::ser::Serialize; use crate::algorithms::AlgorithmFamily; -use crate::crypto; +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::serialization::{b64_encode, b64_encode_part}; +use crate::Algorithm; +// Crypto +#[cfg(feature = "aws_lc_rs")] +use crate::crypto::aws_lc::{ + ecdsa::{Es256Signer, Es384Signer}, + eddsa::EdDSASigner, + hmac::{Hs256Signer, Hs384Signer, Hs512Signer}, + rsa::{ + Rsa256Signer, Rsa384Signer, Rsa512Signer, RsaPss256Signer, RsaPss384Signer, RsaPss512Signer, + }, +}; +#[cfg(feature = "rust_crypto")] +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. @@ -122,10 +142,52 @@ 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 signing_provider = jwt_signer_factory(&header.alg, key)?; + + _encode(header, claims, signing_provider) +} + +/// # 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 = crypto::sign(message.as_bytes(), key, header.alg)?; + + 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> { + let jwt_signer = match algorithm { + 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 => 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, + 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, + }; + + Ok(jwt_signer) +} diff --git a/src/errors.rs b/src/errors.rs index 2edd7df5..7ad913f3 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,8 +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 - Crypto(::ring::error::Unspecified), } impl StdError for Error { @@ -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::Crypto(err) => Some(err), } } } @@ -121,12 +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), - ErrorKind::Crypto(err) => write!(f, "Crypto error: {}", err), ErrorKind::Base64(err) => write!(f, "Base64 error: {}", err), } } @@ -159,18 +159,6 @@ impl From<::std::string::FromUtf8Error> for Error { } } -impl From<::ring::error::Unspecified> for Error { - fn from(err: ::ring::error::Unspecified) -> Error { - new_error(ErrorKind::Crypto(err)) - } -} - -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 d1686dab..15501cf0 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)] @@ -438,11 +440,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..6ed1fc82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,22 @@ //! 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" +); + +#[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}; +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; @@ -15,10 +31,4 @@ pub mod jwk; #[cfg(feature = "use_pem")] 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}; +mod validation; \ No newline at end of file diff --git a/src/validation.rs b/src/validation.rs index bfceded5..a6b5dd2b 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -195,12 +195,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, @@ -212,6 +214,7 @@ impl<'de, T: Deserialize<'de>> Deserialize<'de> for TryParse { }) } } + impl Default for TryParse { fn default() -> Self { Self::NotPresent @@ -237,6 +240,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 @@ -371,14 +375,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 dba0a169..e7762a4b 100644 --- a/tests/hmac.rs +++ b/tests/hmac.rs @@ -1,14 +1,22 @@ -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 std::collections::HashMap; 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, +}; + #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct Claims { sub: String, @@ -16,23 +24,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 +52,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"), @@ -171,21 +180,19 @@ fn decode_token_missing_parts() { #[test] #[wasm_bindgen_test] -#[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"), &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::( @@ -193,12 +200,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(), @@ -206,7 +212,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] diff --git a/tests/rsa/mod.rs b/tests/rsa/mod.rs index 3297149f..5c00cca4 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, @@ -26,6 +26,8 @@ pub struct Claims { exp: i64, } +// 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]