wire_e2e_identity/acme/
jws.rs

1use jwt_simple::prelude::*;
2use rusty_jwt_tools::prelude::*;
3
4use crate::acme::prelude::*;
5
6#[derive(Debug, serde::Serialize, serde::Deserialize)]
7#[cfg_attr(test, derive(Clone))]
8#[serde(rename_all = "camelCase")]
9pub struct AcmeJws {
10    pub protected: String,
11    pub payload: String,
12    pub signature: String,
13}
14
15impl AcmeJws {
16    pub fn new<T>(
17        alg: JwsAlgorithm,
18        nonce: String,
19        url: &url::Url,
20        kid: Option<&url::Url>,
21        payload: Option<T>,
22        kp: &Pem,
23    ) -> RustyAcmeResult<Self>
24    where
25        T: serde::Serialize,
26        for<'de> T: serde::Deserialize<'de>,
27    {
28        let with_jwk = kid.is_none();
29        let header = Self::header(alg, nonce, url, kid);
30
31        let is_empty_payload = payload.is_none();
32        let claims = payload.map(Self::claims);
33        let jwt = RustyJwtTools::generate_jwt(alg, header, claims, kp, with_jwk)?;
34        let (protected, jwt) = jwt.split_once('.').ok_or(RustyAcmeError::ImplementationError)?;
35        let (payload, signature) = jwt.split_once('.').ok_or(RustyAcmeError::ImplementationError)?;
36        if signature.contains('.') {
37            // we would have a malformed jwt
38            return Err(RustyAcmeError::ImplementationError);
39        }
40
41        let payload = if is_empty_payload { "" } else { payload };
42
43        Ok(Self {
44            protected: protected.to_string(),
45            payload: payload.to_string(),
46            signature: signature.to_string(),
47        })
48    }
49
50    fn claims<T>(custom: T) -> JWTClaims<T> {
51        JWTClaims {
52            custom,
53            nonce: None,
54            issuer: None,
55            subject: None,
56            jwt_id: None,
57            audiences: None,
58            expires_at: None,
59            invalid_before: None,
60            issued_at: None,
61        }
62    }
63
64    fn header(alg: JwsAlgorithm, nonce: String, url: &url::Url, kid: Option<&url::Url>) -> JWTHeader {
65        JWTHeader {
66            algorithm: alg.to_string(),
67            custom: Some(serde_json::json!({
68                "nonce": nonce,
69                "url": url,
70            })),
71            key_id: kid.map(url::Url::to_string),
72            ..Default::default()
73        }
74    }
75}