(&self, ciphertext: &str)
| 99 | } |
| 100 | |
| 101 | async fn decrypt_with_vault(&self, ciphertext: &str) -> Result<Zeroizing<[u8; 32]>> { |
| 102 | let token = self.read_token()?; |
| 103 | let url = format!( |
| 104 | "{}/v1/{}/decrypt/{}", |
| 105 | self.addr.trim_end_matches('/'), |
| 106 | self.mount, |
| 107 | self.key_name |
| 108 | ); |
| 109 | |
| 110 | let resp = self |
| 111 | .client |
| 112 | .post(&url) |
| 113 | .header("X-Vault-Token", &token) |
| 114 | .json(&serde_json::json!({ "ciphertext": ciphertext })) |
| 115 | .send() |
| 116 | .await |
| 117 | .map_err(|e| crate::Error::Encryption { |
| 118 | detail: format!("Vault HTTP request failed: {e}"), |
| 119 | })?; |
| 120 | |
| 121 | if !resp.status().is_success() { |
| 122 | let status = resp.status(); |
| 123 | let body = resp.text().await.unwrap_or_default(); |
| 124 | return Err(crate::Error::Encryption { |
| 125 | detail: format!("Vault decrypt returned {status}: {body}"), |
| 126 | }); |
| 127 | } |
| 128 | |
| 129 | let body: serde_json::Value = resp.json().await.map_err(|e| crate::Error::Encryption { |
| 130 | detail: format!("Vault response JSON parse failed: {e}"), |
| 131 | })?; |
| 132 | |
| 133 | let plaintext_b64 = body |
| 134 | .pointer("/data/plaintext") |
| 135 | .and_then(|v| v.as_str()) |
| 136 | .ok_or_else(|| crate::Error::Encryption { |
| 137 | detail: "Vault response missing /data/plaintext field".into(), |
| 138 | })?; |
| 139 | |
| 140 | decode_32_byte_b64(plaintext_b64) |
| 141 | } |
| 142 | |
| 143 | async fn rewrap_with_vault(&self, old_ciphertext: &str) -> Result<String> { |
| 144 | let token = self.read_token()?; |
no test coverage detected