use std::sync::Arc;
use agent_pay::{
did_key_from_public_key, generate_key_pair, public_key_from_did_key, sign_compact,
verification_method_id, verify_compact, Error, ResolveKey,
};
use serde_json::{json, Value};
fn make_resolver(did: String) -> ResolveKey {
Arc::new(move |kid: String| {
let did = did.clone();
Box::pin(async move {
if !kid.starts_with(&did) {
return Err(Error::Other(format!("unknown kid {kid}")));
}
public_key_from_did_key(&did)
})
})
}
#[tokio::test]
async fn compact_jws_roundtrips_a_json_payload() {
let kp = generate_key_pair();
let did = did_key_from_public_key(&kp.public_key).unwrap();
let kid = verification_method_id(&did).unwrap();
let payload = json!({"v": "agent-pay/0.1", "hello": "world"});
let token = sign_compact(&payload, &kp.private_key, &kid).await.unwrap();
assert_eq!(token.split('.').count(), 3);
let resolver = make_resolver(did);
let (p, k) = verify_compact(&token, &resolver).await.unwrap();
assert_eq!(p, payload);
assert_eq!(k, kid);
}
#[tokio::test]
async fn verify_compact_rejects_tampered_payload() {
let kp = generate_key_pair();
let did = did_key_from_public_key(&kp.public_key).unwrap();
let kid = verification_method_id(&did).unwrap();
let token = sign_compact(&json!({"a": 1}), &kp.private_key, &kid)
.await
.unwrap();
let parts: Vec<&str> = token.split('.').collect();
let bad = format!("{}.{}AA.{}", parts[0], parts[1], parts[2]);
let resolver = make_resolver(did);
let err = verify_compact(&bad, &resolver).await.unwrap_err();
let msg = format!("{err}").to_lowercase();
assert!(msg.contains("signature"), "msg={msg}");
}
#[tokio::test]
async fn verify_compact_rejects_unsupported_alg() {
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
let header =
URL_SAFE_NO_PAD.encode(serde_json::to_vec(&json!({"alg": "HS256", "kid": "x"})).unwrap());
let payload = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&json!({"a": 1})).unwrap());
let token = format!("{header}.{payload}.AAAA");
let resolver: ResolveKey = Arc::new(|_| {
Box::pin(async move {
let _ = Value::Null;
Ok([0u8; 32])
})
});
let err = verify_compact(&token, &resolver).await.unwrap_err();
let msg = format!("{err}").to_lowercase();
assert!(msg.contains("alg"), "msg={msg}");
}