Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ target
Cargo.lock
.idea/
.vscode
.zed
14 changes: 12 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ ed25519-dalek = { version = "2.1.1", optional = true, features = ["pkcs8"] }
hmac = { version = "0.12.1", optional = true }
p256 = { version = "0.13.2", optional = true, features = ["ecdsa"] }
p384 = { version = "0.13.0", optional = true, features = ["ecdsa"] }
rand = { version = "0.8.5", optional = true, features = ["std"], default-features = false }
rand = { version = "0.8.5", optional = true, features = [
"std",
], default-features = false }
rsa = { version = "0.9.6", optional = true }
sha2 = { version = "0.10.7", optional = true, features = ["oid"] }
zeroize = { version = "1.8.2", features = ["derive"] }
Expand All @@ -67,7 +69,15 @@ criterion = { version = "0.8", default-features = false }
[features]
default = ["use_pem"]
use_pem = ["dep:pem", "dep:simple_asn1"]
rust_crypto = ["dep:ed25519-dalek", "dep:hmac", "dep:p256", "dep:p384", "dep:rand", "dep:rsa", "dep:sha2"]
rust_crypto = [
"dep:ed25519-dalek",
"dep:hmac",
"dep:p256",
"dep:p384",
"dep:rand",
"dep:rsa",
"dep:sha2",
]
aws_lc_rs = ["dep:aws-lc-rs"]

[[bench]]
Expand Down
17 changes: 16 additions & 1 deletion src/crypto/aws_lc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use aws_lc_rs::{
digest,
signature::{
self as aws_sig, ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING,
EcdsaKeyPair, KeyPair,
EcdsaKeyPair, Ed25519KeyPair, KeyPair,
},
};

Expand Down Expand Up @@ -56,6 +56,20 @@ fn ec_components_from_private_key(
Ok((curve, x.to_vec(), y.to_vec()))
}

fn ed_pub_components_from_private_key(
encoding_key: &[u8],
curve_type: &EllipticCurve,
) -> errors::Result<Vec<u8>> {
match curve_type {
EllipticCurve::Ed25519 => Ok(Ed25519KeyPair::from_pkcs8(encoding_key)
.map_err(|_| ErrorKind::InvalidEddsaKey)?
.public_key()
.as_ref()
.to_vec()),
_ => Err(ErrorKind::InvalidAlgorithm.into()),
}
}

fn compute_digest(data: &[u8], hash_function: ThumbprintHash) -> errors::Result<Vec<u8>> {
let algorithm = match hash_function {
ThumbprintHash::SHA256 => &digest::SHA256,
Expand Down Expand Up @@ -114,6 +128,7 @@ pub static DEFAULT_PROVIDER: CryptoProvider = CryptoProvider {
rsa_pub_components_from_private_key: rsa_components_from_private_key,
rsa_pub_components_from_public_key: rsa_components_from_public_key,
ec_pub_components_from_private_key: ec_components_from_private_key,
ed_pub_components_from_private_key,
compute_digest,
},
};
7 changes: 6 additions & 1 deletion src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ pub struct KeyUtils {
#[allow(clippy::type_complexity)]
pub ec_pub_components_from_private_key:
fn(&[u8], Algorithm) -> Result<(EllipticCurve, Vec<u8>, Vec<u8>)>,
/// Given a DER encoded private key and the curve type, extract the ED public key component (x)
pub ed_pub_components_from_private_key: fn(&[u8], &EllipticCurve) -> Result<Vec<u8>>,
/// Given some data and a name of a hash function, compute hash_function(data)
pub compute_digest: fn(&[u8], ThumbprintHash) -> Result<Vec<u8>>,
}
Expand All @@ -169,12 +171,15 @@ See the documentation of the CryptoProvider type for more information.
ec_pub_components_from_private_key: |_, _| {
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
},
ed_pub_components_from_private_key: |_, _| {
panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR)
},
compute_digest: |_, _| panic!("{}", NOT_INSTALLED_OR_UNIMPLEMENTED_ERROR),
}
}
}

/// Given bitstring from DER encoded private key, extract the associated curve
/// Given bitstring from DER encoded public key, extract the associated curve
/// and the EC public key components (x, y)
pub(crate) fn ec_pub_components_from_public_key(
pub_bytes: &[u8],
Expand Down
16 changes: 16 additions & 0 deletions src/crypto/rust_crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use ::rsa::{
pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey},
traits::PublicKeyParts,
};
use ed25519_dalek::SigningKey as Ed25519SigningKey;
use p256::{ecdsa::SigningKey as P256SigningKey, pkcs8::DecodePrivateKey};
use p384::ecdsa::SigningKey as P384SigningKey;
use sha2::{Digest, Sha256, Sha384, Sha512};
Expand Down Expand Up @@ -65,6 +66,20 @@ fn ec_components_from_private_key(
}
}

fn ed_pub_components_from_private_key(
encoding_key: &[u8],
curve_type: &EllipticCurve,
) -> errors::Result<Vec<u8>> {
match curve_type {
EllipticCurve::Ed25519 => Ok(Ed25519SigningKey::from_pkcs8_der(encoding_key)
.map_err(|_| ErrorKind::InvalidEddsaKey)?
.verifying_key()
.as_bytes()
.to_vec()),
_ => Err(ErrorKind::InvalidAlgorithm.into()),
}
}

fn compute_digest(data: &[u8], hash_function: ThumbprintHash) -> errors::Result<Vec<u8>> {
Ok(match hash_function {
ThumbprintHash::SHA256 => Sha256::digest(data).to_vec(),
Expand Down Expand Up @@ -122,6 +137,7 @@ pub static DEFAULT_PROVIDER: CryptoProvider = CryptoProvider {
rsa_pub_components_from_private_key: rsa_components_from_private_key,
rsa_pub_components_from_public_key: rsa_components_from_public_key,
ec_pub_components_from_private_key: ec_components_from_private_key,
ed_pub_components_from_private_key,
compute_digest,
},
};
1 change: 1 addition & 0 deletions src/decoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ impl DecodingKey {

/// If you have a EdDSA public key in PEM format, use this.
/// Only exists if the feature `use_pem` is enabled.
/// Note: Ed448 keys are not supported
#[cfg(feature = "use_pem")]
pub fn from_ed_pem(key: &[u8]) -> Result<Self> {
let pem_key = PemEncodedKey::new(key)?;
Expand Down
61 changes: 54 additions & 7 deletions src/jwk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ pub enum EllipticCurveKeyType {
/// Type of cryptographic curve used by a key. This is defined in
/// [RFC 7518 #7.6](https://tools.ietf.org/html/rfc7518#section-7.6)
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[non_exhaustive]
Comment thread
arckoor marked this conversation as resolved.
pub enum EllipticCurve {
/// P-256 curve
#[serde(rename = "P-256")]
Expand Down Expand Up @@ -478,8 +479,6 @@ impl Jwk {
}

/// Create a `JWK` from an `EncodingKey`.
///
/// Edwards curve based keys are not supported.
pub fn from_encoding_key(key: &EncodingKey, alg: Algorithm) -> errors::Result<Self> {
Ok(Self {
common: CommonParameters { key_algorithm: Some(alg.into()), ..Default::default() },
Expand Down Expand Up @@ -514,15 +513,32 @@ impl Jwk {
})
}
AlgorithmFamily::Ed => {
unimplemented!("Edwards curves are not supported");
// Get the curve type based off the encoding key length
// Note: here we will receive a DER key which contains a 16 byte ANS.1 header
let curve_type: EllipticCurve = match key.inner().len() {
// 16 byte header + 32 byte Ed25519 key
48 => Ok(EllipticCurve::Ed25519),
_ => Err(Error::from(ErrorKind::InvalidEddsaKey)),
}?;

// Extract the public key from the encoding key
let public_key_bytes = (CryptoProvider::get_default()
.key_utils
.ed_pub_components_from_private_key)(
key.inner(), &curve_type
)?;

AlgorithmParameters::OctetKeyPair(OctetKeyPairParameters {
key_type: OctetKeyPairType::OctetKeyPair,
curve: curve_type,
x: b64_encode(public_key_bytes),
})
}
},
})
}

/// Create a `JWK` from a `DecodingKey`.
///
/// Edwards curve based keys are not supported.
pub fn from_decoding_key(
key: &DecodingKey,
alg: Option<Algorithm>,
Expand Down Expand Up @@ -574,7 +590,22 @@ impl Jwk {
})
}
crate::algorithms::AlgorithmFamily::Ed => {
unimplemented!("Edwards curves are not supported");
let (curve_type, x) = match &key.kind() {
DecodingKeyKind::SecretOrDer(pub_bytes) => {
match pub_bytes.len() {
// ED25519: https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5
32 => (EllipticCurve::Ed25519, pub_bytes),
_ => return Err(ErrorKind::InvalidEddsaKey.into()),
}
}
_ => return Err(ErrorKind::InvalidKeyFormat.into()),
};
Comment on lines +593 to +602

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #515 should be able to use try_get_as_bytes (just note to self so I don't forget :p)


AlgorithmParameters::OctetKeyPair(OctetKeyPairParameters {
key_type: OctetKeyPairType::OctetKeyPair,
curve: curve_type,
x: b64_encode(x),
})
}
},
})
Expand All @@ -595,7 +626,9 @@ impl Jwk {
a.y,
)
}
EllipticCurve::Ed25519 => panic!("EllipticCurve can't contain this curve type"),
EllipticCurve::Ed25519 => {
panic!("EllipticCurve can't contain this curve type")
}
},
AlgorithmParameters::RSA(a) => {
format!(
Expand Down Expand Up @@ -784,4 +817,18 @@ mod tests {
let jwk = Jwk::from_decoding_key(&dec_key, Some(Algorithm::ES256)).unwrap();
assert_eq!(jwk, expected_jwk);
}

#[test]
#[cfg(feature = "use_pem")]
fn check_jwk_from_decoding_key_ed() {
let enc_key =
EncodingKey::from_ed_pem(include_bytes!("../tests/eddsa/private_ed25519_key.pem"))
.unwrap();
let dec_key =
DecodingKey::from_ed_pem(include_bytes!("../tests/eddsa/public_ed25519_key.pem"))
.unwrap();
let expected_jwk = Jwk::from_encoding_key(&enc_key, Algorithm::EdDSA).unwrap();
let jwk = Jwk::from_decoding_key(&dec_key, Some(Algorithm::EdDSA)).unwrap();
assert_eq!(jwk, expected_jwk);
}
}
2 changes: 2 additions & 0 deletions src/pem/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,13 @@ fn extract_first_bitstring(asn1: &[simple_asn1::ASN1Block]) -> Result<&[u8]> {
}

/// Find whether this is EC, RSA, or Ed
/// Note: Ed448 keys are not supported
fn classify_pem(asn1: &[simple_asn1::ASN1Block]) -> Option<Classification> {
// These should be constant but the macro requires
// #![feature(const_vec_new)]
let ec_public_key_oid = simple_asn1::oid!(1, 2, 840, 10_045, 2, 1);
let rsa_public_key_oid = simple_asn1::oid!(1, 2, 840, 113_549, 1, 1, 1);
// Defined: https://datatracker.ietf.org/doc/html/rfc8410#section-3 id-Ed25519)
let ed25519_oid = simple_asn1::oid!(1, 3, 101, 112);

for asn1_entry in asn1 {
Expand Down
60 changes: 59 additions & 1 deletion tests/eddsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use jsonwebtoken::{
crypto::{sign, verify},
};
#[cfg(feature = "use_pem")]
use jsonwebtoken::{Header, Validation, decode, encode};
use jsonwebtoken::{Header, Validation, decode, encode, errors::ErrorKind};

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct Claims {
Expand Down Expand Up @@ -138,3 +138,61 @@ fn ed_jwk() {
);
assert!(res.is_ok());
}

#[cfg(feature = "use_pem")]
#[test]
#[wasm_bindgen_test]
fn ed_jwk_from_ed25519_key() {
use jsonwebtoken::jwk::Jwk;
use serde_json::json;

let privkey_pem = include_bytes!("private_ed25519_key.pem");
let encoding_key = EncodingKey::from_ed_pem(privkey_pem).unwrap();

let jwk = Jwk::from_encoding_key(&encoding_key, Algorithm::EdDSA).unwrap();

assert_eq!(
jwk,
serde_json::from_value(json!({
"kty": "OKP",
"crv": "Ed25519",
"x": "2-Jj2UvNCvQiUPNYRgSi0cJSPiJI6Rs6D0UTeEpQVj8",
"alg": "EdDSA",
}))
.unwrap()
);
}

#[test]
#[wasm_bindgen_test]
fn ed_jwk_from_ed25519_der_key() {
use jsonwebtoken::jwk::Jwk;
use serde_json::json;

let privkey_der = include_bytes!("private_ed25519_der_key.bin");
let encoding_key = EncodingKey::from_ed_der(privkey_der);

let jwk = Jwk::from_encoding_key(&encoding_key, Algorithm::EdDSA).unwrap();

assert_eq!(
jwk,
serde_json::from_value(json!({
"kty": "OKP",
"crv": "Ed25519",
"x": "2-Jj2UvNCvQiUPNYRgSi0cJSPiJI6Rs6D0UTeEpQVj8",
"alg": "EdDSA",
}))
.unwrap()
);
}

#[cfg(feature = "use_pem")]
#[test]
#[wasm_bindgen_test]
fn ed_jwk_from_ed448_key() {
let privkey_pem = include_bytes!("private_ed448_key.pem");
assert_eq!(
EncodingKey::from_ed_pem(privkey_pem).unwrap_err().into_kind(),
ErrorKind::InvalidKeyFormat
);
}
Binary file added tests/eddsa/private_ed25519_der_key.bin
Binary file not shown.
4 changes: 4 additions & 0 deletions tests/eddsa/private_ed448_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOXkzGoM834lGCR1VS1qNA+fldfH2c31QnRqEeV+voTVX
dkKVmRC1TYzvN52g2mAbFhEHV2DxtXSUUA==
-----END PRIVATE KEY-----
Loading