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}