Skip to content

Commit 4016f48

Browse files
committed
feat: add Jwk::from_decoding_key
Allow for constructing a Jwk from a decoding key. This allows it to be created from a DER encoded file, for example. This patch flattens JwkUtils but adds separate structs for RSA and EC component extraction.
1 parent 245858f commit 4016f48

4 files changed

Lines changed: 227 additions & 57 deletions

File tree

src/crypto/aws_lc/mod.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use aws_lc_rs::{
88

99
use crate::{
1010
Algorithm, DecodingKey, EncodingKey,
11-
crypto::{CryptoProvider, JwkUtils, JwtSigner, JwtVerifier},
11+
crypto::{CryptoProvider, JwtSigner, JwtVerifier},
1212
errors::{self, Error, ErrorKind},
1313
jwk::{EllipticCurve, ThumbprintHash},
1414
};
@@ -18,15 +18,23 @@ mod eddsa;
1818
mod hmac;
1919
mod rsa;
2020

21-
fn extract_rsa_public_key_components(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
21+
fn rsa_components_from_private_key(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
2222
let key_pair = aws_sig::RsaKeyPair::from_der(key_content)
2323
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
2424
let public = key_pair.public_key();
2525
let components = aws_sig::RsaPublicKeyComponents::<Vec<u8>>::from(public);
2626
Ok((components.n, components.e))
2727
}
2828

29-
fn extract_ec_public_key_coordinates(
29+
fn rsa_components_from_public_key(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
30+
let public = aws_lc_rs::rsa::PublicKey::from_der(key_content)
31+
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
32+
33+
let components = aws_sig::RsaPublicKeyComponents::<Vec<u8>>::from(&public);
34+
Ok((components.n, components.e))
35+
}
36+
37+
fn ec_components_from_private_key(
3038
key_content: &[u8],
3139
alg: Algorithm,
3240
) -> errors::Result<(EllipticCurve, Vec<u8>, Vec<u8>)> {
@@ -102,9 +110,13 @@ fn new_verifier(
102110
pub static DEFAULT_PROVIDER: CryptoProvider = CryptoProvider {
103111
signer_factory: new_signer,
104112
verifier_factory: new_verifier,
105-
jwk_utils: JwkUtils {
106-
extract_rsa_public_key_components,
107-
extract_ec_public_key_coordinates,
108-
compute_digest,
113+
rsa_public_components: super::RsaPublicComponents {
114+
from_private_key: rsa_components_from_private_key,
115+
from_public_key: rsa_components_from_public_key,
116+
},
117+
ec_public_components: super::EcPublicComponents {
118+
from_private_key: ec_components_from_private_key,
119+
from_public_key: super::ec_components_from_public_key,
109120
},
121+
compute_digest,
110122
};

src/crypto/mod.rs

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010
//! [`CryptoProvider`]: crate::crypto::CryptoProvider
1111
1212
use crate::algorithms::Algorithm;
13-
use crate::errors::Result;
13+
use crate::errors::{self, ErrorKind, Result};
1414
use crate::jwk::{EllipticCurve, ThumbprintHash};
1515
use crate::{DecodingKey, EncodingKey};
1616

17+
const NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR: &'static str = r###"
18+
Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features, or your CryptoProvider does not support JWKs.
19+
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.
20+
See the documentation of the CryptoProvider type for more information.
21+
"###;
1722
/// `aws_lc_rs` based CryptoProvider.
1823
#[cfg(feature = "aws_lc_rs")]
1924
pub mod aws_lc;
@@ -85,8 +90,13 @@ pub struct CryptoProvider {
8590
pub signer_factory: fn(&Algorithm, &EncodingKey) -> Result<Box<dyn JwtSigner>>,
8691
/// A function that produces a [`JwtVerifier`] for a given [`Algorithm`]
8792
pub verifier_factory: fn(&Algorithm, &DecodingKey) -> Result<Box<dyn JwtVerifier>>,
88-
/// Struct with utility functions for JWK processing.
89-
pub jwk_utils: JwkUtils,
93+
/// Functions to extract RSA public key components from private and public keys
94+
pub rsa_public_components: RsaPublicComponents,
95+
/// Functions to extract EC public key components from private and public keys
96+
pub ec_public_components: EcPublicComponents,
97+
98+
/// Given some data and a name of a hash function, compute hash_function(data)
99+
pub compute_digest: fn(&[u8], ThumbprintHash) -> Vec<u8>,
90100
}
91101

92102
impl CryptoProvider {
@@ -123,51 +133,95 @@ See the documentation of the CryptoProvider type for more information.
123133
static INSTANCE: CryptoProvider = CryptoProvider {
124134
signer_factory: |_, _| panic!("{}", NOT_INSTALLED_ERROR),
125135
verifier_factory: |_, _| panic!("{}", NOT_INSTALLED_ERROR),
126-
jwk_utils: JwkUtils::new_unimplemented(),
136+
rsa_public_components: RsaPublicComponents::new_unimplemented(),
137+
ec_public_components: EcPublicComponents::new_unimplemented(),
138+
compute_digest: |_, _| panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR),
127139
};
128140

129141
&INSTANCE
130142
}
131143
}
132144
}
133145

134-
/// Holds utility functions required for JWK processing.
135-
/// Use the [`JwkUtils::new_unimplemented`] function to initialize all values to dummies.
146+
/// Holds utility functions to extract RSA public key components from private and public keys
147+
/// Use the [`RsaPublicComponents::new_unimplemented`] function to initialize all values to dummies.
136148
#[derive(Clone, Debug)]
137-
pub struct JwkUtils {
149+
pub struct RsaPublicComponents {
138150
/// Given a DER encoded private key, extract the RSA public key components (n, e)
139151
#[allow(clippy::type_complexity)]
140-
pub extract_rsa_public_key_components: fn(&[u8]) -> Result<(Vec<u8>, Vec<u8>)>,
152+
pub from_private_key: fn(&[u8]) -> Result<(Vec<u8>, Vec<u8>)>,
153+
154+
/// Given a DER encoded public key, extract the RSA public key components (n, e)
155+
#[allow(clippy::type_complexity)]
156+
pub from_public_key: fn(&[u8]) -> Result<(Vec<u8>, Vec<u8>)>,
157+
}
158+
159+
impl RsaPublicComponents {
160+
/// Creates a instance filled with dummy functions that always panic
161+
pub const fn new_unimplemented() -> Self {
162+
Self {
163+
from_private_key: |_| {
164+
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
165+
},
166+
167+
from_public_key: |_| {
168+
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
169+
},
170+
171+
}
172+
}
173+
}
174+
175+
/// Holds utility functions to extract EC public key components from private and public keys
176+
/// Use the [`EcPublicComponents::new_unimplemented`] function to initialize all values to dummies.
177+
#[derive(Clone, Debug)]
178+
pub struct EcPublicComponents {
141179
/// Given a DER encoded private key and an algorithm, extract the associated curve
142180
/// and the EC public key components (x, y)
143181
#[allow(clippy::type_complexity)]
144-
pub extract_ec_public_key_coordinates:
182+
pub from_private_key:
145183
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>,
184+
185+
/// Given a DER encoded public key and an algorithm, extract the associated curve
186+
/// and the EC public key components (x, y)
187+
#[allow(clippy::type_complexity)]
188+
pub from_public_key:
189+
fn(&[u8]) -> Result<(EllipticCurve, Vec<u8>, Vec<u8>)>,
148190
}
149191

150-
impl 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.
192+
impl EcPublicComponents {
193+
/// Creates a instance filled with dummy functions that always panic
153194
pub const fn new_unimplemented() -> Self {
154-
const NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR: &str = r###"
155-
Could not automatically determine the process-level CryptoProvider from jsonwebtoken crate features, or your CryptoProvider does not support JWKs.
156-
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.
157-
See the documentation of the CryptoProvider type for more information.
158-
"###;
159195
Self {
160-
extract_rsa_public_key_components: |_| {
196+
from_private_key: |_, _| {
161197
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
162198
},
163-
extract_ec_public_key_coordinates: |_, _| {
199+
200+
from_public_key: |_| {
164201
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
165202
},
166-
compute_digest: |_, _| panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR),
203+
167204
}
168205
}
169206
}
170207

208+
pub(crate) fn ec_components_from_public_key(
209+
pub_bytes: &[u8],
210+
) -> errors::Result<(EllipticCurve, Vec<u8>, Vec<u8>)> {
211+
let (curve, pub_elem_bytes) = match pub_bytes.len() {
212+
65 => (EllipticCurve::P256, 32),
213+
97 => (EllipticCurve::P384, 48),
214+
_ => return Err(ErrorKind::InvalidEcdsaKey.into()),
215+
};
216+
217+
if pub_bytes[0] != 4 {
218+
return Err(ErrorKind::InvalidEcdsaKey.into());
219+
}
220+
221+
let (x, y) = pub_bytes[1..].split_at(pub_elem_bytes);
222+
Ok((curve, x.to_vec(), y.to_vec()))
223+
}
224+
171225
mod static_default {
172226
use std::sync::OnceLock;
173227

src/crypto/rust_crypto/mod.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
use ::rsa::{RsaPrivateKey, pkcs1::DecodeRsaPrivateKey, traits::PublicKeyParts};
1+
use ::rsa::{
2+
RsaPrivateKey, RsaPublicKey,
3+
pkcs1::{
4+
DecodeRsaPrivateKey, DecodeRsaPublicKey
5+
},
6+
traits::PublicKeyParts
7+
};
28
use p256::{ecdsa::SigningKey as P256SigningKey, pkcs8::DecodePrivateKey};
39
use p384::ecdsa::SigningKey as P384SigningKey;
410
use sha2::{Digest, Sha256, Sha384, Sha512};
511

612
use crate::{
713
Algorithm, DecodingKey, EncodingKey,
8-
crypto::{CryptoProvider, JwkUtils, JwtSigner, JwtVerifier},
14+
crypto::{CryptoProvider, JwtSigner, JwtVerifier},
915
errors::{self, Error, ErrorKind},
1016
jwk::{EllipticCurve, ThumbprintHash},
1117
};
@@ -15,14 +21,20 @@ mod eddsa;
1521
mod hmac;
1622
mod rsa;
1723

18-
fn extract_rsa_public_key_components(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
24+
fn rsa_components_from_private_key(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
1925
let private_key = RsaPrivateKey::from_pkcs1_der(key_content)
2026
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
2127
let public_key = private_key.to_public_key();
2228
Ok((public_key.n().to_bytes_be(), public_key.e().to_bytes_be()))
2329
}
2430

25-
fn extract_ec_public_key_coordinates(
31+
fn rsa_components_from_public_key(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
32+
let public_key = RsaPublicKey::from_pkcs1_der(key_content)
33+
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
34+
Ok((public_key.n().to_bytes_be(), public_key.e().to_bytes_be()))
35+
}
36+
37+
fn ec_components_from_private_key(
2638
key_content: &[u8],
2739
alg: Algorithm,
2840
) -> errors::Result<(EllipticCurve, Vec<u8>, Vec<u8>)> {
@@ -108,9 +120,13 @@ fn new_verifier(
108120
pub static DEFAULT_PROVIDER: CryptoProvider = CryptoProvider {
109121
signer_factory: new_signer,
110122
verifier_factory: new_verifier,
111-
jwk_utils: JwkUtils {
112-
extract_rsa_public_key_components,
113-
extract_ec_public_key_coordinates,
114-
compute_digest,
123+
rsa_public_components: super::RsaPublicComponents {
124+
from_private_key: rsa_components_from_private_key,
125+
from_public_key: rsa_components_from_public_key,
126+
},
127+
ec_public_components: super::EcPublicComponents {
128+
from_private_key: ec_components_from_private_key,
129+
from_public_key: super::ec_components_from_public_key,
115130
},
131+
compute_digest,
116132
};

0 commit comments

Comments
 (0)