1extern crate core;
2
3mod c;
4mod error;
5mod json;
6mod status;
7mod utils;
8
9pub use error::Error;
10pub use etsi014_client::ETSI014Client;
11pub use secrets::SecretVec;
12pub use status::Status;
13
14pub mod etsi014_client {
15 use crate::error::ErrorType::{
16 ConnectionError, InvalidArgument, InvalidHost, InvalidResponse,
17 };
18 use crate::json::key_container::KeyContainer;
19 use crate::json::key_id::KeyId;
20 use crate::json::key_request::KeyRequest;
21 use crate::json::keys_by_ids_request::KeysByIdsRequest;
22 use crate::json::status_response::StatusResponse;
23 use crate::status::Status;
24 use crate::utils::read_file;
25 use crate::Error;
26 use base64ct::{Base64, Encoding};
27 use reqwest::header::CONTENT_TYPE;
28 use reqwest::{Client, Identity, Url};
29 pub use secrets::Secret;
30 pub use secrets::SecretBox;
31 pub use secrets::SecretVec;
32 use serde::de;
33 use std::path::PathBuf;
34
35 #[derive(Debug)]
36 pub struct ETSI014Client {
37 http_client: Client,
38 base_url: Url,
39 }
40
41 impl ETSI014Client {
42 const PATH_PREFIX: &'static str = "api/v1/keys";
43
44 pub fn new(
45 host: &str,
46 port: u16,
47 cert_path: &PathBuf,
48 key_path: &PathBuf,
49 server_ca_path: &PathBuf,
50 ) -> Result<Self, Error> {
51 let mut base_url =
53 Url::parse("https://localhost").expect("Error parsing hardcoded URL");
54 base_url
55 .set_scheme("https")
56 .expect("Error setting https as scheme");
57 base_url.set_host(Some(host)).map_err(|e| {
58 Error::new(
59 format!("Invalid host: {host}"),
60 InvalidHost,
61 Some(Box::new(e)),
62 )
63 })?;
64 base_url
65 .set_port(Some(port))
66 .map_err(|_| {
68 Error::new(
69 format!("Error setting port for host: '{host}"),
70 InvalidHost,
71 None,
72 )
73 })?;
74 let server_ca = reqwest::Certificate::from_pem(&read_file(server_ca_path)?)
75 .map_err(|e| {
76 Error::new(
77 format!("Error parsing {server_ca_path:?}"),
78 InvalidArgument,
79 Some(Box::new(e)),
80 )
81 })?;
82 let identity =
83 Identity::from_pkcs8_pem(&read_file(cert_path)?, &read_file(key_path)?)
84 .map_err(|e| {
85 Error::new(
86 format!("Error parsing {cert_path:?} or {key_path:?}"),
87 InvalidArgument,
88 Some(Box::new(e)),
89 )
90 })?;
91 let http_client = Client::builder()
92 .use_native_tls()
93 .tls_built_in_root_certs(false)
94 .add_root_certificate(server_ca)
95 .identity(identity)
96 .build()
97 .map_err(|e| {
98 Error::new(
99 "Error creating http client".to_string(),
100 InvalidArgument,
101 Some(Box::new(e)),
102 )
103 })?;
104 Ok(ETSI014Client {
105 http_client,
106 base_url,
107 })
108 }
109
110 async fn send_request<T>(
111 &self,
112 target_sae_id: &str,
113 endpoint: &str,
114 body: Option<&str>,
115 ) -> Result<T, Error>
116 where
117 T: de::DeserializeOwned,
118 {
119 let mut url = self.base_url.clone();
120 let path_prefix = Self::PATH_PREFIX;
121 url.set_path(&format!("{path_prefix}/{target_sae_id}/{endpoint}"));
122 let request = match body {
123 None => self
124 .http_client
125 .get(url.clone())
126 .build()
127 .map_err(|e| Error::new(
128 format!("Error building request for url: {url}"),
129 InvalidArgument,
130 Some(Box::new(e)),
131 )),
132 Some(body) =>
133 self.http_client
134 .post(url.clone())
135 .header(CONTENT_TYPE, "application/json")
136 .body(body.to_owned())
137 .build()
138 .map_err(|e| Error::new(
139 format!("Error building request for url: {url}\n\nRequest body: {body}"),
140 InvalidArgument,
141 Some(Box::new(e))
142 )),
143 }?;
144
145 let response = self
146 .http_client
147 .execute(request.try_clone().unwrap())
148 .await
149 .map_err(|e| {
150 Error::new(
151 format!("Error sending request: {request:#?}"),
152 ConnectionError,
153 Some(Box::new(e)),
154 )
155 })?;
156 let http_code = response.status();
157 let response_string = response.text().await.map_err(|e| {
158 Error::new(
159 "Response not UTF-8".to_string(),
160 InvalidResponse,
161 Some(Box::new(e)),
162 )
163 })?;
164 let error_info = |s: String| {
165 let body_info = match body {
166 None => "".to_owned(),
167 Some(body) => format!("\nUsing POST body: {body}"),
168 };
169 format!(
170 "{s}\n\n\
171 HTTP Code: {http_code}\n\
172 Response:\n{response_string}\n\
173 Using request: {request:#?}{body_info}"
174 )
175 };
176 if !http_code.is_success() {
177 return Err(Error::new(
178 error_info("Unsuccessful HTTP code".to_string()),
179 InvalidResponse,
180 None,
181 ));
182 }
183 serde_json::from_str::<T>(&response_string).map_err(|e| {
184 Error::new(
185 error_info("Unable to deserialize JSON from response.".to_string()),
186 InvalidResponse,
187 Some(Box::new(e)),
188 )
189 })
190 }
191
192 pub async fn get_status(&self, target_sae_id: &str) -> Result<Status, Error> {
193 let sr: StatusResponse =
194 self.send_request(target_sae_id, "status", None).await?;
195 Ok(Status {
196 source_kme_id: sr.source_kme_id,
197 target_kme_id: sr.target_kme_id,
198 source_sae_id: sr.source_sae_id,
199 target_sae_id: sr.target_sae_id,
200 key_size: sr.key_size,
201 stored_key_count: sr.stored_key_count,
202 max_key_count: sr.max_key_count,
203 max_key_per_request: sr.max_key_per_request,
204 max_key_size: sr.max_key_size,
205 min_key_size: sr.min_key_size,
206 max_sae_id_count: sr.max_sae_id_count,
207 })
208 }
209
210 fn key_container_to_vector(
211 kc: KeyContainer,
212 ) -> Result<Vec<(String, SecretVec<u8>)>, Error> {
213 let amount_of_keys = kc.keys.len();
214 kc.keys.into_iter().try_fold(
215 Vec::with_capacity(amount_of_keys),
216 |mut l, key_and_id| {
217 let uuid = &key_and_id.key_id;
218 let base64_string = key_and_id.key;
219 let mut base64_vec = base64_string.into_bytes();
220 let base64_slice = base64_vec.as_mut();
221 let mut secret_base64 = SecretVec::from(base64_slice);
222 let mut secret_base64_ref_mut = secret_base64.borrow_mut();
223 let secret_slice = Base64::decode_in_place(
224 secret_base64_ref_mut.as_mut(),
225 )
226 .map_err(|_| {
227 Error::new(
228 format!("Error decoding base64 for uuid {uuid}"),
229 InvalidResponse,
230 None,
231 )
232 })?;
233 let secret = SecretVec::new(secret_slice.len(), |sv| {
235 sv.copy_from_slice(secret_slice);
236 });
237 l.push((key_and_id.key_id, secret));
238 Ok(l)
239 },
240 )
241 }
242
243 pub async fn get_keys(
244 &self,
245 key_size_bits: u32,
246 target_sae_id: &str,
247 additional_target_sae_ids: &[&str],
248 amount_of_keys: u32,
249 ) -> Result<Vec<(String, SecretVec<u8>)>, Error> {
250 let post_body = serde_json::to_string(&KeyRequest {
251 number: amount_of_keys,
252 size: Some(key_size_bits),
253 additional_target_sae_ids,
254 extension_mandatory: None,
255 })
256 .expect("Error serializing key request.");
257 let key_container = self
258 .send_request::<KeyContainer>(target_sae_id, "enc_keys", Some(&post_body))
259 .await?;
260 Self::key_container_to_vector(key_container)
261 }
262
263 pub async fn get_keys_by_ids(
264 &self,
265 target_sae_id: &str,
266 key_ids: &[&str],
267 ) -> Result<Vec<(String, SecretVec<u8>)>, Error> {
268 let post_body = serde_json::to_string(&KeysByIdsRequest {
269 key_ids: key_ids.iter().map(|key_id| KeyId { key_id }).collect(),
270 })
271 .expect("Error serializing keys by ids reqeust");
272 let key_container = self
273 .send_request::<KeyContainer>(target_sae_id, "dec_keys", Some(&post_body))
274 .await?;
275 Self::key_container_to_vector(key_container)
276 }
277 }
278}