spiffe_rs/internal/
jwk.rs

1use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD};
2use base64::Engine;
3use serde::Deserialize;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum JwtKey {
7    Ec { crv: String, x: Vec<u8>, y: Vec<u8> },
8    Rsa { n: Vec<u8>, e: Vec<u8> },
9}
10
11#[derive(Debug, Deserialize)]
12pub struct JwkDocument {
13    pub keys: Option<Vec<JwkKeyEntry>>,
14    #[serde(rename = "spiffe_sequence")]
15    pub spiffe_sequence: Option<u64>,
16    #[serde(rename = "spiffe_refresh_hint")]
17    pub spiffe_refresh_hint: Option<i64>,
18}
19
20#[derive(Debug, Deserialize)]
21pub struct JwkKeyEntry {
22    pub kty: String,
23    pub kid: Option<String>,
24    #[serde(rename = "use")]
25    pub use_field: Option<String>,
26    pub crv: Option<String>,
27    pub x: Option<String>,
28    pub y: Option<String>,
29    pub n: Option<String>,
30    pub e: Option<String>,
31    pub x5c: Option<Vec<String>>,
32}
33
34impl JwkKeyEntry {
35    pub fn key_id(&self) -> Option<&str> {
36        self.kid.as_deref()
37    }
38
39    pub fn to_jwt_key(&self) -> Result<JwtKey, String> {
40        match self.kty.as_str() {
41            "EC" => {
42                let crv = self
43                    .crv
44                    .as_ref()
45                    .ok_or_else(|| "missing crv".to_string())?;
46                let x = self
47                    .x
48                    .as_ref()
49                    .ok_or_else(|| "missing x".to_string())
50                    .and_then(|value| decode_base64(value))?;
51                let y = self
52                    .y
53                    .as_ref()
54                    .ok_or_else(|| "missing y".to_string())
55                    .and_then(|value| decode_base64(value))?;
56                Ok(JwtKey::Ec {
57                    crv: crv.clone(),
58                    x,
59                    y,
60                })
61            }
62            "RSA" => {
63                let n = self
64                    .n
65                    .as_ref()
66                    .ok_or_else(|| "missing n".to_string())
67                    .and_then(|value| decode_base64(value))?;
68                let e = self
69                    .e
70                    .as_ref()
71                    .ok_or_else(|| "missing e".to_string())
72                    .and_then(|value| decode_base64(value))?;
73                Ok(JwtKey::Rsa { n, e })
74            }
75            _ => Err(format!("unsupported kty {}", self.kty)),
76        }
77    }
78
79    pub fn x509_certificate_der(&self) -> Option<Vec<u8>> {
80        let mut iter = self.x5c.as_ref()?.iter();
81        let first = iter.next()?.clone();
82        STANDARD.decode(first.as_bytes()).ok()
83    }
84}
85
86pub fn decode_base64(value: &str) -> Result<Vec<u8>, String> {
87    URL_SAFE_NO_PAD
88        .decode(value.as_bytes())
89        .map_err(|err| err.to_string())
90}