1#[cfg(feature = "serde")]
70use serde::{Deserialize, Serialize};
71use subtle::ConstantTimeEq;
72use zeroize::Zeroize;
73
74use crate::constants::{
75 CRYPTO_SECRETBOX_KEYBYTES, CRYPTO_SECRETBOX_MACBYTES, CRYPTO_SECRETBOX_NONCEBYTES,
76};
77use crate::error::Error;
78pub use crate::types::*;
79
80pub type Key = StackByteArray<CRYPTO_SECRETBOX_KEYBYTES>;
82pub type Nonce = StackByteArray<CRYPTO_SECRETBOX_NONCEBYTES>;
84pub type Mac = StackByteArray<CRYPTO_SECRETBOX_MACBYTES>;
86
87#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
88#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
89pub mod protected {
90 use super::*;
122 pub use crate::protected::*;
123
124 pub type Key = HeapByteArray<CRYPTO_SECRETBOX_KEYBYTES>;
127 pub type Nonce = HeapByteArray<CRYPTO_SECRETBOX_NONCEBYTES>;
130 pub type Mac = HeapByteArray<CRYPTO_SECRETBOX_MACBYTES>;
133
134 pub type LockedBox = DryocSecretBox<Locked<Mac>, LockedBytes>;
136}
137
138#[cfg_attr(
139 feature = "serde",
140 derive(Zeroize, Clone, Debug, Serialize, Deserialize)
141)]
142#[cfg_attr(not(feature = "serde"), derive(Zeroize, Clone, Debug))]
143pub struct DryocSecretBox<
148 Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize,
149 Data: Bytes + Zeroize,
150> {
151 tag: Mac,
152 data: Data,
153}
154
155pub type VecBox = DryocSecretBox<Mac, Vec<u8>>;
157
158#[cfg(feature = "wincode")]
159unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C> for VecBox {
163 type Src = Self;
164
165 fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
166 Ok(
167 <[u8; CRYPTO_SECRETBOX_MACBYTES] as wincode::SchemaWrite<C>>::size_of(
168 src.tag.as_array(),
169 )? + <Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?,
170 )
171 }
172
173 fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
174 <[u8; CRYPTO_SECRETBOX_MACBYTES] as wincode::SchemaWrite<C>>::write(
175 writer.by_ref(),
176 src.tag.as_array(),
177 )?;
178 <Vec<u8> as wincode::SchemaWrite<C>>::write(writer, &src.data)
179 }
180}
181
182#[cfg(feature = "wincode")]
183unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C> for VecBox {
186 type Dst = Self;
187
188 fn read(
189 mut reader: impl wincode::io::Reader<'de>,
190 dst: &mut std::mem::MaybeUninit<Self::Dst>,
191 ) -> wincode::ReadResult<()> {
192 let tag =
193 <[u8; CRYPTO_SECRETBOX_MACBYTES] as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
194 let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader)?;
195 dst.write(Self {
196 tag: tag.into(),
197 data,
198 });
199 Ok(())
200 }
201}
202
203impl<
204 Mac: NewByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize,
205 Data: NewBytes + ResizableBytes + Zeroize,
206> DryocSecretBox<Mac, Data>
207{
208 pub fn encrypt<
218 Message: Bytes + ?Sized,
219 Nonce: ByteArray<CRYPTO_SECRETBOX_NONCEBYTES>,
220 SecretKey: ByteArray<CRYPTO_SECRETBOX_KEYBYTES>,
221 >(
222 message: &Message,
223 nonce: &Nonce,
224 secret_key: &SecretKey,
225 ) -> Self {
226 use crate::classic::crypto_secretbox::crypto_secretbox_detached;
227
228 let mut new = Self {
229 tag: Mac::new_byte_array(),
230 data: Data::new_bytes(),
231 };
232 new.data.resize(message.len(), 0);
233
234 crypto_secretbox_detached(
235 new.data.as_mut_slice(),
236 new.tag.as_mut_array(),
237 message.as_slice(),
238 nonce.as_array(),
239 secret_key.as_array(),
240 )
241 .expect("allocated ciphertext length matches message length");
242
243 new
244 }
245}
246
247impl<
248 'a,
249 Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
250 Data: Bytes + From<&'a [u8]> + Zeroize,
251> DryocSecretBox<Mac, Data>
252{
253 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
263 if bytes.len() < CRYPTO_SECRETBOX_MACBYTES {
264 Err(
265 length_error!(crate::ErrorContext::SecretBox, bytes.len(), min CRYPTO_SECRETBOX_MACBYTES),
266 )
267 } else {
268 let (tag, data) = bytes.split_at(CRYPTO_SECRETBOX_MACBYTES);
269 Ok(Self {
270 tag: Mac::try_from(tag)
271 .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
272 data: Data::from(data),
273 })
274 }
275 }
276}
277
278impl<Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize, Data: Bytes + Zeroize>
279 DryocSecretBox<Mac, Data>
280{
281 pub fn from_parts(tag: Mac, data: Data) -> Self {
283 Self { tag, data }
284 }
285
286 pub fn to_vec(&self) -> Vec<u8> {
288 self.to_bytes()
289 }
290
291 pub fn into_parts(self) -> (Mac, Data) {
293 (self.tag, self.data)
294 }
295}
296
297impl<Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize, Data: Bytes + Zeroize>
298 DryocSecretBox<Mac, Data>
299{
300 pub fn decrypt<
308 Output: ResizableBytes + NewBytes,
309 Nonce: ByteArray<CRYPTO_SECRETBOX_NONCEBYTES>,
310 SecretKey: ByteArray<CRYPTO_SECRETBOX_KEYBYTES>,
311 >(
312 &self,
313 nonce: &Nonce,
314 secret_key: &SecretKey,
315 ) -> Result<Output, Error> {
316 use crate::classic::crypto_secretbox::crypto_secretbox_open_detached;
317
318 let mut message = Output::new_bytes();
319 message.resize(self.data.as_slice().len(), 0);
320
321 crypto_secretbox_open_detached(
322 message.as_mut_slice(),
323 self.tag.as_array(),
324 self.data.as_slice(),
325 nonce.as_array(),
326 secret_key.as_array(),
327 )?;
328
329 Ok(message)
330 }
331
332 pub fn to_bytes<Bytes: NewBytes + ResizableBytes>(&self) -> Bytes {
334 let mut data = Bytes::new_bytes();
335 data.resize(self.tag.len() + self.data.len(), 0);
336 let s = data.as_mut_slice();
337 s[..CRYPTO_SECRETBOX_MACBYTES].copy_from_slice(self.tag.as_slice());
338 s[CRYPTO_SECRETBOX_MACBYTES..].copy_from_slice(self.data.as_slice());
339 data
340 }
341}
342
343impl DryocSecretBox<Mac, Vec<u8>> {
344 pub fn encrypt_to_vecbox<
347 Message: Bytes + ?Sized,
348 Nonce: ByteArray<CRYPTO_SECRETBOX_NONCEBYTES>,
349 SecretKey: ByteArray<CRYPTO_SECRETBOX_KEYBYTES>,
350 >(
351 message: &Message,
352 nonce: &Nonce,
353 secret_key: &SecretKey,
354 ) -> Self {
355 Self::encrypt(message, nonce, secret_key)
356 }
357
358 pub fn decrypt_to_vec<
365 Nonce: ByteArray<CRYPTO_SECRETBOX_NONCEBYTES>,
366 SecretKey: ByteArray<CRYPTO_SECRETBOX_KEYBYTES>,
367 >(
368 &self,
369 nonce: &Nonce,
370 secret_key: &SecretKey,
371 ) -> Result<Vec<u8>, Error> {
372 self.decrypt(nonce, secret_key)
373 }
374
375 pub fn into_vec(mut self) -> Vec<u8> {
377 self.data
378 .resize(self.data.len() + CRYPTO_SECRETBOX_MACBYTES, 0);
379 self.data.rotate_right(CRYPTO_SECRETBOX_MACBYTES);
380 self.data[0..CRYPTO_SECRETBOX_MACBYTES].copy_from_slice(self.tag.as_array());
381 self.data
382 }
383}
384
385impl<
386 'a,
387 Mac: NewByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize,
388 Data: NewBytes + ResizableBytes + From<&'a [u8]> + Zeroize,
389> DryocSecretBox<Mac, Data>
390{
391 pub fn with_data(input: &'a [u8]) -> Self {
393 Self {
394 tag: Mac::new_byte_array(),
395 data: input.into(),
396 }
397 }
398}
399
400impl<
401 'a,
402 Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize,
403 Data: Bytes + ResizableBytes + From<&'a [u8]> + Zeroize,
404> DryocSecretBox<Mac, Data>
405{
406 pub fn with_data_and_mac(tag: Mac, input: &'a [u8]) -> Self {
409 Self {
410 tag,
411 data: input.into(),
412 }
413 }
414}
415
416impl<Mac: ByteArray<CRYPTO_SECRETBOX_MACBYTES> + Zeroize, Data: Bytes + Zeroize>
417 PartialEq<DryocSecretBox<Mac, Data>> for DryocSecretBox<Mac, Data>
418{
419 fn eq(&self, other: &Self) -> bool {
420 self.tag.as_slice().ct_eq(other.tag.as_slice()).unwrap_u8() == 1
421 && self
422 .data
423 .as_slice()
424 .ct_eq(other.data.as_slice())
425 .unwrap_u8()
426 == 1
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433
434 #[test]
435 fn test_copy() {
436 for _ in 0..20 {
437 use std::convert::TryFrom;
438
439 use crate::rng::*;
440
441 let mut data1: Vec<u8> = vec![0u8; 1024];
442 copy_randombytes(data1.as_mut_slice());
443 let data1_copy = data1.clone();
444
445 let dryocsecretbox: VecBox = DryocSecretBox::from_bytes(&data1).expect("ok");
446 assert_eq!(
447 dryocsecretbox.data.as_slice(),
448 &data1_copy[CRYPTO_SECRETBOX_MACBYTES..]
449 );
450 assert_eq!(
451 dryocsecretbox.tag.as_slice(),
452 &data1_copy[..CRYPTO_SECRETBOX_MACBYTES]
453 );
454
455 let data1 = data1_copy.clone();
456 let dryocsecretbox: VecBox = DryocSecretBox::with_data(&data1);
457 assert_eq!(&dryocsecretbox.data, &data1_copy);
458
459 let data1 = data1_copy.clone();
460 let (tag, data) = data1.split_at(CRYPTO_SECRETBOX_MACBYTES);
461 let dryocsecretbox: VecBox =
462 DryocSecretBox::with_data_and_mac(Mac::try_from(tag).expect("mac"), data);
463 assert_eq!(
464 dryocsecretbox.data.as_slice(),
465 &data1_copy[CRYPTO_SECRETBOX_MACBYTES..]
466 );
467 assert_eq!(
468 dryocsecretbox.tag.as_array(),
469 &data1_copy[..CRYPTO_SECRETBOX_MACBYTES]
470 );
471 }
472 }
473
474 #[cfg(dryoc_native_tests)]
475 mod native_tests {
476 #[test]
477 fn test_dryocbox() {
478 for i in 0..20 {
479 use base64::Engine as _;
480 use base64::engine::general_purpose;
481 use sodiumoxide::crypto::secretbox;
482 use sodiumoxide::crypto::secretbox::{Key as SOKey, Nonce as SONonce};
483
484 use crate::dryocsecretbox::*;
485
486 let secret_key = Key::generate();
487 let nonce = Nonce::generate();
488 let words = vec!["hello1".to_string(); i];
489 let message = words.join(" :D ").into_bytes();
490 let message_copy = message.clone();
491 let dryocsecretbox: VecBox = DryocSecretBox::encrypt(&message, &nonce, &secret_key);
492
493 let ciphertext = dryocsecretbox.clone().into_vec();
494 assert_eq!(&ciphertext, &dryocsecretbox.to_vec());
495
496 let ciphertext_copy = ciphertext.clone();
497
498 let so_ciphertext = secretbox::seal(
499 &message_copy,
500 &SONonce::from_slice(&nonce).unwrap(),
501 &SOKey::from_slice(&secret_key).unwrap(),
502 );
503 assert_eq!(
504 general_purpose::STANDARD.encode(&ciphertext),
505 general_purpose::STANDARD.encode(&so_ciphertext)
506 );
507
508 let so_decrypted = secretbox::open(
509 &ciphertext_copy,
510 &SONonce::from_slice(&nonce).unwrap(),
511 &SOKey::from_slice(&secret_key).unwrap(),
512 )
513 .expect("decrypt failed");
514
515 let m = DryocSecretBox::decrypt::<Vec<u8>, Nonce, Key>(
516 &dryocsecretbox,
517 &nonce,
518 &secret_key,
519 )
520 .expect("decrypt failed");
521 assert_eq!(m, message_copy);
522 assert_eq!(m, so_decrypted);
523 }
524 }
525
526 #[test]
527 fn test_dryocbox_vec() {
528 for i in 0..20 {
529 use base64::Engine as _;
530 use base64::engine::general_purpose;
531 use sodiumoxide::crypto::secretbox;
532 use sodiumoxide::crypto::secretbox::{Key as SOKey, Nonce as SONonce};
533
534 use crate::dryocsecretbox::*;
535
536 let secret_key = Key::generate();
537 let nonce = Nonce::generate();
538 let words = vec!["hello1".to_string(); i];
539 let message = words.join(" :D ").into_bytes();
540 let message_copy = message.clone();
541 let dryocsecretbox =
542 DryocSecretBox::encrypt_to_vecbox(&message, &nonce, &secret_key);
543
544 let ciphertext = dryocsecretbox.clone().into_vec();
545 assert_eq!(&ciphertext, &dryocsecretbox.to_vec());
546
547 let ciphertext_copy = ciphertext.clone();
548
549 let so_ciphertext = secretbox::seal(
550 &message_copy,
551 &SONonce::from_slice(&nonce).unwrap(),
552 &SOKey::from_slice(&secret_key).unwrap(),
553 );
554 assert_eq!(
555 general_purpose::STANDARD.encode(&ciphertext),
556 general_purpose::STANDARD.encode(&so_ciphertext)
557 );
558
559 let so_decrypted = secretbox::open(
560 &ciphertext_copy,
561 &SONonce::from_slice(&nonce).unwrap(),
562 &SOKey::from_slice(&secret_key).unwrap(),
563 )
564 .expect("decrypt failed");
565
566 let m = dryocsecretbox
567 .decrypt_to_vec(&nonce, &secret_key)
568 .expect("decrypt failed");
569 assert_eq!(m, message_copy);
570 assert_eq!(m, so_decrypted);
571 }
572 }
573
574 #[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
575 #[cfg(all(feature = "protected", any(unix, windows)))]
576 #[test]
577 fn test_dryocbox_locked() {
578 for i in 0..20 {
579 use base64::Engine as _;
580 use base64::engine::general_purpose;
581 use sodiumoxide::crypto::secretbox;
582 use sodiumoxide::crypto::secretbox::{Key as SOKey, Nonce as SONonce};
583
584 use crate::dryocsecretbox::*;
585 use crate::protected::*;
586
587 let secret_key = protected::Key::generate_locked().expect("generate failed");
588 let nonce = protected::Nonce::generate_locked().expect("generate failed");
589 let words = vec!["hello1".to_string(); i];
590 let message = words.join(" :D ");
591 let message_copy = message.clone();
592 let dryocsecretbox: protected::LockedBox =
593 DryocSecretBox::encrypt(message.as_bytes(), &nonce, &secret_key);
594
595 let ciphertext = dryocsecretbox.to_vec();
596
597 let ciphertext_copy = ciphertext.clone();
598
599 let so_ciphertext = secretbox::seal(
600 message_copy.as_bytes(),
601 &SONonce::from_slice(nonce.as_slice()).unwrap(),
602 &SOKey::from_slice(secret_key.as_slice()).unwrap(),
603 );
604 assert_eq!(
605 general_purpose::STANDARD.encode(&ciphertext),
606 general_purpose::STANDARD.encode(&so_ciphertext)
607 );
608
609 let so_decrypted = secretbox::open(
610 &ciphertext_copy,
611 &SONonce::from_slice(nonce.as_slice()).unwrap(),
612 &SOKey::from_slice(secret_key.as_slice()).unwrap(),
613 )
614 .expect("decrypt failed");
615
616 let m: LockedBytes = dryocsecretbox
617 .decrypt(&nonce, &secret_key)
618 .expect("decrypt failed");
619
620 assert_eq!(m.as_slice(), message_copy.as_bytes());
621 assert_eq!(m.as_slice(), so_decrypted);
622 }
623 }
624 }
625}