Skip to main content

jsonwebtoken/crypto/
mod.rs

1//! The cryptography of the `jsonwebtoken` crate is decoupled behind
2//! [`JwtSigner`] and [`JwtVerifier`] traits. These make use of `signature`'s
3//! [`Signer`] and [`Verifier`] traits respectively.
4//! Crypto provider selection is handled by [`CryptoProvider`].
5//!
6//! [`JwtSigner`]: crate::crypto::JwtSigner
7//! [`JwtVerifier`]: crate::crypto::JwtVerifier
8//! [`Signer`]: signature::Signer
9//! [`Verifier`]: signature::Verifier
10//! [`CryptoProvider`]: crate::crypto::CryptoProvider
11
12use crate::algorithms::Algorithm;
13use crate::errors::Result;
14use crate::jwk::{EllipticCurve, ThumbprintHash};
15use crate::{DecodingKey, EncodingKey};
16
17/// `aws_lc_rs` based CryptoProvider.
18#[cfg(feature = "aws_lc_rs")]
19pub mod aws_lc;
20
21/// `RustCrypto` based CryptoProvider.
22#[cfg(feature = "rust_crypto")]
23pub mod rust_crypto;
24
25use crate::serialization::{b64_decode, b64_encode};
26use signature::{Signer, Verifier};
27
28/// Trait providing the functionality to sign a JWT.
29///
30/// Allows an arbitrary crypto backend to be provided.
31pub trait JwtSigner: Signer<Vec<u8>> {
32    /// Return the [`Algorithm`] corresponding to the signing module.
33    fn algorithm(&self) -> Algorithm;
34}
35
36/// Trait providing the functionality to verify a JWT.
37///
38/// Allows an arbitrary crypto backend to be provided.
39pub trait JwtVerifier: Verifier<Vec<u8>> {
40    /// Return the [`Algorithm`] corresponding to the signing module.
41    fn algorithm(&self) -> Algorithm;
42}
43
44/// Take the payload of a JWT, sign it using the algorithm given and return
45/// the base64 url safe encoded of the result.
46///
47/// If you just want to encode a JWT, use `encode` instead.
48pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result<String> {
49    let provider = (CryptoProvider::get_default().signer_factory)(&algorithm, key)?;
50    Ok(b64_encode(provider.try_sign(message)?))
51}
52
53/// Compares the signature given with a re-computed signature for HMAC or using the public key
54/// for RSA/EC.
55///
56/// If you just want to decode a JWT, use `decode` instead.
57///
58/// `signature` is the signature part of a jwt (text after the second '.')
59///
60/// `message` is base64(header) + "." + base64(claims)
61pub fn verify(
62    signature: &str,
63    message: &[u8],
64    key: &DecodingKey,
65    algorithm: Algorithm,
66) -> Result<bool> {
67    let provider = (CryptoProvider::get_default().verifier_factory)(&algorithm, key)?;
68    Ok(provider.verify(message, &b64_decode(signature)?).is_ok())
69}
70
71/// Controls the cryptography used by jsonwebtoken.
72///
73/// You can either install one of the built-in options:
74/// - [`crypto::aws_lc::DEFAULT_PROVIDER`]: (behind the `aws_lc_rs` crate feature).
75///   This provider uses the [aws-lc-rs](https://github.com/aws/aws-lc-rs) crate.
76/// - [`crypto::rust_crypto::DEFAULT_PROVIDER`]: (behind the `rust_crypto` crate feature)
77///   This provider uses crates from the [Rust Crypto](https://github.com/RustCrypto) project.
78///
79/// or provide your own custom custom implementation of `CryptoProvider`.
80// This implementation appropriates a good chunk of code from the `rustls` CryptoProvider,
81// and is very much inspired by it.
82#[derive(Clone, Debug)]
83pub struct CryptoProvider {
84    /// A function that produces a [`JwtSigner`] for a given [`Algorithm`]
85    pub signer_factory: fn(&Algorithm, &EncodingKey) -> Result<Box<dyn JwtSigner>>,
86    /// A function that produces a [`JwtVerifier`] for a given [`Algorithm`]
87    pub verifier_factory: fn(&Algorithm, &DecodingKey) -> Result<Box<dyn JwtVerifier>>,
88    /// Struct with utility functions for JWK processing.
89    pub jwk_utils: JwkUtils,
90}
91
92impl CryptoProvider {
93    /// Set this `CryptoProvider` as the default for this process.
94    ///
95    /// This can be called successfully at most once in any process execution.
96    pub fn install_default(&'static self) -> std::result::Result<(), &'static Self> {
97        static_default::install_default(self)
98    }
99
100    pub(crate) fn get_default() -> &'static Self {
101        static_default::get_default()
102    }
103
104    fn from_crate_features() -> &'static Self {
105        #[cfg(all(feature = "rust_crypto", not(feature = "aws_lc_rs")))]
106        {
107            return &rust_crypto::DEFAULT_PROVIDER;
108        }
109
110        #[cfg(all(feature = "aws_lc_rs", not(feature = "rust_crypto")))]
111        {
112            return &aws_lc::DEFAULT_PROVIDER;
113        }
114
115        #[allow(unreachable_code)]
116        {
117            const NOT_INSTALLED_ERROR: &str = r###"
118Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features.
119Call 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.
120See the documentation of the CryptoProvider type for more information.
121"###;
122
123            static INSTANCE: CryptoProvider = CryptoProvider {
124                signer_factory: |_, _| panic!("{}", NOT_INSTALLED_ERROR),
125                verifier_factory: |_, _| panic!("{}", NOT_INSTALLED_ERROR),
126                jwk_utils: JwkUtils::new_unimplemented(),
127            };
128
129            &INSTANCE
130        }
131    }
132}
133
134/// Holds utility functions required for JWK processing.
135/// Use the [`JwkUtils::new_unimplemented`] function to initialize all values to dummies.
136#[derive(Clone, Debug)]
137pub struct JwkUtils {
138    /// Given a DER encoded private key, extract the RSA public key components (n, e)
139    #[allow(clippy::type_complexity)]
140    pub extract_rsa_public_key_components: fn(&[u8]) -> Result<(Vec<u8>, Vec<u8>)>,
141    /// Given a DER encoded private key and an algorithm, extract the associated curve
142    /// and the EC public key components (x, y)
143    #[allow(clippy::type_complexity)]
144    pub extract_ec_public_key_coordinates:
145        fn(&[u8], Algorithm) -> Result<(EllipticCurve, Vec<u8>, Vec<u8>)>,
146    /// Given some data and a name of a hash function, compute hash_function(data)
147    pub compute_digest: fn(&[u8], ThumbprintHash) -> Vec<u8>,
148}
149
150impl JwkUtils {
151    /// Initialises all values to dummies.
152    /// Will lead to a panic when JWKs are required, so only use it if you don't want to support JWKs.
153    pub const fn new_unimplemented() -> Self {
154        const NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR: &str = r###"
155Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features, or your CryptoProvider does not support JWKs.
156Call 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.
157See the documentation of the CryptoProvider type for more information.
158"###;
159        Self {
160            extract_rsa_public_key_components: |_| {
161                panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
162            },
163            extract_ec_public_key_coordinates: |_, _| {
164                panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
165            },
166            compute_digest: |_, _| panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR),
167        }
168    }
169}
170
171mod static_default {
172    use std::sync::OnceLock;
173
174    use super::CryptoProvider;
175
176    static PROCESS_DEFAULT_PROVIDER: OnceLock<&'static CryptoProvider> = OnceLock::new();
177
178    pub(crate) fn install_default(
179        default_provider: &'static CryptoProvider,
180    ) -> Result<(), &'static CryptoProvider> {
181        PROCESS_DEFAULT_PROVIDER.set(default_provider)
182    }
183
184    pub(crate) fn get_default() -> &'static CryptoProvider {
185        PROCESS_DEFAULT_PROVIDER.get_or_init(CryptoProvider::from_crate_features)
186    }
187}