1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-31 09:24:31 +03:00

Private to public JWKS conversion

This commit is contained in:
Quentin Gliech
2022-08-29 12:23:50 +02:00
parent 0b6e4cdece
commit dd51c4a51b
4 changed files with 237 additions and 40 deletions

View File

@ -32,10 +32,12 @@ use crate::constraints::Constrainable;
pub(crate) mod private_parameters; pub(crate) mod private_parameters;
pub(crate) mod public_parameters; pub(crate) mod public_parameters;
use self::private_parameters::SymetricKeyError;
pub use self::public_parameters::JsonWebKeyPublicParameters as JsonWebKeyParameters; pub use self::public_parameters::JsonWebKeyPublicParameters as JsonWebKeyParameters;
pub trait JwkKty { pub trait ParametersInfo {
fn kty(&self) -> JsonWebKeyType; fn kty(&self) -> JsonWebKeyType;
fn possible_algs(&self) -> &'static [JsonWebSignatureAlg];
} }
trait JwkEcCurve { trait JwkEcCurve {
@ -96,6 +98,24 @@ pub struct JsonWebKey<P> {
pub type PublicJsonWebKey = JsonWebKey<self::public_parameters::JsonWebKeyPublicParameters>; pub type PublicJsonWebKey = JsonWebKey<self::public_parameters::JsonWebKeyPublicParameters>;
pub type PrivateJsonWebKey = JsonWebKey<self::private_parameters::JsonWebKeyPrivateParameters>; pub type PrivateJsonWebKey = JsonWebKey<self::private_parameters::JsonWebKeyPrivateParameters>;
impl TryFrom<PrivateJsonWebKey> for PublicJsonWebKey {
type Error = SymetricKeyError;
fn try_from(value: PrivateJsonWebKey) -> Result<Self, Self::Error> {
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<P> JsonWebKey<P> { impl<P> JsonWebKey<P> {
#[must_use] #[must_use]
pub const fn new(parameters: P) -> Self { pub const fn new(parameters: P) -> Self {
@ -149,7 +169,7 @@ impl<P> JsonWebKey<P> {
impl<P> Constrainable for JsonWebKey<P> impl<P> Constrainable for JsonWebKey<P>
where where
P: JwkKty, P: ParametersInfo,
{ {
fn kid(&self) -> Option<&str> { fn kid(&self) -> Option<&str> {
self.kid.as_deref() self.kid.as_deref()
@ -163,32 +183,7 @@ where
if let Some(alg) = self.alg { if let Some(alg) = self.alg {
Some(vec![alg]) Some(vec![alg])
} else { } else {
match self.parameters.kty() { Some(self.parameters.possible_algs().to_vec())
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,
]),
}
} }
} }
@ -206,6 +201,17 @@ pub type PublicJsonWebKeySet = JsonWebKeySet<self::public_parameters::JsonWebKey
pub type PrivateJsonWebKeySet = pub type PrivateJsonWebKeySet =
JsonWebKeySet<self::private_parameters::JsonWebKeyPrivateParameters>; JsonWebKeySet<self::private_parameters::JsonWebKeyPrivateParameters>;
impl From<PrivateJsonWebKeySet> 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<P> std::ops::Deref for JsonWebKeySet<P> { impl<P> std::ops::Deref for JsonWebKeySet<P> {
type Target = Vec<JsonWebKey<P>>; type Target = Vec<JsonWebKey<P>>;
@ -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 // Both keys are RSA public keys
for jwk in &jwks.keys { for jwk in &jwks.keys {
rsa::RsaPublicKey::try_from(jwk.parameters.clone()).unwrap(); 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 // The first 6 keys are RSA, 7th is P-256
let mut keys = jwks.keys.into_iter(); let mut keys = jwks.keys.into_iter();
rsa::RsaPublicKey::try_from(keys.next().unwrap().parameters).unwrap(); rsa::RsaPublicKey::try_from(keys.next().unwrap().parameters).unwrap();

View File

@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use mas_iana::jose::{JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType}; use mas_iana::jose::{
JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType, JsonWebSignatureAlg,
};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{ use serde_with::{
@ -20,8 +22,9 @@ use serde_with::{
formats::Unpadded, formats::Unpadded,
serde_as, serde_as,
}; };
use thiserror::Error;
use super::JwkKty; use super::{public_parameters::JsonWebKeyPublicParameters, ParametersInfo};
#[serde_as] #[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
@ -40,7 +43,7 @@ pub enum JsonWebKeyPrivateParameters {
Okp(OkpPrivateParameters), Okp(OkpPrivateParameters),
} }
impl JwkKty for JsonWebKeyPrivateParameters { impl ParametersInfo for JsonWebKeyPrivateParameters {
fn kty(&self) -> JsonWebKeyType { fn kty(&self) -> JsonWebKeyType {
match self { match self {
Self::Oct(_) => JsonWebKeyType::Oct, Self::Oct(_) => JsonWebKeyType::Oct,
@ -49,6 +52,32 @@ impl JwkKty for JsonWebKeyPrivateParameters {
Self::Okp(_) => JsonWebKeyType::Okp, 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<JsonWebKeyPrivateParameters> for JsonWebKeyPublicParameters {
type Error = SymetricKeyError;
fn try_from(value: JsonWebKeyPrivateParameters) -> Result<Self, Self::Error> {
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] #[serde_as]
@ -60,6 +89,20 @@ pub struct OctPrivateParameters {
k: Vec<u8>, k: Vec<u8>,
} }
impl ParametersInfo for OctPrivateParameters {
fn kty(&self) -> JsonWebKeyType {
JsonWebKeyType::Oct
}
fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] {
&[
JsonWebSignatureAlg::Hs256,
JsonWebSignatureAlg::Hs384,
JsonWebSignatureAlg::Hs512,
]
}
}
#[serde_as] #[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct RsaPrivateParameters { pub struct RsaPrivateParameters {
@ -108,6 +151,29 @@ pub struct RsaPrivateParameters {
oth: Option<Vec<RsaOtherPrimeInfo>>, oth: Option<Vec<RsaOtherPrimeInfo>>,
} }
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<RsaPrivateParameters> for super::public_parameters::RsaPublicParameters {
fn from(params: RsaPrivateParameters) -> Self {
Self::new(params.n, params.e)
}
}
#[serde_as] #[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
struct RsaOtherPrimeInfo { struct RsaOtherPrimeInfo {
@ -177,6 +243,27 @@ pub struct EcPrivateParameters {
d: Vec<u8>, d: Vec<u8>,
} }
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<EcPrivateParameters> for super::public_parameters::EcPublicParameters {
fn from(params: EcPrivateParameters) -> Self {
Self::new(params.crv, params.x, params.y)
}
}
mod ec_impls { mod ec_impls {
use digest::typenum::Unsigned; use digest::typenum::Unsigned;
use ecdsa::EncodedPoint; use ecdsa::EncodedPoint;
@ -267,3 +354,19 @@ pub struct OkpPrivateParameters {
#[serde_as(as = "Base64<UrlSafe, Unpadded>")] #[serde_as(as = "Base64<UrlSafe, Unpadded>")]
x: Vec<u8>, x: Vec<u8>,
} }
impl ParametersInfo for OkpPrivateParameters {
fn kty(&self) -> JsonWebKeyType {
JsonWebKeyType::Okp
}
fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] {
&[JsonWebSignatureAlg::EdDsa]
}
}
impl From<OkpPrivateParameters> for super::public_parameters::OkpPublicParameters {
fn from(params: OkpPrivateParameters) -> Self {
Self::new(params.crv, params.x)
}
}

View File

@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use mas_iana::jose::{JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType}; use mas_iana::jose::{
JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType, JsonWebSignatureAlg,
};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{ use serde_with::{
@ -21,7 +23,7 @@ use serde_with::{
serde_as, serde_as,
}; };
use super::JwkKty; use super::ParametersInfo;
#[serde_as] #[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
@ -37,7 +39,7 @@ pub enum JsonWebKeyPublicParameters {
Okp(OkpPublicParameters), Okp(OkpPublicParameters),
} }
impl JwkKty for JsonWebKeyPublicParameters { impl ParametersInfo for JsonWebKeyPublicParameters {
fn kty(&self) -> JsonWebKeyType { fn kty(&self) -> JsonWebKeyType {
match self { match self {
Self::Rsa(_) => JsonWebKeyType::Rsa, Self::Rsa(_) => JsonWebKeyType::Rsa,
@ -45,6 +47,14 @@ impl JwkKty for JsonWebKeyPublicParameters {
Self::Okp(_) => JsonWebKeyType::Okp, 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] #[serde_as]
@ -59,6 +69,29 @@ pub struct RsaPublicParameters {
e: Vec<u8>, e: Vec<u8>,
} }
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<u8>, e: Vec<u8>) -> Self {
Self { n, e }
}
}
#[serde_as] #[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct EcPublicParameters { pub struct EcPublicParameters {
@ -73,6 +106,31 @@ pub struct EcPublicParameters {
y: Vec<u8>, y: Vec<u8>,
} }
impl EcPublicParameters {
pub const fn new(crv: JsonWebKeyEcEllipticCurve, x: Vec<u8>, y: Vec<u8>) -> 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] #[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct OkpPublicParameters { pub struct OkpPublicParameters {
@ -83,8 +141,22 @@ pub struct OkpPublicParameters {
x: Vec<u8>, x: Vec<u8>,
} }
impl EcPublicParameters { impl ParametersInfo for OkpPublicParameters {
pub const fn crv(&self) -> JsonWebKeyEcEllipticCurve { fn kty(&self) -> JsonWebKeyType {
JsonWebKeyType::Okp
}
fn possible_algs(&self) -> &'static [JsonWebSignatureAlg] {
&[JsonWebSignatureAlg::EdDsa]
}
}
impl OkpPublicParameters {
pub const fn new(crv: JsonWebKeyOkpEllipticCurve, x: Vec<u8>) -> Self {
Self { crv, x }
}
pub const fn crv(&self) -> JsonWebKeyOkpEllipticCurve {
self.crv self.crv
} }
} }

View File

@ -14,7 +14,11 @@
use std::ops::Deref; use std::ops::Deref;
use mas_jose::{constraints::ConstraintSet, JsonWebKeySet, Jwt}; use mas_jose::{
constraints::ConstraintSet,
jwk::{PrivateJsonWebKeySet, PublicJsonWebKeySet},
Jwt,
};
use serde::Deserialize; use serde::Deserialize;
static HS256_JWT: &str = include_str!("./jwts/hs256.jwt"); 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 EDDSA_ED448_JWT: &str = include_str!("./jwts/eddsa-ed448.jwt");
static OCT_KEY: &[u8] = include_bytes!("./keys/oct.bin"); 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() 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<u8> { fn oct_key() -> Vec<u8> {
OCT_KEY.to_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_es256k, ES256K_JWT);
asymetric_jwt_test!(test_eddsa_ed25519, EDDSA_ED25519_JWT, verify = false); asymetric_jwt_test!(test_eddsa_ed25519, EDDSA_ED25519_JWT, verify = false);
asymetric_jwt_test!(test_eddsa_ed448, EDDSA_ED448_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());
}