spiffe_rs/internal/
jwk.rs1use 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}