Skip to main content

aes_siv/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
7)]
8
9//! # Usage
10//!
11//! Simple usage (allocating, no associated data):
12//!
13#![cfg_attr(feature = "getrandom", doc = "```")]
14#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
15//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
16//! // NOTE: requires the `getrandom` feature is enabled
17//!
18//! use aes_siv::{
19//!     aead::{Aead, AeadCore, Generate, Key, KeyInit},
20//!     Aes256SivAead, Nonce // Or `Aes128SivAead`
21//! };
22//!
23//! let key = Key::<Aes256SivAead>::generate();
24//! let cipher = Aes256SivAead::new(&key);
25//!
26//! let nonce = Nonce::generate(); // MUST be unique per message
27//! let ciphertext = cipher.encrypt(&nonce, b"plaintext message".as_ref())?;
28//!
29//! let plaintext = cipher.decrypt(&nonce, ciphertext.as_ref())?;
30//! assert_eq!(&plaintext, b"plaintext message");
31//! # Ok(())
32//! # }
33//! ```
34//!
35//! ## In-place Usage (eliminates `alloc` requirement)
36//!
37//! This crate has an optional `alloc` feature which can be disabled in e.g.
38//! microcontroller environments that don't have a heap.
39//!
40//! The [`AeadInOut::encrypt_in_place`] and [`AeadInOut::decrypt_in_place`]
41//! methods accept any type that impls the [`aead::Buffer`] trait which
42//! contains the plaintext for encryption or ciphertext for decryption.
43//!
44//! Enabling the `arrayvec` feature of this crate will provide an impl of
45//! [`aead::Buffer`] for `arrayvec::ArrayVec` (re-exported from the [`aead`] crate as
46//! [`aead::arrayvec::ArrayVec`]), and enabling the `bytes` feature of this crate will
47//! provide an impl of [`aead::Buffer`] for `bytes::BytesMut` (re-exported from the
48//! [`aead`] crate as [`aead::bytes::BytesMut`]).
49//!
50//! It can then be passed as the `buffer` parameter to the in-place encrypt
51//! and decrypt methods:
52//!
53#![cfg_attr(all(feature = "getrandom", feature = "arrayvec"), doc = "```")]
54#![cfg_attr(
55    not(all(feature = "getrandom", feature = "arrayvec")),
56    doc = "```ignore"
57)]
58//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
59//! // NOTE: requires the `arrayvec` and `getrandom` features are enabled
60//!
61//! use aes_siv::{
62//!     aead::{AeadCore, AeadInOut, Generate, Key, KeyInit, arrayvec::ArrayVec},
63//!     Aes256SivAead, Nonce, // Or `Aes128SivAead`
64//! };
65//!
66//! let key = Key::<Aes256SivAead>::generate();
67//! let cipher = Aes256SivAead::new(&key);
68//!
69//! let nonce = Nonce::generate(); // MUST be unique per message
70//! let mut buffer: ArrayVec<u8, 128> = ArrayVec::new(); // Note: buffer needs 16-bytes overhead for auth tag
71//! buffer.try_extend_from_slice(b"plaintext message").unwrap();
72//!
73//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
74//! cipher.encrypt_in_place(&nonce, b"", &mut buffer)?;
75//!
76//! // `buffer` now contains the message ciphertext
77//! assert_ne!(buffer.as_ref(), b"plaintext message");
78//!
79//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
80//! cipher.decrypt_in_place(&nonce, b"", &mut buffer)?;
81//! assert_eq!(buffer.as_ref(), b"plaintext message");
82//! # Ok(())
83//! # }
84//! ```
85
86#[cfg(feature = "alloc")]
87extern crate alloc;
88
89pub mod siv;
90
91pub use aead::{self, AeadCore, AeadInOut, Error, Key, KeyInit, KeySizeUser};
92
93use crate::siv::Siv;
94use aead::{
95    TagPosition,
96    array::Array,
97    consts::{U1, U16, U32, U64},
98    inout::InOutBuf,
99};
100use aes::{Aes128, Aes256};
101use cipher::{BlockCipherEncrypt, BlockSizeUser, array::ArraySize, typenum::IsGreaterOrEqual};
102use cmac::Cmac;
103use core::{fmt, marker::PhantomData, ops::Add};
104use digest::{FixedOutputReset, Mac};
105
106#[cfg(feature = "pmac")]
107use pmac::Pmac;
108
109/// AES-SIV nonces
110pub type Nonce<NonceSize = U16> = Array<u8, NonceSize>;
111
112/// AES-SIV tags (i.e. the Synthetic Initialization Vector value)
113pub type Tag = Array<u8, U16>;
114
115/// Convenience wrapper around `Siv` interface.
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.
120/// See the [`Siv`](mod@siv) module documentation for more information and examples.
121pub struct SivAead<C, M, NonceSize = U16>
122where
123    Self: KeySizeUser,
124    C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit + KeySizeUser,
125    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
126    <C as KeySizeUser>::KeySize: Add,
127    NonceSize: ArraySize + IsGreaterOrEqual<U1>,
128{
129    key: Array<u8, <Self as KeySizeUser>::KeySize>,
130    mac: PhantomData<M>, // TODO(tarcieri): include `M` in `KeySize` calculation
131}
132
133/// SIV AEAD modes based on CMAC
134pub type CmacSivAead<BlockCipher> = SivAead<BlockCipher, Cmac<BlockCipher>>;
135
136/// SIV AEAD modes based on PMAC
137#[cfg(feature = "pmac")]
138#[cfg_attr(docsrs, doc(cfg(feature = "pmac")))]
139pub type PmacSivAead<BlockCipher> = SivAead<BlockCipher, Pmac<BlockCipher>>;
140
141/// AES-CMAC-SIV in AEAD mode with 256-bit key size (128-bit security)
142pub type Aes128SivAead = CmacSivAead<Aes128>;
143
144/// AES-CMAC-SIV in AEAD mode with 512-bit key size (256-bit security)
145pub type Aes256SivAead = CmacSivAead<Aes256>;
146
147/// AES-PMAC-SIV in AEAD mode with 256-bit key size (128-bit security)
148#[cfg(feature = "pmac")]
149#[cfg_attr(docsrs, doc(cfg(feature = "pmac")))]
150pub type Aes128PmacSivAead = PmacSivAead<Aes128>;
151
152/// AES-PMAC-SIV in AEAD mode with 512-bit key size (256-bit security)
153#[cfg(feature = "pmac")]
154#[cfg_attr(docsrs, doc(cfg(feature = "pmac")))]
155pub type Aes256PmacSivAead = PmacSivAead<Aes256>;
156
157impl<M, NonceSize> KeySizeUser for SivAead<Aes128, M, NonceSize>
158where
159    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
160    NonceSize: ArraySize + IsGreaterOrEqual<U1>,
161{
162    type KeySize = U32;
163}
164
165impl<M, NonceSize> KeySizeUser for SivAead<Aes256, M, NonceSize>
166where
167    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
168    NonceSize: ArraySize + IsGreaterOrEqual<U1>,
169{
170    type KeySize = U64;
171}
172
173impl<M, NonceSize> KeyInit for SivAead<Aes128, M, NonceSize>
174where
175    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
176    NonceSize: ArraySize + IsGreaterOrEqual<U1>,
177{
178    fn new(key: &Array<u8, Self::KeySize>) -> Self {
179        Self {
180            key: *key,
181            mac: PhantomData,
182        }
183    }
184}
185
186impl<M, NonceSize> KeyInit for SivAead<Aes256, M, NonceSize>
187where
188    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
189    NonceSize: ArraySize + IsGreaterOrEqual<U1>,
190{
191    fn new(key: &Array<u8, Self::KeySize>) -> Self {
192        Self {
193            key: *key,
194            mac: PhantomData,
195        }
196    }
197}
198
199impl<C, M, NonceSize> AeadCore for SivAead<C, M, NonceSize>
200where
201    Self: KeySizeUser,
202    C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit + KeySizeUser,
203    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
204    <C as KeySizeUser>::KeySize: Add,
205    NonceSize: ArraySize + IsGreaterOrEqual<U1>,
206{
207    // "If the nonce is random, it SHOULD be at least 128 bits in length"
208    // https://tools.ietf.org/html/rfc5297#section-3
209    // "N_MIN  is 1 octet."
210    // https://tools.ietf.org/html/rfc5297#section-6
211    type NonceSize = NonceSize;
212    type TagSize = U16;
213    const TAG_POSITION: TagPosition = TagPosition::Prefix;
214}
215
216impl<C, M, NonceSize> AeadInOut for SivAead<C, M, NonceSize>
217where
218    Self: KeySizeUser,
219    Siv<C, M>: KeyInit + KeySizeUser<KeySize = <Self as KeySizeUser>::KeySize>,
220    C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit + KeySizeUser,
221    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
222    <C as KeySizeUser>::KeySize: Add,
223    NonceSize: ArraySize + IsGreaterOrEqual<U1>,
224{
225    fn encrypt_inout_detached(
226        &self,
227        nonce: &Array<u8, Self::NonceSize>,
228        associated_data: &[u8],
229        buffer: InOutBuf<'_, '_, u8>,
230    ) -> Result<Array<u8, Self::TagSize>, Error> {
231        Siv::<C, M>::new(&self.key)
232            .encrypt_inout_detached([associated_data, nonce.as_slice()], buffer)
233    }
234
235    fn decrypt_inout_detached(
236        &self,
237        nonce: &Array<u8, Self::NonceSize>,
238        associated_data: &[u8],
239        buffer: InOutBuf<'_, '_, u8>,
240        tag: &Array<u8, Self::TagSize>,
241    ) -> Result<(), Error> {
242        Siv::<C, M>::new(&self.key).decrypt_inout_detached(
243            [associated_data, nonce.as_slice()],
244            buffer,
245            tag,
246        )
247    }
248}
249
250impl<C, M, NonceSize> fmt::Debug for SivAead<C, M, NonceSize>
251where
252    Self: KeySizeUser,
253    C: BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt + KeyInit + KeySizeUser,
254    M: Mac<OutputSize = U16> + FixedOutputReset + KeyInit,
255    <C as KeySizeUser>::KeySize: Add,
256    NonceSize: ArraySize + IsGreaterOrEqual<U1>,
257{
258    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
259        f.debug_struct("SivAead").finish_non_exhaustive()
260    }
261}