diff --git a/Cargo.toml b/Cargo.toml index f62f3a5e..61862f0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,8 @@ 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" +# for the custom provider example +botan = { version = "0.12.0", features = ["vendored"] } [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/README.md b/README.md index 09b9f719..ec1b5a06 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ jsonwebtoken = { version = "10", features = ["aws_lc_rs"] } serde = {version = "1.0", features = ["derive"] } ``` -Two crypto backends are available via features, `aws_lc_rs` and `rust_crypto`, exactly one of which must be enabled. +Two crypto backends are available via features, `aws_lc_rs` and `rust_crypto`, at most one of which must be enabled. If you select neither feature, you need to provide your own `CryptoProvider`. The minimum required Rust version (MSRV) is specified in the `rust-version` field in this project's [Cargo.toml](Cargo.toml). diff --git a/examples/custom_provider.rs b/examples/custom_provider.rs new file mode 100644 index 00000000..1f7e9754 --- /dev/null +++ b/examples/custom_provider.rs @@ -0,0 +1,138 @@ +use jsonwebtoken::{ + Algorithm, AlgorithmFamily, DecodingKey, EncodingKey, Header, Validation, + crypto::{CryptoProvider, JwkUtils, JwtSigner, JwtVerifier}, + decode, encode, + errors::{Error, ErrorKind}, + signature::{Error as SigError, Signer, Verifier}, +}; +use serde::{Deserialize, Serialize}; + +fn new_signer(algorithm: &Algorithm, key: &EncodingKey) -> Result, Error> { + let jwt_signer = match algorithm { + Algorithm::EdDSA => Box::new(EdDSASigner::new(key)?) as Box, + _ => unimplemented!(), + }; + + Ok(jwt_signer) +} + +fn new_verifier(algorithm: &Algorithm, key: &DecodingKey) -> Result, Error> { + let jwt_verifier = match algorithm { + Algorithm::EdDSA => Box::new(EdDSAVerifier::new(key)?) as Box, + _ => unimplemented!(), + }; + + Ok(jwt_verifier) +} + +struct EdDSASigner(botan::Privkey); + +impl EdDSASigner { + fn new(encoding_key: &EncodingKey) -> Result { + if encoding_key.family() != AlgorithmFamily::Ed { + return Err(ErrorKind::InvalidKeyFormat.into()); + } + + Ok(Self( + botan::Privkey::load_der(encoding_key.inner()) + .map_err(|_| ErrorKind::InvalidEddsaKey)?, + )) + } +} + +impl Signer> for EdDSASigner { + fn try_sign(&self, msg: &[u8]) -> std::result::Result, SigError> { + let mut rng = botan::RandomNumberGenerator::new_system().map_err(SigError::from_source)?; + let mut signer = botan::Signer::new(&self.0, "Pure").map_err(SigError::from_source)?; + signer.update(msg).map_err(SigError::from_source)?; + signer.finish(&mut rng).map_err(SigError::from_source) + } +} + +impl JwtSigner for EdDSASigner { + fn algorithm(&self) -> Algorithm { + Algorithm::EdDSA + } +} + +struct EdDSAVerifier(botan::Pubkey); + +impl EdDSAVerifier { + fn new(decoding_key: &DecodingKey) -> Result { + if decoding_key.family() != AlgorithmFamily::Ed { + return Err(ErrorKind::InvalidKeyFormat.into()); + } + + Ok(Self( + botan::Pubkey::load_ed25519(decoding_key.as_bytes()) + .map_err(|_| ErrorKind::InvalidEddsaKey)?, + )) + } +} + +impl Verifier> for EdDSAVerifier { + fn verify(&self, msg: &[u8], signature: &Vec) -> std::result::Result<(), SigError> { + let mut verifier = botan::Verifier::new(&self.0, "Pure").map_err(SigError::from_source)?; + verifier.update(msg).map_err(SigError::from_source)?; + verifier + .finish(signature) + .map_err(SigError::from_source)? + .then_some(()) + .ok_or(SigError::new()) + } +} + +impl JwtVerifier for EdDSAVerifier { + fn algorithm(&self) -> Algorithm { + Algorithm::EdDSA + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct Claims { + sub: String, + exp: u64, +} + +fn main() { + // create and install our custom provider + let my_crypto_provider = CryptoProvider { + signer_factory: new_signer, + verifier_factory: new_verifier, + // the default impl uses dummy functions that panic, but we don't need them here + jwk_utils: JwkUtils::default(), + }; + my_crypto_provider.install_default().unwrap(); + + // generate a new key + let (privkey, pubkey) = { + let key = botan::Privkey::create( + "Ed25519", + "", + &mut botan::RandomNumberGenerator::new_system().unwrap(), + ) + .unwrap(); + (key.pem_encode().unwrap(), key.pubkey().unwrap().pem_encode().unwrap()) + }; + let my_claims = Claims { sub: "me".to_owned(), exp: 10000000000 }; + + // our crypto provider only supports EdDSA + let header = Header::new(Algorithm::EdDSA); + + let token = + match encode(&header, &my_claims, &EncodingKey::from_ed_pem(privkey.as_bytes()).unwrap()) { + Ok(t) => t, + Err(_) => panic!(), // in practice you would return an error + }; + + let claims = match decode::( + token, + &DecodingKey::from_ed_pem(pubkey.as_bytes()).unwrap(), + &Validation::new(Algorithm::EdDSA), + ) { + Ok(c) => c.claims, + Err(_) => panic!(), + }; + + assert_eq!(my_claims, claims); +} diff --git a/src/algorithms.rs b/src/algorithms.rs index 94eb3637..a3fb42d1 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::errors::{Error, ErrorKind, Result}; #[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)] +#[allow(missing_docs)] pub enum AlgorithmFamily { Hmac, Rsa, diff --git a/src/crypto/aws_lc/ecdsa.rs b/src/crypto/aws_lc/ecdsa.rs index c278b934..5a5b364e 100644 --- a/src/crypto/aws_lc/ecdsa.rs +++ b/src/crypto/aws_lc/ecdsa.rs @@ -18,7 +18,7 @@ macro_rules! define_ecdsa_signer { impl $name { pub(crate) fn new(encoding_key: &EncodingKey) -> Result { - if encoding_key.family != AlgorithmFamily::Ec { + if encoding_key.family() != AlgorithmFamily::Ec { return Err(new_error(ErrorKind::InvalidKeyFormat)); } @@ -51,7 +51,7 @@ macro_rules! define_ecdsa_verifier { impl $name { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { - if decoding_key.family != AlgorithmFamily::Ec { + if decoding_key.family() != AlgorithmFamily::Ec { return Err(new_error(ErrorKind::InvalidKeyFormat)); } diff --git a/src/crypto/aws_lc/eddsa.rs b/src/crypto/aws_lc/eddsa.rs index d6e60a50..085bf7c3 100644 --- a/src/crypto/aws_lc/eddsa.rs +++ b/src/crypto/aws_lc/eddsa.rs @@ -11,7 +11,7 @@ pub struct EdDSASigner(Ed25519KeyPair); impl EdDSASigner { pub(crate) fn new(encoding_key: &EncodingKey) -> Result { - if encoding_key.family != AlgorithmFamily::Ed { + if encoding_key.family() != AlgorithmFamily::Ed { return Err(new_error(ErrorKind::InvalidKeyFormat)); } @@ -38,7 +38,7 @@ pub struct EdDSAVerifier(DecodingKey); impl EdDSAVerifier { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { - if decoding_key.family != AlgorithmFamily::Ed { + if decoding_key.family() != AlgorithmFamily::Ed { return Err(new_error(ErrorKind::InvalidKeyFormat)); } diff --git a/src/crypto/aws_lc/mod.rs b/src/crypto/aws_lc/mod.rs index 16f66b5f..d3861952 100644 --- a/src/crypto/aws_lc/mod.rs +++ b/src/crypto/aws_lc/mod.rs @@ -1,4 +1,61 @@ -pub(crate) mod ecdsa; -pub(crate) mod eddsa; -pub(crate) mod hmac; -pub(crate) mod rsa; +use aws_lc_rs::{ + digest, + signature::{self as aws_sig, KeyPair}, +}; + +use crate::{ + Algorithm, DecodingKey, EncodingKey, + crypto::{CryptoProvider, JwkUtils, JwtSigner, JwtVerifier}, + errors::{self, Error, ErrorKind}, + jwk::{EllipticCurve, ThumbprintHash}, +}; + +mod ecdsa; +mod eddsa; +mod hmac; +mod rsa; + +fn extract_rsa_public_key_components(key_content: &[u8]) -> errors::Result<(Vec, Vec)> { + let key_pair = aws_sig::RsaKeyPair::from_der(key_content) + .map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; + let public = key_pair.public_key(); + let components = aws_sig::RsaPublicKeyComponents::>::from(public); + Ok((components.n, components.e)) +} + +fn extract_ec_public_key_coordinates( + key_content: &[u8], + alg: Algorithm, +) -> errors::Result<(EllipticCurve, Vec, Vec)> { + use aws_lc_rs::signature::{ + ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING, EcdsaKeyPair, + }; + + let (signing_alg, curve, pub_elem_bytes) = match alg { + Algorithm::ES256 => (&ECDSA_P256_SHA256_FIXED_SIGNING, EllipticCurve::P256, 32), + Algorithm::ES384 => (&ECDSA_P384_SHA384_FIXED_SIGNING, EllipticCurve::P384, 48), + _ => return Err(ErrorKind::InvalidEcdsaKey.into()), + }; + + let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_content) + .map_err(|_| ErrorKind::InvalidEcdsaKey)?; + + let pub_bytes = key_pair.public_key().as_ref(); + if pub_bytes[0] != 4 { + return Err(ErrorKind::InvalidEcdsaKey.into()); + } + + let (x, y) = pub_bytes[1..].split_at(pub_elem_bytes); + Ok((curve, x.to_vec(), y.to_vec())) +} + +fn compute_digest(data: &[u8], hash_function: ThumbprintHash) -> Vec { + let algorithm = match hash_function { + ThumbprintHash::SHA256 => &digest::SHA256, + ThumbprintHash::SHA384 => &digest::SHA384, + ThumbprintHash::SHA512 => &digest::SHA512, + }; + digest::digest(algorithm, data).as_ref().to_vec() +} + +define_default_provider!("aws_lc_rs", "https://github.com/aws/aws-lc-rs"); diff --git a/src/crypto/aws_lc/rsa.rs b/src/crypto/aws_lc/rsa.rs index 7e74eebf..d72b0680 100644 --- a/src/crypto/aws_lc/rsa.rs +++ b/src/crypto/aws_lc/rsa.rs @@ -37,7 +37,7 @@ fn verify_rsa( msg: &[u8], signature: &[u8], ) -> std::result::Result<(), signature::Error> { - match &decoding_key.kind { + 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)?; @@ -57,7 +57,7 @@ macro_rules! define_rsa_signer { impl $name { pub(crate) fn new(encoding_key: &EncodingKey) -> Result { - if encoding_key.family != AlgorithmFamily::Rsa { + if encoding_key.family() != AlgorithmFamily::Rsa { return Err(new_error(ErrorKind::InvalidKeyFormat)); } @@ -85,7 +85,7 @@ macro_rules! define_rsa_verifier { impl $name { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { - if decoding_key.family != AlgorithmFamily::Rsa { + if decoding_key.family() != AlgorithmFamily::Rsa { return Err(new_error(ErrorKind::InvalidKeyFormat)); } diff --git a/src/crypto/macros.rs b/src/crypto/macros.rs new file mode 100644 index 00000000..0b6ef710 --- /dev/null +++ b/src/crypto/macros.rs @@ -0,0 +1,90 @@ +#[cfg(any(feature = "rust_crypto", feature = "aws_lc_rs"))] +macro_rules! define_default_provider { + ($name:literal, $link:literal) => { + #[doc = "The default [`CryptoProvider`] backed by [`"] + #[doc = $name] + #[doc = "`]"] + #[doc = concat!("The default [`CryptoProvider`] backed by [`", $name, "`]")] + #[doc = ""] + #[doc = concat!("[`", $name, "`]: ", $link)] + pub const DEFAULT_PROVIDER: CryptoProvider = CryptoProvider { + signer_factory: new_signer, + verifier_factory: new_verifier, + jwk_utils: JwkUtils { + extract_rsa_public_key_components, + extract_ec_public_key_coordinates, + compute_digest, + }, + }; + + #[doc = "Create a new [`JwtSigner`] for a given [`Algorithm`]."] + pub fn new_signer( + algorithm: &Algorithm, + key: &EncodingKey, + ) -> Result, Error> { + let jwt_signer = match algorithm { + Algorithm::HS256 => Box::new(hmac::Hs256Signer::new(key)?) as Box, + Algorithm::HS384 => Box::new(hmac::Hs384Signer::new(key)?) as Box, + Algorithm::HS512 => Box::new(hmac::Hs512Signer::new(key)?) as Box, + Algorithm::ES256 => Box::new(ecdsa::Es256Signer::new(key)?) as Box, + Algorithm::ES384 => Box::new(ecdsa::Es384Signer::new(key)?) as Box, + Algorithm::RS256 => Box::new(rsa::Rsa256Signer::new(key)?) as Box, + Algorithm::RS384 => Box::new(rsa::Rsa384Signer::new(key)?) as Box, + Algorithm::RS512 => Box::new(rsa::Rsa512Signer::new(key)?) as Box, + Algorithm::PS256 => Box::new(rsa::RsaPss256Signer::new(key)?) as Box, + Algorithm::PS384 => Box::new(rsa::RsaPss384Signer::new(key)?) as Box, + Algorithm::PS512 => Box::new(rsa::RsaPss512Signer::new(key)?) as Box, + Algorithm::EdDSA => Box::new(eddsa::EdDSASigner::new(key)?) as Box, + }; + + Ok(jwt_signer) + } + + #[doc = "Create a new [`JwtVerifier`] for a given [`Algorithm`]."] + pub fn new_verifier( + algorithm: &Algorithm, + key: &DecodingKey, + ) -> Result, Error> { + let jwt_encoder = match algorithm { + Algorithm::HS256 => { + Box::new(hmac::Hs256Verifier::new(key)?) as Box + } + Algorithm::HS384 => { + Box::new(hmac::Hs384Verifier::new(key)?) as Box + } + Algorithm::HS512 => { + Box::new(hmac::Hs512Verifier::new(key)?) as Box + } + Algorithm::ES256 => { + Box::new(ecdsa::Es256Verifier::new(key)?) as Box + } + Algorithm::ES384 => { + Box::new(ecdsa::Es384Verifier::new(key)?) as Box + } + Algorithm::RS256 => { + Box::new(rsa::Rsa256Verifier::new(key)?) as Box + } + Algorithm::RS384 => { + Box::new(rsa::Rsa384Verifier::new(key)?) as Box + } + Algorithm::RS512 => { + Box::new(rsa::Rsa512Verifier::new(key)?) as Box + } + Algorithm::PS256 => { + Box::new(rsa::RsaPss256Verifier::new(key)?) as Box + } + Algorithm::PS384 => { + Box::new(rsa::RsaPss384Verifier::new(key)?) as Box + } + Algorithm::PS512 => { + Box::new(rsa::RsaPss512Verifier::new(key)?) as Box + } + Algorithm::EdDSA => { + Box::new(eddsa::EdDSAVerifier::new(key)?) as Box + } + }; + + Ok(jwt_encoder) + } + }; +} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index adec0d0b..14bdad22 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,20 +1,31 @@ //! The cryptography of the `jsonwebtoken` crate is decoupled behind -//! [`JwtSigner`] and [`JwtVerifier`] traits. These make use of `RustCrypto`'s +//! [`JwtSigner`] and [`JwtVerifier`] traits. These make use of `signature`'s //! [`Signer`] and [`Verifier`] traits respectively. +//! Crypto provider selection is handled by [`CryptoProvider`]. //! //! [`JwtSigner`]: crate::crypto::JwtSigner //! [`JwtVerifier`]: crate::crypto::JwtVerifier //! [`Signer`]: signature::Signer //! [`Verifier`]: signature::Verifier +//! [`CryptoProvider`]: crate::crypto::CryptoProvider + +use std::sync::Arc; use crate::algorithms::Algorithm; use crate::errors::Result; +use crate::jwk::{EllipticCurve, ThumbprintHash}; use crate::{DecodingKey, EncodingKey}; +#[macro_use] +mod macros; + +/// `aws_lc_rs` based CryptoProvider. #[cfg(feature = "aws_lc_rs")] -pub(crate) mod aws_lc; +pub mod aws_lc; + +/// `RustCrypto` based CryptoProvider. #[cfg(feature = "rust_crypto")] -pub(crate) mod rust_crypto; +pub mod rust_crypto; use crate::serialization::{b64_decode, b64_encode}; use signature::{Signer, Verifier}; @@ -40,7 +51,9 @@ pub trait JwtVerifier: Verifier> { /// /// If you just want to encode a JWT, use `encode` instead. pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result { - let provider = crate::encoding::jwt_signer_factory(&algorithm, key)?; + let provider = (CryptoProvider::get_default_or_install_from_crate_features().signer_factory)( + &algorithm, key, + )?; Ok(b64_encode(provider.sign(message))) } @@ -58,6 +71,122 @@ pub fn verify( key: &DecodingKey, algorithm: Algorithm, ) -> Result { - let provider = crate::decoding::jwt_verifier_factory(&algorithm, key)?; + let provider = (CryptoProvider::get_default_or_install_from_crate_features().verifier_factory)( + &algorithm, key, + )?; Ok(provider.verify(message, &b64_decode(signature)?).is_ok()) } + +/// Controls the cryptography used be jsonwebtoken. +/// +/// You can either install one of the built-in options: +/// - [`crypto::aws_lc::DEFAULT_PROVIDER`]: (behind the `aws-lc` crate feature). +/// This provider uses the [aws-lc-rs](https://github.com/aws/aws-lc-rs) crate. +/// - [`crypto::rust_crypto::DEFAULT_PROVIDER`]: (behind the `rust_crypto` crate feature) +/// This provider uses crates from the [Rust Crypto](https://github.com/RustCrypto) project. +/// +/// or provide your own custom custom implementation of `CryptoProvider` +/// (see the `custom_provider` example). +// This implementation appropriates a good chunk of code from the `rustls` CryptoProvider, +// and is very much inspired by it. +#[derive(Clone, Debug)] +pub struct CryptoProvider { + /// A function that produces a [`JwtSigner`] for a given [`Algorithm`] + pub signer_factory: fn(&Algorithm, &EncodingKey) -> Result>, + /// A function that produces a [`JwtVerifier`] for a given [`Algorithm`] + pub verifier_factory: fn(&Algorithm, &DecodingKey) -> Result>, + /// Struct with utility functions for JWK processing. + pub jwk_utils: JwkUtils, +} + +impl CryptoProvider { + /// Set this `CryptoProvider` as the default for this process. + /// + /// This can be called successfully at most once in any process execution. + pub fn install_default(self) -> std::result::Result<(), Arc> { + static_default::install_default(self) + } + + /// Get the default `CryptoProvider` for this process. + /// + /// This will be `None` if no default has been set yet. + pub fn get_default() -> Option<&'static Arc> { + static_default::get_default() + } + + /// Get the default if it has been set yet, or determine one from the crate features if possible. + pub(crate) fn get_default_or_install_from_crate_features() -> &'static Arc { + if let Some(provider) = Self::get_default() { + return provider; + } + + let provider = Self::from_crate_features() + .expect(r###" +Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features. +Call CryptoProvider::install_default() before this point to select a provider manually, or make sure exactly one of the 'rust_crypto' and 'aws_lc_rs' features is enabled. +See the documentation of the CryptoProvider type for more information. + "###); + let _ = provider.install_default(); + Self::get_default().unwrap() + } + + /// Determine a `CryptoProvider` based on crate features. + pub fn from_crate_features() -> Option { + #[cfg(all(feature = "rust_crypto", not(feature = "aws_lc_rs")))] + { + return Some(rust_crypto::DEFAULT_PROVIDER); + } + + #[cfg(all(feature = "aws_lc_rs", not(feature = "rust_crypto")))] + { + return Some(aws_lc::DEFAULT_PROVIDER); + } + + #[allow(unreachable_code)] + None + } +} + +/// Holds utility functions needed for JWK processing. +/// The `Default` implementation initializes all functions to `unimplemented!()`. +#[derive(Clone, Debug)] +pub struct JwkUtils { + /// Given a DER encoded private key, extract the RSA public key components (n, e) + #[allow(clippy::type_complexity)] + pub extract_rsa_public_key_components: fn(&[u8]) -> Result<(Vec, Vec)>, + /// Given a DER encoded private key and an algorithm, extract the associated curve + /// and the EC public key components (x, y) + #[allow(clippy::type_complexity)] + pub extract_ec_public_key_coordinates: + fn(&[u8], Algorithm) -> Result<(EllipticCurve, Vec, Vec)>, + /// Given some data and a name of a hash function, compute hash_function(data) + pub compute_digest: fn(&[u8], ThumbprintHash) -> Vec, +} + +impl Default for JwkUtils { + fn default() -> Self { + Self { + extract_rsa_public_key_components: |_| unimplemented!(), + extract_ec_public_key_coordinates: |_, _| unimplemented!(), + compute_digest: |_, _| unimplemented!(), + } + } +} + +mod static_default { + use std::sync::{Arc, OnceLock}; + + use super::CryptoProvider; + + static PROCESS_DEFAULT_PROVIDER: OnceLock> = OnceLock::new(); + + pub(crate) fn install_default( + default_provider: CryptoProvider, + ) -> Result<(), Arc> { + PROCESS_DEFAULT_PROVIDER.set(Arc::new(default_provider)) + } + + pub(crate) fn get_default() -> Option<&'static Arc> { + PROCESS_DEFAULT_PROVIDER.get() + } +} diff --git a/src/crypto/rust_crypto/ecdsa.rs b/src/crypto/rust_crypto/ecdsa.rs index f192adea..9aad882e 100644 --- a/src/crypto/rust_crypto/ecdsa.rs +++ b/src/crypto/rust_crypto/ecdsa.rs @@ -20,7 +20,7 @@ macro_rules! define_ecdsa_signer { impl $name { pub(crate) fn new(encoding_key: &EncodingKey) -> Result { - if encoding_key.family != AlgorithmFamily::Ec { + if encoding_key.family() != AlgorithmFamily::Ec { return Err(new_error(ErrorKind::InvalidKeyFormat)); } @@ -52,7 +52,7 @@ macro_rules! define_ecdsa_verifier { impl $name { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { - if decoding_key.family != AlgorithmFamily::Ec { + if decoding_key.family() != AlgorithmFamily::Ec { return Err(new_error(ErrorKind::InvalidKeyFormat)); } diff --git a/src/crypto/rust_crypto/eddsa.rs b/src/crypto/rust_crypto/eddsa.rs index dc0fcecd..9b77a9f8 100644 --- a/src/crypto/rust_crypto/eddsa.rs +++ b/src/crypto/rust_crypto/eddsa.rs @@ -12,7 +12,7 @@ pub struct EdDSASigner(SigningKey); impl EdDSASigner { pub(crate) fn new(encoding_key: &EncodingKey) -> Result { - if encoding_key.family != AlgorithmFamily::Ed { + if encoding_key.family() != AlgorithmFamily::Ed { return Err(new_error(ErrorKind::InvalidKeyFormat)); } @@ -39,7 +39,7 @@ pub struct EdDSAVerifier(VerifyingKey); impl EdDSAVerifier { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { - if decoding_key.family != AlgorithmFamily::Ed { + if decoding_key.family() != AlgorithmFamily::Ed { return Err(new_error(ErrorKind::InvalidKeyFormat)); } diff --git a/src/crypto/rust_crypto/mod.rs b/src/crypto/rust_crypto/mod.rs index 16f66b5f..20d14e3b 100644 --- a/src/crypto/rust_crypto/mod.rs +++ b/src/crypto/rust_crypto/mod.rs @@ -1,4 +1,66 @@ -pub(crate) mod ecdsa; -pub(crate) mod eddsa; -pub(crate) mod hmac; -pub(crate) mod rsa; +use ::rsa::{RsaPrivateKey, pkcs1::DecodeRsaPrivateKey, traits::PublicKeyParts}; +use p256::{ecdsa::SigningKey as P256SigningKey, pkcs8::DecodePrivateKey}; +use p384::ecdsa::SigningKey as P384SigningKey; +use sha2::{Digest, Sha256, Sha384, Sha512}; + +use crate::{ + Algorithm, DecodingKey, EncodingKey, + crypto::{CryptoProvider, JwkUtils, JwtSigner, JwtVerifier}, + errors::{self, Error, ErrorKind}, + jwk::{EllipticCurve, ThumbprintHash}, +}; + +mod ecdsa; +mod eddsa; +mod hmac; +mod rsa; + +fn extract_rsa_public_key_components(key_content: &[u8]) -> errors::Result<(Vec, Vec)> { + let private_key = RsaPrivateKey::from_pkcs1_der(key_content) + .map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; + let public_key = private_key.to_public_key(); + Ok((public_key.n().to_bytes_be(), public_key.e().to_bytes_be())) +} + +fn extract_ec_public_key_coordinates( + key_content: &[u8], + alg: Algorithm, +) -> errors::Result<(EllipticCurve, Vec, Vec)> { + match alg { + Algorithm::ES256 => { + let signing_key = P256SigningKey::from_pkcs8_der(key_content) + .map_err(|_| ErrorKind::InvalidEcdsaKey)?; + let public_key = signing_key.verifying_key(); + let encoded = public_key.to_encoded_point(false); + match encoded.coordinates() { + p256::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => { + Ok((EllipticCurve::P256, x.to_vec(), y.to_vec())) + } + _ => Err(ErrorKind::InvalidEcdsaKey.into()), + } + } + Algorithm::ES384 => { + let signing_key = P384SigningKey::from_pkcs8_der(key_content) + .map_err(|_| ErrorKind::InvalidEcdsaKey)?; + let public_key = signing_key.verifying_key(); + let encoded = public_key.to_encoded_point(false); + match encoded.coordinates() { + p384::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => { + Ok((EllipticCurve::P384, x.to_vec(), y.to_vec())) + } + _ => Err(ErrorKind::InvalidEcdsaKey.into()), + } + } + _ => Err(ErrorKind::InvalidEcdsaKey.into()), + } +} + +fn compute_digest(data: &[u8], hash_function: ThumbprintHash) -> Vec { + match hash_function { + ThumbprintHash::SHA256 => Sha256::digest(data).to_vec(), + ThumbprintHash::SHA384 => Sha384::digest(data).to_vec(), + ThumbprintHash::SHA512 => Sha512::digest(data).to_vec(), + } +} + +define_default_provider!("rust_crypto", "https://github.com/RustCrypto"); diff --git a/src/crypto/rust_crypto/rsa.rs b/src/crypto/rust_crypto/rsa.rs index b73797e8..ba0af0fe 100644 --- a/src/crypto/rust_crypto/rsa.rs +++ b/src/crypto/rust_crypto/rsa.rs @@ -49,7 +49,7 @@ fn verify_rsa( ) -> std::result::Result<(), signature::Error> { let digest = H::digest(msg); - match &decoding_key.kind { + match decoding_key.kind() { DecodingKeyKind::SecretOrDer(bytes) => { RsaPublicKey::from_pkcs1_der(bytes) .map_err(signature::Error::from_source)? @@ -72,7 +72,7 @@ macro_rules! define_rsa_signer { impl $name { pub(crate) fn new(encoding_key: &EncodingKey) -> Result { - if encoding_key.family != AlgorithmFamily::Rsa { + if encoding_key.family() != AlgorithmFamily::Rsa { return Err(new_error(ErrorKind::InvalidKeyFormat)); } @@ -100,7 +100,7 @@ macro_rules! define_rsa_verifier { impl $name { pub(crate) fn new(decoding_key: &DecodingKey) -> Result { - if decoding_key.family != AlgorithmFamily::Rsa { + if decoding_key.family() != AlgorithmFamily::Rsa { return Err(new_error(ErrorKind::InvalidKeyFormat)); } diff --git a/src/decoding.rs b/src/decoding.rs index 51d793e7..a041592e 100644 --- a/src/decoding.rs +++ b/src/decoding.rs @@ -3,9 +3,8 @@ use std::fmt::{Debug, Formatter}; use base64::{Engine, engine::general_purpose::STANDARD}; use serde::de::DeserializeOwned; -use crate::Algorithm; use crate::algorithms::AlgorithmFamily; -use crate::crypto::JwtVerifier; +use crate::crypto::{CryptoProvider, JwtVerifier}; use crate::errors::{ErrorKind, Result, new_error}; use crate::header::Header; use crate::jwk::{AlgorithmParameters, Jwk}; @@ -13,27 +12,6 @@ use crate::jwk::{AlgorithmParameters, Jwk}; use crate::pem::decoder::PemEncodedKey; use crate::serialization::{DecodedJwtPartClaims, b64_decode}; use crate::validation::{Validation, validate}; -// 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)] @@ -66,7 +44,7 @@ macro_rules! expect_two { } #[derive(Clone)] -pub(crate) enum DecodingKeyKind { +pub enum DecodingKeyKind { SecretOrDer(Vec), RsaModulusExponent { n: Vec, e: Vec }, } @@ -88,8 +66,8 @@ impl Debug for DecodingKeyKind { /// This key can be re-used so make sure you only initialize it once if you can for better performance. #[derive(Clone, Debug)] pub struct DecodingKey { - pub(crate) family: AlgorithmFamily, - pub(crate) kind: DecodingKeyKind, + family: AlgorithmFamily, + kind: DecodingKeyKind, } impl DecodingKey { @@ -98,6 +76,11 @@ impl DecodingKey { self.family } + /// The kind of decoding key + pub fn kind(&self) -> &DecodingKeyKind { + &self.kind + } + /// If you're using HMAC, use this. pub fn from_secret(secret: &[u8]) -> Self { DecodingKey { @@ -235,14 +218,16 @@ impl DecodingKey { } } - pub(crate) fn as_bytes(&self) -> &[u8] { + /// Get the value of the key. + pub fn as_bytes(&self) -> &[u8] { match &self.kind { DecodingKeyKind::SecretOrDer(b) => b, DecodingKeyKind::RsaModulusExponent { .. } => unreachable!(), } } - pub(crate) fn try_get_hmac_secret(&self) -> Result<&[u8]> { + /// Try to get the HMAC secret from a key. + pub fn try_get_hmac_secret(&self) -> Result<&[u8]> { if self.family == AlgorithmFamily::Hmac { Ok(self.as_bytes()) } else { @@ -289,7 +274,8 @@ pub fn decode( return Err(new_error(ErrorKind::InvalidAlgorithm)); } - let verifying_provider = jwt_verifier_factory(&header.alg, key)?; + let verifying_provider = (CryptoProvider::get_default_or_install_from_crate_features() + .verifier_factory)(&header.alg, key)?; let (header, claims) = verify_signature(token, validation, verifying_provider)?; @@ -315,29 +301,6 @@ pub fn insecure_decode(token: impl AsRef<[u8]>) -> Result 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). /// /// If the token has an invalid format (ie 3 parts separated by a `.`), it will return an error. diff --git a/src/encoding.rs b/src/encoding.rs index 30a31953..3faec591 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -6,40 +6,21 @@ use base64::{ }; use serde::ser::Serialize; -use crate::Algorithm; use crate::algorithms::AlgorithmFamily; -use crate::crypto::JwtSigner; +use crate::crypto::CryptoProvider; use crate::errors::{ErrorKind, Result, new_error}; use crate::header::Header; #[cfg(feature = "use_pem")] use crate::pem::decoder::PemEncodedKey; use crate::serialization::{b64_encode, b64_encode_part}; -// 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. #[derive(Clone)] + pub struct EncodingKey { - pub(crate) family: AlgorithmFamily, - pub(crate) content: Vec, + family: AlgorithmFamily, + content: Vec, } impl EncodingKey { @@ -127,11 +108,13 @@ impl EncodingKey { EncodingKey { family: AlgorithmFamily::Ed, content: der.to_vec() } } - pub(crate) fn inner(&self) -> &[u8] { + /// Get the value of the key. + pub fn inner(&self) -> &[u8] { &self.content } - pub(crate) fn try_get_hmac_secret(&self) -> Result<&[u8]> { + /// Try to get the HMAC secret from a key. + pub fn try_get_hmac_secret(&self) -> Result<&[u8]> { if self.family == AlgorithmFamily::Hmac { Ok(self.inner()) } else { @@ -176,7 +159,8 @@ pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> R return Err(new_error(ErrorKind::InvalidAlgorithm)); } - let signing_provider = jwt_signer_factory(&header.alg, key)?; + let signing_provider = (CryptoProvider::get_default_or_install_from_crate_features() + .signer_factory)(&header.alg, key)?; if signing_provider.algorithm() != header.alg { return Err(new_error(ErrorKind::InvalidAlgorithm)); @@ -190,26 +174,3 @@ pub fn encode(header: &Header, claims: &T, key: &EncodingKey) -> R 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/jwk.rs b/src/jwk.rs index 31f944d2..655034f1 100644 --- a/src/jwk.rs +++ b/src/jwk.rs @@ -8,25 +8,13 @@ use std::{fmt, str::FromStr}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de}; +use crate::crypto::CryptoProvider; use crate::serialization::b64_encode; use crate::{ Algorithm, EncodingKey, errors::{self, Error, ErrorKind}, }; -#[cfg(feature = "aws_lc_rs")] -use aws_lc_rs::{digest, signature as aws_sig}; -#[cfg(feature = "aws_lc_rs")] -use aws_sig::KeyPair; -#[cfg(feature = "rust_crypto")] -use p256::{ecdsa::SigningKey as P256SigningKey, pkcs8::DecodePrivateKey}; -#[cfg(feature = "rust_crypto")] -use p384::ecdsa::SigningKey as P384SigningKey; -#[cfg(feature = "rust_crypto")] -use rsa::{RsaPrivateKey, pkcs1::DecodeRsaPrivateKey, traits::PublicKeyParts}; -#[cfg(feature = "rust_crypto")] -use sha2::{Digest, Sha256, Sha384, Sha512}; - /// The intended usage of the public `KeyType`. This enum is serialized `untagged` #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum PublicKeyUse { @@ -439,103 +427,6 @@ pub struct Jwk { pub algorithm: AlgorithmParameters, } -#[cfg(feature = "aws_lc_rs")] -fn extract_rsa_public_key_components(key_content: &[u8]) -> errors::Result<(Vec, Vec)> { - let key_pair = aws_sig::RsaKeyPair::from_der(key_content) - .map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; - let public = key_pair.public_key(); - let components = aws_sig::RsaPublicKeyComponents::>::from(public); - Ok((components.n, components.e)) -} - -#[cfg(feature = "rust_crypto")] -fn extract_rsa_public_key_components(key_content: &[u8]) -> errors::Result<(Vec, Vec)> { - let private_key = RsaPrivateKey::from_pkcs1_der(key_content) - .map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?; - let public_key = private_key.to_public_key(); - Ok((public_key.n().to_bytes_be(), public_key.e().to_bytes_be())) -} - -#[cfg(feature = "aws_lc_rs")] -fn extract_ec_public_key_coordinates( - key_content: &[u8], - alg: Algorithm, -) -> errors::Result<(EllipticCurve, Vec, Vec)> { - use aws_lc_rs::signature::{ - ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING, EcdsaKeyPair, - }; - - let (signing_alg, curve, pub_elem_bytes) = match alg { - Algorithm::ES256 => (&ECDSA_P256_SHA256_FIXED_SIGNING, EllipticCurve::P256, 32), - Algorithm::ES384 => (&ECDSA_P384_SHA384_FIXED_SIGNING, EllipticCurve::P384, 48), - _ => return Err(ErrorKind::InvalidEcdsaKey.into()), - }; - - let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_content) - .map_err(|_| ErrorKind::InvalidEcdsaKey)?; - - let pub_bytes = key_pair.public_key().as_ref(); - if pub_bytes[0] != 4 { - return Err(ErrorKind::InvalidEcdsaKey.into()); - } - - let (x, y) = pub_bytes[1..].split_at(pub_elem_bytes); - Ok((curve, x.to_vec(), y.to_vec())) -} - -#[cfg(feature = "rust_crypto")] -fn extract_ec_public_key_coordinates( - key_content: &[u8], - alg: Algorithm, -) -> errors::Result<(EllipticCurve, Vec, Vec)> { - match alg { - Algorithm::ES256 => { - let signing_key = P256SigningKey::from_pkcs8_der(key_content) - .map_err(|_| ErrorKind::InvalidEcdsaKey)?; - let public_key = signing_key.verifying_key(); - let encoded = public_key.to_encoded_point(false); - match encoded.coordinates() { - p256::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => { - Ok((EllipticCurve::P256, x.to_vec(), y.to_vec())) - } - _ => Err(ErrorKind::InvalidEcdsaKey.into()), - } - } - Algorithm::ES384 => { - let signing_key = P384SigningKey::from_pkcs8_der(key_content) - .map_err(|_| ErrorKind::InvalidEcdsaKey)?; - let public_key = signing_key.verifying_key(); - let encoded = public_key.to_encoded_point(false); - match encoded.coordinates() { - p384::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => { - Ok((EllipticCurve::P384, x.to_vec(), y.to_vec())) - } - _ => Err(ErrorKind::InvalidEcdsaKey.into()), - } - } - _ => Err(ErrorKind::InvalidEcdsaKey.into()), - } -} - -#[cfg(feature = "aws_lc_rs")] -fn compute_digest(data: &[u8], hash_function: ThumbprintHash) -> Vec { - let algorithm = match hash_function { - ThumbprintHash::SHA256 => &digest::SHA256, - ThumbprintHash::SHA384 => &digest::SHA384, - ThumbprintHash::SHA512 => &digest::SHA512, - }; - digest::digest(algorithm, data).as_ref().to_vec() -} - -#[cfg(feature = "rust_crypto")] -fn compute_digest(data: &[u8], hash_function: ThumbprintHash) -> Vec { - match hash_function { - ThumbprintHash::SHA256 => Sha256::digest(data).to_vec(), - ThumbprintHash::SHA384 => Sha384::digest(data).to_vec(), - ThumbprintHash::SHA512 => Sha512::digest(data).to_vec(), - } -} - impl Jwk { /// Find whether the Algorithm is implemented and supported pub fn is_supported(&self) -> bool { @@ -563,15 +454,19 @@ impl Jwk { }), ..Default::default() }, - algorithm: match key.family { + algorithm: match key.family() { crate::algorithms::AlgorithmFamily::Hmac => { AlgorithmParameters::OctetKey(OctetKeyParameters { key_type: OctetKeyType::Octet, - value: b64_encode(&key.content), + value: b64_encode(key.inner()), }) } crate::algorithms::AlgorithmFamily::Rsa => { - let (n, e) = extract_rsa_public_key_components(&key.content)?; + let (n, e) = (CryptoProvider::get_default_or_install_from_crate_features() + .jwk_utils + .extract_rsa_public_key_components)( + key.inner() + )?; AlgorithmParameters::RSA(RSAKeyParameters { key_type: RSAKeyType::RSA, n: b64_encode(n), @@ -579,7 +474,12 @@ impl Jwk { }) } crate::algorithms::AlgorithmFamily::Ec => { - let (curve, x, y) = extract_ec_public_key_coordinates(&key.content, alg)?; + let (curve, x, y) = + (CryptoProvider::get_default_or_install_from_crate_features() + .jwk_utils + .extract_ec_public_key_coordinates)( + key.inner(), alg + )?; AlgorithmParameters::EllipticCurve(EllipticCurveKeyParameters { key_type: EllipticCurveKeyType::EC, curve, @@ -640,7 +540,10 @@ impl Jwk { } }, }; - b64_encode(compute_digest(pre.as_bytes(), hash_function)) + + b64_encode((CryptoProvider::get_default_or_install_from_crate_features() + .jwk_utils + .compute_digest)(pre.as_bytes(), hash_function)) } } diff --git a/src/jws.rs b/src/jws.rs index 57dc02a2..1e6e8153 100644 --- a/src/jws.rs +++ b/src/jws.rs @@ -1,13 +1,13 @@ //! JSON Web Signatures data type. use std::marker::PhantomData; -use crate::crypto::sign; +use crate::crypto::{CryptoProvider, sign}; use crate::errors::{ErrorKind, Result, new_error}; use crate::serialization::{DecodedJwtPartClaims, b64_encode_part}; use crate::validation::validate; use crate::{DecodingKey, EncodingKey, Header, TokenData, Validation}; -use crate::decoding::{jwt_verifier_factory, verify_signature_body}; +use crate::decoding::verify_signature_body; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -39,7 +39,7 @@ pub fn encode( claims: Option<&T>, key: &EncodingKey, ) -> Result> { - if key.family != header.alg.family() { + if key.family() != header.alg.family() { return Err(new_error(ErrorKind::InvalidAlgorithm)); } let encoded_header = b64_encode_part(header)?; @@ -67,7 +67,8 @@ pub fn decode( let header = Header::from_encoded(&jws.protected)?; let message = [jws.protected.as_str(), jws.payload.as_str()].join("."); - let verifying_provider = jwt_verifier_factory(&header.alg, key)?; + let verifying_provider = (CryptoProvider::get_default_or_install_from_crate_features() + .verifier_factory)(&header.alg, key)?; verify_signature_body( message.as_bytes(), jws.signature.as_bytes(), diff --git a/src/lib.rs b/src/lib.rs index 920b996b..f962ca11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,18 +5,11 @@ #![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 algorithms::{Algorithm, AlgorithmFamily}; pub use decoding::{DecodingKey, TokenData, decode, decode_header}; pub use encoding::{EncodingKey, encode}; pub use header::Header; +pub use signature; pub use validation::{Validation, get_current_timestamp}; /// Dangerous decoding functions that should be audited and used with extreme care.