etsi014_client/
lib.rs

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            // Can not set host and port without parsing something first
52            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                // Might fail if host invalid
67                .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                    // Cannot resize SecretVec, so create a new shorter one.
234                    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}