ncryptf/
request.rs

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