libcrux/hpke/
aead.rs

1//! # Authenticated Encryption (AEAD)
2//!
3//! > 💡 This is a hacspec representation of the [HPKE RFC].
4//! > The text is mostly verbatim from the RFC with changes where required.
5//! > It demonstrates the possibilities of hacspec for specifications.
6//!
7//! - [`Seal(key, nonce, aad, pt)`](`AeadSeal`): Encrypt and authenticate plaintext
8//! `pt` with associated data `aad` using symmetric key `key` and nonce
9//! `nonce`, yielding ciphertext and tag `ct`. This function
10//!  can raise a [`MessageLimitReachedError`](`HpkeError::MessageLimitReachedError`) upon failure.
11//! - [`Open(key, nonce, aad, ct)`](`AeadOpen`): Decrypt ciphertext and tag `ct` using
12//! associated data `aad` with symmetric key `key` and nonce `nonce`,
13//! returning plaintext message `pt`. This function can raise an
14//! [`OpenError`](`HpkeError::OpenError`) or [`MessageLimitReachedError`](`HpkeError::MessageLimitReachedError`) upon failure.
15//! - [`Nk`]: The length in bytes of a key for this algorithm.
16//! - [`Nn`]: The length in bytes of a nonce for this algorithm.
17//! - [`Nt`]: The length in bytes of the authentication tag for this algorithm.
18//!
19//! ## Security Requirements on an AEAD
20//!
21//! All AEADs MUST be IND-CCA2-secure, as is currently true for all AEADs
22//! listed in [`AEAD`].
23//!
24//! [hpke rfc]: https://datatracker.ietf.org/doc/draft-irtf-cfrg-hpke/
25//! [publication queue]: https://www.rfc-editor.org/current_queue.php
26#![allow(
27    non_camel_case_types,
28    non_snake_case,
29    unused_imports,
30    non_upper_case_globals
31)]
32
33use crate::{
34    aead::{self, *},
35    hmac::tag_size,
36};
37
38use super::errors::*;
39
40type AeadAlgResult = Result<Algorithm, HpkeError>;
41
42/// ## Authenticated Encryption with Associated Data (AEAD) Functions
43///
44/// The `0xFFFF` AEAD ID is reserved for applications which only use the Export
45/// interface; see HPKE for more details.
46///
47/// | Value  | AEAD             | Nk  | Nn  | Nt  | Reference |
48/// | :----- | :--------------- | :-- | :-- | :-- | :-------- |
49/// | 0x0000 | (reserved)       | N/A | N/A | N/A | N/A       |
50/// | 0x0001 | AES-128-GCM      | 16  | 12  | 16  | [GCM]     |
51/// | 0x0002 | AES-256-GCM      | 32  | 12  | 16  | [GCM]     |
52/// | 0x0003 | ChaCha20Poly1305 | 32  | 12  | 16  | [RFC8439] |
53/// | 0xFFFF | Export-only      | N/A | N/A | N/A | RFCXXXX   |
54///
55/// The "HPKE AEAD Identifiers" registry lists identifiers for authenticated
56/// encryption with associated data (AEAD) algorithms defined for use with HPKE.
57/// These identifiers are two-byte values, so the maximum possible value is
58/// 0xFFFF = 65535.
59///
60/// Template:
61///
62/// * Value: The two-byte identifier for the algorithm
63/// * AEAD: The name of the algorithm
64/// * Nk: The length in bytes of a key for this algorithm
65/// * Nn: The length in bytes of a nonce for this algorithm
66/// * Nt: The length in bytes of an authentication tag for this algorithm
67/// * Reference: Where this algorithm is defined
68///
69/// [GCM]: https://doi.org/10.6028/nist.sp.800-38d
70/// [RFC8439]: https://www.rfc-editor.org/info/rfc8439
71#[derive(Clone, Copy, PartialEq, Debug)]
72pub enum AEAD {
73    /// 0x0001
74    AES_128_GCM,
75    /// 0x0002
76    AES_256_GCM,
77    /// 0x0003
78    ChaCha20Poly1305,
79    /// 0xFFFF
80    Export_only,
81}
82
83/// The length in bytes of a key for this algorithm.
84///
85/// See [`AEAD`] for details.
86pub fn Nk(aead_id: AEAD) -> usize {
87    match aead_id {
88        AEAD::AES_128_GCM => 16,
89        AEAD::AES_256_GCM => 32,
90        AEAD::ChaCha20Poly1305 => 32,
91        AEAD::Export_only => 0,
92    }
93}
94
95/// The length in bytes of the authentication tag for this algorithm.
96///
97/// See [`AEAD`] for details.
98pub fn Nt(aead_id: AEAD) -> usize {
99    match aead_id {
100        AEAD::AES_128_GCM => 16,
101        AEAD::AES_256_GCM => 16,
102        AEAD::ChaCha20Poly1305 => 16,
103        AEAD::Export_only => 0,
104    }
105}
106
107/// The length in bytes of a nonce for this algorithm.
108///
109/// See [`AEAD`] for details.
110pub fn Nn(aead_id: AEAD) -> usize {
111    match aead_id {
112        AEAD::AES_128_GCM => 12,
113        AEAD::AES_256_GCM => 12,
114        AEAD::ChaCha20Poly1305 => 12,
115        AEAD::Export_only => 0,
116    }
117}
118
119/// An AEAD key is a sequence of bytes.
120pub type Key = Vec<u8>;
121
122/// An AEAD nonce is a sequence of bytes.
123pub type Nonce = Vec<u8>;
124
125fn alg_for_aead(aead_id: AEAD) -> AeadAlgResult {
126    match aead_id {
127        AEAD::AES_128_GCM => AeadAlgResult::Ok(Algorithm::Aes128Gcm),
128        AEAD::AES_256_GCM => AeadAlgResult::Ok(Algorithm::Aes256Gcm),
129        AEAD::ChaCha20Poly1305 => AeadAlgResult::Ok(Algorithm::Chacha20Poly1305),
130        AEAD::Export_only => AeadAlgResult::Err(HpkeError::UnsupportedAlgorithm),
131    }
132}
133
134/// Encrypt and authenticate plaintext `pt` with associated data `aad` using
135/// symmetric key `key` and nonce `nonce`, yielding ciphertext and tag `ct`.
136/// This function can raise a [`MessageLimitReachedError`](`HpkeError::MessageLimitReachedError`) upon failure.
137pub fn AeadSeal(aead_id: AEAD, key: &Key, nonce: &Nonce, aad: &[u8], pt: &[u8]) -> HpkeBytesResult {
138    let algorithm = alg_for_aead(aead_id)?;
139    let key = aead::Key::from_slice(algorithm, key)?;
140    match encrypt_detached(&key, pt, Iv::new(nonce)?, aad) {
141        Ok((tag, mut ct)) => {
142            ct.extend_from_slice(tag.as_ref());
143            HpkeBytesResult::Ok(ct)
144        }
145        Err(_) => HpkeBytesResult::Err(HpkeError::CryptoError),
146    }
147}
148
149/// Decrypt ciphertext and tag `ct` using
150/// associated data `aad` with symmetric key `key` and nonce `nonce`,
151/// returning plaintext message `pt`. This function can raise an
152/// [`OpenError`](`HpkeError::OpenError`) or [`MessageLimitReachedError`](`HpkeError::MessageLimitReachedError`) upon failure.
153pub fn AeadOpen(aead_id: AEAD, key: &Key, nonce: &Nonce, aad: &[u8], ct: &[u8]) -> HpkeBytesResult {
154    let algorithm = alg_for_aead(aead_id)?;
155    let key = aead::Key::from_slice(algorithm, key)?;
156    let mut ct = ct.to_vec();
157    let tag = ct.split_off(ct.len() - Nt(aead_id));
158    match decrypt_detached(&key, ct, Iv::new(nonce)?, aad, &Tag::from_slice(tag)?) {
159        Ok(pt) => HpkeBytesResult::Ok(pt.into()),
160        Err(_) => HpkeBytesResult::Err(HpkeError::CryptoError),
161    }
162}