aes_siv/
lib.rs

1//! [AES-SIV][1] ([RFC 5297][2]):
2//! [Authenticated Encryption with Associated Data (AEAD)][3] cipher which also
3//! provides [nonce reuse misuse resistance][4].
4//!
5//! # Usage
6//!
7//! Simple usage (allocating, no associated data):
8//!
9#![cfg_attr(all(feature = "getrandom", feature = "std"), doc = "```")]
10#![cfg_attr(not(all(feature = "getrandom", feature = "std")), doc = "```ignore")]
11//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
12//! use aes_siv::{
13//!     aead::{Aead, KeyInit, OsRng},
14//!     Aes256SivAead, Nonce // Or `Aes128SivAead`
15//! };
16//!
17//! let key = Aes256SivAead::generate_key(&mut OsRng);
18//! let cipher = Aes256SivAead::new(&key);
19//! let nonce = Nonce::from_slice(b"any unique nonce"); // 128-bits; unique per message
20//! let ciphertext = cipher.encrypt(nonce, b"plaintext message".as_ref())?;
21//! let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())?;
22//! assert_eq!(&plaintext, b"plaintext message");
23//! # Ok(())
24//! # }
25//! ```
26//!
27//! ## In-place Usage (eliminates `alloc` requirement)
28//!
29//! This crate has an optional `alloc` feature which can be disabled in e.g.
30//! microcontroller environments that don't have a heap.
31//!
32//! The [`AeadInPlace::encrypt_in_place`] and [`AeadInPlace::decrypt_in_place`]
33//! methods accept any type that impls the [`aead::Buffer`] trait which
34//! contains the plaintext for encryption or ciphertext for decryption.
35//!
36//! Note that if you enable the `heapless` feature of this crate,
37//! you will receive an impl of [`aead::Buffer`] for `heapless::Vec`
38//! (re-exported from the [`aead`] crate as [`aead::heapless::Vec`]),
39//! which can then be passed as the `buffer` parameter to the in-place encrypt
40//! and decrypt methods:
41//!
42#![cfg_attr(
43    all(feature = "getrandom", feature = "heapless", feature = "std"),
44    doc = "```"
45)]
46#![cfg_attr(
47    not(all(feature = "getrandom", feature = "heapless", feature = "std")),
48    doc = "```ignore"
49)]
50//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
51//! use aes_siv::{
52//!     aead::{AeadInPlace, KeyInit, OsRng, heapless::Vec},
53//!     Aes256SivAead, Nonce, // Or `Aes128SivAead`
54//! };
55//!
56//! let key = Aes256SivAead::generate_key(&mut OsRng);
57//! let cipher = Aes256SivAead::new(&key);
58//! let nonce = Nonce::from_slice(b"any unique nonce"); // 128-bits; unique per message
59//!
60//! let mut buffer: Vec<u8, 128> = Vec::new(); // Note: buffer needs 16-bytes overhead for auth tag tag
61//! buffer.extend_from_slice(b"plaintext message");
62//!
63//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
64//! cipher.encrypt_in_place(nonce, b"", &mut buffer)?;
65//!
66//! // `buffer` now contains the message ciphertext
67//! assert_ne!(&buffer, b"plaintext message");
68//!
69//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
70//! cipher.decrypt_in_place(nonce, b"", &mut buffer)?;
71//! assert_eq!(&buffer, b"plaintext message");
72//! # Ok(())
73//! # }
74//! ```
75//!
76//! [1]: https://github.com/miscreant/meta/wiki/AES-SIV
77//! [2]: https://tools.ietf.org/html/rfc5297
78//! [3]: https://en.wikipedia.org/wiki/Authenticated_encryption
79//! [4]: https://github.com/miscreant/meta/wiki/Nonce-Reuse-Misuse-Resistance
80
81#![no_std]
82#![cfg_attr(docsrs, feature(doc_cfg))]
83#![doc(
84    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
85    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
86)]
87#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
88
89#[cfg(feature = "alloc")]
90extern crate alloc;
91
92pub mod siv;
93
94pub use aead::{self, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
95
96use crate::siv::Siv;
97use aead::{
98    consts::{U0, U16, U32, U64},
99    generic_array::GenericArray,
100    Buffer,
101};
102use aes::{Aes128, Aes256};
103use cipher::{BlockCipher, BlockEncryptMut};
104use cmac::Cmac;
105use core::{marker::PhantomData, ops::Add};
106use digest::{FixedOutputReset, Mac};
107
108#[cfg(feature = "pmac")]
109use pmac::Pmac;
110
111/// AES-SIV nonces
112pub type Nonce = GenericArray<u8, U16>;
113
114/// AES-SIV tags (i.e. the Synthetic Initialization Vector value)
115pub type Tag = GenericArray<u8, U16>;
116
117/// The `SivAead` type wraps the more powerful `Siv` interface in a more
118/// commonly used Authenticated Encryption with Associated Data (AEAD) API,
119/// which accepts a key, nonce, and associated data when encrypting/decrypting.
120pub struct SivAead<C, M>
121where
122    Self: KeySizeUser,
123    C: BlockCipher<BlockSize = U16> + BlockEncryptMut + KeyInit + KeySizeUser,
124    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
125    <C as KeySizeUser>::KeySize: Add,
126{
127    key: GenericArray<u8, <Self as KeySizeUser>::KeySize>,
128    mac: PhantomData<M>, // TODO(tarcieri): include `M` in `KeySize` calculation
129}
130
131/// SIV AEAD modes based on CMAC
132pub type CmacSivAead<BlockCipher> = SivAead<BlockCipher, Cmac<BlockCipher>>;
133
134/// SIV AEAD modes based on PMAC
135#[cfg(feature = "pmac")]
136#[cfg_attr(docsrs, doc(cfg(feature = "pmac")))]
137pub type PmacSivAead<BlockCipher> = SivAead<BlockCipher, Pmac<BlockCipher>>;
138
139/// AES-CMAC-SIV in AEAD mode with 256-bit key size (128-bit security)
140pub type Aes128SivAead = CmacSivAead<Aes128>;
141
142/// AES-CMAC-SIV in AEAD mode with 512-bit key size (256-bit security)
143pub type Aes256SivAead = CmacSivAead<Aes256>;
144
145/// AES-PMAC-SIV in AEAD mode with 256-bit key size (128-bit security)
146#[cfg(feature = "pmac")]
147#[cfg_attr(docsrs, doc(cfg(feature = "pmac")))]
148pub type Aes128PmacSivAead = PmacSivAead<Aes128>;
149
150/// AES-PMAC-SIV in AEAD mode with 512-bit key size (256-bit security)
151#[cfg(feature = "pmac")]
152#[cfg_attr(docsrs, doc(cfg(feature = "pmac")))]
153pub type Aes256PmacSivAead = PmacSivAead<Aes256>;
154
155impl<M> KeySizeUser for SivAead<Aes128, M>
156where
157    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
158{
159    type KeySize = U32;
160}
161
162impl<M> KeySizeUser for SivAead<Aes256, M>
163where
164    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
165{
166    type KeySize = U64;
167}
168
169impl<M> KeyInit for SivAead<Aes128, M>
170where
171    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
172{
173    fn new(key: &GenericArray<u8, Self::KeySize>) -> Self {
174        Self {
175            key: *key,
176            mac: PhantomData,
177        }
178    }
179}
180
181impl<M> KeyInit for SivAead<Aes256, M>
182where
183    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
184{
185    fn new(key: &GenericArray<u8, Self::KeySize>) -> Self {
186        Self {
187            key: *key,
188            mac: PhantomData,
189        }
190    }
191}
192
193impl<C, M> AeadCore for SivAead<C, M>
194where
195    Self: KeySizeUser,
196    C: BlockCipher<BlockSize = U16> + BlockEncryptMut + KeyInit + KeySizeUser,
197    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
198    <C as KeySizeUser>::KeySize: Add,
199{
200    // "If the nonce is random, it SHOULD be at least 128 bits in length"
201    // https://tools.ietf.org/html/rfc5297#section-3
202    // TODO(tarcieri): generic nonce sizes
203    type NonceSize = U16;
204    type TagSize = U16;
205    type CiphertextOverhead = U0;
206}
207
208impl<C, M> AeadInPlace for SivAead<C, M>
209where
210    Self: KeySizeUser,
211    Siv<C, M>: KeyInit + KeySizeUser<KeySize = <Self as KeySizeUser>::KeySize>,
212    C: BlockCipher<BlockSize = U16> + BlockEncryptMut + KeyInit + KeySizeUser,
213    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
214    <C as KeySizeUser>::KeySize: Add,
215{
216    fn encrypt_in_place(
217        &self,
218        nonce: &GenericArray<u8, Self::NonceSize>,
219        associated_data: &[u8],
220        buffer: &mut dyn Buffer,
221    ) -> Result<(), Error> {
222        // "SIV performs nonce-based authenticated encryption when a component of
223        // the associated data is a nonce.  For purposes of interoperability the
224        // final component -- i.e., the string immediately preceding the
225        // plaintext in the vector input to S2V -- is used for the nonce."
226        // https://tools.ietf.org/html/rfc5297#section-3
227        Siv::<C, M>::new(&self.key).encrypt_in_place(&[associated_data, nonce.as_slice()], buffer)
228    }
229
230    fn encrypt_in_place_detached(
231        &self,
232        nonce: &GenericArray<u8, Self::NonceSize>,
233        associated_data: &[u8],
234        buffer: &mut [u8],
235    ) -> Result<GenericArray<u8, Self::TagSize>, Error> {
236        Siv::<C, M>::new(&self.key)
237            .encrypt_in_place_detached(&[associated_data, nonce.as_slice()], buffer)
238    }
239
240    fn decrypt_in_place(
241        &self,
242        nonce: &GenericArray<u8, Self::NonceSize>,
243        associated_data: &[u8],
244        buffer: &mut dyn Buffer,
245    ) -> Result<(), Error> {
246        Siv::<C, M>::new(&self.key).decrypt_in_place(&[associated_data, nonce.as_slice()], buffer)
247    }
248
249    fn decrypt_in_place_detached(
250        &self,
251        nonce: &GenericArray<u8, Self::NonceSize>,
252        associated_data: &[u8],
253        buffer: &mut [u8],
254        tag: &GenericArray<u8, Self::TagSize>,
255    ) -> Result<(), Error> {
256        Siv::<C, M>::new(&self.key).decrypt_in_place_detached(
257            &[associated_data, nonce.as_slice()],
258            buffer,
259            tag,
260        )
261    }
262}