ncryptf/
request.rs

1use libsodium_sys::{
2    crypto_box_MACBYTES as CRYPTO_BOX_MACBYTES, crypto_box_NONCEBYTES as CRYPTO_BOX_NONCEBYTES,
3    crypto_box_PUBLICKEYBYTES as CRYPTO_BOX_PUBLICKEYBYTES,
4    crypto_box_SECRETKEYBYTES as CRYPTO_BOX_SECRETKEYBYTES, crypto_box_easy, crypto_generichash,
5    crypto_scalarmult_base, crypto_sign_BYTES as CRYPTO_SIGN_BYTES,
6    crypto_sign_PUBLICKEYBYTES as CRYPTO_SIGN_PUBLICKEYBYTES,
7    crypto_sign_SECRETKEYBYTES as CRYPTO_SIGN_SECRETKEYBYTES, crypto_sign_detached,
8    crypto_sign_ed25519_sk_to_pk,
9};
10
11use crate::{error::NcryptfError as Error, util::randombytes_buf, VERSION_2_HEADER};
12
13/// A request that emits a encrypted string for submission to the server.
14pub struct Request {
15    secret_key: Vec<u8>,
16    signature_secret_key: Vec<u8>,
17    nonce: Option<Vec<u8>>,
18    message: Option<Vec<u8>>,
19}
20
21impl Request {
22    /// Encrypts a message with a given public key
23    pub fn encrypt(&mut self, data: String, public_key: Vec<u8>) -> Result<Vec<u8>, Error> {
24        match self.encrypt_with_nonce(data, public_key, None, Some(2)) {
25            Ok(result) => {
26                self.message = Some(result.clone());
27                return Ok(result);
28            }
29            Err(error) => return Err(error),
30        };
31    }
32
33    /// Returns the message, if it has been set
34    pub fn get_message(&self) -> Option<Vec<u8>> {
35        return self.message.clone();
36    }
37
38    /// Encrypts a message with a given public key, none, and version identifier
39    pub fn encrypt_with_nonce(
40        &mut self,
41        data: String,
42        public_key: Vec<u8>,
43        nonce: Option<Vec<u8>>,
44        version: Option<i8>,
45    ) -> Result<Vec<u8>, Error> {
46        let n = match nonce {
47            Some(n) => n,
48            None => randombytes_buf(CRYPTO_BOX_NONCEBYTES as usize),
49        };
50
51        self.nonce = Some(n.clone());
52
53        if public_key.len() != (CRYPTO_BOX_PUBLICKEYBYTES as usize) {
54            return Err(Error::InvalidArgument(format!(
55                "Public key should be {} bytes",
56                CRYPTO_BOX_PUBLICKEYBYTES
57            )));
58        }
59
60        if n.clone().len() != (CRYPTO_BOX_NONCEBYTES as usize) {
61            return Err(Error::InvalidArgument(format!(
62                "Nonce should be {} bytes",
63                CRYPTO_BOX_NONCEBYTES
64            )));
65        }
66
67        match version {
68            Some(2) => {
69                // Version 2 header is a fixed value
70                let h = VERSION_2_HEADER;
71                let header = hex::decode(h.to_string()).unwrap();
72                let mut body = match self.encrypt_body(data.clone(), public_key, n.clone()) {
73                    Ok(body) => body,
74                    Err(error) => return Err(error),
75                };
76
77                // Extract the public key from the secret key
78                let mut ipk: [u8; CRYPTO_BOX_PUBLICKEYBYTES as usize] =
79                    vec![0; CRYPTO_BOX_PUBLICKEYBYTES as usize]
80                        .try_into()
81                        .unwrap();
82                let csk: [u8; CRYPTO_BOX_SECRETKEYBYTES as usize] =
83                    self.secret_key.clone().try_into().unwrap();
84                let _result = unsafe { crypto_scalarmult_base(ipk.as_mut_ptr(), csk.as_ptr()) };
85
86                if _result != 0 {
87                    return Err(Error::EncryptError);
88                }
89
90                // Convert the signature secret key, into a public key
91                let mut isk: [u8; CRYPTO_SIGN_PUBLICKEYBYTES as usize] =
92                    vec![0; CRYPTO_SIGN_PUBLICKEYBYTES as usize]
93                        .try_into()
94                        .unwrap();
95                let ssk: [u8; CRYPTO_SIGN_SECRETKEYBYTES as usize] =
96                    self.signature_secret_key.clone().try_into().unwrap();
97                let _result =
98                    unsafe { crypto_sign_ed25519_sk_to_pk(isk.as_mut_ptr(), ssk.as_ptr()) };
99                if _result != 0 {
100                    return Err(Error::EncryptError);
101                }
102
103                // Calculate the signature
104                let mut signature = match self.sign(data.clone()) {
105                    Ok(signature) => signature,
106                    Err(error) => return Err(error),
107                };
108
109                let mut payload: Vec<u8> = Vec::<u8>::new();
110                payload.append(&mut header.clone());
111                payload.append(&mut n.clone());
112                payload.append(&mut ipk.to_vec());
113                payload.append(&mut body);
114                payload.append(&mut isk.to_vec());
115                payload.append(&mut signature);
116
117                let s: &[u8; CRYPTO_BOX_NONCEBYTES as usize] = &n.clone().try_into().unwrap();
118                let input = payload.clone();
119                let mut hash: [u8; 64] = vec![0; 64].try_into().unwrap();
120
121                let _result = unsafe {
122                    crypto_generichash(
123                        hash.as_mut_ptr(),
124                        64,
125                        input.as_ptr(),
126                        input.len() as u64,
127                        s.as_ptr(),
128                        CRYPTO_BOX_NONCEBYTES as usize,
129                    )
130                };
131
132                if _result != 0 {
133                    return Err(Error::EncryptError);
134                }
135
136                payload.append(&mut hash.to_vec());
137                return Ok(payload);
138            }
139            _ => {
140                return self.encrypt_body(data.clone(), public_key, n.clone());
141            }
142        }
143    }
144
145    /// Internal encryption method
146    fn encrypt_body(
147        &self,
148        data: String,
149        public_key: Vec<u8>,
150        nonce: Vec<u8>,
151    ) -> Result<Vec<u8>, Error> {
152        let message = data.into_bytes();
153        let len = message.len();
154
155        let mut ciphertext = Box::new(vec![0u8; len + (CRYPTO_BOX_MACBYTES as usize)]);
156        let sk: [u8; CRYPTO_BOX_SECRETKEYBYTES as usize] =
157            self.secret_key.clone().try_into().unwrap();
158        let pk: [u8; CRYPTO_BOX_PUBLICKEYBYTES as usize] = public_key.clone().try_into().unwrap();
159        let n: [u8; CRYPTO_BOX_NONCEBYTES as usize] = nonce.clone().try_into().unwrap();
160
161        let result: i32 = unsafe {
162            crypto_box_easy(
163                ciphertext.as_mut_ptr(),
164                message.as_ptr(),
165                message.len().try_into().unwrap(),
166                n.as_ptr(),
167                pk.as_ptr(),
168                sk.as_ptr(),
169            )
170        };
171
172        match result {
173            0 => {
174                return Ok(ciphertext.to_vec());
175            }
176            _ => {
177                return Err(Error::DecryptError);
178            }
179        }
180    }
181
182    /// Returns the nonce
183    pub fn get_nonce(&self) -> Option<Vec<u8>> {
184        return self.nonce.clone();
185    }
186
187    /// Signs the given data, then returns a detached signature
188    pub fn sign(&self, data: String) -> Result<Vec<u8>, Error> {
189        let mut signature = [0u8; (CRYPTO_SIGN_BYTES as usize)];
190        let key: [u8; CRYPTO_SIGN_SECRETKEYBYTES as usize] =
191            self.signature_secret_key.clone().try_into().unwrap();
192
193        let mut signature_size = signature.len() as u64;
194        let _result = unsafe {
195            crypto_sign_detached(
196                signature.as_mut_ptr(),
197                &mut signature_size,
198                data.as_ptr(),
199                data.len() as u64,
200                key.as_ptr(),
201            )
202        };
203
204        if _result != 0 {
205            return Err(Error::EncryptError);
206        }
207
208        return Ok(signature.to_vec());
209    }
210
211    /// Creates a new request from a given secret key and signature secret key
212    pub fn from(secret_key: Vec<u8>, signature_secret_key: Vec<u8>) -> Result<Self, Error> {
213        if secret_key.len() != (CRYPTO_BOX_SECRETKEYBYTES as usize) {
214            return Err(Error::InvalidArgument(format!(
215                "Secret key should be {} bytes",
216                CRYPTO_BOX_SECRETKEYBYTES
217            )));
218        }
219        if signature_secret_key.len() != (CRYPTO_SIGN_SECRETKEYBYTES as usize) {
220            return Err(Error::InvalidArgument(format!(
221                "Signature key should be {} bytes",
222                CRYPTO_SIGN_SECRETKEYBYTES
223            )));
224        }
225
226        return Ok(Request {
227            secret_key,
228            signature_secret_key,
229            nonce: None,
230            message: None,
231        });
232    }
233}