ncryptf/client/
response.rs

1use base64::{engine::general_purpose, Engine as _};
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5pub enum ResponseError {
6    #[error("decrypting response to plain text failed.")]
7    DecryptingResponseFailed,
8    #[error("the remote server is not implemented correctly.")]
9    ResponseImplementationError,
10    #[error("the server did not return a response.")]
11    ResponseMissing,
12}
13
14/// Client feature Response provides a simple API for handling encrypted responses
15///
16/// `ncryptf::Client::Response::response` contains the common elements you'd receive from a response, including the http status as a `reqwest::StatusCode`,
17/// the response headers as a `reqwest::header::HeaderMap`, and the raw body response as a `Option<String>`.
18///
19/// If you have a defined struct, you can use the `into` method to deserialize the object into a struct. Handle as you would a serde_json::from_str()
20///
21/// ```rust
22/// let structure = response.into::<MyStructure>();
23/// ```
24#[derive(Debug, Clone)]
25pub struct Response {
26    pub status: reqwest::StatusCode,
27    pub headers: reqwest::header::HeaderMap,
28    pub body: Option<String>,
29    pub pk: Option<Vec<u8>>,
30    pub sk: Option<Vec<u8>>,
31}
32
33impl Response {
34    /// Constructs a new response
35    pub async fn new(response: reqwest::Response, sk: Vec<u8>) -> Result<Self, ResponseError> {
36        let mut r = Self {
37            status: response.status(),
38            headers: response.headers().to_owned(),
39            body: None,
40            pk: None,
41            sk: None,
42        };
43
44        match response.text().await {
45            Ok(body) => {
46                match r.headers.get("Content-Type") {
47                    Some(h) => match h.to_str() {
48                        // If this is an NCRYPTF response, we need to decrypt it
49                        Ok(crate::rocket::NCRYPTF_CONTENT_TYPE) => {
50                            // If the body is empty, don't attempt to decrypt the response
51                            if body.is_empty() {
52                                r.body = None;
53                                return Ok(r);
54                            }
55                            let body_bytes = general_purpose::STANDARD.decode(body).unwrap();
56                            let ncryptf_response = crate::Response::from(sk.clone()).unwrap();
57                            match ncryptf_response.decrypt(body_bytes.clone(), None, None) {
58                                Ok(message) => {
59                                    // If we've already decrypted the response then these will succeed
60                                    let pk = crate::Response::get_public_key_from_response(
61                                        body_bytes.clone(),
62                                    )
63                                    .unwrap();
64                                    let sk = crate::Response::get_signing_public_key_from_response(
65                                        body_bytes.clone(),
66                                    )
67                                    .unwrap();
68                                    r.body = Some(message);
69                                    r.pk = Some(pk);
70                                    r.sk = Some(sk);
71                                    return Ok(r);
72                                }
73                                Err(_error) => return Err(ResponseError::DecryptingResponseFailed),
74                            }
75                        }
76                        _ => {
77                            if body.is_empty() {
78                                r.body = None
79                            } else {
80                                r.body = Some(body)
81                            }
82
83                            return Ok(r);
84                        }
85                    },
86                    // If we don't have a content type on the return, then the server isn't implemented
87                    // correctly. We cannot proceed.
88                    _ => return Err(ResponseError::ResponseImplementationError),
89                }
90            }
91            // There's no response
92            Err(_error) => return Err(ResponseError::ResponseMissing),
93        }
94    }
95
96    /// Converts the response into an actual struct object
97    pub fn into<T: for<'a> serde::Deserialize<'a>>(self) -> Result<T, serde_json::Error> {
98        return serde_json::from_str::<T>(&self.body.unwrap());
99    }
100}