1use base64::Engine;
2use base64::engine::general_purpose::STANDARD as B64;
3use reqwest::Method;
4use secrecy::{ExposeSecret, SecretString};
5
6use crate::VaultClient;
7use crate::api::traits::TransitOperations;
8use crate::client::{encode_path, to_body};
9use crate::types::error::VaultError;
10use crate::types::transit::*;
11
12#[derive(Debug)]
13pub struct TransitHandler<'a> {
14 pub(crate) client: &'a VaultClient,
15 pub(crate) mount: String,
16}
17
18impl TransitOperations for TransitHandler<'_> {
19 async fn create_key(&self, name: &str, params: &TransitKeyParams) -> Result<(), VaultError> {
22 let body = to_body(params)?;
23 self.client
24 .exec_empty(
25 Method::POST,
26 &format!("{}/keys/{}", self.mount, encode_path(name)),
27 Some(&body),
28 )
29 .await
30 }
31
32 async fn read_key(&self, name: &str) -> Result<TransitKeyInfo, VaultError> {
33 self.client
34 .exec_with_data(
35 Method::GET,
36 &format!("{}/keys/{}", self.mount, encode_path(name)),
37 None,
38 )
39 .await
40 }
41
42 async fn list_keys(&self) -> Result<Vec<String>, VaultError> {
43 self.client.exec_list(&format!("{}/keys", self.mount)).await
44 }
45
46 async fn delete_key(&self, name: &str) -> Result<(), VaultError> {
47 self.client
48 .exec_empty(
49 Method::DELETE,
50 &format!("{}/keys/{}", self.mount, encode_path(name)),
51 None,
52 )
53 .await
54 }
55
56 async fn update_key_config(
57 &self,
58 name: &str,
59 cfg: &TransitKeyConfig,
60 ) -> Result<(), VaultError> {
61 let body = to_body(cfg)?;
62 self.client
63 .exec_empty(
64 Method::POST,
65 &format!("{}/keys/{}/config", self.mount, encode_path(name)),
66 Some(&body),
67 )
68 .await
69 }
70
71 async fn rotate_key(&self, name: &str) -> Result<(), VaultError> {
72 self.client
73 .exec_empty(
74 Method::POST,
75 &format!("{}/keys/{}/rotate", self.mount, encode_path(name)),
76 None,
77 )
78 .await
79 }
80
81 async fn export_key(
82 &self,
83 name: &str,
84 key_type: &str,
85 version: Option<u64>,
86 ) -> Result<TransitExportedKey, VaultError> {
87 let path = match version {
88 Some(v) => format!(
89 "{}/export/{}/{}/{}",
90 self.mount,
91 encode_path(key_type),
92 encode_path(name),
93 v
94 ),
95 None => format!(
96 "{}/export/{}/{}",
97 self.mount,
98 encode_path(key_type),
99 encode_path(name)
100 ),
101 };
102 self.client.exec_with_data(Method::GET, &path, None).await
103 }
104
105 async fn encrypt(&self, name: &str, plaintext: &SecretString) -> Result<String, VaultError> {
108 let body = serde_json::json!({
109 "plaintext": B64.encode(plaintext.expose_secret()),
110 });
111 let resp: TransitEncryptResponse = self
112 .client
113 .exec_with_data(
114 Method::POST,
115 &format!("{}/encrypt/{}", self.mount, encode_path(name)),
116 Some(&body),
117 )
118 .await?;
119 Ok(resp.ciphertext)
120 }
121
122 async fn decrypt(&self, name: &str, ciphertext: &str) -> Result<SecretString, VaultError> {
123 let body = serde_json::json!({ "ciphertext": ciphertext });
124 let resp: TransitDecryptResponse = self
125 .client
126 .exec_with_data(
127 Method::POST,
128 &format!("{}/decrypt/{}", self.mount, encode_path(name)),
129 Some(&body),
130 )
131 .await?;
132 let decoded = B64
134 .decode(resp.plaintext.expose_secret())
135 .map_err(|e| VaultError::Config(format!("base64 decode: {e}")))?;
136 let s = String::from_utf8(decoded)
137 .map_err(|e| VaultError::Config(format!("utf-8 decode: {e}")))?;
138 Ok(SecretString::from(s))
139 }
140
141 async fn rewrap(&self, name: &str, ciphertext: &str) -> Result<String, VaultError> {
142 let body = serde_json::json!({ "ciphertext": ciphertext });
143 let resp: TransitRewrapResponse = self
144 .client
145 .exec_with_data(
146 Method::POST,
147 &format!("{}/rewrap/{}", self.mount, encode_path(name)),
148 Some(&body),
149 )
150 .await?;
151 Ok(resp.ciphertext)
152 }
153
154 async fn batch_encrypt(
157 &self,
158 name: &str,
159 items: &[TransitBatchPlaintext],
160 ) -> Result<Vec<TransitBatchCiphertext>, VaultError> {
161 let body = serde_json::json!({ "batch_input": items });
162 let resp: TransitBatchEncryptResponse = self
163 .client
164 .exec_with_data(
165 Method::POST,
166 &format!("{}/encrypt/{}", self.mount, encode_path(name)),
167 Some(&body),
168 )
169 .await?;
170 Ok(resp.batch_results)
171 }
172
173 async fn batch_decrypt(
174 &self,
175 name: &str,
176 items: &[TransitBatchCiphertext],
177 ) -> Result<Vec<TransitBatchDecryptItem>, VaultError> {
178 let body = serde_json::json!({ "batch_input": items });
179 let resp: TransitBatchDecryptResponse = self
180 .client
181 .exec_with_data(
182 Method::POST,
183 &format!("{}/decrypt/{}", self.mount, encode_path(name)),
184 Some(&body),
185 )
186 .await?;
187 Ok(resp.batch_results)
188 }
189
190 async fn sign(
193 &self,
194 name: &str,
195 input: &[u8],
196 params: &TransitSignParams,
197 ) -> Result<String, VaultError> {
198 let mut body = to_body(params)?;
199 body["input"] = serde_json::Value::String(B64.encode(input));
200 let resp: TransitSignResponse = self
201 .client
202 .exec_with_data(
203 Method::POST,
204 &format!("{}/sign/{}", self.mount, encode_path(name)),
205 Some(&body),
206 )
207 .await?;
208 Ok(resp.signature)
209 }
210
211 async fn verify(&self, name: &str, input: &[u8], signature: &str) -> Result<bool, VaultError> {
212 let body = serde_json::json!({
213 "input": B64.encode(input),
214 "signature": signature,
215 });
216 let resp: TransitVerifyResponse = self
217 .client
218 .exec_with_data(
219 Method::POST,
220 &format!("{}/verify/{}", self.mount, encode_path(name)),
221 Some(&body),
222 )
223 .await?;
224 Ok(resp.valid)
225 }
226
227 async fn hash(&self, input: &[u8], algorithm: &str) -> Result<String, VaultError> {
230 let body = serde_json::json!({
231 "input": B64.encode(input),
232 "algorithm": algorithm,
233 });
234 let resp: TransitHashResponse = self
235 .client
236 .exec_with_data(Method::POST, &format!("{}/hash", self.mount), Some(&body))
237 .await?;
238 Ok(resp.sum)
239 }
240
241 async fn hmac(&self, name: &str, input: &[u8], algorithm: &str) -> Result<String, VaultError> {
242 let body = serde_json::json!({
243 "input": B64.encode(input),
244 "algorithm": algorithm,
245 });
246 let resp: TransitHmacResponse = self
247 .client
248 .exec_with_data(
249 Method::POST,
250 &format!("{}/hmac/{}", self.mount, encode_path(name)),
251 Some(&body),
252 )
253 .await?;
254 Ok(resp.hmac)
255 }
256
257 async fn random(&self, num_bytes: u32, format: &str) -> Result<String, VaultError> {
258 let body = serde_json::json!({
259 "bytes": num_bytes,
260 "format": format,
261 });
262 let resp: TransitRandomResponse = self
263 .client
264 .exec_with_data(Method::POST, &format!("{}/random", self.mount), Some(&body))
265 .await?;
266 Ok(resp.random_bytes)
267 }
268
269 async fn generate_data_key(
270 &self,
271 name: &str,
272 key_type: &str,
273 ) -> Result<TransitDataKey, VaultError> {
274 self.client
275 .exec_with_data(
276 Method::POST,
277 &format!(
278 "{}/datakey/{}/{}",
279 self.mount,
280 encode_path(key_type),
281 encode_path(name)
282 ),
283 None,
284 )
285 .await
286 }
287
288 async fn trim_key(&self, name: &str, min_version: u64) -> Result<(), VaultError> {
291 let body = serde_json::json!({ "min_available_version": min_version });
292 self.client
293 .exec_empty(
294 Method::POST,
295 &format!("{}/keys/{}/trim", self.mount, encode_path(name)),
296 Some(&body),
297 )
298 .await
299 }
300
301 async fn backup_key(&self, name: &str) -> Result<SecretString, VaultError> {
302 let resp: TransitBackupResponse = self
303 .client
304 .exec_with_data(
305 Method::GET,
306 &format!("{}/backup/{}", self.mount, encode_path(name)),
307 None,
308 )
309 .await?;
310 Ok(resp.backup.clone())
311 }
312
313 async fn restore_key(&self, name: &str, backup: &SecretString) -> Result<(), VaultError> {
314 let body = serde_json::json!({ "backup": backup.expose_secret() });
315 self.client
316 .exec_empty(
317 Method::POST,
318 &format!("{}/restore/{}", self.mount, encode_path(name)),
319 Some(&body),
320 )
321 .await
322 }
323
324 async fn batch_sign(
325 &self,
326 name: &str,
327 items: &[TransitBatchSignInput],
328 params: &TransitSignParams,
329 ) -> Result<Vec<TransitBatchSignResult>, VaultError> {
330 let mut body = to_body(params)?;
331 body["batch_input"] = serde_json::to_value(items)
332 .map_err(|e| VaultError::Config(format!("serialize batch sign input: {e}")))?;
333 let resp: TransitBatchSignResponse = self
334 .client
335 .exec_with_data(
336 Method::POST,
337 &format!("{}/sign/{}", self.mount, encode_path(name)),
338 Some(&body),
339 )
340 .await?;
341 Ok(resp.batch_results)
342 }
343
344 async fn batch_verify(
345 &self,
346 name: &str,
347 items: &[TransitBatchVerifyInput],
348 ) -> Result<Vec<TransitBatchVerifyResult>, VaultError> {
349 let body = serde_json::json!({ "batch_input": items });
350 let resp: TransitBatchVerifyResponse = self
351 .client
352 .exec_with_data(
353 Method::POST,
354 &format!("{}/verify/{}", self.mount, encode_path(name)),
355 Some(&body),
356 )
357 .await?;
358 Ok(resp.batch_results)
359 }
360
361 async fn read_cache_config(&self) -> Result<TransitCacheConfig, VaultError> {
362 self.client
363 .exec_with_data(Method::GET, &format!("{}/cache-config", self.mount), None)
364 .await
365 }
366
367 async fn write_cache_config(&self, size: u64) -> Result<(), VaultError> {
368 let body = serde_json::json!({ "size": size });
369 self.client
370 .exec_empty(
371 Method::POST,
372 &format!("{}/cache-config", self.mount),
373 Some(&body),
374 )
375 .await
376 }
377}