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::{ErrorKind, 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 key_utils: KeyUtils,
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                key_utils: KeyUtils::new_unimplemented(),
127            };
128
129            &INSTANCE
130        }
131    }
132}
133
134/// Holds utility functions required for JWK processing.
135/// Use the [`KeyUtils::new_unimplemented`] function to initialize all values to dummies.
136#[derive(Clone, Debug)]
137pub struct KeyUtils {
138    /// Given a DER encoded private key, extract the RSA public key components (n, e)
139    #[allow(clippy::type_complexity)]
140    pub rsa_pub_components_from_private_key: fn(&[u8]) -> Result<(Vec<u8>, Vec<u8>)>,
141    /// Given a DER encoded public key, extract the RSA public key components (n, e)
142    #[allow(clippy::type_complexity)]
143    pub rsa_pub_components_from_public_key: fn(&[u8]) -> Result<(Vec<u8>, Vec<u8>)>,
144    /// Given a DER encoded private key and an algorithm, extract the associated curve
145    /// and the EC public key components (x, y)
146    #[allow(clippy::type_complexity)]
147    pub ec_pub_components_from_private_key:
148        fn(&[u8], Algorithm) -> Result<(EllipticCurve, Vec<u8>, Vec<u8>)>,
149    /// Given a DER encoded private key and the curve type, extract the ED public key component (x)
150    pub ed_pub_components_from_private_key: fn(&[u8], &EllipticCurve) -> Result<Vec<u8>>,
151    /// Given some data and a name of a hash function, compute hash_function(data)
152    pub compute_digest: fn(&[u8], ThumbprintHash) -> Result<Vec<u8>>,
153}
154
155impl KeyUtils {
156    /// Initialises all values to dummies.
157    /// Will lead to a panic when JWKs are required, so only use it if you don't want to support JWKs.
158    pub const fn new_unimplemented() -> Self {
159        const NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR: &str = r"
160Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features, or your CryptoProvider does not support JWKs.
161Call 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.
162See the documentation of the CryptoProvider type for more information.
163";
164        Self {
165            rsa_pub_components_from_private_key: |_| {
166                panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
167            },
168            rsa_pub_components_from_public_key: |_| {
169                panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
170            },
171            ec_pub_components_from_private_key: |_, _| {
172                panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
173            },
174            ed_pub_components_from_private_key: |_, _| {
175                panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
176            },
177            compute_digest: |_, _| panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR),
178        }
179    }
180}
181
182/// Given bitstring from DER encoded public key, extract the associated curve
183/// and the EC public key components (x, y)
184pub(crate) fn ec_pub_components_from_public_key(
185    pub_bytes: &[u8],
186) -> Result<(EllipticCurve, Vec<u8>, Vec<u8>)> {
187    let (curve, pub_elem_bytes) = match pub_bytes.len() {
188        65 => (EllipticCurve::P256, 32),
189        97 => (EllipticCurve::P384, 48),
190        _ => return Err(ErrorKind::InvalidEcdsaKey.into()),
191    };
192
193    if pub_bytes[0] != 4 {
194        return Err(ErrorKind::InvalidEcdsaKey.into());
195    }
196
197    let (x, y) = pub_bytes[1..].split_at(pub_elem_bytes);
198    Ok((curve, x.to_vec(), y.to_vec()))
199}
200
201mod static_default {
202    use std::sync::OnceLock;
203
204    use super::CryptoProvider;
205
206    static PROCESS_DEFAULT_PROVIDER: OnceLock<&'static CryptoProvider> = OnceLock::new();
207
208    pub(crate) fn install_default(
209        default_provider: &'static CryptoProvider,
210    ) -> Result<(), &'static CryptoProvider> {
211        PROCESS_DEFAULT_PROVIDER.set(default_provider)
212    }
213
214    pub(crate) fn get_default() -> &'static CryptoProvider {
215        PROCESS_DEFAULT_PROVIDER.get_or_init(CryptoProvider::from_crate_features)
216    }
217}