]> git.proxmox.com Git - proxmox.git/blob - proxmox-acme/src/jws.rs
move to proxmox-acme
[proxmox.git] / proxmox-acme / src / jws.rs
1 use std::convert::TryFrom;
2
3 use openssl::hash::{Hasher, MessageDigest};
4 use openssl::pkey::{HasPrivate, PKeyRef};
5 use openssl::sign::Signer;
6 use serde::Serialize;
7
8 use crate::b64u;
9 use crate::key::{Jwk, PublicKey};
10 use crate::Error;
11
12 #[derive(Debug, Serialize)]
13 #[serde(rename_all = "camelCase")]
14 pub struct Protected {
15 alg: &'static str,
16 nonce: String,
17 url: String,
18 #[serde(flatten)]
19 key: KeyId,
20 }
21
22 /// Acme requires to the use of *either* `jwk` *or* `kid` depending on the action taken.
23 #[derive(Debug, Serialize)]
24 #[serde(rename_all = "camelCase")]
25 pub enum KeyId {
26 /// This is the actual JWK structure.
27 Jwk(Jwk),
28
29 /// This should be the account location.
30 Kid(String),
31 }
32
33 #[derive(Debug, Serialize)]
34 #[serde(rename_all = "camelCase")]
35 pub struct Jws {
36 protected: String,
37 payload: String,
38 signature: String,
39 }
40
41 impl Jws {
42 pub fn new<P, T>(
43 key: &PKeyRef<P>,
44 location: Option<String>,
45 url: String,
46 nonce: String,
47 payload: &T,
48 ) -> Result<Self, Error>
49 where
50 P: HasPrivate,
51 T: Serialize,
52 {
53 Self::new_full(
54 key,
55 location,
56 url,
57 nonce,
58 b64u::encode(serde_json::to_string(payload)?.as_bytes()),
59 )
60 }
61
62 pub fn new_full<P: HasPrivate>(
63 key: &PKeyRef<P>,
64 location: Option<String>,
65 url: String,
66 nonce: String,
67 payload: String,
68 ) -> Result<Self, Error> {
69 let jwk = Jwk::try_from(key)?;
70
71 let pubkey = jwk.key.clone();
72 let mut protected = Protected {
73 alg: "",
74 nonce,
75 url,
76 key: match location {
77 Some(location) => KeyId::Kid(location),
78 None => KeyId::Jwk(jwk),
79 },
80 };
81
82 let (digest, ec_order_bytes): (MessageDigest, usize) = match &pubkey {
83 PublicKey::Rsa(_) => (Self::prepare_rsa(key, &mut protected), 0),
84 PublicKey::Ec(_) => Self::prepare_ec(key, &mut protected),
85 };
86
87 let protected_data = b64u::encode(serde_json::to_string(&protected)?.as_bytes());
88
89 let signature = {
90 let prot = protected_data.as_bytes();
91 let payload = payload.as_bytes();
92 match &pubkey {
93 PublicKey::Rsa(_) => Self::sign_rsa(key, digest, prot, payload),
94 PublicKey::Ec(_) => Self::sign_ec(key, digest, ec_order_bytes, prot, payload),
95 }?
96 };
97
98 let signature = b64u::encode(&signature);
99
100 Ok(Jws {
101 protected: protected_data,
102 payload,
103 signature,
104 })
105 }
106
107 fn prepare_rsa<P>(_key: &PKeyRef<P>, protected: &mut Protected) -> MessageDigest
108 where
109 P: HasPrivate,
110 {
111 protected.alg = "RS256";
112 MessageDigest::sha256()
113 }
114
115 /// Returns the digest and the size of the two signature components 'r' and 's'.
116 fn prepare_ec<P>(_key: &PKeyRef<P>, protected: &mut Protected) -> (MessageDigest, usize)
117 where
118 P: HasPrivate,
119 {
120 // Note: if we support >256 bit keys we'll want to also support using ES512 here probably
121 protected.alg = "ES256";
122 // 'r' and 's' are each 256 bit numbers:
123 (MessageDigest::sha256(), 32)
124 }
125
126 fn sign_rsa<P>(
127 key: &PKeyRef<P>,
128 digest: MessageDigest,
129 protected: &[u8],
130 payload: &[u8],
131 ) -> Result<Vec<u8>, Error>
132 where
133 P: HasPrivate,
134 {
135 let mut signer = Signer::new(digest, key)?;
136 signer.set_rsa_padding(openssl::rsa::Padding::PKCS1)?;
137 signer.update(protected)?;
138 signer.update(b".")?;
139 signer.update(payload)?;
140 Ok(signer.sign_to_vec()?)
141 }
142
143 fn sign_ec<P>(
144 key: &PKeyRef<P>,
145 digest: MessageDigest,
146 ec_order_bytes: usize,
147 protected: &[u8],
148 payload: &[u8],
149 ) -> Result<Vec<u8>, Error>
150 where
151 P: HasPrivate,
152 {
153 let mut hasher = Hasher::new(digest)?;
154 hasher.update(protected)?;
155 hasher.update(b".")?;
156 hasher.update(payload)?;
157 let sig =
158 openssl::ecdsa::EcdsaSig::sign(hasher.finish()?.as_ref(), key.ec_key()?.as_ref())?;
159 let r = sig.r().to_vec();
160 let s = sig.s().to_vec();
161 let mut out = Vec::with_capacity(ec_order_bytes * 2);
162 out.extend(std::iter::repeat(0u8).take(ec_order_bytes - r.len()));
163 out.extend(r);
164 out.extend(std::iter::repeat(0u8).take(ec_order_bytes - s.len()));
165 out.extend(s);
166 Ok(out)
167 }
168 }