use base64::Engine;
use ed25519_dalek::Signer;
use serde_json::Value;
use crate::keystore::KeyStore;
pub fn sign(store: &KeyStore, key_id: &str, payload: &Value) -> anyhow::Result<String> {
let key = store.load_key(key_id)?;
let header_json = format!(
r#"{{"alg":"{}","kid":"{}"}}"#,
key.algorithm.jose_alg(),
key.fingerprint
);
let b64 = base64::engine::general_purpose::URL_SAFE_NO_PAD;
let canonical = super::canonical::canonicalize(payload)?;
let header_b64 = b64.encode(header_json.as_bytes());
let payload_b64 = b64.encode(&canonical);
let signing_input = format!("{header_b64}.{payload_b64}");
let signature = key.signing_key.sign(signing_input.as_bytes());
let sig_b64 = b64.encode(signature.to_bytes());
Ok(format!("{signing_input}.{sig_b64}"))
}
pub fn verify(store: &KeyStore, key_id: &str, jws: &str) -> anyhow::Result<bool> {
let parts: Vec<&str> = jws.splitn(3, '.').collect();
if parts.len() != 3 {
return Ok(false);
}
let b64 = base64::engine::general_purpose::URL_SAFE_NO_PAD;
let key = store.load_key(key_id)?;
if !super::verifier::header_alg_matches(&b64, parts[0], key.algorithm) {
return Ok(false);
}
let signing_input = format!("{}.{}", parts[0], parts[1]);
let sig_bytes = b64
.decode(parts[2])
.map_err(|e| anyhow::anyhow!("base64: {e}"))?;
let sig_arr: [u8; 64] = sig_bytes
.as_slice()
.try_into()
.map_err(|_| anyhow::anyhow!("invalid signature length"))?;
let signature = ed25519_dalek::Signature::from_bytes(&sig_arr);
Ok(key
.verifying_key
.verify_strict(signing_input.as_bytes(), &signature)
.is_ok())
}