1#[cfg(feature = "serde")]
57use serde::{Deserialize, Serialize};
58use subtle::ConstantTimeEq;
59use zeroize::Zeroize;
60
61use crate::constants::{
62 CRYPTO_SECRETBOX_KEYBYTES, CRYPTO_SECRETBOX_MACBYTES, CRYPTO_SECRETBOX_NONCEBYTES,
63};
64use crate::error::Error;
65pub use crate::types::*;
66
67pub type Key = StackByteArray<CRYPTO_SECRETBOX_KEYBYTES>;
69pub type Nonce = StackByteArray<CRYPTO_SECRETBOX_NONCEBYTES>;
71pub type Mac = StackByteArray<CRYPTO_SECRETBOX_MACBYTES>;
73
74#[cfg(any(feature = "nightly", all(doc, not(doctest))))]
75#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))]
76pub mod protected {
77 use super::*;
111 pub use crate::protected::*;
112
113 pub type Key = HeapByteArray<CRYPTO_SECRETBOX_KEYBYTES>;
116 pub type Nonce = HeapByteArray<CRYPTO_SECRETBOX_NONCEBYTES>;
119 pub type Mac = HeapByteArray<CRYPTO_SECRETBOX_MACBYTES>;
122
123 pub type LockedBox = DryocSecretBox<Locked<Mac>, LockedBytes>;
125}
126
127#[cfg_attr(
128 feature = "serde",
129 derive(Zeroize, Clone, Debug, Serialize, Deserialize)
130)]
131#[cfg_attr(not(feature = "serde"), derive(Zeroize, Clone, Debug))]
132pub struct DryocSecretBox<
137 Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize,
138 Data: Bytes + Zeroize,
139> {
140 tag: Mac,
141 data: Data,
142}
143
144pub type VecBox = DryocSecretBox<Mac, Vec<u8>>;
146
147impl<
148 Mac: NewByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize,
149 Data: NewBytes + ResizableBytes + Zeroize,
150> DryocSecretBox<Mac, Data>
151{
152 pub fn encrypt<
155 Message: Bytes + ?Sized,
156 Nonce: ByteArray<CRYPTO_SECRETBOX_NONCEBYTES>,
157 SecretKey: ByteArray<CRYPTO_SECRETBOX_KEYBYTES>,
158 >(
159 message: &Message,
160 nonce: &Nonce,
161 secret_key: &SecretKey,
162 ) -> Self {
163 use crate::classic::crypto_secretbox::crypto_secretbox_detached;
164
165 let mut new = Self {
166 tag: Mac::new_byte_array(),
167 data: Data::new_bytes(),
168 };
169 new.data.resize(message.len(), 0);
170
171 crypto_secretbox_detached(
172 new.data.as_mut_slice(),
173 new.tag.as_mut_array(),
174 message.as_slice(),
175 nonce.as_array(),
176 secret_key.as_array(),
177 );
178
179 new
180 }
181}
182
183impl<
184 'a,
185 Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
186 Data: Bytes + From<&'a [u8]> + Zeroize,
187> DryocSecretBox<Mac, Data>
188{
189 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
194 if bytes.len() < CRYPTO_SECRETBOX_MACBYTES {
195 Err(dryoc_error!(format!(
196 "bytes of len {} less than expected minimum of {}",
197 bytes.len(),
198 CRYPTO_SECRETBOX_MACBYTES
199 )))
200 } else {
201 let (tag, data) = bytes.split_at(CRYPTO_SECRETBOX_MACBYTES);
202 Ok(Self {
203 tag: Mac::try_from(tag).map_err(|_e| dryoc_error!("invalid tag"))?,
204 data: Data::from(data),
205 })
206 }
207 }
208}
209
210impl<Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize, Data: Bytes + Zeroize>
211 DryocSecretBox<Mac, Data>
212{
213 pub fn from_parts(tag: Mac, data: Data) -> Self {
215 Self { tag, data }
216 }
217
218 pub fn to_vec(&self) -> Vec<u8> {
220 self.to_bytes()
221 }
222
223 pub fn into_parts(self) -> (Mac, Data) {
225 (self.tag, self.data)
226 }
227}
228
229impl<Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize, Data: Bytes + Zeroize>
230 DryocSecretBox<Mac, Data>
231{
232 pub fn decrypt<
235 Output: ResizableBytes + NewBytes,
236 Nonce: ByteArray<CRYPTO_SECRETBOX_NONCEBYTES>,
237 SecretKey: ByteArray<CRYPTO_SECRETBOX_KEYBYTES>,
238 >(
239 &self,
240 nonce: &Nonce,
241 secret_key: &SecretKey,
242 ) -> Result<Output, Error> {
243 use crate::classic::crypto_secretbox::crypto_secretbox_open_detached;
244
245 let mut message = Output::new_bytes();
246 message.resize(self.data.as_slice().len(), 0);
247
248 crypto_secretbox_open_detached(
249 message.as_mut_slice(),
250 self.tag.as_array(),
251 self.data.as_slice(),
252 nonce.as_array(),
253 secret_key.as_array(),
254 )?;
255
256 Ok(message)
257 }
258
259 pub fn to_bytes<Bytes: NewBytes + ResizableBytes>(&self) -> Bytes {
261 let mut data = Bytes::new_bytes();
262 data.resize(self.tag.len() + self.data.len(), 0);
263 let s = data.as_mut_slice();
264 s[..CRYPTO_SECRETBOX_MACBYTES].copy_from_slice(self.tag.as_slice());
265 s[CRYPTO_SECRETBOX_MACBYTES..].copy_from_slice(self.data.as_slice());
266 data
267 }
268}
269
270impl DryocSecretBox<Mac, Vec<u8>> {
271 pub fn encrypt_to_vecbox<
274 Message: Bytes + ?Sized,
275 Nonce: ByteArray<CRYPTO_SECRETBOX_NONCEBYTES>,
276 SecretKey: ByteArray<CRYPTO_SECRETBOX_KEYBYTES>,
277 >(
278 message: &Message,
279 nonce: &Nonce,
280 secret_key: &SecretKey,
281 ) -> Self {
282 Self::encrypt(message, nonce, secret_key)
283 }
284
285 pub fn decrypt_to_vec<
288 Nonce: ByteArray<CRYPTO_SECRETBOX_NONCEBYTES>,
289 SecretKey: ByteArray<CRYPTO_SECRETBOX_KEYBYTES>,
290 >(
291 &self,
292 nonce: &Nonce,
293 secret_key: &SecretKey,
294 ) -> Result<Vec<u8>, Error> {
295 self.decrypt(nonce, secret_key)
296 }
297
298 pub fn into_vec(mut self) -> Vec<u8> {
300 self.data
301 .resize(self.data.len() + CRYPTO_SECRETBOX_MACBYTES, 0);
302 self.data.rotate_right(CRYPTO_SECRETBOX_MACBYTES);
303 self.data[0..CRYPTO_SECRETBOX_MACBYTES].copy_from_slice(self.tag.as_array());
304 self.data
305 }
306}
307
308impl<
309 'a,
310 Mac: NewByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize,
311 Data: NewBytes + ResizableBytes + From<&'a [u8]> + Zeroize,
312> DryocSecretBox<Mac, Data>
313{
314 pub fn with_data(input: &'a [u8]) -> Self {
316 Self {
317 tag: Mac::new_byte_array(),
318 data: input.into(),
319 }
320 }
321}
322
323impl<
324 'a,
325 Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize,
326 Data: Bytes + ResizableBytes + From<&'a [u8]> + Zeroize,
327> DryocSecretBox<Mac, Data>
328{
329 pub fn with_data_and_mac(tag: Mac, input: &'a [u8]) -> Self {
332 Self {
333 tag,
334 data: input.into(),
335 }
336 }
337}
338
339impl<Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize, Data: Bytes + Zeroize>
340 PartialEq<DryocSecretBox<Mac, Data>> for DryocSecretBox<Mac, Data>
341{
342 fn eq(&self, other: &Self) -> bool {
343 self.tag.as_slice().ct_eq(other.tag.as_slice()).unwrap_u8() == 1
344 && self
345 .data
346 .as_slice()
347 .ct_eq(other.data.as_slice())
348 .unwrap_u8()
349 == 1
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[test]
358 fn test_dryocbox() {
359 for i in 0..20 {
360 use base64::Engine as _;
361 use base64::engine::general_purpose;
362 use sodiumoxide::crypto::secretbox;
363 use sodiumoxide::crypto::secretbox::{Key as SOKey, Nonce as SONonce};
364
365 use crate::dryocsecretbox::*;
366
367 let secret_key = Key::gen();
368 let nonce = Nonce::gen();
369 let words = vec!["hello1".to_string(); i];
370 let message = words.join(" :D ").into_bytes();
371 let message_copy = message.clone();
372 let dryocsecretbox: VecBox = DryocSecretBox::encrypt(&message, &nonce, &secret_key);
373
374 let ciphertext = dryocsecretbox.clone().into_vec();
375 assert_eq!(&ciphertext, &dryocsecretbox.to_vec());
376
377 let ciphertext_copy = ciphertext.clone();
378
379 let so_ciphertext = secretbox::seal(
380 &message_copy,
381 &SONonce::from_slice(&nonce).unwrap(),
382 &SOKey::from_slice(&secret_key).unwrap(),
383 );
384 assert_eq!(
385 general_purpose::STANDARD.encode(&ciphertext),
386 general_purpose::STANDARD.encode(&so_ciphertext)
387 );
388
389 let so_decrypted = secretbox::open(
390 &ciphertext_copy,
391 &SONonce::from_slice(&nonce).unwrap(),
392 &SOKey::from_slice(&secret_key).unwrap(),
393 )
394 .expect("decrypt failed");
395
396 let m = DryocSecretBox::decrypt::<Vec<u8>, Nonce, Key>(
397 &dryocsecretbox,
398 &nonce,
399 &secret_key,
400 )
401 .expect("decrypt failed");
402 assert_eq!(m, message_copy);
403 assert_eq!(m, so_decrypted);
404 }
405 }
406
407 #[test]
408 fn test_dryocbox_vec() {
409 for i in 0..20 {
410 use base64::Engine as _;
411 use base64::engine::general_purpose;
412 use sodiumoxide::crypto::secretbox;
413 use sodiumoxide::crypto::secretbox::{Key as SOKey, Nonce as SONonce};
414
415 use crate::dryocsecretbox::*;
416
417 let secret_key = Key::gen();
418 let nonce = Nonce::gen();
419 let words = vec!["hello1".to_string(); i];
420 let message = words.join(" :D ").into_bytes();
421 let message_copy = message.clone();
422 let dryocsecretbox = DryocSecretBox::encrypt_to_vecbox(&message, &nonce, &secret_key);
423
424 let ciphertext = dryocsecretbox.clone().into_vec();
425 assert_eq!(&ciphertext, &dryocsecretbox.to_vec());
426
427 let ciphertext_copy = ciphertext.clone();
428
429 let so_ciphertext = secretbox::seal(
430 &message_copy,
431 &SONonce::from_slice(&nonce).unwrap(),
432 &SOKey::from_slice(&secret_key).unwrap(),
433 );
434 assert_eq!(
435 general_purpose::STANDARD.encode(&ciphertext),
436 general_purpose::STANDARD.encode(&so_ciphertext)
437 );
438
439 let so_decrypted = secretbox::open(
440 &ciphertext_copy,
441 &SONonce::from_slice(&nonce).unwrap(),
442 &SOKey::from_slice(&secret_key).unwrap(),
443 )
444 .expect("decrypt failed");
445
446 let m = dryocsecretbox
447 .decrypt_to_vec(&nonce, &secret_key)
448 .expect("decrypt failed");
449 assert_eq!(m, message_copy);
450 assert_eq!(m, so_decrypted);
451 }
452 }
453
454 #[test]
455 fn test_copy() {
456 for _ in 0..20 {
457 use std::convert::TryFrom;
458
459 use crate::rng::*;
460
461 let mut data1: Vec<u8> = vec![0u8; 1024];
462 copy_randombytes(data1.as_mut_slice());
463 let data1_copy = data1.clone();
464
465 let dryocsecretbox: VecBox = DryocSecretBox::from_bytes(&data1).expect("ok");
466 assert_eq!(
467 dryocsecretbox.data.as_slice(),
468 &data1_copy[CRYPTO_SECRETBOX_MACBYTES..]
469 );
470 assert_eq!(
471 dryocsecretbox.tag.as_slice(),
472 &data1_copy[..CRYPTO_SECRETBOX_MACBYTES]
473 );
474
475 let data1 = data1_copy.clone();
476 let dryocsecretbox: VecBox = DryocSecretBox::with_data(&data1);
477 assert_eq!(&dryocsecretbox.data, &data1_copy);
478
479 let data1 = data1_copy.clone();
480 let (tag, data) = data1.split_at(CRYPTO_SECRETBOX_MACBYTES);
481 let dryocsecretbox: VecBox =
482 DryocSecretBox::with_data_and_mac(Mac::try_from(tag).expect("mac"), data);
483 assert_eq!(
484 dryocsecretbox.data.as_slice(),
485 &data1_copy[CRYPTO_SECRETBOX_MACBYTES..]
486 );
487 assert_eq!(
488 dryocsecretbox.tag.as_array(),
489 &data1_copy[..CRYPTO_SECRETBOX_MACBYTES]
490 );
491 }
492 }
493
494 #[cfg(any(feature = "nightly", all(doc, not(doctest))))]
495 #[cfg(feature = "nightly")]
496 #[test]
497 fn test_dryocbox_locked() {
498 for i in 0..20 {
499 use base64::Engine as _;
500 use base64::engine::general_purpose;
501 use sodiumoxide::crypto::secretbox;
502 use sodiumoxide::crypto::secretbox::{Key as SOKey, Nonce as SONonce};
503
504 use crate::dryocsecretbox::*;
505 use crate::protected::*;
506
507 let secret_key = protected::Key::gen_locked().expect("gen failed");
508 let nonce = protected::Nonce::gen_locked().expect("gen failed");
509 let words = vec!["hello1".to_string(); i];
510 let message = words.join(" :D ");
511 let message_copy = message.clone();
512 let dryocsecretbox: protected::LockedBox =
513 DryocSecretBox::encrypt(message.as_bytes(), &nonce, &secret_key);
514
515 let ciphertext = dryocsecretbox.to_vec();
516
517 let ciphertext_copy = ciphertext.clone();
518
519 let so_ciphertext = secretbox::seal(
520 message_copy.as_bytes(),
521 &SONonce::from_slice(nonce.as_slice()).unwrap(),
522 &SOKey::from_slice(secret_key.as_slice()).unwrap(),
523 );
524 assert_eq!(
525 general_purpose::STANDARD.encode(&ciphertext),
526 general_purpose::STANDARD.encode(&so_ciphertext)
527 );
528
529 let so_decrypted = secretbox::open(
530 &ciphertext_copy,
531 &SONonce::from_slice(nonce.as_slice()).unwrap(),
532 &SOKey::from_slice(secret_key.as_slice()).unwrap(),
533 )
534 .expect("decrypt failed");
535
536 let m: LockedBytes = dryocsecretbox
537 .decrypt(&nonce, &secret_key)
538 .expect("decrypt failed");
539
540 assert_eq!(m.as_slice(), message_copy.as_bytes());
541 assert_eq!(m.as_slice(), so_decrypted);
542 }
543 }
544}