1use crate::errors::*;
2use crypto_box::{aead::AeadInPlace, Nonce, Tag};
3pub use crypto_box::{PublicKey, SalsaBox, SecretKey};
4use data_encoding::BASE64;
5
6pub const CRYPTO_TAG_SIZE: usize = 16;
7pub const CRYPTO_NONCE_SIZE: usize = 24;
8pub const CRYPTO_SECRET_KEY_SIZE: usize = 32;
9pub const CRYPTO_PUBLIC_KEY_SIZE: usize = 32;
10
11pub trait Rng {
12 fn getrandom(buf: &mut [u8]);
13}
14
15pub struct Random;
16
17#[cfg(not(target_os = "espidf"))]
18impl Rng for Random {
19 fn getrandom(buf: &mut [u8]) {
20 getrandom::getrandom(buf).unwrap();
21 }
22}
23
24pub fn encrypt<'a, R: Rng>(salsa: &SalsaBox, src: &[u8], dest: &'a mut [u8]) -> Result<&'a [u8]> {
25 let buffer_size = dest.len();
26 if buffer_size < src.len() + CRYPTO_NONCE_SIZE + CRYPTO_TAG_SIZE {
27 return Err(Error::BufferLimit);
28 }
29
30 let length = {
31 let (nonce, cursor) = dest.split_at_mut(CRYPTO_NONCE_SIZE);
32 let nonce = {
33 let mut buf = [0u8; CRYPTO_NONCE_SIZE];
34 R::getrandom(&mut buf);
35 nonce.copy_from_slice(&buf);
36 Nonce::from(buf)
37 };
38
39 let (buf, cursor) = cursor.split_at_mut(src.len());
40 buf.copy_from_slice(src);
41 let tag = salsa.encrypt_in_place_detached(&nonce, &[], buf)?;
42
43 let (buf, cursor) = cursor.split_at_mut(tag.len());
44 buf.copy_from_slice(&tag);
45
46 buffer_size - cursor.len()
47 };
48
49 Ok(&dest[..length])
50}
51
52pub fn decrypt<'a>(salsa: &SalsaBox, src: &[u8], dest: &'a mut [u8]) -> Result<&'a [u8]> {
53 if src.len() < CRYPTO_NONCE_SIZE + CRYPTO_TAG_SIZE {
54 return Err(Error::BufferLimit);
55 }
56
57 let dest = &mut dest[..src.len() - CRYPTO_NONCE_SIZE - CRYPTO_TAG_SIZE];
58
59 let (nonce, cursor) = src.split_at(CRYPTO_NONCE_SIZE);
60 let nonce = {
61 let mut buf = [0u8; CRYPTO_NONCE_SIZE];
62 buf.copy_from_slice(nonce);
63 Nonce::from(buf)
64 };
65
66 let (cursor, tag) = cursor.split_at(cursor.len() - CRYPTO_TAG_SIZE);
67 let tag = {
68 let mut buf = [0u8; CRYPTO_TAG_SIZE];
69 buf.copy_from_slice(tag);
70 Tag::from(buf)
71 };
72
73 dest.copy_from_slice(cursor);
74 salsa.decrypt_in_place_detached(&nonce, &[], dest, &tag)?;
75
76 Ok(dest)
77}
78
79pub fn generate_secret_key<R: Rng>() -> SecretKey {
80 let mut buf = [0u8; crypto_box::KEY_SIZE];
81 R::getrandom(&mut buf);
82 SecretKey::from_bytes(buf)
83}
84
85pub fn secret_key(bytes: &str) -> Result<SecretKey> {
86 let bytes = BASE64.decode(bytes.as_bytes())?;
87 if bytes.len() != CRYPTO_SECRET_KEY_SIZE {
88 return Err(Error::InvalidKeyLength(bytes.len()));
89 }
90
91 let mut buf = [0u8; CRYPTO_SECRET_KEY_SIZE];
92 buf.copy_from_slice(&bytes);
93
94 Ok(SecretKey::from(buf))
95}
96
97pub fn public_key(bytes: &str) -> Result<PublicKey> {
98 let bytes = BASE64.decode(bytes.as_bytes())?;
99 if bytes.len() != CRYPTO_PUBLIC_KEY_SIZE {
100 return Err(Error::InvalidKeyLength(bytes.len()));
101 }
102
103 let mut buf = [0u8; CRYPTO_PUBLIC_KEY_SIZE];
104 buf.copy_from_slice(&bytes);
105
106 Ok(PublicKey::from(buf))
107}
108
109pub fn test_sodium_crypto<R: Rng>() -> Result<()> {
110 let alice_secret_key = generate_secret_key::<R>();
117
118 let alice_public_key_bytes = *alice_secret_key.public_key().as_bytes();
120
121 let bob_public_key = PublicKey::from([
123 0xe8, 0x98, 0xc, 0x86, 0xe0, 0x32, 0xf1, 0xeb, 0x29, 0x75, 0x5, 0x2e, 0x8d, 0x65, 0xbd,
124 0xdd, 0x15, 0xc3, 0xb5, 0x96, 0x41, 0x17, 0x4e, 0xc9, 0x67, 0x8a, 0x53, 0x78, 0x9d, 0x92,
125 0xc7, 0x54,
126 ]);
127
128 let alice_box = SalsaBox::new(&bob_public_key, &alice_secret_key);
131
132 let plaintext = b"Top secret message we're encrypting";
134
135 let mut ciphertext = [0u8; 4096];
137 let ciphertext = encrypt::<R>(&alice_box, plaintext, &mut ciphertext)?;
138
139 let bob_secret_key = SecretKey::from([
146 0xb5, 0x81, 0xfb, 0x5a, 0xe1, 0x82, 0xa1, 0x6f, 0x60, 0x3f, 0x39, 0x27, 0xd, 0x4e, 0x3b,
147 0x95, 0xbc, 0x0, 0x83, 0x10, 0xb7, 0x27, 0xa1, 0x1d, 0xd4, 0xe7, 0x84, 0xa0, 0x4, 0x4d,
148 0x46, 0x1b,
149 ]);
150
151 let alice_public_key = PublicKey::from(alice_public_key_bytes);
153
154 let bob_box = SalsaBox::new(&alice_public_key, &bob_secret_key);
157
158 let mut decrypted_plaintext = [0u8; 4096];
160 let decrypted_plaintext = decrypt(&bob_box, ciphertext, &mut decrypted_plaintext)?;
161
162 assert_eq!(&plaintext[..], decrypted_plaintext);
163
164 Ok(())
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_encrypt() -> Result<()> {
173 let alice_secret_key = generate_secret_key::<Random>();
174
175 let bob_public_key = PublicKey::from([
177 0xe8, 0x98, 0xc, 0x86, 0xe0, 0x32, 0xf1, 0xeb, 0x29, 0x75, 0x5, 0x2e, 0x8d, 0x65, 0xbd,
178 0xdd, 0x15, 0xc3, 0xb5, 0x96, 0x41, 0x17, 0x4e, 0xc9, 0x67, 0x8a, 0x53, 0x78, 0x9d,
179 0x92, 0xc7, 0x54,
180 ]);
181
182 let alice_box = SalsaBox::new(&bob_public_key, &alice_secret_key);
185
186 let mut dest = [0u8; 4096];
187 let ciphertext = encrypt::<Random>(&alice_box, b"hello world", &mut dest)?;
188 assert_eq!(ciphertext.len(), 51);
189
190 Ok(())
191 }
192
193 #[test]
194 fn run_test_sodium_crypto() -> Result<()> {
195 test_sodium_crypto::<Random>()?;
196 Ok(())
197 }
198}