Skip to main content

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;
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            // 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                .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                    // Cannot resize SecretVec, so create a new shorter one.
233                    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}