deoxys/
lib.rs

1//! The [Deoxys][1] [Authenticated Encryption and Associated Data (AEAD)][2] cipher.
2//!
3//! The Deoxys-II variant has been selected as the first choice for defense in-depth
4//! scenario during the [CAESAR competition][3].
5//!
6//! ## Security Notes
7//!
8//! This crate has *NOT* received any security audit.
9//!
10//! Although encryption and decryption passes the test vector, there is no guarantee
11//! of constant-time operation.
12//!
13//! **USE AT YOUR OWN RISK.**
14//!
15//! # Usage
16//!
17#![cfg_attr(all(feature = "getrandom", feature = "std"), doc = "```")]
18#![cfg_attr(not(all(feature = "getrandom", feature = "std")), doc = "```ignore")]
19//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
20//! use deoxys::{
21//!     aead::{Aead, KeyInit, OsRng},
22//!     DeoxysII256, // Can be `DeoxysI128`, `DeoxysI256`, `DeoxysII128` of `DeoxysII256`
23//!     Nonce // Or `Aes128Gcm`
24//! };
25//!
26//! let key = DeoxysII256::generate_key(&mut OsRng);
27//! let cipher = DeoxysII256::new(&key);
28//! let nonce = Nonce::from_slice(b"unique nonce123"); // 64-bits for Deoxys-I or 120-bits for Deoxys-II; unique per message
29//! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref())?;
30//! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())?;
31//! assert_eq!(&plaintext, b"plaintext message");
32//! # Ok(())
33//! # }
34//! ```
35//!
36//! ## Usage with AAD
37//! Deoxys can authenticate additionnal data that is not encrypted alongside with the ciphertext.
38//! ```
39//! use deoxys::{DeoxysII256, Key, Nonce}; // Can be `DeoxysI128`, `DeoxysI256`, `DeoxysII128` of `DeoxysII256`
40//! use deoxys::aead::{Aead, KeyInit, Payload};
41//!
42//! let key = Key::<DeoxysII256>::from_slice(b"an example very very secret key.");
43//! let cipher = DeoxysII256::new(key);
44//!
45//! let nonce = Nonce::from_slice(b"unique nonce123"); // 64-bits for Deoxys-I or 120-bits for Deoxys-II; unique per message
46//!
47//!let payload = Payload {
48//!    msg: &b"this will be encrypted".as_ref(),
49//!    aad: &b"this will NOT be encrypted, but will be authenticated".as_ref(),
50//!};
51//!
52//! let ciphertext = cipher.encrypt(nonce, payload)
53//!     .expect("encryption failure!"); // NOTE: handle this error to avoid panics!
54//!
55//!let payload = Payload {
56//!    msg: &ciphertext,
57//!    aad: &b"this will NOT be encrypted, but will be authenticated".as_ref(),
58//!};
59//!
60//! let plaintext = cipher.decrypt(nonce, payload)
61//!     .expect("decryption failure!"); // NOTE: handle this error to avoid panics!
62//!
63//! assert_eq!(&plaintext, b"this will be encrypted");
64//! ```
65//!
66//! ## In-place Usage (eliminates `alloc` requirement)
67//!
68//! This crate has an optional `alloc` feature which can be disabled in e.g.
69//! microcontroller environments that don't have a heap.
70//!
71//! The [`AeadInPlace::encrypt_in_place`] and [`AeadInPlace::decrypt_in_place`]
72//! methods accept any type that impls the [`aead::Buffer`] trait which
73//! contains the plaintext for encryption or ciphertext for decryption.
74//!
75//! Note that if you enable the `heapless` feature of this crate,
76//! you will receive an impl of [`aead::Buffer`] for `heapless::Vec`
77//! (re-exported from the [`aead`] crate as [`aead::heapless::Vec`]),
78//! which can then be passed as the `buffer` parameter to the in-place encrypt
79//! and decrypt methods:
80//!
81//! ```
82//! # #[cfg(feature = "heapless")]
83//! # {
84//! use deoxys::{DeoxysII256, Key, Nonce}; // Can be `DeoxysI128`, `DeoxysI256`, `DeoxysII128` of `DeoxysII256`
85//! use deoxys::aead::{AeadInPlace, KeyInit};
86//! use deoxys::aead::heapless::Vec;
87//!
88//! let key = Key::<DeoxysII256>::from_slice(b"an example very very secret key.");
89//! let cipher = DeoxysII256::new(key);
90//!
91//! let nonce = Nonce::from_slice(b"unique nonce123"); // 64-bits for Deoxys-I or 120-bits for Deoxys-II; unique per message
92//!
93//! let mut buffer: Vec<u8, 128> = Vec::new(); // Buffer needs 16-bytes overhead for tag
94//! buffer.extend_from_slice(b"plaintext message");
95//!
96//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
97//! cipher.encrypt_in_place(nonce, b"", &mut buffer).expect("encryption failure!");
98//!
99//! // `buffer` now contains the message ciphertext
100//! assert_ne!(&buffer, b"plaintext message");
101//!
102//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
103//! cipher.decrypt_in_place(nonce, b"", &mut buffer).expect("decryption failure!");
104//! assert_eq!(&buffer, b"plaintext message");
105//! # }
106//! ```
107//!
108//! [1]: https://sites.google.com/view/deoxyscipher
109//! [2]: https://en.wikipedia.org/wiki/Authenticated_encryption
110//! [3]: https://competitions.cr.yp.to/caesar-submissions.html
111
112#![no_std]
113#![doc(
114    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
115    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
116)]
117#![warn(missing_docs, rust_2018_idioms)]
118
119/// Deoxys-BC implementations.
120mod deoxys_bc;
121
122/// Operation modes for Deoxys.
123mod modes;
124
125pub use aead::{self, consts, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
126
127use aead::{
128    consts::{U0, U16},
129    generic_array::{ArrayLength, GenericArray},
130};
131use core::marker::PhantomData;
132
133use zeroize::Zeroize;
134
135/// Deoxys-I with 128-bit keys
136pub type DeoxysI128 = Deoxys<modes::DeoxysI<deoxys_bc::DeoxysBc256>, deoxys_bc::DeoxysBc256>;
137
138/// Deoxys-I with 256-bit keys
139pub type DeoxysI256 = Deoxys<modes::DeoxysI<deoxys_bc::DeoxysBc384>, deoxys_bc::DeoxysBc384>;
140
141/// Deoxys-II with 128-bit keys
142#[allow(clippy::upper_case_acronyms)]
143pub type DeoxysII128 = Deoxys<modes::DeoxysII<deoxys_bc::DeoxysBc256>, deoxys_bc::DeoxysBc256>;
144
145/// Deoxys-II with 256-bit keys
146#[allow(clippy::upper_case_acronyms)]
147pub type DeoxysII256 = Deoxys<modes::DeoxysII<deoxys_bc::DeoxysBc384>, deoxys_bc::DeoxysBc384>;
148
149/// Deoxys nonces
150pub type Nonce<NonceSize> = GenericArray<u8, NonceSize>;
151
152/// Deoxys tags
153pub type Tag = GenericArray<u8, U16>;
154
155/// Deoxys encryption modes.
156/// This type contains the public API for a Deoxys mode, like Deoxys-I and Deoxys-II.
157pub trait DeoxysMode<B>: modes::DeoxysModeInternal<B>
158where
159    B: DeoxysBcType,
160{
161    /// The size of the required nonce
162    type NonceSize: ArrayLength<u8>;
163
164    /// Encrypts the data in place with the specified parameters
165    /// Returns the tag
166    fn encrypt_in_place(
167        nonce: &GenericArray<u8, Self::NonceSize>,
168        associated_data: &[u8],
169        buffer: &mut [u8],
170        subkeys: &GenericArray<[u8; 16], B::SubkeysSize>,
171    ) -> [u8; 16];
172
173    /// Decrypts the data in place with the specified parameters
174    /// Returns an error if the tag verification fails
175    fn decrypt_in_place(
176        nonce: &GenericArray<u8, Self::NonceSize>,
177        associated_data: &[u8],
178        buffer: &mut [u8],
179        tag: &Tag,
180        subkeys: &GenericArray<[u8; 16], B::SubkeysSize>,
181    ) -> Result<(), aead::Error>;
182}
183
184/// Deoxys-BC trait.
185/// This type contains the public API for Deoxys-BC implementations, which varies depending on the size of the key.
186pub trait DeoxysBcType: deoxys_bc::DeoxysBcInternal {
187    /// The size of the required tweakey.
188    type KeySize: ArrayLength<u8>;
189
190    /// Precompute the subkeys
191    fn precompute_subkeys(
192        key: &GenericArray<u8, Self::KeySize>,
193    ) -> GenericArray<[u8; 16], Self::SubkeysSize>;
194
195    /// Encrypts a block of data in place.
196    fn encrypt_in_place(
197        block: &mut [u8; 16],
198        tweak: &[u8; 16],
199        subkeys: &GenericArray<[u8; 16], Self::SubkeysSize>,
200    ) {
201        let keys = Self::key_schedule(tweak, subkeys);
202
203        for (b, k) in block.iter_mut().zip(keys[0].iter()) {
204            *b ^= k;
205        }
206
207        for k in &keys[1..] {
208            aes::hazmat::cipher_round(block.into(), k.into());
209        }
210    }
211
212    /// Decrypts a block of data in place.
213    fn decrypt_in_place(
214        block: &mut [u8; 16],
215        tweak: &[u8; 16],
216        subkeys: &GenericArray<[u8; 16], Self::SubkeysSize>,
217    ) {
218        let mut keys = Self::key_schedule(tweak, subkeys);
219
220        let r = keys.len();
221
222        for (b, k) in block.iter_mut().zip(keys[r - 1].iter()) {
223            *b ^= k;
224        }
225
226        aes::hazmat::inv_mix_columns(block.into());
227
228        for k in keys[..r - 1].iter_mut().rev() {
229            aes::hazmat::inv_mix_columns(k.into());
230            aes::hazmat::equiv_inv_cipher_round(block.into(), (&*k).into());
231        }
232
233        aes::hazmat::mix_columns(block.into());
234    }
235}
236
237/// Generic Deoxys implementation.
238///
239/// This type is generic to support multiple Deoxys modes(namely Deoxys-I and Deoxys-II) and key size.
240pub struct Deoxys<M, B>
241where
242    M: DeoxysMode<B>,
243    B: DeoxysBcType,
244{
245    subkeys: GenericArray<[u8; 16], B::SubkeysSize>,
246    mode: PhantomData<M>,
247}
248
249impl<M, B> KeySizeUser for Deoxys<M, B>
250where
251    M: DeoxysMode<B>,
252    B: DeoxysBcType,
253{
254    type KeySize = B::KeySize;
255}
256
257impl<M, B> KeyInit for Deoxys<M, B>
258where
259    M: DeoxysMode<B>,
260    B: DeoxysBcType,
261{
262    fn new(key: &Key<Self>) -> Self {
263        Self {
264            subkeys: B::precompute_subkeys(key),
265            mode: PhantomData,
266        }
267    }
268}
269
270impl<M, B> AeadCore for Deoxys<M, B>
271where
272    M: DeoxysMode<B>,
273    B: DeoxysBcType,
274{
275    type NonceSize = M::NonceSize;
276    type TagSize = U16;
277    type CiphertextOverhead = U0;
278}
279
280impl<M, B> AeadInPlace for Deoxys<M, B>
281where
282    M: DeoxysMode<B>,
283    B: DeoxysBcType,
284{
285    fn encrypt_in_place_detached(
286        &self,
287        nonce: &Nonce<M::NonceSize>,
288        associated_data: &[u8],
289        buffer: &mut [u8],
290    ) -> Result<Tag, Error> {
291        Ok(Tag::from(M::encrypt_in_place(
292            nonce,
293            associated_data,
294            buffer,
295            &self.subkeys,
296        )))
297    }
298
299    fn decrypt_in_place_detached(
300        &self,
301        nonce: &Nonce<M::NonceSize>,
302        associated_data: &[u8],
303        buffer: &mut [u8],
304        tag: &Tag,
305    ) -> Result<(), Error> {
306        M::decrypt_in_place(nonce, associated_data, buffer, tag, &self.subkeys)
307    }
308}
309
310impl<M, B> Drop for Deoxys<M, B>
311where
312    M: DeoxysMode<B>,
313    B: DeoxysBcType,
314{
315    fn drop(&mut self) {
316        for s in self.subkeys.iter_mut() {
317            s.zeroize();
318        }
319    }
320}