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;
16 use crate::error::ErrorType::{
17 ConnectionError, InvalidArgument, InvalidHost, InvalidResponse,
18 };
19 use crate::json::key_container::KeyContainer;
20 use crate::json::key_id::KeyId;
21 use crate::json::key_request::KeyRequest;
22 use crate::json::keys_by_ids_request::KeysByIdsRequest;
23 use crate::json::status_response::StatusResponse;
24 use crate::status::Status;
25 use crate::utils::read_file;
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 .tls_backend_native()
93 .tls_certs_only([server_ca])
94 .identity(identity)
95 .build()
96 .map_err(|e| {
97 Error::new(
98 "Error creating http client".to_string(),
99 InvalidArgument,
100 Some(Box::new(e)),
101 )
102 })?;
103 Ok(ETSI014Client {
104 http_client,
105 base_url,
106 })
107 }
108
109 async fn send_request<T>(
110 &self,
111 target_sae_id: &str,
112 endpoint: &str,
113 body: Option<&str>,
114 ) -> Result<T, Error>
115 where
116 T: de::DeserializeOwned,
117 {
118 let mut url = self.base_url.clone();
119 let path_prefix = Self::PATH_PREFIX;
120 url.set_path(&format!("{path_prefix}/{target_sae_id}/{endpoint}"));
121 let request = match body {
122 None => self
123 .http_client
124 .get(url.clone())
125 .build()
126 .map_err(|e| Error::new(
127 format!("Error building request for url: {url}"),
128 InvalidArgument,
129 Some(Box::new(e)),
130 )),
131 Some(body) =>
132 self.http_client
133 .post(url.clone())
134 .header(CONTENT_TYPE, "application/json")
135 .body(body.to_owned())
136 .build()
137 .map_err(|e| Error::new(
138 format!("Error building request for url: {url}\n\nRequest body: {body}"),
139 InvalidArgument,
140 Some(Box::new(e))
141 )),
142 }?;
143
144 let response = self
145 .http_client
146 .execute(request.try_clone().unwrap())
147 .await
148 .map_err(|e| {
149 Error::new(
150 format!("Error sending request: {request:#?}"),
151 ConnectionError,
152 Some(Box::new(e)),
153 )
154 })?;
155 let http_code = response.status();
156 let response_string = response.text().await.map_err(|e| {
157 Error::new(
158 "Response not UTF-8".to_string(),
159 InvalidResponse,
160 Some(Box::new(e)),
161 )
162 })?;
163 let error_info = |s: String| {
164 let body_info = match body {
165 None => "".to_owned(),
166 Some(body) => format!("\nUsing POST body: {body}"),
167 };
168 format!(
169 "{s}\n\n\
170 HTTP Code: {http_code}\n\
171 Response:\n{response_string}\n\
172 Using request: {request:#?}{body_info}"
173 )
174 };
175 if !http_code.is_success() {
176 return Err(Error::new(
177 error_info("Unsuccessful HTTP code".to_string()),
178 InvalidResponse,
179 None,
180 ));
181 }
182 serde_json::from_str::<T>(&response_string).map_err(|e| {
183 Error::new(
184 error_info("Unable to deserialize JSON from response.".to_string()),
185 InvalidResponse,
186 Some(Box::new(e)),
187 )
188 })
189 }
190
191 pub async fn get_status(&self, target_sae_id: &str) -> Result<Status, Error> {
192 let sr: StatusResponse =
193 self.send_request(target_sae_id, "status", None).await?;
194 Ok(Status {
195 source_kme_id: sr.source_kme_id,
196 target_kme_id: sr.target_kme_id,
197 source_sae_id: sr.source_sae_id,
198 target_sae_id: sr.target_sae_id,
199 key_size: sr.key_size,
200 stored_key_count: sr.stored_key_count,
201 max_key_count: sr.max_key_count,
202 max_key_per_request: sr.max_key_per_request,
203 max_key_size: sr.max_key_size,
204 min_key_size: sr.min_key_size,
205 max_sae_id_count: sr.max_sae_id_count,
206 })
207 }
208
209 fn key_container_to_vector(
210 kc: KeyContainer,
211 ) -> Result<Vec<(String, SecretVec<u8>)>, Error> {
212 let amount_of_keys = kc.keys.len();
213 kc.keys.into_iter().try_fold(
214 Vec::with_capacity(amount_of_keys),
215 |mut l, key_and_id| {
216 let uuid = &key_and_id.key_id;
217 let base64_string = key_and_id.key;
218 let mut base64_vec = base64_string.into_bytes();
219 let base64_slice = base64_vec.as_mut();
220 let mut secret_base64 = SecretVec::from(base64_slice);
221 let mut secret_base64_ref_mut = secret_base64.borrow_mut();
222 let secret_slice = Base64::decode_in_place(
223 secret_base64_ref_mut.as_mut(),
224 )
225 .map_err(|_| {
226 Error::new(
227 format!("Error decoding base64 for uuid {uuid}"),
228 InvalidResponse,
229 None,
230 )
231 })?;
232 let secret = SecretVec::new(secret_slice.len(), |sv| {
234 sv.copy_from_slice(secret_slice);
235 });
236 l.push((key_and_id.key_id, secret));
237 Ok(l)
238 },
239 )
240 }
241
242 pub async fn get_keys(
243 &self,
244 key_size_bits: u32,
245 target_sae_id: &str,
246 additional_target_sae_ids: &[&str],
247 amount_of_keys: u32,
248 ) -> Result<Vec<(String, SecretVec<u8>)>, Error> {
249 let post_body = serde_json::to_string(&KeyRequest {
250 number: amount_of_keys,
251 size: Some(key_size_bits),
252 additional_target_sae_ids,
253 extension_mandatory: None,
254 })
255 .expect("Error serializing key request.");
256 let key_container = self
257 .send_request::<KeyContainer>(target_sae_id, "enc_keys", Some(&post_body))
258 .await?;
259 Self::key_container_to_vector(key_container)
260 }
261
262 pub async fn get_keys_by_ids(
263 &self,
264 target_sae_id: &str,
265 key_ids: &[&str],
266 ) -> Result<Vec<(String, SecretVec<u8>)>, Error> {
267 let post_body = serde_json::to_string(&KeysByIdsRequest {
268 key_ids: key_ids.iter().map(|key_id| KeyId { key_id }).collect(),
269 })
270 .expect("Error serializing keys by ids reqeust");
271 let key_container = self
272 .send_request::<KeyContainer>(target_sae_id, "dec_keys", Some(&post_body))
273 .await?;
274 Self::key_container_to_vector(key_container)
275 }
276 }
277}