agentguard_core/
jwk.rs

1//! JWK (JSON Web Key) primitives shared by the JWT and DPoP
2//! verifiers.
3//!
4//! Currently exposes only the RFC 7638 thumbprint for an Ed25519
5//! public key, used as the deterministic kid when an IdP doesn't
6//! supply one of its own. The two callers (jwt.rs, dpop.rs) used to
7//! maintain identical copies of this logic; they now both call
8//! [`thumbprint_ed25519`].
9
10use base64::Engine as _;
11use sha2::{Digest, Sha256};
12
13/// RFC 7638 JWK thumbprint for an Ed25519 public key.
14///
15/// Returns the base64url-no-pad SHA-256 of the canonical JSON
16/// `{"crv":"<crv>","kty":"OKP","x":"<base64url-x>"}`. The argument
17/// `crv` defaults to `"Ed25519"` if the caller passes an empty
18/// string; the caller is expected to validate non-default `crv`
19/// values upstream.
20///
21/// # Errors
22/// Returns `Err` only if the `x` argument is not valid base64url.
23pub fn thumbprint_ed25519(x_b64: &str, crv: &str) -> String {
24    let crv = if crv.is_empty() { "Ed25519" } else { crv };
25    let canonical = format!(r#"{{"crv":"{}","kty":"OKP","x":"{}"}}"#, crv, x_b64);
26    let hash = Sha256::digest(canonical.as_bytes());
27    base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(hash)
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn deterministic() {
36        let x1 = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode([1u8; 32]);
37        let x2 = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode([2u8; 32]);
38        assert_ne!(
39            thumbprint_ed25519(&x1, "Ed25519"),
40            thumbprint_ed25519(&x2, "Ed25519")
41        );
42        assert_eq!(
43            thumbprint_ed25519(&x1, "Ed25519"),
44            thumbprint_ed25519(&x1, "Ed25519")
45        );
46    }
47
48    #[test]
49    fn empty_crv_defaults_to_ed25519() {
50        let x = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode([1u8; 32]);
51        assert_eq!(
52            thumbprint_ed25519(&x, ""),
53            thumbprint_ed25519(&x, "Ed25519"),
54        );
55    }
56}