Skip to content

Commit 1e6562e

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 renames JwkUtils to KeyUtils.
1 parent bba16eb commit 1e6562e

4 files changed

Lines changed: 176 additions & 29 deletions

File tree

src/crypto/aws_lc/mod.rs

Lines changed: 16 additions & 6 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, KeyUtils},
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,11 @@ 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,
113+
key_utils: KeyUtils {
114+
rsa_pub_components_from_private_key: rsa_components_from_private_key,
115+
rsa_pub_components_from_public_key: rsa_components_from_public_key,
116+
ec_pub_components_from_private_key: ec_components_from_private_key,
117+
ec_pub_components_from_public_key: crate::crypto::ec_pub_components_from_public_key,
108118
compute_digest,
109119
},
110120
};

src/crypto/mod.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
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

@@ -86,7 +86,7 @@ pub struct CryptoProvider {
8686
/// A function that produces a [`JwtVerifier`] for a given [`Algorithm`]
8787
pub verifier_factory: fn(&Algorithm, &DecodingKey) -> Result<Box<dyn JwtVerifier>>,
8888
/// Struct with utility functions for JWK processing.
89-
pub jwk_utils: JwkUtils,
89+
pub key_utils: KeyUtils,
9090
}
9191

9292
impl CryptoProvider {
@@ -123,7 +123,7 @@ See the documentation of the CryptoProvider type for more information.
123123
static INSTANCE: CryptoProvider = CryptoProvider {
124124
signer_factory: |_, _| panic!("{}", NOT_INSTALLED_ERROR),
125125
verifier_factory: |_, _| panic!("{}", NOT_INSTALLED_ERROR),
126-
jwk_utils: JwkUtils::new_unimplemented(),
126+
key_utils: KeyUtils::new_unimplemented(),
127127
};
128128

129129
&INSTANCE
@@ -132,22 +132,29 @@ See the documentation of the CryptoProvider type for more information.
132132
}
133133

134134
/// Holds utility functions required for JWK processing.
135-
/// Use the [`JwkUtils::new_unimplemented`] function to initialize all values to dummies.
135+
/// Use the [`KeyUtils::new_unimplemented`] function to initialize all values to dummies.
136136
#[derive(Clone, Debug)]
137-
pub struct JwkUtils {
137+
pub struct KeyUtils {
138138
/// Given a DER encoded private key, extract the RSA public key components (n, e)
139139
#[allow(clippy::type_complexity)]
140-
pub extract_rsa_public_key_components: fn(&[u8]) -> Result<(Vec<u8>, Vec<u8>)>,
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>)>,
141144
/// Given a DER encoded private key and an algorithm, extract the associated curve
142145
/// and the EC public key components (x, y)
143146
#[allow(clippy::type_complexity)]
144-
pub extract_ec_public_key_coordinates:
147+
pub ec_pub_components_from_private_key:
145148
fn(&[u8], Algorithm) -> Result<(EllipticCurve, Vec<u8>, Vec<u8>)>,
149+
/// Given bitstring from DER encoded private key, extract the associated curve
150+
/// and the EC public key components (x, y)
151+
#[allow(clippy::type_complexity)]
152+
pub ec_pub_components_from_public_key: fn(&[u8]) -> Result<(EllipticCurve, Vec<u8>, Vec<u8>)>,
146153
/// Given some data and a name of a hash function, compute hash_function(data)
147154
pub compute_digest: fn(&[u8], ThumbprintHash) -> Result<Vec<u8>>,
148155
}
149156

150-
impl JwkUtils {
157+
impl KeyUtils {
151158
/// Initialises all values to dummies.
152159
/// Will lead to a panic when JWKs are required, so only use it if you don't want to support JWKs.
153160
pub const fn new_unimplemented() -> Self {
@@ -157,17 +164,41 @@ Call CryptoProvider::install_default() before this point to select a provider ma
157164
See the documentation of the CryptoProvider type for more information.
158165
";
159166
Self {
160-
extract_rsa_public_key_components: |_| {
167+
rsa_pub_components_from_private_key: |_| {
161168
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
162169
},
163-
extract_ec_public_key_coordinates: |_, _| {
170+
rsa_pub_components_from_public_key: |_| {
171+
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
172+
},
173+
ec_pub_components_from_private_key: |_, _| {
174+
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
175+
},
176+
ec_pub_components_from_public_key: |_| {
164177
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
165178
},
166179
compute_digest: |_, _| panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR),
167180
}
168181
}
169182
}
170183

184+
#[allow(unused)]
185+
fn ec_pub_components_from_public_key(
186+
pub_bytes: &[u8],
187+
) -> errors::Result<(EllipticCurve, Vec<u8>, Vec<u8>)> {
188+
let (curve, pub_elem_bytes) = match pub_bytes.len() {
189+
65 => (EllipticCurve::P256, 32),
190+
97 => (EllipticCurve::P384, 48),
191+
_ => return Err(ErrorKind::InvalidEcdsaKey.into()),
192+
};
193+
194+
if pub_bytes[0] != 4 {
195+
return Err(ErrorKind::InvalidEcdsaKey.into());
196+
}
197+
198+
let (x, y) = pub_bytes[1..].split_at(pub_elem_bytes);
199+
Ok((curve, x.to_vec(), y.to_vec()))
200+
}
201+
171202
mod static_default {
172203
use std::sync::OnceLock;
173204

src/crypto/rust_crypto/mod.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
use ::rsa::{RsaPrivateKey, pkcs1::DecodeRsaPrivateKey, traits::PublicKeyParts};
1+
use ::rsa::{
2+
RsaPrivateKey, RsaPublicKey,
3+
pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey},
4+
traits::PublicKeyParts,
5+
};
26
use p256::{ecdsa::SigningKey as P256SigningKey, pkcs8::DecodePrivateKey};
37
use p384::ecdsa::SigningKey as P384SigningKey;
48
use sha2::{Digest, Sha256, Sha384, Sha512};
59

610
use crate::{
711
Algorithm, DecodingKey, EncodingKey,
8-
crypto::{CryptoProvider, JwkUtils, JwtSigner, JwtVerifier},
12+
crypto::{CryptoProvider, JwtSigner, JwtVerifier, KeyUtils},
913
errors::{self, Error, ErrorKind},
1014
jwk::{EllipticCurve, ThumbprintHash},
1115
};
@@ -15,14 +19,20 @@ mod eddsa;
1519
mod hmac;
1620
mod rsa;
1721

18-
fn extract_rsa_public_key_components(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
22+
fn rsa_components_from_private_key(key_content: &[u8]) -> errors::Result<(Vec<u8>, Vec<u8>)> {
1923
let private_key = RsaPrivateKey::from_pkcs1_der(key_content)
2024
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
2125
let public_key = private_key.to_public_key();
2226
Ok((public_key.n().to_bytes_be(), public_key.e().to_bytes_be()))
2327
}
2428

25-
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_key = RsaPublicKey::from_pkcs1_der(key_content)
31+
.map_err(|e| ErrorKind::InvalidRsaKey(e.to_string()))?;
32+
Ok((public_key.n().to_bytes_be(), public_key.e().to_bytes_be()))
33+
}
34+
35+
fn ec_components_from_private_key(
2636
key_content: &[u8],
2737
alg: Algorithm,
2838
) -> errors::Result<(EllipticCurve, Vec<u8>, Vec<u8>)> {
@@ -108,9 +118,11 @@ fn new_verifier(
108118
pub static DEFAULT_PROVIDER: CryptoProvider = CryptoProvider {
109119
signer_factory: new_signer,
110120
verifier_factory: new_verifier,
111-
jwk_utils: JwkUtils {
112-
extract_rsa_public_key_components,
113-
extract_ec_public_key_coordinates,
121+
key_utils: KeyUtils {
122+
rsa_pub_components_from_private_key: rsa_components_from_private_key,
123+
rsa_pub_components_from_public_key: rsa_components_from_public_key,
124+
ec_pub_components_from_private_key: ec_components_from_private_key,
125+
ec_pub_components_from_public_key: crate::crypto::ec_pub_components_from_public_key,
114126
compute_digest,
115127
},
116128
};

src/jwk.rs

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
1010
use crate::crypto::CryptoProvider;
1111
use crate::errors::{self, Error, ErrorKind, new_error};
1212
use crate::serialization::b64_encode;
13-
use crate::{Algorithm, AlgorithmFamily, EncodingKey};
13+
use crate::{
14+
Algorithm, AlgorithmFamily,DecodingKey, EncodingKey,
15+
decoding::DecodingKeyKind,
16+
};
1417

1518
/// The intended usage of the public `KeyType`. This enum is serialized `untagged`
1619
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
@@ -490,8 +493,8 @@ impl Jwk {
490493
}),
491494
AlgorithmFamily::Rsa => {
492495
let (n, e) = (CryptoProvider::get_default()
493-
.jwk_utils
494-
.extract_rsa_public_key_components)(
496+
.key_utils
497+
.rsa_pub_components_from_private_key)(
495498
key.inner()
496499
)?;
497500
AlgorithmParameters::RSA(RSAKeyParameters {
@@ -502,8 +505,8 @@ impl Jwk {
502505
}
503506
AlgorithmFamily::Ec => {
504507
let (curve, x, y) = (CryptoProvider::get_default()
505-
.jwk_utils
506-
.extract_ec_public_key_coordinates)(
508+
.key_utils
509+
.ec_pub_components_from_private_key)(
507510
key.inner(), alg
508511
)?;
509512
AlgorithmParameters::EllipticCurve(EllipticCurveKeyParameters {
@@ -520,6 +523,68 @@ impl Jwk {
520523
})
521524
}
522525

526+
/// Create a `JWK` from a `DecodingKey`.
527+
///
528+
/// Edwards curve based keys are not supported.
529+
pub fn from_decoding_key(
530+
key: &DecodingKey,
531+
alg: Option<Algorithm>,
532+
) -> crate::errors::Result<Self> {
533+
Ok(Self {
534+
common: CommonParameters { key_algorithm: alg.map(|a| a.into()), ..Default::default() },
535+
algorithm: match key.family() {
536+
crate::algorithms::AlgorithmFamily::Hmac => {
537+
let secret = match &key.kind() {
538+
DecodingKeyKind::SecretOrDer(secret) => secret,
539+
_ => return Err(ErrorKind::InvalidKeyFormat.into()),
540+
};
541+
542+
AlgorithmParameters::OctetKey(OctetKeyParameters {
543+
key_type: OctetKeyType::Octet,
544+
value: b64_encode(secret),
545+
})
546+
}
547+
crate::algorithms::AlgorithmFamily::Rsa => {
548+
let (n, e) = match &key.kind() {
549+
DecodingKeyKind::RsaModulusExponent { n, e } => {
550+
(b64_encode(n), b64_encode(e))
551+
}
552+
DecodingKeyKind::SecretOrDer(der) => {
553+
let (n, e) = (CryptoProvider::get_default()
554+
.key_utils
555+
.rsa_pub_components_from_public_key)(
556+
der
557+
)?;
558+
(b64_encode(n), b64_encode(e))
559+
}
560+
};
561+
562+
AlgorithmParameters::RSA(RSAKeyParameters { key_type: RSAKeyType::RSA, n, e })
563+
}
564+
crate::algorithms::AlgorithmFamily::Ec => {
565+
let (curve, x, y) = match &key.kind() {
566+
DecodingKeyKind::SecretOrDer(pub_bytes) => (CryptoProvider::get_default()
567+
.key_utils
568+
.ec_pub_components_from_public_key)(
569+
pub_bytes
570+
)?,
571+
_ => return Err(ErrorKind::InvalidKeyFormat.into()),
572+
};
573+
574+
AlgorithmParameters::EllipticCurve(EllipticCurveKeyParameters {
575+
key_type: EllipticCurveKeyType::EC,
576+
curve,
577+
x: b64_encode(x),
578+
y: b64_encode(y),
579+
})
580+
}
581+
crate::algorithms::AlgorithmFamily::Ed => {
582+
unimplemented!("Edwards curves are not supported");
583+
}
584+
},
585+
})
586+
}
587+
523588
/// Compute the thumbprint of the JWK.
524589
///
525590
/// Per [RFC-7638](https://datatracker.ietf.org/doc/html/rfc7638)
@@ -567,7 +632,7 @@ impl Jwk {
567632
},
568633
};
569634

570-
Ok(b64_encode((CryptoProvider::get_default().jwk_utils.compute_digest)(
635+
Ok(b64_encode((CryptoProvider::get_default().key_utils.compute_digest)(
571636
pre.as_bytes(),
572637
hash_function,
573638
)?))
@@ -602,6 +667,7 @@ mod tests {
602667
ThumbprintHash,
603668
};
604669
use crate::serialization::b64_encode;
670+
use crate::{DecodingKey, EncodingKey};
605671

606672
#[test]
607673
#[wasm_bindgen_test]
@@ -695,4 +761,32 @@ mod tests {
695761
.is_err_and(|e| *e.kind() == ErrorKind::UnsupportedAlgorithm)
696762
);
697763
}
764+
765+
#[test]
766+
#[cfg(feature = "use_pem")]
767+
fn check_jwk_from_decoding_key_rsa() {
768+
let enc_key =
769+
EncodingKey::from_rsa_pem(include_bytes!("../tests/rsa/private_rsa_key_pkcs8.pem"))
770+
.unwrap();
771+
let dec_key =
772+
DecodingKey::from_rsa_pem(include_bytes!("../tests/rsa/public_rsa_key_pkcs8.pem"))
773+
.unwrap();
774+
let expected_jwk = Jwk::from_encoding_key(&enc_key, Algorithm::RS256).unwrap();
775+
let jwk = Jwk::from_decoding_key(&dec_key, Some(Algorithm::RS256)).unwrap();
776+
assert_eq!(jwk, expected_jwk);
777+
}
778+
779+
#[test]
780+
#[cfg(feature = "use_pem")]
781+
fn check_jwk_from_decoding_key_ec() {
782+
let enc_key =
783+
EncodingKey::from_ec_pem(include_bytes!("../tests/ecdsa/private_ecdsa_key.pem"))
784+
.unwrap();
785+
let dec_key =
786+
DecodingKey::from_ec_pem(include_bytes!("../tests/ecdsa/public_ecdsa_key.pem"))
787+
.unwrap();
788+
let expected_jwk = Jwk::from_encoding_key(&enc_key, Algorithm::ES256).unwrap();
789+
let jwk = Jwk::from_decoding_key(&dec_key, Some(Algorithm::ES256)).unwrap();
790+
assert_eq!(jwk, expected_jwk);
791+
}
698792
}

0 commit comments

Comments
 (0)