use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use rsa::RsaPublicKey;
use rsa::traits::PublicKeyParts;
use serde::{Deserialize, Deserializer, Serialize};
pub const RS256: &str = "RS256";
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct Jwks {
pub keys: Vec<Jwk>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Jwk {
pub kty: String,
#[serde(default = "default_alg")]
pub alg: String,
#[serde(rename = "use", default = "default_use")]
pub use_: String,
pub kid: String,
pub n: String,
pub e: String,
}
fn default_alg() -> String {
RS256.to_owned()
}
fn default_use() -> String {
"sig".to_owned()
}
#[derive(Deserialize)]
struct PublishedKey {
#[serde(default)]
kty: Option<String>,
#[serde(default)]
alg: Option<String>,
#[serde(rename = "use", default)]
use_: Option<String>,
#[serde(default)]
key_ops: Option<Vec<String>>,
#[serde(default)]
kid: Option<String>,
#[serde(default)]
n: Option<String>,
#[serde(default)]
e: Option<String>,
}
impl PublishedKey {
fn into_rs256_verification_key(self) -> Option<Jwk> {
if self.kty.as_deref() != Some("RSA") {
return None;
}
if self.use_.as_deref().is_some_and(|u| u != "sig") {
return None;
}
if self
.key_ops
.as_ref()
.is_some_and(|ops| !ops.iter().any(|op| op == "verify"))
{
return None;
}
if self.alg.as_deref().is_some_and(|alg| alg != RS256) {
return None;
}
let kid = self.kid.filter(|kid| !kid.is_empty())?;
Some(Jwk {
kty: "RSA".to_owned(),
alg: RS256.to_owned(),
use_: default_use(),
kid,
n: self.n.filter(|n| !n.is_empty())?,
e: self.e.filter(|e| !e.is_empty())?,
})
}
}
impl<'de> Deserialize<'de> for Jwks {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
struct PublishedSet {
keys: Vec<serde_json::Value>,
}
let set = PublishedSet::deserialize(deserializer)?;
let keys = set
.keys
.into_iter()
.filter_map(|raw| serde_json::from_value::<PublishedKey>(raw).ok())
.filter_map(PublishedKey::into_rs256_verification_key)
.collect();
Ok(Self { keys })
}
}
impl Jwk {
pub fn from_rsa_public_key(public_key: &RsaPublicKey, kid: String) -> Self {
let n = URL_SAFE_NO_PAD.encode(public_key.n().to_bytes_be());
let e = URL_SAFE_NO_PAD.encode(public_key.e().to_bytes_be());
Self {
kty: "RSA".to_owned(),
alg: RS256.to_owned(),
use_: default_use(),
kid,
n,
e,
}
}
}