1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! # Authenticated Encryption (AEAD)
//!
//! > 💡 This is a hacspec representation of the [HPKE RFC].
//! > The text is mostly verbatim from the RFC with changes where required.
//! > It demonstrates the possibilities of hacspec for specifications.
//!
//! - [`Seal(key, nonce, aad, pt)`](`AeadSeal`): Encrypt and authenticate plaintext
//! `pt` with associated data `aad` using symmetric key `key` and nonce
//! `nonce`, yielding ciphertext and tag `ct`. This function
//!  can raise a [`MessageLimitReachedError`](`HpkeError::MessageLimitReachedError`) upon failure.
//! - [`Open(key, nonce, aad, ct)`](`AeadOpen`): Decrypt ciphertext and tag `ct` using
//! associated data `aad` with symmetric key `key` and nonce `nonce`,
//! returning plaintext message `pt`. This function can raise an
//! [`OpenError`](`HpkeError::OpenError`) or [`MessageLimitReachedError`](`HpkeError::MessageLimitReachedError`) upon failure.
//! - [`Nk`]: The length in bytes of a key for this algorithm.
//! - [`Nn`]: The length in bytes of a nonce for this algorithm.
//! - [`Nt`]: The length in bytes of the authentication tag for this algorithm.
//!
//! ## Security Requirements on an AEAD
//!
//! All AEADs MUST be IND-CCA2-secure, as is currently true for all AEADs
//! listed in [`AEAD`].
//!
//! [hpke rfc]: https://datatracker.ietf.org/doc/draft-irtf-cfrg-hpke/
//! [publication queue]: https://www.rfc-editor.org/current_queue.php
#![allow(
    non_camel_case_types,
    non_snake_case,
    unused_imports,
    non_upper_case_globals
)]

#[cfg(feature = "evercrypt")]
use evercrypt_cryptolib::*;
#[cfg(not(feature = "evercrypt"))]
use hacspec_cryptolib::*;

use hacspec_lib::*;

use hpke_errors::*;

type CryptoResult = Result<ByteSeq, CryptoError>;
type AeadAlgResult = Result<AeadAlgorithm, HpkeError>;

/// ## Authenticated Encryption with Associated Data (AEAD) Functions
///
/// The `0xFFFF` AEAD ID is reserved for applications which only use the Export
/// interface; see HPKE for more details.
///
/// | Value  | AEAD             | Nk  | Nn  | Nt  | Reference |
/// | :----- | :--------------- | :-- | :-- | :-- | :-------- |
/// | 0x0000 | (reserved)       | N/A | N/A | N/A | N/A       |
/// | 0x0001 | AES-128-GCM      | 16  | 12  | 16  | [GCM]     |
/// | 0x0002 | AES-256-GCM      | 32  | 12  | 16  | [GCM]     |
/// | 0x0003 | ChaCha20Poly1305 | 32  | 12  | 16  | [RFC8439] |
/// | 0xFFFF | Export-only      | N/A | N/A | N/A | RFCXXXX   |
///
/// The "HPKE AEAD Identifiers" registry lists identifiers for authenticated
/// encryption with associated data (AEAD) algorithms defined for use with HPKE.
/// These identifiers are two-byte values, so the maximum possible value is
/// 0xFFFF = 65535.
///
/// Template:
///
/// * Value: The two-byte identifier for the algorithm
/// * AEAD: The name of the algorithm
/// * Nk: The length in bytes of a key for this algorithm
/// * Nn: The length in bytes of a nonce for this algorithm
/// * Nt: The length in bytes of an authentication tag for this algorithm
/// * Reference: Where this algorithm is defined
///
/// [GCM]: https://doi.org/10.6028/nist.sp.800-38d
/// [RFC8439]: https://www.rfc-editor.org/info/rfc8439
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum AEAD {
    /// 0x0001
    AES_128_GCM,
    /// 0x0002
    AES_256_GCM,
    /// 0x0003
    ChaCha20Poly1305,
    /// 0xFFFF
    Export_only,
}

/// The length in bytes of a key for this algorithm.
///
/// See [`AEAD`] for details.
pub fn Nk(aead_id: AEAD) -> usize {
    match aead_id {
        AEAD::AES_128_GCM => 16,
        AEAD::AES_256_GCM => 32,
        AEAD::ChaCha20Poly1305 => 32,
        AEAD::Export_only => 0,
    }
}

/// The length in bytes of the authentication tag for this algorithm.
///
/// See [`AEAD`] for details.
pub fn Nt(aead_id: AEAD) -> usize {
    match aead_id {
        AEAD::AES_128_GCM => 16,
        AEAD::AES_256_GCM => 16,
        AEAD::ChaCha20Poly1305 => 16,
        AEAD::Export_only => 0,
    }
}

/// The length in bytes of a nonce for this algorithm.
///
/// See [`AEAD`] for details.
pub fn Nn(aead_id: AEAD) -> usize {
    match aead_id {
        AEAD::AES_128_GCM => 12,
        AEAD::AES_256_GCM => 12,
        AEAD::ChaCha20Poly1305 => 12,
        AEAD::Export_only => 0,
    }
}

/// An AEAD key is a sequence of bytes.
pub type Key = ByteSeq;

/// An AEAD nonce is a sequence of bytes.
pub type Nonce = ByteSeq;

fn alg_for_aead(aead_id: AEAD) -> AeadAlgResult {
    match aead_id {
        AEAD::AES_128_GCM => AeadAlgResult::Ok(AeadAlgorithm::Aes128Gcm),
        AEAD::AES_256_GCM => AeadAlgResult::Ok(AeadAlgorithm::Aes256Gcm),
        AEAD::ChaCha20Poly1305 => AeadAlgResult::Ok(AeadAlgorithm::Chacha20Poly1305),
        AEAD::Export_only => AeadAlgResult::Err(HpkeError::UnsupportedAlgorithm),
    }
}

/// Encrypt and authenticate plaintext `pt` with associated data `aad` using
/// symmetric key `key` and nonce `nonce`, yielding ciphertext and tag `ct`.
/// This function can raise a [`MessageLimitReachedError`](`HpkeError::MessageLimitReachedError`) upon failure.
pub fn AeadSeal(
    aead_id: AEAD,
    key: &Key,
    nonce: &Nonce,
    aad: &Seq<U8>,
    pt: &Seq<U8>,
) -> HpkeByteSeqResult {
    let algorithm = alg_for_aead(aead_id)?;
    match aead_encrypt(&algorithm, key, nonce, pt, aad) {
        CryptoResult::Ok(ct) => HpkeByteSeqResult::Ok(ct),
        CryptoResult::Err(_) => HpkeByteSeqResult::Err(HpkeError::CryptoError),
    }
}

/// Decrypt ciphertext and tag `ct` using
/// associated data `aad` with symmetric key `key` and nonce `nonce`,
/// returning plaintext message `pt`. This function can raise an
/// [`OpenError`](`HpkeError::OpenError`) or [`MessageLimitReachedError`](`HpkeError::MessageLimitReachedError`) upon failure.
pub fn AeadOpen(
    aead_id: AEAD,
    key: &Key,
    nonce: &Nonce,
    aad: &ByteSeq,
    ct: &ByteSeq,
) -> HpkeByteSeqResult {
    let algorithm = alg_for_aead(aead_id)?;
    match aead_decrypt(&algorithm, key, nonce, ct, aad) {
        CryptoResult::Ok(pt) => HpkeByteSeqResult::Ok(pt),
        CryptoResult::Err(_) => HpkeByteSeqResult::Err(HpkeError::CryptoError),
    }
}