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}