1use constant_time_eq::constant_time_eq;
2use dryoc::constants::{
3 CRYPTO_BOX_MACBYTES,
4 CRYPTO_BOX_NONCEBYTES,
5 CRYPTO_BOX_PUBLICKEYBYTES,
6 CRYPTO_BOX_SECRETKEYBYTES,
7 CRYPTO_SIGN_BYTES,
8 CRYPTO_SIGN_PUBLICKEYBYTES
9};
10use dryoc::classic::crypto_box;
11use dryoc::classic::crypto_sign;
12use dryoc::generichash::GenericHash;
13
14use crate::{error::NcryptfError as Error, VERSION_2_HEADER};
15
16pub struct Response {
18 pub secret_key: Vec<u8>,
19}
20
21impl Response {
22 pub fn decrypt(
24 &self,
25 response: Vec<u8>,
26 public_key: Option<Vec<u8>>,
27 nonce: Option<Vec<u8>>,
28 ) -> Result<String, Error> {
29 let n = match nonce {
31 Some(nonce) => nonce,
32 None => response.get(4..28).unwrap().to_vec(),
33 };
34
35 return self.decrypt_body(response, public_key, n.clone());
36 }
37
38 fn decrypt_body(
39 &self,
40 response: Vec<u8>,
41 public_key: Option<Vec<u8>>,
42 nonce: Vec<u8>,
43 ) -> Result<String, Error> {
44 if nonce.len() != (CRYPTO_BOX_NONCEBYTES as usize) {
45 return Err(Error::InvalidArgument(format!(
46 "Nonce should be {} bytes",
47 CRYPTO_BOX_NONCEBYTES
48 )));
49 }
50
51 let r = response.clone();
52 match Self::get_version(r) {
53 Ok(version) => match version {
54 2 => return self.decrypt_v2(response, nonce),
55 _ => return self.decrypt_v1(response, public_key.unwrap(), nonce),
56 },
57 Err(error) => return Err(error),
58 };
59 }
60
61 fn decrypt_v1(
62 &self,
63 response: Vec<u8>,
64 public_key: Vec<u8>,
65 nonce: Vec<u8>,
66 ) -> Result<String, Error> {
67 if public_key.len() != (CRYPTO_BOX_PUBLICKEYBYTES as usize) {
68 return Err(Error::InvalidArgument(format!(
69 "Public key should be {} bytes",
70 CRYPTO_BOX_NONCEBYTES
71 )));
72 }
73
74 if response.len() < (CRYPTO_BOX_MACBYTES as usize) {
75 return Err(Error::InvalidArgument(format!(
76 "Response is too short to be decrypted"
77 )));
78 }
79
80 let sk: [u8; CRYPTO_BOX_SECRETKEYBYTES as usize] =
81 self.secret_key.clone().try_into().unwrap();
82 let pk: [u8; CRYPTO_BOX_PUBLICKEYBYTES as usize] = public_key.try_into().unwrap();
83 let n: [u8; CRYPTO_BOX_NONCEBYTES as usize] = nonce.try_into().unwrap();
84
85 let mut message = vec![0u8; response.len() - CRYPTO_BOX_MACBYTES as usize];
86 crypto_box::crypto_box_open_easy(
87 &mut message,
88 &response,
89 &n,
90 &pk,
91 &sk,
92 ).map_err(|_| Error::DecryptError)?;
93
94 let string = String::from_utf8(message).map_err(|_| Error::DecryptError)?;
95 return Ok(string);
96 }
97
98 fn decrypt_v2(&self, response: Vec<u8>, nonce: Vec<u8>) -> Result<String, Error> {
99 let length = response.len();
100 if length < 236 {
101 return Err(Error::InvalidArgument(format!(
102 "Response length is too short for a v2 response."
103 )));
104 }
105
106 let payload = response.get(0..length - 64).unwrap().to_vec();
107 let checksum = response.get(length - 64..length).unwrap().to_vec();
108
109 let s: &[u8; CRYPTO_BOX_NONCEBYTES as usize] = &nonce.clone().try_into().unwrap();
110 let input = payload.clone();
111
112 let hash: [u8; 64] = GenericHash::hash(&input, Some(s))
113 .map_err(|_| Error::DecryptError)?;
114
115 if !constant_time_eq(&checksum, &hash) {
117 return Err(Error::DecryptError);
118 }
119
120 let public_key = Self::get_public_key_from_response(response.clone()).unwrap();
121 let payload_len = payload.len();
122 let signature = payload.get(payload_len - 64..payload_len).unwrap().to_vec();
123 let signature_public_key = Self::get_signing_public_key_from_response(response).unwrap();
124 let body = payload.get(60..payload_len - 96).unwrap().to_vec();
125
126 let decrypted = self.decrypt_v1(body, public_key, nonce.clone())?;
127
128 Self::is_signature_valid(decrypted.clone(), signature, signature_public_key)?;
129
130 return Ok(decrypted);
131 }
132
133 pub fn is_signature_valid(
135 response: String,
136 signature: Vec<u8>,
137 public_key: Vec<u8>,
138 ) -> Result<bool, Error> {
139 if signature.len() != (CRYPTO_SIGN_BYTES as usize) {
140 return Err(Error::InvalidArgument(format!(
141 "Signature must be {} bytes",
142 CRYPTO_SIGN_BYTES
143 )));
144 }
145
146 if public_key.len() != (CRYPTO_SIGN_PUBLICKEYBYTES as usize) {
147 return Err(Error::InvalidArgument(format!(
148 "Public key must be {} bytes",
149 CRYPTO_SIGN_PUBLICKEYBYTES
150 )));
151 }
152
153 let sig: [u8; CRYPTO_SIGN_BYTES as usize] = signature.try_into().unwrap();
154 let pk: [u8; CRYPTO_SIGN_PUBLICKEYBYTES as usize] = public_key.try_into().unwrap();
155
156 let result = crypto_sign::crypto_sign_verify_detached(
157 &sig,
158 response.as_bytes(),
159 &pk,
160 );
161
162 match result {
163 Ok(_) => return Ok(true),
164 Err(_) => return Ok(false),
165 };
166 }
167
168 pub fn get_public_key_from_response(response: Vec<u8>) -> Result<Vec<u8>, Error> {
170 match Self::get_version(response.clone()) {
171 Ok(version) => match version {
172 2 => {
173 let length = response.len();
174 if length < 236 {
175 return Err(Error::InvalidArgument(format!("Message is too short.")));
176 }
177
178 return Ok(response.get(28..60).unwrap().to_vec());
179 }
180 _ => {
181 return Err(Error::InvalidArgument(format!(
182 "The response provided is not suitable for public key extraction."
183 )));
184 }
185 },
186 _ => {
187 return Err(Error::InvalidArgument(format!(
188 "The response provided is not suitable for public key extraction."
189 )));
190 }
191 }
192 }
193
194 pub fn get_signing_public_key_from_response(response: Vec<u8>) -> Result<Vec<u8>, Error> {
196 match Self::get_version(response.clone()) {
197 Ok(version) => match version {
198 2 => {
199 let length = response.len();
200 if length < 236 {
201 return Err(Error::InvalidArgument(format!("Message is too short.")));
202 }
203
204 return Ok(response
205 .get(length - 160..(length - 160 + 32))
206 .unwrap()
207 .to_vec());
208 }
209 _ => {
210 return Err(Error::InvalidArgument(format!(
211 "The response provided is not suitable for public key extraction."
212 )));
213 }
214 },
215 _ => {
216 return Err(Error::InvalidArgument(format!(
217 "The response provided is not suitable for public key extraction."
218 )));
219 }
220 }
221 }
222
223 pub fn get_version(response: Vec<u8>) -> Result<i32, Error> {
225 if response.len() < 16 {
226 return Err(Error::InvalidArgument(format!(
227 "Message length is too short to determine version"
228 )));
229 }
230
231 match response.get(0..4) {
232 Some(header) => {
233 let s = hex::encode(header.to_vec()).to_string().to_uppercase();
234
235 if s.as_str().eq(VERSION_2_HEADER) {
236 return Ok(2);
237 }
238
239 return Ok(1);
240 }
241 _ => {
242 return Ok(1);
243 }
244 }
245 }
246
247 pub fn from(secret_key: Vec<u8>) -> Result<Self, Error> {
249 if secret_key.len() != (CRYPTO_BOX_SECRETKEYBYTES as usize) {
250 return Err(Error::InvalidArgument(format!(
251 "Secret key should be {} bytes",
252 CRYPTO_BOX_SECRETKEYBYTES
253 )));
254 }
255
256 return Ok(Response { secret_key });
257 }
258}