ssb_crypto/
secretbox.rs

1//! Secret (encrypted) "boxes" of data. (libsodium's secretbox, aka xsalsa20poly1305)
2use core::mem::size_of;
3use zerocopy::{AsBytes, FromBytes};
4use zeroize::Zeroize;
5
6#[cfg(all(feature = "dalek", not(feature = "force_sodium")))]
7use crate::dalek::secretbox as sb;
8#[cfg(all(
9    feature = "sodium",
10    any(feature = "force_sodium", not(feature = "dalek"))
11))]
12use crate::sodium::secretbox as sb;
13
14/// A key used to seal and open an encrypted box.
15///
16/// The underlying memory is zeroed on drop.
17#[derive(AsBytes, FromBytes, Clone, Zeroize)]
18#[repr(C)]
19#[zeroize(drop)]
20pub struct Key(pub [u8; 32]);
21impl Key {
22    /// The size of a key, in bytes (32).
23    pub const SIZE: usize = size_of::<Self>();
24
25    /// Deserialize a key from a byte slice.
26    ///
27    /// Returns `None` if the slice length is not 32.
28    pub fn from_slice(s: &[u8]) -> Option<Self> {
29        if s.len() == Self::SIZE {
30            let mut out = Self([0; Self::SIZE]);
31            out.0.copy_from_slice(s);
32            Some(out)
33        } else {
34            None
35        }
36    }
37
38    /// Generate a new random key using the given cryptographically-secure
39    /// random number generator.
40    #[cfg(feature = "rand")]
41    pub fn generate_with_rng<R>(r: &mut R) -> Key
42    where
43        R: rand::CryptoRng + rand::RngCore,
44    {
45        let mut buf = [0; Key::SIZE];
46        r.fill_bytes(&mut buf);
47        Key(buf)
48    }
49
50    /// Generate a new random key.
51    #[cfg(all(feature = "getrandom", not(feature = "sodium")))]
52    pub fn generate() -> Key {
53        Key::generate_with_rng(&mut rand::rngs::OsRng {})
54    }
55
56    #[allow(missing_docs)]
57    #[cfg(feature = "sodium")]
58    pub fn generate() -> Key {
59        crate::sodium::secretbox::generate_key()
60    }
61}
62
63#[cfg(any(feature = "sodium", feature = "dalek"))]
64impl Key {
65    /// Encrypt a message in place, returning the authentication code.
66    pub fn seal(&self, msg: &mut [u8], n: &Nonce) -> Hmac {
67        sb::seal(self, msg, n)
68    }
69
70    /// Decrypt an encrypted message in place.
71    #[must_use]
72    pub fn open(&self, c: &mut [u8], hmac: &Hmac, n: &Nonce) -> bool {
73        sb::open(self, c, hmac, n)
74    }
75
76    /// Decrypt an encrypted message with attached authentication code,
77    /// writing the decrypted message into the provided buffer.
78    ///
79    /// If the decryption fails, `out` will contain a copy of the encrypted message.
80    ///
81    /// # Panics
82    ///
83    /// Panics if the output buffer length isn't big enough to hold the plaintext message.
84    /// The output buffer length should be at least `input.len() - Hmac::SIZE`.
85    #[must_use]
86    pub fn open_attached_into(&self, input: &[u8], n: &Nonce, mut out: &mut [u8]) -> bool {
87        let (h, c) = input.split_at(Hmac::SIZE);
88        let hmac = Hmac::from_slice(h).unwrap();
89        out.copy_from_slice(c);
90        self.open(&mut out, &hmac, n)
91    }
92
93    /// Encrypt a message, writing the resulting [`Hmac`] and ciphertext into the
94    /// given output buffer. The output buffer size must be at least `msg.len() + Hmac::SIZE`.
95    ///
96    /// [`Hmac`]: ./struct.Hmac.html
97    pub fn seal_attached_into(&self, msg: &[u8], nonce: &Nonce, out: &mut [u8]) {
98        assert!(out.len() >= msg.len() + Hmac::SIZE);
99
100        let (h, mut c) = out.split_at_mut(Hmac::SIZE);
101        c.copy_from_slice(msg);
102        let hmac = self.seal(&mut c, nonce);
103        h.copy_from_slice(hmac.as_bytes());
104    }
105}
106
107/// A single-use value used during encryption.
108/// Each encrypted/sealed box must have its own nonce.
109#[derive(AsBytes, FromBytes, Copy, Clone)]
110#[repr(C)]
111pub struct Nonce(pub [u8; 24]);
112impl Nonce {
113    /// The size of a nonce, in bytes (24).
114    pub const SIZE: usize = size_of::<Self>();
115
116    /// A nonce, filled with zeros. This is used during the ssb handshake,
117    /// and probably shouldn't be used otherwise.
118    pub fn zero() -> Nonce {
119        Nonce([0; 24])
120    }
121
122    /// Generate a new, random nonce.
123    #[cfg(all(feature = "getrandom", not(feature = "sodium")))]
124    pub fn generate() -> Nonce {
125        Nonce::generate_with_rng(&mut rand::rngs::OsRng {})
126    }
127
128    #[allow(missing_docs)]
129    #[cfg(feature = "sodium")]
130    pub fn generate() -> Nonce {
131        crate::sodium::secretbox::generate_nonce()
132    }
133
134    /// Generate a new, random nonce using the given cryptographically-secure
135    /// random number generator.
136    #[cfg(feature = "rand")]
137    pub fn generate_with_rng<R>(r: &mut R) -> Nonce
138    where
139        R: rand::CryptoRng + rand::RngCore,
140    {
141        let mut buf = [0; Nonce::SIZE];
142        r.fill_bytes(&mut buf);
143        Nonce(buf)
144    }
145
146    /// Deserialize a nonce from a byte slice.
147    ///
148    /// Returns `None` if the byte slice length isn't 24.
149    pub fn from_slice(s: &[u8]) -> Option<Self> {
150        if s.len() == Self::SIZE {
151            let mut out = Self([0; Self::SIZE]);
152            out.0.copy_from_slice(s);
153            Some(out)
154        } else {
155            None
156        }
157    }
158}
159
160/// The authentication code for an encrypted secret box.
161#[derive(Copy, Clone, AsBytes, FromBytes)]
162#[repr(C)]
163pub struct Hmac(pub [u8; 16]);
164impl Hmac {
165    /// The size of an Hmac, in bytes (24).
166    pub const SIZE: usize = size_of::<Self>();
167
168    /// Deserialize an Hmac from a byte slice.
169    ///
170    /// Returns `None` if the byte slice length isn't 16.
171    pub fn from_slice(s: &[u8]) -> Option<Self> {
172        if s.len() == Self::SIZE {
173            let mut out = Self([0; Self::SIZE]);
174            out.0.copy_from_slice(s);
175            Some(out)
176        } else {
177            None
178        }
179    }
180}