]> git.proxmox.com Git - proxmox.git/blob - proxmox-acme/src/eab.rs
f3221904e705ab1c453d6c507f03e1edd8c052c3
[proxmox.git] / proxmox-acme / src / eab.rs
1 use openssl::hash::MessageDigest;
2 use openssl::pkey::{HasPrivate, PKeyRef};
3 use openssl::sign::Signer;
4 use serde::{Deserialize, Serialize};
5
6 use crate::key::Jwk;
7 use crate::{b64u, Error};
8
9 #[derive(Debug, Serialize)]
10 #[serde(rename_all = "camelCase")]
11 struct Protected {
12 alg: &'static str,
13 url: String,
14 kid: String,
15 }
16
17 #[cfg_attr(feature = "api-types", proxmox_schema::api())]
18 /// External Account Bindings
19 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
20 #[serde(rename_all = "camelCase")]
21 pub struct ExternalAccountBinding {
22 /// JOSE Header (see RFC 7515)
23 protected: String,
24 /// Payload
25 payload: String,
26 /// HMAC signature
27 signature: String,
28 }
29
30 impl ExternalAccountBinding {
31 pub fn new<P>(
32 eab_kid: &str,
33 eab_hmac_key: &PKeyRef<P>,
34 jwk: Jwk,
35 url: String,
36 ) -> Result<Self, Error>
37 where
38 P: HasPrivate,
39 {
40 let protected = Protected {
41 alg: "HS256",
42 kid: eab_kid.to_string(),
43 url,
44 };
45 let payload = b64u::encode(serde_json::to_string(&jwk)?.as_bytes());
46 let protected_data = b64u::encode(serde_json::to_string(&protected)?.as_bytes());
47 let signature = {
48 let protected = protected_data.as_bytes();
49 let payload = payload.as_bytes();
50 Self::sign_hmac(eab_hmac_key, protected, payload)?
51 };
52
53 let signature = b64u::encode(&signature);
54 Ok(ExternalAccountBinding {
55 protected: protected_data,
56 payload,
57 signature,
58 })
59 }
60
61 fn sign_hmac<P>(key: &PKeyRef<P>, protected: &[u8], payload: &[u8]) -> Result<Vec<u8>, Error>
62 where
63 P: HasPrivate,
64 {
65 let mut signer = Signer::new(MessageDigest::sha256(), key)?;
66 signer.update(protected)?;
67 signer.update(b".")?;
68 signer.update(payload)?;
69 Ok(signer.sign_to_vec()?)
70 }
71 }