1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +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 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<P> {
pub type PublicJsonWebKey = JsonWebKey<self::public_parameters::JsonWebKeyPublicParameters>;
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> {
#[must_use]
pub const fn new(parameters: P) -> Self {
@ -149,7 +169,7 @@ impl<P> JsonWebKey<P> {
impl<P> Constrainable for JsonWebKey<P>
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<self::public_parameters::JsonWebKey
pub type PrivateJsonWebKeySet =
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> {
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
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();

View File

@ -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<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]
@ -60,6 +89,20 @@ pub struct OctPrivateParameters {
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]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct RsaPrivateParameters {
@ -108,6 +151,29 @@ pub struct RsaPrivateParameters {
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]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
struct RsaOtherPrimeInfo {
@ -177,6 +243,27 @@ pub struct EcPrivateParameters {
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 {
use digest::typenum::Unsigned;
use ecdsa::EncodedPoint;
@ -267,3 +354,19 @@ pub struct OkpPrivateParameters {
#[serde_as(as = "Base64<UrlSafe, Unpadded>")]
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
// 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<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]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct EcPublicParameters {
@ -73,6 +106,31 @@ pub struct EcPublicParameters {
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]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct OkpPublicParameters {
@ -83,8 +141,22 @@ pub struct OkpPublicParameters {
x: Vec<u8>,
}
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<u8>) -> Self {
Self { crv, x }
}
pub const fn crv(&self) -> JsonWebKeyOkpEllipticCurve {
self.crv
}
}

View File

@ -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<u8> {
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());
}