diff --git a/.gitignore b/.gitignore index 96ef6c0..869df07 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target -Cargo.lock +Cargo.lock \ No newline at end of file diff --git a/.rustfmt.toml b/.rustfmt.toml index afb030d..0e3f40f 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -18,7 +18,7 @@ fn_single_line = false where_single_line = false imports_indent = "Block" imports_layout = "Mixed" -merge_imports = true +imports_granularity="Crate" reorder_imports = true reorder_modules = true reorder_impl_items = false diff --git a/Cargo.toml b/Cargo.toml index 673ac0d..cf89747 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,15 +13,15 @@ edition = "2018" base64 = "0.13" bitflags = "1.2" generic-array = "0.14" -jsonwebtoken = { version = "8.0", optional = true } -num-bigint = { version = "0.4", optional = true } -p256 = { version = "0.9", optional = true, features = ["arithmetic"] } -rand = { version = "0.8", optional = true } -serde = { version = "1.0", features = ["derive"] } +jsonwebtoken = { version = "8.1", optional = true } +num-bigint = { version = "0.4", optional = true } +p256 = { version = "0.10", optional = true, features = ["arithmetic"] } +rand = { version = "0.8", optional = true } +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -yasna = { version = "0.4", optional = true, features = ["num-bigint"] } -zeroize = { version = "1.4", features = ["zeroize_derive"] } +yasna = { version = "0.5", optional = true, features = ["num-bigint"] } +zeroize = { version = "1.4", features = ["zeroize_derive"] } [features] pkcs-convert = ["num-bigint", "yasna"] @@ -29,7 +29,7 @@ jwt-convert = ["pkcs-convert", "jsonwebtoken"] generate = ["p256", "rand"] [dev-dependencies] -jsonwebtoken = "8.0" +jsonwebtoken = "8.1" [package.metadata.docs.rs] all-features = true diff --git a/README.md b/README.md index 97624ee..b6902bc 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ tl;dr: get keys into a format that can be used by other crates; be as safe as possible while doing so. -- Serialization and deserialization of _Required_ and _Recommended_ key types (HS256, RS256, ES256) +- Serialization and deserialization of _Required_ and _Recommended_ key types (HS256, HS384, HS512 RS256, RS384, RS512, ES256, ES384) - Conversion to PEM for interop with existing JWT libraries (e.g., [jsonwebtoken](https://crates.io/crates/jsonwebtoken)) - Key generation (particularly useful for testing) diff --git a/src/lib.rs b/src/lib.rs index 2e8f014..2c3f37c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ mod utils; use std::{borrow::Cow, fmt}; -use generic_array::typenum::U32; +use generic_array::typenum::{U32, U48}; use serde::{Deserialize, Serialize}; pub use byte_array::ByteArray; @@ -164,8 +164,18 @@ impl JsonWebKey { curve: Curve::P256 { .. }, }, ) + | ( + ES384, + EC { + curve: Curve::P384 { .. }, + }, + ) | (RS256, RSA { .. }) + | (RS384, RSA { .. }) + | (RS512, RSA { .. }) | (HS256, Symmetric { .. }) => Ok(()), + (HS384, Symmetric { .. }) => Ok(()), + (HS512, Symmetric { .. }) => Ok(()), _ => Err(Error::MismatchedAlgorithm), } } @@ -230,6 +240,10 @@ impl Key { curve: Curve::P256 { d: Some(_), .. }, .. } + | Self::EC { + curve: Curve::P384 { d: Some(_), .. }, + .. + } | Self::RSA { private: Some(_), .. @@ -253,6 +267,15 @@ impl Key { d: None, }, }, + Self::EC { + curve: Curve::P384 { x, y, .. }, + } => Self::EC { + curve: Curve::P384 { + x: x.clone(), + y: y.clone(), + d: None, + }, + }, Self::RSA { public, .. } => Self::RSA { public: public.clone(), private: None, @@ -307,6 +330,34 @@ impl Key { None => pkcs8::write_public(oids, write_public), } } + Self::EC { + curve: Curve::P384 { d, x, y }, + } => { + let ec_public_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 2, 1]); + let prime384v1_oid = ObjectIdentifier::from_slice(&[1, 3, 132, 0, 34]); + let oids = &[Some(&ec_public_oid), Some(&prime384v1_oid)]; + + let write_public = |writer: DERWriter<'_>| { + let public_bytes: Vec = [0x04 /* uncompressed */] + .iter() + .chain(x.iter()) + .chain(y.iter()) + .copied() + .collect(); + writer.write_bitvec_bytes(&public_bytes, 8 * (48 * 2 + 1)); + }; + + match d { + Some(private_point) => { + pkcs8::write_private(oids, |writer: &mut DERWriterSeq<'_>| { + writer.next().write_i8(1); // version + writer.next().write_bytes(&**private_point); + writer.next().write_tagged(Tag::context(1), write_public); + }) + } + None => pkcs8::write_public(oids, write_public), + } + } Self::RSA { public, private } => { let rsa_encryption_oid = ObjectIdentifier::from_slice(&[ 1, 2, 840, 113549, 1, 1, 1, // rsaEncryption @@ -417,7 +468,7 @@ impl Key { let sk = elliptic_curve::SecretKey::random(&mut rand::thread_rng()); let sk_scalar = p256::Scalar::from(&sk); - let pk = p256::ProjectivePoint::generator() * sk_scalar; + let pk = p256::ProjectivePoint::GENERATOR * sk_scalar; let pk_bytes = &pk .to_affine() .to_encoded_point(false /* compress */) @@ -448,13 +499,29 @@ pub enum Curve { /// The curve point y coordinate. y: ByteArray, }, + /// Parameters of the prime384v1 (P384) curve. + #[serde(rename = "P-384")] + P384 { + /// The private scalar. + #[serde(skip_serializing_if = "Option::is_none")] + d: Option>, + /// The curve point x coordinate. + x: ByteArray, + /// The curve point y coordinate. + y: ByteArray, + }, } impl fmt::Debug for Curve { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::P256 { x, y, .. } => f - .debug_struct("Curve:P256") + .debug_struct("Curve::P256") + .field("x", x) + .field("y", y) + .finish(), + Self::P384 { x, y, .. } => f + .debug_struct("Curve::P384") .field("x", x) .field("y", y) .finish(), @@ -525,7 +592,7 @@ impl fmt::Debug for RsaPrivate { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)] pub enum KeyUse { #[serde(rename = "sig")] Signing, @@ -533,12 +600,17 @@ pub enum KeyUse { Encryption, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)] #[allow(clippy::upper_case_acronyms)] pub enum Algorithm { HS256, + HS384, + HS512, RS256, + RS384, + RS512, ES256, + ES384, } #[cfg(feature = "jwt-convert")] @@ -549,8 +621,13 @@ const _IMPL_JWT_CONVERSIONS: () = { fn from(alg: Algorithm) -> Self { match alg { Algorithm::HS256 => Self::HS256, + Algorithm::HS384 => Self::HS384, + Algorithm::HS512 => Self::HS512, Algorithm::ES256 => Self::ES256, + Algorithm::ES384 => Self::ES384, Algorithm::RS256 => Self::RS256, + Algorithm::RS384 => Self::RS384, + Algorithm::RS512 => Self::RS512, } } } @@ -590,7 +667,8 @@ const _IMPL_JWT_CONVERSIONS: () = { .unwrap() } Self::RSA { .. } => { - jwt::DecodingKey::from_rsa_pem(self.to_pem().as_bytes()).unwrap() + jwt::DecodingKey::from_rsa_pem(self.to_public().unwrap().to_pem().as_bytes()) + .unwrap() } } } diff --git a/src/tests.rs b/src/tests.rs index ec0e775..83d764e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -16,9 +16,52 @@ static P256_JWK_FIXTURE: &str = r#"{ "alg": "ES256" }"#; -static RSA_JWK_FIXTURE: &str = r#"{ +#[cfg(feature = "pkcs-convert")] +static P384_JWK_FIXTURE: &str = r#"{ + "kty": "EC", + "d": "qzp82B9d-COgCfNp-u33PYAcggkgoC5n3Unxc-yhiawKdrDSVw65NwqgscQAxNI3", + "use": "sig", + "crv": "P-384", + "kid": "sig-1650284838", + "x": "3VdrPTk7esh1TmQqE0LNswQVY1kGCD0dnxhaPc6Ei9nXSCUQf46lLUoXg00zWYPM", + "y": "z3P8BMHgDX9wiVkM-cTU0zj_N8uijd1hPhzXC4TSf-FgeMvP7QUqEYImAoEaGMkZ", + "alg": "ES384" + }"#; + +static RSA256_JWK_FIXTURE: &str = r#"{ "p": "6AQ4yHef17an_i5LQPHNIxzpH65xWOSf_qCB7q-lXyM", "kty": "RSA", + "alg": "RS256", + "q": "tSVfpefCsf1iWmAs1zYvxdEsUiv0VMEuQBtbTijj_OE", + "d": "Qdp8a8Df5TlMaaloXApNF_3eu8sLHNWbXdg70e5YVTAs0WUfaIf5c3n96RrDDAzmMEwgKnJ7A1NJ9Nlzz4Z0AQ", + "e": "AQAB", + "use": "enc", + "qi": "adhQHH8IGXFfLEMnZ5t_TeCp5zgSwQktJ2lmylxUG0M", + "dp": "qVnLiKeoSG_Olz17OGBGd4a2sqVFnrjh_51wuaQDdTk", + "dq": "GL_Ec6xYg2z1FRfyyGyU1lgf0BJFTZcfNI8ISIN5ssE", + "key_ops": ["wrapKey"], + "n": "pCzbcd9kjvg5rfGHdEMWnXo49zbB6FLQ-m0B0BvVp0aojVWYa0xujC-ZP7ZhxByPxyc2PazwFJJi9ivZ_ggRww" + }"#; + +static RSA384_JWK_FIXTURE: &str = r#"{ + "p": "6AQ4yHef17an_i5LQPHNIxzpH65xWOSf_qCB7q-lXyM", + "kty": "RSA", + "alg": "RS384", + "q": "tSVfpefCsf1iWmAs1zYvxdEsUiv0VMEuQBtbTijj_OE", + "d": "Qdp8a8Df5TlMaaloXApNF_3eu8sLHNWbXdg70e5YVTAs0WUfaIf5c3n96RrDDAzmMEwgKnJ7A1NJ9Nlzz4Z0AQ", + "e": "AQAB", + "use": "enc", + "qi": "adhQHH8IGXFfLEMnZ5t_TeCp5zgSwQktJ2lmylxUG0M", + "dp": "qVnLiKeoSG_Olz17OGBGd4a2sqVFnrjh_51wuaQDdTk", + "dq": "GL_Ec6xYg2z1FRfyyGyU1lgf0BJFTZcfNI8ISIN5ssE", + "key_ops": ["wrapKey"], + "n": "pCzbcd9kjvg5rfGHdEMWnXo49zbB6FLQ-m0B0BvVp0aojVWYa0xujC-ZP7ZhxByPxyc2PazwFJJi9ivZ_ggRww" + }"#; + +static RSA512_JWK_FIXTURE: &str = r#"{ + "p": "6AQ4yHef17an_i5LQPHNIxzpH65xWOSf_qCB7q-lXyM", + "kty": "RSA", + "alg": "RS512", "q": "tSVfpefCsf1iWmAs1zYvxdEsUiv0VMEuQBtbTijj_OE", "d": "Qdp8a8Df5TlMaaloXApNF_3eu8sLHNWbXdg70e5YVTAs0WUfaIf5c3n96RrDDAzmMEwgKnJ7A1NJ9Nlzz4Z0AQ", "e": "AQAB", @@ -162,9 +205,95 @@ fn serialize_hs256() { ); } +#[test] +fn deserialize_hs384() { + let jwk_str = r#"{ + "kty": "oct", + "k": "tAON6Q", + "alg": "HS384", + "key_ops": ["verify", "sign"] + }"#; + let jwk = JsonWebKey::from_str(jwk_str).unwrap(); + assert_eq!( + jwk, + JsonWebKey { + key: Box::new(Key::Symmetric { + // The parameters were decoded using a 10-liner Rust script. + key: vec![180, 3, 141, 233].into(), + }), + algorithm: Some(Algorithm::HS384), + key_id: None, + key_ops: KeyOps::SIGN | KeyOps::VERIFY, + key_use: None, + x5: Default::default(), + } + ); +} + +#[test] +fn serialize_hs384() { + let jwk = JsonWebKey { + key: Box::new(Key::Symmetric { + key: vec![42; 16].into(), + }), + key_id: None, + algorithm: Some(Algorithm::HS384), + key_ops: KeyOps::empty(), + key_use: None, + x5: Default::default(), + }; + assert_eq!( + jwk.to_string(), + r#"{"kty":"oct","k":"KioqKioqKioqKioqKioqKg","alg":"HS384"}"# + ); +} + +#[test] +fn deserialize_hs512() { + let jwk_str = r#"{ + "kty": "oct", + "k": "tAON6Q", + "alg": "HS512", + "key_ops": ["verify", "sign"] + }"#; + let jwk = JsonWebKey::from_str(jwk_str).unwrap(); + assert_eq!( + jwk, + JsonWebKey { + key: Box::new(Key::Symmetric { + // The parameters were decoded using a 10-liner Rust script. + key: vec![180, 3, 141, 233].into(), + }), + algorithm: Some(Algorithm::HS512), + key_id: None, + key_ops: KeyOps::SIGN | KeyOps::VERIFY, + key_use: None, + x5: Default::default(), + } + ); +} + +#[test] +fn serialize_hs512() { + let jwk = JsonWebKey { + key: Box::new(Key::Symmetric { + key: vec![42; 16].into(), + }), + key_id: None, + algorithm: Some(Algorithm::HS512), + key_ops: KeyOps::empty(), + key_use: None, + x5: Default::default(), + }; + assert_eq!( + jwk.to_string(), + r#"{"kty":"oct","k":"KioqKioqKioqKioqKioqKg","alg":"HS512"}"# + ); +} + #[test] fn deserialize_rs256() { - let jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap(); + let jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); assert_eq!( jwk, JsonWebKey { @@ -227,7 +356,7 @@ fn deserialize_rs256() { ) }) }), - algorithm: None, + algorithm: Some(Algorithm::RS256), key_id: None, key_ops: KeyOps::WRAP_KEY, key_use: Some(KeyUse::Encryption), @@ -237,7 +366,7 @@ fn deserialize_rs256() { } #[test] -fn serialize_rs256() { +fn serialize_rs() { let jwk = JsonWebKey { key: Box::new(Key::RSA { public: RsaPublic { @@ -265,6 +394,154 @@ fn serialize_rs256() { ); } +#[test] +fn deserialize_rs384() { + let jwk = JsonWebKey::from_str(RSA384_JWK_FIXTURE).unwrap(); + assert_eq!( + jwk, + JsonWebKey { + key: Box::new(Key::RSA { + public: RsaPublic { + e: PublicExponent, + n: vec![ + 164, 44, 219, 113, 223, 100, 142, 248, 57, 173, 241, 135, 116, 67, 22, 157, + 122, 56, 247, 54, 193, 232, 82, 208, 250, 109, 1, 208, 27, 213, 167, 70, + 168, 141, 85, 152, 107, 76, 110, 140, 47, 153, 63, 182, 97, 196, 28, 143, + 199, 39, 54, 61, 172, 240, 20, 146, 98, 246, 43, 217, 254, 8, 17, 195 + ] + .into() + }, + private: Some(RsaPrivate { + d: vec![ + 65, 218, 124, 107, 192, 223, 229, 57, 76, 105, 169, 104, 92, 10, 77, 23, + 253, 222, 187, 203, 11, 28, 213, 155, 93, 216, 59, 209, 238, 88, 85, 48, + 44, 209, 101, 31, 104, 135, 249, 115, 121, 253, 233, 26, 195, 12, 12, 230, + 48, 76, 32, 42, 114, 123, 3, 83, 73, 244, 217, 115, 207, 134, 116, 1 + ] + .into(), + p: Some( + vec![ + 232, 4, 56, 200, 119, 159, 215, 182, 167, 254, 46, 75, 64, 241, 205, + 35, 28, 233, 31, 174, 113, 88, 228, 159, 254, 160, 129, 238, 175, 165, + 95, 35 + ] + .into() + ), + q: Some( + vec![ + 181, 37, 95, 165, 231, 194, 177, 253, 98, 90, 96, 44, 215, 54, 47, 197, + 209, 44, 82, 43, 244, 84, 193, 46, 64, 27, 91, 78, 40, 227, 252, 225 + ] + .into() + ), + dp: Some( + vec![ + 169, 89, 203, 136, 167, 168, 72, 111, 206, 151, 61, 123, 56, 96, 70, + 119, 134, 182, 178, 165, 69, 158, 184, 225, 255, 157, 112, 185, 164, 3, + 117, 57 + ] + .into() + ), + dq: Some( + vec![ + 24, 191, 196, 115, 172, 88, 131, 108, 245, 21, 23, 242, 200, 108, 148, + 214, 88, 31, 208, 18, 69, 77, 151, 31, 52, 143, 8, 72, 131, 121, 178, + 193 + ] + .into() + ), + qi: Some( + vec![ + 105, 216, 80, 28, 127, 8, 25, 113, 95, 44, 67, 39, 103, 155, 127, 77, + 224, 169, 231, 56, 18, 193, 9, 45, 39, 105, 102, 202, 92, 84, 27, 67 + ] + .into() + ) + }) + }), + algorithm: Some(Algorithm::RS384), + key_id: None, + key_ops: KeyOps::WRAP_KEY, + key_use: Some(KeyUse::Encryption), + x5: Default::default(), + } + ); +} + +#[test] +fn deserialize_rs512() { + let jwk = JsonWebKey::from_str(RSA512_JWK_FIXTURE).unwrap(); + assert_eq!( + jwk, + JsonWebKey { + key: Box::new(Key::RSA { + public: RsaPublic { + e: PublicExponent, + n: vec![ + 164, 44, 219, 113, 223, 100, 142, 248, 57, 173, 241, 135, 116, 67, 22, 157, + 122, 56, 247, 54, 193, 232, 82, 208, 250, 109, 1, 208, 27, 213, 167, 70, + 168, 141, 85, 152, 107, 76, 110, 140, 47, 153, 63, 182, 97, 196, 28, 143, + 199, 39, 54, 61, 172, 240, 20, 146, 98, 246, 43, 217, 254, 8, 17, 195 + ] + .into() + }, + private: Some(RsaPrivate { + d: vec![ + 65, 218, 124, 107, 192, 223, 229, 57, 76, 105, 169, 104, 92, 10, 77, 23, + 253, 222, 187, 203, 11, 28, 213, 155, 93, 216, 59, 209, 238, 88, 85, 48, + 44, 209, 101, 31, 104, 135, 249, 115, 121, 253, 233, 26, 195, 12, 12, 230, + 48, 76, 32, 42, 114, 123, 3, 83, 73, 244, 217, 115, 207, 134, 116, 1 + ] + .into(), + p: Some( + vec![ + 232, 4, 56, 200, 119, 159, 215, 182, 167, 254, 46, 75, 64, 241, 205, + 35, 28, 233, 31, 174, 113, 88, 228, 159, 254, 160, 129, 238, 175, 165, + 95, 35 + ] + .into() + ), + q: Some( + vec![ + 181, 37, 95, 165, 231, 194, 177, 253, 98, 90, 96, 44, 215, 54, 47, 197, + 209, 44, 82, 43, 244, 84, 193, 46, 64, 27, 91, 78, 40, 227, 252, 225 + ] + .into() + ), + dp: Some( + vec![ + 169, 89, 203, 136, 167, 168, 72, 111, 206, 151, 61, 123, 56, 96, 70, + 119, 134, 182, 178, 165, 69, 158, 184, 225, 255, 157, 112, 185, 164, 3, + 117, 57 + ] + .into() + ), + dq: Some( + vec![ + 24, 191, 196, 115, 172, 88, 131, 108, 245, 21, 23, 242, 200, 108, 148, + 214, 88, 31, 208, 18, 69, 77, 151, 31, 52, 143, 8, 72, 131, 121, 178, + 193 + ] + .into() + ), + qi: Some( + vec![ + 105, 216, 80, 28, 127, 8, 25, 113, 95, 44, 67, 39, 103, 155, 127, 77, + 224, 169, 231, 56, 18, 193, 9, 45, 39, 105, 102, 202, 92, 84, 27, 67 + ] + .into() + ) + }) + }), + algorithm: Some(Algorithm::RS512), + key_id: None, + key_ops: KeyOps::WRAP_KEY, + key_use: Some(KeyUse::Encryption), + x5: Default::default(), + } + ); +} + #[test] fn mismatched_algorithm() { macro_rules! assert_mismatched_alg { @@ -333,10 +610,43 @@ LTXKRomXYnav0N1ONhmgedy1q0QTo0KsqZdB0kk+c3NkRfycGZl17cBjiQ== ); } +#[cfg(feature = "pkcs-convert")] +#[test] +fn p384_private_to_pem() { + let jwk = JsonWebKey::from_str(P384_JWK_FIXTURE).unwrap(); + #[rustfmt::skip] + assert_eq!( + jwk.key.to_pem(), +"-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCrOnzYH134I6AJ82n6 +7fc9gByCCSCgLmfdSfFz7KGJrAp2sNJXDrk3CqCxxADE0jehZANiAATdV2s9OTt6 +yHVOZCoTQs2zBBVjWQYIPR2fGFo9zoSL2ddIJRB/jqUtSheDTTNZg8zPc/wEweAN +f3CJWQz5xNTTOP83y6KN3WE+HNcLhNJ/4WB4y8/tBSoRgiYCgRoYyRk= +-----END PRIVATE KEY----- +" + ); +} + +#[cfg(feature = "pkcs-convert")] +#[test] +fn p384_public_to_pem() { + let jwk = JsonWebKey::from_str(P384_JWK_FIXTURE).unwrap(); + #[rustfmt::skip] + assert_eq!( + jwk.key.to_public().unwrap().to_pem(), +"-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3VdrPTk7esh1TmQqE0LNswQVY1kGCD0d +nxhaPc6Ei9nXSCUQf46lLUoXg00zWYPMz3P8BMHgDX9wiVkM+cTU0zj/N8uijd1h +PhzXC4TSf+FgeMvP7QUqEYImAoEaGMkZ +-----END PUBLIC KEY----- +" + ); +} + #[cfg(feature = "pkcs-convert")] #[test] fn rsa_private_to_pem() { - let jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap(); + let jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); #[rustfmt::skip] assert_eq!( jwk.key.to_pem(), @@ -357,7 +667,7 @@ J2lmylxUG0M= #[cfg(feature = "pkcs-convert")] #[test] fn rsa_public_to_pem() { - let jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap(); + let jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); assert_eq!( jwk.key.to_public().unwrap().to_pem(), "-----BEGIN PUBLIC KEY----- @@ -368,6 +678,57 @@ MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKQs23HfZI74Oa3xh3RDFp16OPc2wehS ); } +#[cfg(feature = "pkcs-convert")] +#[test] +fn rs256_private_to_encoding_key_via_pem() { + let jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); + let pem = jwk.key.to_pem(); + + jsonwebtoken::EncodingKey::from_rsa_pem(pem.as_bytes()).unwrap(); +} + +#[cfg(feature = "pkcs-convert")] +#[test] +fn rs256_public_to_decoding_key_via_pem() { + let jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); + let pem = jwk.key.to_public().unwrap().to_pem(); + jsonwebtoken::DecodingKey::from_rsa_pem(pem.as_bytes()).unwrap(); +} + +#[cfg(feature = "pkcs-convert")] +#[test] +fn rs384_private_to_encoding_key_via_pem() { + let jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); + let pem = jwk.key.to_pem(); + + jsonwebtoken::EncodingKey::from_rsa_pem(pem.as_bytes()).unwrap(); +} + +#[cfg(feature = "pkcs-convert")] +#[test] +fn rs384_public_to_decoding_key_via_pem() { + let jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); + let pem = jwk.key.to_public().unwrap().to_pem(); + jsonwebtoken::DecodingKey::from_rsa_pem(pem.as_bytes()).unwrap(); +} + +#[cfg(feature = "pkcs-convert")] +#[test] +fn rs512_private_to_encoding_key_via_pem() { + let jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); + let pem = jwk.key.to_pem(); + + jsonwebtoken::EncodingKey::from_rsa_pem(pem.as_bytes()).unwrap(); +} + +#[cfg(feature = "pkcs-convert")] +#[test] +fn rs512_public_to_decoding_key_via_pem() { + let jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); + let pem = jwk.key.to_public().unwrap().to_pem(); + jsonwebtoken::DecodingKey::from_rsa_pem(pem.as_bytes()).unwrap(); +} + #[cfg(feature = "pkcs-convert")] #[test] fn oct_to_pem() { @@ -407,7 +768,7 @@ fn ec_is_private() { #[test] fn rsa_is_private() { - let private_jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap(); + let private_jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); assert!(private_jwk.key.is_private()); assert!(!private_jwk.key.to_public().unwrap().is_private()); @@ -424,7 +785,7 @@ fn rsa_is_private() { #[test] fn x509_params() { - let private_jwk = JsonWebKey::from_str(RSA_JWK_FIXTURE).unwrap(); + let private_jwk = JsonWebKey::from_str(RSA256_JWK_FIXTURE).unwrap(); assert!(private_jwk.key.is_private()); assert!(!private_jwk.key.to_public().unwrap().is_private());