Skip to content

Commit f273be8

Browse files
committed
Expose cryptography backends via CryptoProvider
1 parent 53a3fc2 commit f273be8

18 files changed

Lines changed: 448 additions & 268 deletions

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ jsonwebtoken = { version = "10", features = ["aws_lc_rs"] }
1515
serde = {version = "1.0", features = ["derive"] }
1616
```
1717

18-
Two crypto backends are available via features, `aws_lc_rs` and `rust_crypto`, exactly one of which must be enabled.
18+
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`.
19+
20+
For examples of how to implement a `CryptoProvider`, see
21+
- [arckoor/jsonwebtoken-botan](https://github.com/arckoor/jsonwebtoken-botan)
1922

2023
The minimum required Rust version (MSRV) is specified in the `rust-version` field in this project's [Cargo.toml](Cargo.toml).
2124

src/algorithms.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ use serde::{Deserialize, Serialize};
55
use crate::errors::{Error, ErrorKind, Result};
66

77
#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
8+
/// Supported families of algorithms.
89
pub enum AlgorithmFamily {
10+
/// HMAC shared secret family.
911
Hmac,
12+
/// RSA-based public key family.
1013
Rsa,
14+
/// Edwards curve public key family.
1115
Ec,
16+
/// Elliptic curve public key family.
1217
Ed,
1318
}
1419

@@ -34,6 +39,7 @@ impl AlgorithmFamily {
3439
/// The algorithms supported for signing/verifying JWTs
3540
#[allow(clippy::upper_case_acronyms)]
3641
#[derive(Debug, Default, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
42+
#[non_exhaustive]
3743
pub enum Algorithm {
3844
/// HMAC using SHA-256
3945
#[default]

src/crypto/aws_lc/ecdsa.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ macro_rules! define_ecdsa_signer {
1818

1919
impl $name {
2020
pub(crate) fn new(encoding_key: &EncodingKey) -> Result<Self> {
21-
if encoding_key.family != AlgorithmFamily::Ec {
21+
if encoding_key.family() != AlgorithmFamily::Ec {
2222
return Err(new_error(ErrorKind::InvalidKeyFormat));
2323
}
2424

@@ -51,7 +51,7 @@ macro_rules! define_ecdsa_verifier {
5151

5252
impl $name {
5353
pub(crate) fn new(decoding_key: &DecodingKey) -> Result<Self> {
54-
if decoding_key.family != AlgorithmFamily::Ec {
54+
if decoding_key.family() != AlgorithmFamily::Ec {
5555
return Err(new_error(ErrorKind::InvalidKeyFormat));
5656
}
5757

src/crypto/aws_lc/eddsa.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub struct EdDSASigner(Ed25519KeyPair);
1111

1212
impl EdDSASigner {
1313
pub(crate) fn new(encoding_key: &EncodingKey) -> Result<Self> {
14-
if encoding_key.family != AlgorithmFamily::Ed {
14+
if encoding_key.family() != AlgorithmFamily::Ed {
1515
return Err(new_error(ErrorKind::InvalidKeyFormat));
1616
}
1717

@@ -38,7 +38,7 @@ pub struct EdDSAVerifier(DecodingKey);
3838

3939
impl EdDSAVerifier {
4040
pub(crate) fn new(decoding_key: &DecodingKey) -> Result<Self> {
41-
if decoding_key.family != AlgorithmFamily::Ed {
41+
if decoding_key.family() != AlgorithmFamily::Ed {
4242
return Err(new_error(ErrorKind::InvalidKeyFormat));
4343
}
4444

src/crypto/aws_lc/mod.rs

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,110 @@
1-
pub(crate) mod ecdsa;
2-
pub(crate) mod eddsa;
3-
pub(crate) mod hmac;
4-
pub(crate) mod rsa;
1+
use aws_lc_rs::{
2+
digest,
3+
signature::{
4+
self as aws_sig, ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING,
5+
EcdsaKeyPair, KeyPair,
6+
},
7+
};
8+
9+
use crate::{
10+
Algorithm, DecodingKey, EncodingKey,
11+
crypto::{CryptoProvider, JwkUtils, JwtSigner, JwtVerifier},
12+
errors::{self, Error, ErrorKind},
13+
jwk::{EllipticCurve, ThumbprintHash},
14+
};
15+
16+
mod ecdsa;
17+
mod eddsa;
18+
mod hmac;
19+
mod rsa;
20+
21+
fn extract_rsa_public_key_components(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
22+
let key_pair = aws_sig::RsaKeyPair::from_der(key_content)
23+
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
24+
let public = key_pair.public_key();
25+
let components = aws_sig::RsaPublicKeyComponents::<Vec<u8>>::from(public);
26+
Ok((components.n, components.e))
27+
}
28+
29+
fn extract_ec_public_key_coordinates(
30+
key_content: &[u8],
31+
alg: Algorithm,
32+
) -> errors::Result<(EllipticCurve, Vec<u8>, Vec<u8>)> {
33+
let (signing_alg, curve, pub_elem_bytes) = match alg {
34+
Algorithm::ES256 => (&ECDSA_P256_SHA256_FIXED_SIGNING, EllipticCurve::P256, 32),
35+
Algorithm::ES384 => (&ECDSA_P384_SHA384_FIXED_SIGNING, EllipticCurve::P384, 48),
36+
_ => return Err(ErrorKind::InvalidEcdsaKey.into()),
37+
};
38+
39+
let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_content)
40+
.map_err(|_| ErrorKind::InvalidEcdsaKey)?;
41+
42+
let pub_bytes = key_pair.public_key().as_ref();
43+
if pub_bytes[0] != 4 {
44+
return Err(ErrorKind::InvalidEcdsaKey.into());
45+
}
46+
47+
let (x, y) = pub_bytes[1..].split_at(pub_elem_bytes);
48+
Ok((curve, x.to_vec(), y.to_vec()))
49+
}
50+
51+
fn compute_digest(data: &[u8], hash_function: ThumbprintHash) -> Vec<u8> {
52+
let algorithm = match hash_function {
53+
ThumbprintHash::SHA256 => &digest::SHA256,
54+
ThumbprintHash::SHA384 => &digest::SHA384,
55+
ThumbprintHash::SHA512 => &digest::SHA512,
56+
};
57+
digest::digest(algorithm, data).as_ref().to_vec()
58+
}
59+
60+
fn new_signer(algorithm: &Algorithm, key: &EncodingKey) -> Result<Box<dyn JwtSigner>, Error> {
61+
let jwt_signer = match algorithm {
62+
Algorithm::HS256 => Box::new(hmac::Hs256Signer::new(key)?) as Box<dyn JwtSigner>,
63+
Algorithm::HS384 => Box::new(hmac::Hs384Signer::new(key)?) as Box<dyn JwtSigner>,
64+
Algorithm::HS512 => Box::new(hmac::Hs512Signer::new(key)?) as Box<dyn JwtSigner>,
65+
Algorithm::ES256 => Box::new(ecdsa::Es256Signer::new(key)?) as Box<dyn JwtSigner>,
66+
Algorithm::ES384 => Box::new(ecdsa::Es384Signer::new(key)?) as Box<dyn JwtSigner>,
67+
Algorithm::RS256 => Box::new(rsa::Rsa256Signer::new(key)?) as Box<dyn JwtSigner>,
68+
Algorithm::RS384 => Box::new(rsa::Rsa384Signer::new(key)?) as Box<dyn JwtSigner>,
69+
Algorithm::RS512 => Box::new(rsa::Rsa512Signer::new(key)?) as Box<dyn JwtSigner>,
70+
Algorithm::PS256 => Box::new(rsa::RsaPss256Signer::new(key)?) as Box<dyn JwtSigner>,
71+
Algorithm::PS384 => Box::new(rsa::RsaPss384Signer::new(key)?) as Box<dyn JwtSigner>,
72+
Algorithm::PS512 => Box::new(rsa::RsaPss512Signer::new(key)?) as Box<dyn JwtSigner>,
73+
Algorithm::EdDSA => Box::new(eddsa::EdDSASigner::new(key)?) as Box<dyn JwtSigner>,
74+
};
75+
76+
Ok(jwt_signer)
77+
}
78+
79+
fn new_verifier(
80+
algorithm: &Algorithm,
81+
key: &DecodingKey,
82+
) -> Result<Box<dyn super::JwtVerifier>, Error> {
83+
let jwt_verifier = match algorithm {
84+
Algorithm::HS256 => Box::new(hmac::Hs256Verifier::new(key)?) as Box<dyn JwtVerifier>,
85+
Algorithm::HS384 => Box::new(hmac::Hs384Verifier::new(key)?) as Box<dyn JwtVerifier>,
86+
Algorithm::HS512 => Box::new(hmac::Hs512Verifier::new(key)?) as Box<dyn JwtVerifier>,
87+
Algorithm::ES256 => Box::new(ecdsa::Es256Verifier::new(key)?) as Box<dyn JwtVerifier>,
88+
Algorithm::ES384 => Box::new(ecdsa::Es384Verifier::new(key)?) as Box<dyn JwtVerifier>,
89+
Algorithm::RS256 => Box::new(rsa::Rsa256Verifier::new(key)?) as Box<dyn JwtVerifier>,
90+
Algorithm::RS384 => Box::new(rsa::Rsa384Verifier::new(key)?) as Box<dyn JwtVerifier>,
91+
Algorithm::RS512 => Box::new(rsa::Rsa512Verifier::new(key)?) as Box<dyn JwtVerifier>,
92+
Algorithm::PS256 => Box::new(rsa::RsaPss256Verifier::new(key)?) as Box<dyn JwtVerifier>,
93+
Algorithm::PS384 => Box::new(rsa::RsaPss384Verifier::new(key)?) as Box<dyn JwtVerifier>,
94+
Algorithm::PS512 => Box::new(rsa::RsaPss512Verifier::new(key)?) as Box<dyn JwtVerifier>,
95+
Algorithm::EdDSA => Box::new(eddsa::EdDSAVerifier::new(key)?) as Box<dyn JwtVerifier>,
96+
};
97+
98+
Ok(jwt_verifier)
99+
}
100+
101+
/// The default [`CryptoProvider`] backed by [`aws_lc_rs`].
102+
pub const DEFAULT_PROVIDER: CryptoProvider = CryptoProvider {
103+
signer_factory: new_signer,
104+
verifier_factory: new_verifier,
105+
jwk_utils: JwkUtils {
106+
extract_rsa_public_key_components,
107+
extract_ec_public_key_coordinates,
108+
compute_digest,
109+
},
110+
};

src/crypto/aws_lc/rsa.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn verify_rsa(
3737
msg: &[u8],
3838
signature: &[u8],
3939
) -> std::result::Result<(), signature::Error> {
40-
match &decoding_key.kind {
40+
match decoding_key.kind() {
4141
DecodingKeyKind::SecretOrDer(bytes) => {
4242
let public_key = crypto_sig::UnparsedPublicKey::new(algorithm, bytes);
4343
public_key.verify(msg, signature).map_err(signature::Error::from_source)?;
@@ -57,7 +57,7 @@ macro_rules! define_rsa_signer {
5757

5858
impl $name {
5959
pub(crate) fn new(encoding_key: &EncodingKey) -> Result<Self> {
60-
if encoding_key.family != AlgorithmFamily::Rsa {
60+
if encoding_key.family() != AlgorithmFamily::Rsa {
6161
return Err(new_error(ErrorKind::InvalidKeyFormat));
6262
}
6363

@@ -85,7 +85,7 @@ macro_rules! define_rsa_verifier {
8585

8686
impl $name {
8787
pub(crate) fn new(decoding_key: &DecodingKey) -> Result<Self> {
88-
if decoding_key.family != AlgorithmFamily::Rsa {
88+
if decoding_key.family() != AlgorithmFamily::Rsa {
8989
return Err(new_error(ErrorKind::InvalidKeyFormat));
9090
}
9191

src/crypto/mod.rs

Lines changed: 130 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
//! The cryptography of the `jsonwebtoken` crate is decoupled behind
2-
//! [`JwtSigner`] and [`JwtVerifier`] traits. These make use of `RustCrypto`'s
2+
//! [`JwtSigner`] and [`JwtVerifier`] traits. These make use of `signature`'s
33
//! [`Signer`] and [`Verifier`] traits respectively.
4+
//! Crypto provider selection is handled by [`CryptoProvider`].
45
//!
56
//! [`JwtSigner`]: crate::crypto::JwtSigner
67
//! [`JwtVerifier`]: crate::crypto::JwtVerifier
78
//! [`Signer`]: signature::Signer
89
//! [`Verifier`]: signature::Verifier
10+
//! [`CryptoProvider`]: crate::crypto::CryptoProvider
11+
12+
use std::sync::Arc;
913

1014
use crate::algorithms::Algorithm;
1115
use crate::errors::Result;
16+
use crate::jwk::{EllipticCurve, ThumbprintHash};
1217
use crate::{DecodingKey, EncodingKey};
1318

19+
/// `aws_lc_rs` based CryptoProvider.
1420
#[cfg(feature = "aws_lc_rs")]
15-
pub(crate) mod aws_lc;
21+
pub mod aws_lc;
22+
23+
/// `RustCrypto` based CryptoProvider.
1624
#[cfg(feature = "rust_crypto")]
17-
pub(crate) mod rust_crypto;
25+
pub mod rust_crypto;
1826

1927
use crate::serialization::{b64_decode, b64_encode};
2028
use signature::{Signer, Verifier};
@@ -40,7 +48,9 @@ pub trait JwtVerifier: Verifier<Vec<u8>> {
4048
///
4149
/// If you just want to encode a JWT, use `encode` instead.
4250
pub fn sign(message: &[u8], key: &EncodingKey, algorithm: Algorithm) -> Result<String> {
43-
let provider = crate::encoding::jwt_signer_factory(&algorithm, key)?;
51+
let provider = (CryptoProvider::get_default_or_install_from_crate_features().signer_factory)(
52+
&algorithm, key,
53+
)?;
4454
Ok(b64_encode(provider.sign(message)))
4555
}
4656

@@ -58,6 +68,121 @@ pub fn verify(
5868
key: &DecodingKey,
5969
algorithm: Algorithm,
6070
) -> Result<bool> {
61-
let provider = crate::decoding::jwt_verifier_factory(&algorithm, key)?;
71+
let provider = (CryptoProvider::get_default_or_install_from_crate_features().verifier_factory)(
72+
&algorithm, key,
73+
)?;
6274
Ok(provider.verify(message, &b64_decode(signature)?).is_ok())
6375
}
76+
77+
/// Controls the cryptography used by jsonwebtoken.
78+
///
79+
/// You can either install one of the built-in options:
80+
/// - [`crypto::aws_lc::DEFAULT_PROVIDER`]: (behind the `aws_lc_rs` crate feature).
81+
/// This provider uses the [aws-lc-rs](https://github.com/aws/aws-lc-rs) crate.
82+
/// - [`crypto::rust_crypto::DEFAULT_PROVIDER`]: (behind the `rust_crypto` crate feature)
83+
/// This provider uses crates from the [Rust Crypto](https://github.com/RustCrypto) project.
84+
///
85+
/// or provide your own custom custom implementation of `CryptoProvider`.
86+
// This implementation appropriates a good chunk of code from the `rustls` CryptoProvider,
87+
// and is very much inspired by it.
88+
#[derive(Clone, Debug)]
89+
pub struct CryptoProvider {
90+
/// A function that produces a [`JwtSigner`] for a given [`Algorithm`]
91+
pub signer_factory: fn(&Algorithm, &EncodingKey) -> Result<Box<dyn JwtSigner>>,
92+
/// A function that produces a [`JwtVerifier`] for a given [`Algorithm`]
93+
pub verifier_factory: fn(&Algorithm, &DecodingKey) -> Result<Box<dyn JwtVerifier>>,
94+
/// Struct with utility functions for JWK processing.
95+
pub jwk_utils: JwkUtils,
96+
}
97+
98+
impl CryptoProvider {
99+
/// Set this `CryptoProvider` as the default for this process.
100+
///
101+
/// This can be called successfully at most once in any process execution.
102+
pub fn install_default(self) -> std::result::Result<(), Arc<Self>> {
103+
static_default::install_default(self)
104+
}
105+
106+
/// Get the default `CryptoProvider` for this process.
107+
///
108+
/// This will be `None` if no default has been set yet.
109+
pub fn get_default() -> Option<&'static Arc<Self>> {
110+
static_default::get_default()
111+
}
112+
113+
/// Get the default if it has been set yet, or determine one from the crate features if possible.
114+
pub(crate) fn get_default_or_install_from_crate_features() -> &'static Arc<Self> {
115+
if let Some(provider) = Self::get_default() {
116+
return provider;
117+
}
118+
119+
let provider = Self::from_crate_features()
120+
.expect(r###"
121+
Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features.
122+
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.
123+
See the documentation of the CryptoProvider type for more information.
124+
"###);
125+
let _ = provider.install_default();
126+
Self::get_default().unwrap()
127+
}
128+
129+
/// Determine a `CryptoProvider` based on crate features.
130+
pub fn from_crate_features() -> Option<Self> {
131+
#[cfg(all(feature = "rust_crypto", not(feature = "aws_lc_rs")))]
132+
{
133+
return Some(rust_crypto::DEFAULT_PROVIDER);
134+
}
135+
136+
#[cfg(all(feature = "aws_lc_rs", not(feature = "rust_crypto")))]
137+
{
138+
return Some(aws_lc::DEFAULT_PROVIDER);
139+
}
140+
141+
#[allow(unreachable_code)]
142+
None
143+
}
144+
}
145+
146+
/// Holds utility functions required for JWK processing.
147+
/// The `Default` implementation initializes all functions to `unimplemented!()`.
148+
#[derive(Clone, Debug)]
149+
pub struct JwkUtils {
150+
/// Given a DER encoded private key, extract the RSA public key components (n, e)
151+
#[allow(clippy::type_complexity)]
152+
pub extract_rsa_public_key_components: fn(&[u8]) -> Result<(Vec<u8>, Vec<u8>)>,
153+
/// Given a DER encoded private key and an algorithm, extract the associated curve
154+
/// and the EC public key components (x, y)
155+
#[allow(clippy::type_complexity)]
156+
pub extract_ec_public_key_coordinates:
157+
fn(&[u8], Algorithm) -> Result<(EllipticCurve, Vec<u8>, Vec<u8>)>,
158+
/// Given some data and a name of a hash function, compute hash_function(data)
159+
pub compute_digest: fn(&[u8], ThumbprintHash) -> Vec<u8>,
160+
}
161+
162+
impl Default for JwkUtils {
163+
fn default() -> Self {
164+
Self {
165+
extract_rsa_public_key_components: |_| unimplemented!(),
166+
extract_ec_public_key_coordinates: |_, _| unimplemented!(),
167+
compute_digest: |_, _| unimplemented!(),
168+
}
169+
}
170+
}
171+
172+
mod static_default {
173+
use std::sync::{Arc, OnceLock};
174+
175+
use super::CryptoProvider;
176+
177+
static PROCESS_DEFAULT_PROVIDER: OnceLock<Arc<CryptoProvider>> = OnceLock::new();
178+
179+
pub(crate) fn install_default(
180+
default_provider: CryptoProvider,
181+
) -> Result<(), Arc<CryptoProvider>> {
182+
PROCESS_DEFAULT_PROVIDER.set(Arc::new(default_provider))
183+
}
184+
185+
pub(crate) fn get_default() -> Option<&'static Arc<CryptoProvider>> {
186+
PROCESS_DEFAULT_PROVIDER.get()
187+
}
188+
}

src/crypto/rust_crypto/ecdsa.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ macro_rules! define_ecdsa_signer {
2020

2121
impl $name {
2222
pub(crate) fn new(encoding_key: &EncodingKey) -> Result<Self> {
23-
if encoding_key.family != AlgorithmFamily::Ec {
23+
if encoding_key.family() != AlgorithmFamily::Ec {
2424
return Err(new_error(ErrorKind::InvalidKeyFormat));
2525
}
2626

@@ -52,7 +52,7 @@ macro_rules! define_ecdsa_verifier {
5252

5353
impl $name {
5454
pub(crate) fn new(decoding_key: &DecodingKey) -> Result<Self> {
55-
if decoding_key.family != AlgorithmFamily::Ec {
55+
if decoding_key.family() != AlgorithmFamily::Ec {
5656
return Err(new_error(ErrorKind::InvalidKeyFormat));
5757
}
5858

0 commit comments

Comments
 (0)