Skip to content

Commit 4483fd7

Browse files
committed
Expose cryptography backends via CryptoProvider
1 parent d96982d commit 4483fd7

File tree

12 files changed

+502
-230
lines changed

12 files changed

+502
-230
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ default = ["use_pem"]
6868
use_pem = ["pem", "simple_asn1"]
6969
rust_crypto = ["ed25519-dalek", "hmac", "p256", "p384", "rand", "rsa", "sha2"]
7070
aws_lc_rs = ["aws-lc-rs"]
71+
custom-provider = []
7172

7273
[[bench]]
7374
name = "jwt"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ 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 wish to use a custom crypto provider, enable the `custom-provider` feature instead.
1919

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

examples/custom_provider.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use jsonwebtoken::{
2+
Algorithm, DecodingKey, EncodingKey, Header, Validation,
3+
crypto::{CryptoProvider, JwkUtils, JwtSigner, JwtVerifier},
4+
decode, encode,
5+
errors::Error,
6+
};
7+
use serde::{Deserialize, Serialize};
8+
use signature::{Signer, Verifier};
9+
10+
fn new_signer(algorithm: &Algorithm, key: &EncodingKey) -> Result<Box<dyn JwtSigner>, Error> {
11+
let jwt_signer = match algorithm {
12+
Algorithm::EdDSA => Box::new(CustomEdDSASigner::new(key)?) as Box<dyn JwtSigner>,
13+
_ => unimplemented!(),
14+
};
15+
16+
Ok(jwt_signer)
17+
}
18+
19+
fn new_verifier(algorithm: &Algorithm, key: &DecodingKey) -> Result<Box<dyn JwtVerifier>, Error> {
20+
let jwt_verifier = match algorithm {
21+
Algorithm::EdDSA => Box::new(CustomEdDSAVerifier::new(key)?) as Box<dyn JwtVerifier>,
22+
_ => unimplemented!(),
23+
};
24+
25+
Ok(jwt_verifier)
26+
}
27+
28+
pub struct CustomEdDSASigner;
29+
30+
impl CustomEdDSASigner {
31+
fn new(_: &EncodingKey) -> Result<Self, Error> {
32+
Ok(CustomEdDSASigner)
33+
}
34+
}
35+
36+
// WARNING: This is obviously not secure at all and should NEVER be done in practice!
37+
impl Signer<Vec<u8>> for CustomEdDSASigner {
38+
fn try_sign(&self, _: &[u8]) -> Result<Vec<u8>, signature::Error> {
39+
Ok(vec![0; 16])
40+
}
41+
}
42+
43+
impl JwtSigner for CustomEdDSASigner {
44+
fn algorithm(&self) -> Algorithm {
45+
Algorithm::EdDSA
46+
}
47+
}
48+
49+
pub struct CustomEdDSAVerifier;
50+
51+
impl CustomEdDSAVerifier {
52+
fn new(_: &DecodingKey) -> Result<Self, Error> {
53+
Ok(CustomEdDSAVerifier)
54+
}
55+
}
56+
57+
impl Verifier<Vec<u8>> for CustomEdDSAVerifier {
58+
fn verify(&self, _: &[u8], signature: &Vec<u8>) -> Result<(), signature::Error> {
59+
if signature == &vec![0; 16] { Ok(()) } else { Err(signature::Error::new()) }
60+
}
61+
}
62+
63+
impl JwtVerifier for CustomEdDSAVerifier {
64+
fn algorithm(&self) -> Algorithm {
65+
Algorithm::EdDSA
66+
}
67+
}
68+
69+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
70+
pub struct Claims {
71+
sub: String,
72+
exp: u64,
73+
}
74+
75+
fn main() {
76+
// create and install our custom provider
77+
let my_crypto_provider = CryptoProvider {
78+
signer_factory: new_signer,
79+
verifier_factory: new_verifier,
80+
// the default impl uses dummy functions that panic, but we don't need them here
81+
jwk_utils: JwkUtils::default(),
82+
};
83+
my_crypto_provider.install_default().unwrap();
84+
85+
// for an actual EdDSA implementation, this would be some private key
86+
let key = b"secret";
87+
let my_claims = Claims { sub: "me".to_owned(), exp: 10000000000 };
88+
89+
// our crypto provider only supports EdDSA
90+
let header = Header::new(Algorithm::EdDSA);
91+
92+
let token = match encode(&header, &my_claims, &EncodingKey::from_ed_der(key)) {
93+
Ok(t) => t,
94+
Err(_) => panic!(), // in practice you would return an error
95+
};
96+
97+
let claims = match decode::<Claims>(
98+
token,
99+
&DecodingKey::from_ed_der(key),
100+
&Validation::new(Algorithm::EdDSA),
101+
) {
102+
Ok(c) => c.claims,
103+
Err(_) => panic!(),
104+
};
105+
106+
assert_eq!(my_claims, claims);
107+
}

src/crypto/aws_lc/mod.rs

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,65 @@
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::{self as aws_sig, KeyPair},
4+
};
5+
6+
use crate::{
7+
Algorithm, DecodingKey, EncodingKey,
8+
crypto::{CryptoProvider, JwkUtils, JwtSigner, JwtVerifier},
9+
errors::{self, Error, ErrorKind},
10+
jwk::{EllipticCurve, ThumbprintHash},
11+
};
12+
13+
mod ecdsa;
14+
mod eddsa;
15+
mod hmac;
16+
mod rsa;
17+
18+
/// Given a DER encoded private key, extract the RSA public key components (n, e)
19+
pub fn extract_rsa_public_key_components(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
20+
let key_pair = aws_sig::RsaKeyPair::from_der(key_content)
21+
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
22+
let public = key_pair.public_key();
23+
let components = aws_sig::RsaPublicKeyComponents::<Vec<u8>>::from(public);
24+
Ok((components.n, components.e))
25+
}
26+
27+
/// Given a DER encoded private key and an algorithm, extract the associated curve
28+
/// and the EC public key components (x, y)
29+
pub fn extract_ec_public_key_coordinates(
30+
key_content: &[u8],
31+
alg: Algorithm,
32+
) -> errors::Result<(EllipticCurve, Vec<u8>, Vec<u8>)> {
33+
use aws_lc_rs::signature::{
34+
ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING, EcdsaKeyPair,
35+
};
36+
37+
let (signing_alg, curve, pub_elem_bytes) = match alg {
38+
Algorithm::ES256 => (&ECDSA_P256_SHA256_FIXED_SIGNING, EllipticCurve::P256, 32),
39+
Algorithm::ES384 => (&ECDSA_P384_SHA384_FIXED_SIGNING, EllipticCurve::P384, 48),
40+
_ => return Err(ErrorKind::InvalidEcdsaKey.into()),
41+
};
42+
43+
let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_content)
44+
.map_err(|_| ErrorKind::InvalidEcdsaKey)?;
45+
46+
let pub_bytes = key_pair.public_key().as_ref();
47+
if pub_bytes[0] != 4 {
48+
return Err(ErrorKind::InvalidEcdsaKey.into());
49+
}
50+
51+
let (x, y) = pub_bytes[1..].split_at(pub_elem_bytes);
52+
Ok((curve, x.to_vec(), y.to_vec()))
53+
}
54+
55+
/// Given some data and a name of a hash function, compute hash_function(data)
56+
pub fn compute_digest(data: &[u8], hash_function: ThumbprintHash) -> Vec<u8> {
57+
let algorithm = match hash_function {
58+
ThumbprintHash::SHA256 => &digest::SHA256,
59+
ThumbprintHash::SHA384 => &digest::SHA384,
60+
ThumbprintHash::SHA512 => &digest::SHA512,
61+
};
62+
digest::digest(algorithm, data).as_ref().to_vec()
63+
}
64+
65+
define_default_provider!("aws_lc_rs", "https://github.com/aws/aws-lc-rs");

src/crypto/macros.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#[cfg(any(feature = "rust_crypto", feature = "aws_lc_rs"))]
2+
macro_rules! define_default_provider {
3+
($name:literal, $link:literal) => {
4+
#[doc = "The default [`CryptoProvider`] backed by [`"]
5+
#[doc = $name]
6+
#[doc = "`]"]
7+
#[doc = concat!("The default [`CryptoProvider`] backed by [`", $name, "`]")]
8+
#[doc = ""]
9+
#[doc = concat!("[`", $name, "`]: ", $link)]
10+
pub const DEFAULT_PROVIDER: CryptoProvider = CryptoProvider {
11+
signer_factory: new_signer,
12+
verifier_factory: new_verifier,
13+
jwk_utils: JwkUtils {
14+
extract_rsa_public_key_components,
15+
extract_ec_public_key_coordinates,
16+
compute_digest,
17+
},
18+
};
19+
20+
#[doc = "Create a new [`JwtSigner`] for a given [`Algorithm`]."]
21+
pub fn new_signer(
22+
algorithm: &Algorithm,
23+
key: &EncodingKey,
24+
) -> Result<Box<dyn JwtSigner>, Error> {
25+
let jwt_signer = match algorithm {
26+
Algorithm::HS256 => Box::new(hmac::Hs256Signer::new(key)?) as Box<dyn JwtSigner>,
27+
Algorithm::HS384 => Box::new(hmac::Hs384Signer::new(key)?) as Box<dyn JwtSigner>,
28+
Algorithm::HS512 => Box::new(hmac::Hs512Signer::new(key)?) as Box<dyn JwtSigner>,
29+
Algorithm::ES256 => Box::new(ecdsa::Es256Signer::new(key)?) as Box<dyn JwtSigner>,
30+
Algorithm::ES384 => Box::new(ecdsa::Es384Signer::new(key)?) as Box<dyn JwtSigner>,
31+
Algorithm::RS256 => Box::new(rsa::Rsa256Signer::new(key)?) as Box<dyn JwtSigner>,
32+
Algorithm::RS384 => Box::new(rsa::Rsa384Signer::new(key)?) as Box<dyn JwtSigner>,
33+
Algorithm::RS512 => Box::new(rsa::Rsa512Signer::new(key)?) as Box<dyn JwtSigner>,
34+
Algorithm::PS256 => Box::new(rsa::RsaPss256Signer::new(key)?) as Box<dyn JwtSigner>,
35+
Algorithm::PS384 => Box::new(rsa::RsaPss384Signer::new(key)?) as Box<dyn JwtSigner>,
36+
Algorithm::PS512 => Box::new(rsa::RsaPss512Signer::new(key)?) as Box<dyn JwtSigner>,
37+
Algorithm::EdDSA => Box::new(eddsa::EdDSASigner::new(key)?) as Box<dyn JwtSigner>,
38+
};
39+
40+
Ok(jwt_signer)
41+
}
42+
43+
#[doc = "Create a new [`JwtVerifier`] for a given [`Algorithm`]."]
44+
pub fn new_verifier(
45+
algorithm: &Algorithm,
46+
key: &DecodingKey,
47+
) -> Result<Box<dyn super::JwtVerifier>, Error> {
48+
let jwt_encoder = match algorithm {
49+
Algorithm::HS256 => {
50+
Box::new(hmac::Hs256Verifier::new(key)?) as Box<dyn JwtVerifier>
51+
}
52+
Algorithm::HS384 => {
53+
Box::new(hmac::Hs384Verifier::new(key)?) as Box<dyn JwtVerifier>
54+
}
55+
Algorithm::HS512 => {
56+
Box::new(hmac::Hs512Verifier::new(key)?) as Box<dyn JwtVerifier>
57+
}
58+
Algorithm::ES256 => {
59+
Box::new(ecdsa::Es256Verifier::new(key)?) as Box<dyn JwtVerifier>
60+
}
61+
Algorithm::ES384 => {
62+
Box::new(ecdsa::Es384Verifier::new(key)?) as Box<dyn JwtVerifier>
63+
}
64+
Algorithm::RS256 => {
65+
Box::new(rsa::Rsa256Verifier::new(key)?) as Box<dyn JwtVerifier>
66+
}
67+
Algorithm::RS384 => {
68+
Box::new(rsa::Rsa384Verifier::new(key)?) as Box<dyn JwtVerifier>
69+
}
70+
Algorithm::RS512 => {
71+
Box::new(rsa::Rsa512Verifier::new(key)?) as Box<dyn JwtVerifier>
72+
}
73+
Algorithm::PS256 => {
74+
Box::new(rsa::RsaPss256Verifier::new(key)?) as Box<dyn JwtVerifier>
75+
}
76+
Algorithm::PS384 => {
77+
Box::new(rsa::RsaPss384Verifier::new(key)?) as Box<dyn JwtVerifier>
78+
}
79+
Algorithm::PS512 => {
80+
Box::new(rsa::RsaPss512Verifier::new(key)?) as Box<dyn JwtVerifier>
81+
}
82+
Algorithm::EdDSA => {
83+
Box::new(eddsa::EdDSAVerifier::new(key)?) as Box<dyn JwtVerifier>
84+
}
85+
};
86+
87+
Ok(jwt_encoder)
88+
}
89+
};
90+
}

0 commit comments

Comments
 (0)