From dd51c4a51bb957ea180d9975574c5130c3b79bf8 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Mon, 29 Aug 2022 12:23:50 +0200 Subject: [PATCH] Private to public JWKS conversion --- crates/jose/src/jwk/mod.rs | 66 +++++++------ crates/jose/src/jwk/private_parameters.rs | 109 +++++++++++++++++++++- crates/jose/src/jwk/public_parameters.rs | 82 +++++++++++++++- crates/jose/tests/jws.rs | 20 +++- 4 files changed, 237 insertions(+), 40 deletions(-) diff --git a/crates/jose/src/jwk/mod.rs b/crates/jose/src/jwk/mod.rs index 7baf4769..aacd6155 100644 --- a/crates/jose/src/jwk/mod.rs +++ b/crates/jose/src/jwk/mod.rs @@ -32,10 +32,12 @@ use crate::constraints::Constrainable; pub(crate) mod private_parameters; pub(crate) mod public_parameters; +use self::private_parameters::SymetricKeyError; pub use self::public_parameters::JsonWebKeyPublicParameters as JsonWebKeyParameters; -pub trait JwkKty { +pub trait ParametersInfo { fn kty(&self) -> JsonWebKeyType; + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg]; } trait JwkEcCurve { @@ -96,6 +98,24 @@ pub struct JsonWebKey

{ pub type PublicJsonWebKey = JsonWebKey; pub type PrivateJsonWebKey = JsonWebKey; +impl TryFrom for PublicJsonWebKey { + type Error = SymetricKeyError; + + fn try_from(value: PrivateJsonWebKey) -> Result { + Ok(Self { + parameters: value.parameters.try_into()?, + r#use: value.r#use, + key_ops: value.key_ops, + alg: value.alg, + kid: value.kid, + x5u: value.x5u, + x5c: value.x5c, + x5t: value.x5t, + x5t_s256: value.x5t_s256, + }) + } +} + impl

JsonWebKey

{ #[must_use] pub const fn new(parameters: P) -> Self { @@ -149,7 +169,7 @@ impl

JsonWebKey

{ impl

Constrainable for JsonWebKey

where - P: JwkKty, + P: ParametersInfo, { fn kid(&self) -> Option<&str> { self.kid.as_deref() @@ -163,32 +183,7 @@ where if let Some(alg) = self.alg { Some(vec![alg]) } else { - match self.parameters.kty() { - JsonWebKeyType::Rsa => Some(vec![ - JsonWebSignatureAlg::Rs256, - JsonWebSignatureAlg::Rs384, - JsonWebSignatureAlg::Rs512, - JsonWebSignatureAlg::Ps256, - JsonWebSignatureAlg::Ps384, - JsonWebSignatureAlg::Ps512, - ]), - JsonWebKeyType::Ec => { - todo!() - /* - match params.crv() { - JsonWebKeyEcEllipticCurve::P256 => Some(vec![JsonWebSignatureAlg::Es256]), - JsonWebKeyEcEllipticCurve::P384 => Some(vec![JsonWebSignatureAlg::Es384]), - JsonWebKeyEcEllipticCurve::P521 => Some(vec![JsonWebSignatureAlg::Es512]), - JsonWebKeyEcEllipticCurve::Secp256K1 => Some(vec![JsonWebSignatureAlg::Es256K]), - }, */ - } - JsonWebKeyType::Okp => Some(vec![JsonWebSignatureAlg::EdDsa]), - JsonWebKeyType::Oct => Some(vec![ - JsonWebSignatureAlg::Hs256, - JsonWebSignatureAlg::Hs384, - JsonWebSignatureAlg::Hs512, - ]), - } + Some(self.parameters.possible_algs().to_vec()) } } @@ -206,6 +201,17 @@ pub type PublicJsonWebKeySet = JsonWebKeySet; +impl From for PublicJsonWebKeySet { + fn from(value: PrivateJsonWebKeySet) -> Self { + let keys = value + .keys + .into_iter() + .filter_map(|key: PrivateJsonWebKey| key.try_into().ok()) + .collect(); + Self { keys } + } +} + impl

std::ops::Deref for JsonWebKeySet

{ type Target = Vec>; @@ -249,7 +255,7 @@ mod tests { ] }); - let jwks: JsonWebKeySet = serde_json::from_value(jwks).unwrap(); + let jwks: PublicJsonWebKeySet = serde_json::from_value(jwks).unwrap(); // Both keys are RSA public keys for jwk in &jwks.keys { rsa::RsaPublicKey::try_from(jwk.parameters.clone()).unwrap(); @@ -383,7 +389,7 @@ mod tests { ] }); - let jwks: JsonWebKeySet = serde_json::from_value(jwks).unwrap(); + let jwks: PublicJsonWebKeySet = serde_json::from_value(jwks).unwrap(); // The first 6 keys are RSA, 7th is P-256 let mut keys = jwks.keys.into_iter(); rsa::RsaPublicKey::try_from(keys.next().unwrap().parameters).unwrap(); diff --git a/crates/jose/src/jwk/private_parameters.rs b/crates/jose/src/jwk/private_parameters.rs index 78a20cd3..6b9f1ea1 100644 --- a/crates/jose/src/jwk/private_parameters.rs +++ b/crates/jose/src/jwk/private_parameters.rs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use mas_iana::jose::{JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType}; +use mas_iana::jose::{ + JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType, JsonWebSignatureAlg, +}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::{ @@ -20,8 +22,9 @@ use serde_with::{ formats::Unpadded, serde_as, }; +use thiserror::Error; -use super::JwkKty; +use super::{public_parameters::JsonWebKeyPublicParameters, ParametersInfo}; #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] @@ -40,7 +43,7 @@ pub enum JsonWebKeyPrivateParameters { Okp(OkpPrivateParameters), } -impl JwkKty for JsonWebKeyPrivateParameters { +impl ParametersInfo for JsonWebKeyPrivateParameters { fn kty(&self) -> JsonWebKeyType { match self { Self::Oct(_) => JsonWebKeyType::Oct, @@ -49,6 +52,32 @@ impl JwkKty for JsonWebKeyPrivateParameters { Self::Okp(_) => JsonWebKeyType::Okp, } } + + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] { + match self { + JsonWebKeyPrivateParameters::Oct(p) => p.possible_algs(), + JsonWebKeyPrivateParameters::Rsa(p) => p.possible_algs(), + JsonWebKeyPrivateParameters::Ec(p) => p.possible_algs(), + JsonWebKeyPrivateParameters::Okp(p) => p.possible_algs(), + } + } +} + +#[derive(Debug, Error)] +#[error("can't extract a public key out of a symetric key")] +pub struct SymetricKeyError; + +impl TryFrom for JsonWebKeyPublicParameters { + type Error = SymetricKeyError; + + fn try_from(value: JsonWebKeyPrivateParameters) -> Result { + match value { + JsonWebKeyPrivateParameters::Oct(_) => Err(SymetricKeyError), + JsonWebKeyPrivateParameters::Rsa(p) => Ok(JsonWebKeyPublicParameters::Rsa(p.into())), + JsonWebKeyPrivateParameters::Ec(p) => Ok(JsonWebKeyPublicParameters::Ec(p.into())), + JsonWebKeyPrivateParameters::Okp(p) => Ok(JsonWebKeyPublicParameters::Okp(p.into())), + } + } } #[serde_as] @@ -60,6 +89,20 @@ pub struct OctPrivateParameters { k: Vec, } +impl ParametersInfo for OctPrivateParameters { + fn kty(&self) -> JsonWebKeyType { + JsonWebKeyType::Oct + } + + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] { + &[ + JsonWebSignatureAlg::Hs256, + JsonWebSignatureAlg::Hs384, + JsonWebSignatureAlg::Hs512, + ] + } +} + #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct RsaPrivateParameters { @@ -108,6 +151,29 @@ pub struct RsaPrivateParameters { oth: Option>, } +impl ParametersInfo for RsaPrivateParameters { + fn kty(&self) -> JsonWebKeyType { + JsonWebKeyType::Rsa + } + + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] { + &[ + JsonWebSignatureAlg::Rs256, + JsonWebSignatureAlg::Rs384, + JsonWebSignatureAlg::Rs512, + JsonWebSignatureAlg::Ps256, + JsonWebSignatureAlg::Ps384, + JsonWebSignatureAlg::Ps512, + ] + } +} + +impl From for super::public_parameters::RsaPublicParameters { + fn from(params: RsaPrivateParameters) -> Self { + Self::new(params.n, params.e) + } +} + #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] struct RsaOtherPrimeInfo { @@ -177,6 +243,27 @@ pub struct EcPrivateParameters { d: Vec, } +impl ParametersInfo for EcPrivateParameters { + fn kty(&self) -> JsonWebKeyType { + JsonWebKeyType::Ec + } + + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] { + match self.crv { + JsonWebKeyEcEllipticCurve::P256 => &[JsonWebSignatureAlg::Es256], + JsonWebKeyEcEllipticCurve::P384 => &[JsonWebSignatureAlg::Es384], + JsonWebKeyEcEllipticCurve::P521 => &[JsonWebSignatureAlg::Es512], + JsonWebKeyEcEllipticCurve::Secp256K1 => &[JsonWebSignatureAlg::Es256K], + } + } +} + +impl From for super::public_parameters::EcPublicParameters { + fn from(params: EcPrivateParameters) -> Self { + Self::new(params.crv, params.x, params.y) + } +} + mod ec_impls { use digest::typenum::Unsigned; use ecdsa::EncodedPoint; @@ -267,3 +354,19 @@ pub struct OkpPrivateParameters { #[serde_as(as = "Base64")] x: Vec, } + +impl ParametersInfo for OkpPrivateParameters { + fn kty(&self) -> JsonWebKeyType { + JsonWebKeyType::Okp + } + + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] { + &[JsonWebSignatureAlg::EdDsa] + } +} + +impl From for super::public_parameters::OkpPublicParameters { + fn from(params: OkpPrivateParameters) -> Self { + Self::new(params.crv, params.x) + } +} diff --git a/crates/jose/src/jwk/public_parameters.rs b/crates/jose/src/jwk/public_parameters.rs index 2fe00dc4..2366a334 100644 --- a/crates/jose/src/jwk/public_parameters.rs +++ b/crates/jose/src/jwk/public_parameters.rs @@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use mas_iana::jose::{JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType}; +use mas_iana::jose::{ + JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType, JsonWebSignatureAlg, +}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::{ @@ -21,7 +23,7 @@ use serde_with::{ serde_as, }; -use super::JwkKty; +use super::ParametersInfo; #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] @@ -37,7 +39,7 @@ pub enum JsonWebKeyPublicParameters { Okp(OkpPublicParameters), } -impl JwkKty for JsonWebKeyPublicParameters { +impl ParametersInfo for JsonWebKeyPublicParameters { fn kty(&self) -> JsonWebKeyType { match self { Self::Rsa(_) => JsonWebKeyType::Rsa, @@ -45,6 +47,14 @@ impl JwkKty for JsonWebKeyPublicParameters { Self::Okp(_) => JsonWebKeyType::Okp, } } + + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] { + match self { + JsonWebKeyPublicParameters::Rsa(p) => p.possible_algs(), + JsonWebKeyPublicParameters::Ec(p) => p.possible_algs(), + JsonWebKeyPublicParameters::Okp(p) => p.possible_algs(), + } + } } #[serde_as] @@ -59,6 +69,29 @@ pub struct RsaPublicParameters { e: Vec, } +impl ParametersInfo for RsaPublicParameters { + fn kty(&self) -> JsonWebKeyType { + JsonWebKeyType::Rsa + } + + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] { + &[ + JsonWebSignatureAlg::Rs256, + JsonWebSignatureAlg::Rs384, + JsonWebSignatureAlg::Rs512, + JsonWebSignatureAlg::Ps256, + JsonWebSignatureAlg::Ps384, + JsonWebSignatureAlg::Ps512, + ] + } +} + +impl RsaPublicParameters { + pub const fn new(n: Vec, e: Vec) -> Self { + Self { n, e } + } +} + #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct EcPublicParameters { @@ -73,6 +106,31 @@ pub struct EcPublicParameters { y: Vec, } +impl EcPublicParameters { + pub const fn new(crv: JsonWebKeyEcEllipticCurve, x: Vec, y: Vec) -> Self { + Self { crv, x, y } + } + + pub const fn crv(&self) -> JsonWebKeyEcEllipticCurve { + self.crv + } +} + +impl ParametersInfo for EcPublicParameters { + fn kty(&self) -> JsonWebKeyType { + JsonWebKeyType::Ec + } + + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] { + match self.crv { + JsonWebKeyEcEllipticCurve::P256 => &[JsonWebSignatureAlg::Es256], + JsonWebKeyEcEllipticCurve::P384 => &[JsonWebSignatureAlg::Es384], + JsonWebKeyEcEllipticCurve::P521 => &[JsonWebSignatureAlg::Es512], + JsonWebKeyEcEllipticCurve::Secp256K1 => &[JsonWebSignatureAlg::Es256K], + } + } +} + #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct OkpPublicParameters { @@ -83,8 +141,22 @@ pub struct OkpPublicParameters { x: Vec, } -impl EcPublicParameters { - pub const fn crv(&self) -> JsonWebKeyEcEllipticCurve { +impl ParametersInfo for OkpPublicParameters { + fn kty(&self) -> JsonWebKeyType { + JsonWebKeyType::Okp + } + + fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] { + &[JsonWebSignatureAlg::EdDsa] + } +} + +impl OkpPublicParameters { + pub const fn new(crv: JsonWebKeyOkpEllipticCurve, x: Vec) -> Self { + Self { crv, x } + } + + pub const fn crv(&self) -> JsonWebKeyOkpEllipticCurve { self.crv } } diff --git a/crates/jose/tests/jws.rs b/crates/jose/tests/jws.rs index 6cabfb67..82078e41 100644 --- a/crates/jose/tests/jws.rs +++ b/crates/jose/tests/jws.rs @@ -14,7 +14,11 @@ use std::ops::Deref; -use mas_jose::{constraints::ConstraintSet, JsonWebKeySet, Jwt}; +use mas_jose::{ + constraints::ConstraintSet, + jwk::{PrivateJsonWebKeySet, PublicJsonWebKeySet}, + Jwt, +}; use serde::Deserialize; static HS256_JWT: &str = include_str!("./jwts/hs256.jwt"); @@ -34,10 +38,14 @@ static EDDSA_ED25519_JWT: &str = include_str!("./jwts/eddsa-ed25519.jwt"); static EDDSA_ED448_JWT: &str = include_str!("./jwts/eddsa-ed448.jwt"); static OCT_KEY: &[u8] = include_bytes!("./keys/oct.bin"); -fn public_jwks() -> JsonWebKeySet { +fn public_jwks() -> PublicJsonWebKeySet { serde_json::from_str(include_str!("./keys/jwks.pub.json")).unwrap() } +fn private_jwks() -> PrivateJsonWebKeySet { + serde_json::from_str(include_str!("./keys/jwks.priv.json")).unwrap() +} + fn oct_key() -> Vec { OCT_KEY.to_vec() } @@ -105,3 +113,11 @@ asymetric_jwt_test!(test_es512, ES512_JWT, verify = false); asymetric_jwt_test!(test_es256k, ES256K_JWT); asymetric_jwt_test!(test_eddsa_ed25519, EDDSA_ED25519_JWT, verify = false); asymetric_jwt_test!(test_eddsa_ed448, EDDSA_ED448_JWT, verify = false); + +#[test] +fn test_private_to_public_jwks() { + let priv_jwks = private_jwks(); + let pub_jwks = PublicJsonWebKeySet::from(priv_jwks); + + assert_eq!(pub_jwks, public_jwks()); +}