use std::path::Path;
use wimsey_jose::{Jwk, PrivateJwk, SigningKey, VerifyingKey};
use crate::Result;
#[derive(serde::Serialize, serde::Deserialize)]
pub struct JwkKey {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub alg: Option<String>,
pub kty: String,
pub crv: String,
pub x: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub y: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub d: Option<String>,
}
impl JwkKey {
pub fn from_signing_key(key: &SigningKey) -> Self {
let jwk = PrivateJwk::from_signing_key(key);
Self {
alg: jwk.alg,
kty: jwk.kty,
crv: jwk.crv,
x: jwk.x,
y: jwk.y,
d: Some(jwk.d),
}
}
pub fn to_public(&self) -> Self {
Self {
d: None,
alg: self.alg.clone(),
kty: self.kty.clone(),
crv: self.crv.clone(),
x: self.x.clone(),
y: self.y.clone(),
}
}
fn public_jwk(&self) -> Result<Jwk> {
let alg = match self.alg.as_deref() {
Some(alg) => alg.to_owned(),
None => match (self.kty.as_str(), self.crv.as_str()) {
("OKP", "Ed25519") => "EdDSA".to_owned(),
("EC", "P-256") => "ES256".to_owned(),
(kty, crv) => {
return Err(format!("unsupported key: kty={kty}, crv={crv}").into());
}
},
};
Ok(Jwk {
alg: Some(alg),
kty: self.kty.clone(),
crv: self.crv.clone(),
x: self.x.clone(),
y: self.y.clone(),
})
}
pub fn signing_key(&self) -> Result<SigningKey> {
let public = self.public_jwk()?;
let d = self
.d
.as_ref()
.ok_or("key file has no private component `d`")?;
PrivateJwk {
alg: public.alg.clone(),
kty: public.kty.clone(),
crv: public.crv.clone(),
x: public.x.clone(),
y: public.y.clone(),
d: d.trim().to_owned(),
}
.to_signing_key()
.map_err(|e| format!("invalid private key: {e}").into())
}
pub fn verifying_key(&self) -> Result<VerifyingKey> {
self.public_jwk()?
.to_verifying_key()
.map_err(|e| format!("invalid public key: {e}").into())
}
}
pub fn load(path: &Path) -> Result<JwkKey> {
let bytes = std::fs::read(path).map_err(|e| format!("reading {}: {e}", path.display()))?;
serde_json::from_slice(&bytes).map_err(|e| format!("parsing {}: {e}", path.display()).into())
}
pub fn to_json(key: &JwkKey) -> Result<String> {
Ok(serde_json::to_string_pretty(key)?)
}