wire_e2e_identity/acme/
jws.rs1use 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 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}