Skip to content

Commit a5e5926

Browse files
authored
Add AES-GCM support to libcrux provider (#98)
* implement AES-GCM support in libcrux provider * test `HpkeLibcrux` provider with `Aes128Gcm` and `Aes256Gcm` * test `HpkeLibcrux` provider with all `KdfAlgorithm` variants * update changelogs
1 parent 23bb774 commit a5e5926

File tree

6 files changed

+312
-31
lines changed

6 files changed

+312
-31
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
810
## [0.3.0] - 2025-07-01
911

12+
- [#98](https://github.com/cryspen/hpke-rs/pull/98): add support for AES-GCM to the Libcrux provider
13+
1014
- [#77]():
1115
- `rustcrypto` and `libcrux` features expose the corresponding crypto providers
1216
- trait types are re-exported as `hpke_types` for convenience

libcrux_provider/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [Unreleased]
8+
9+
- [#98](https://github.com/cryspen/hpke-rs/pull/98): add support for AES-GCM to the provider
10+
711
## 0.3.0 - 2025-07-01
812

913
* initial release

libcrux_provider/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ hpke-rs-crypto = { version = "0.3.0", path = "../traits" }
1414
libcrux-ecdh = { version = "0.0.4", default-features = false }
1515
libcrux-hkdf = { version = "0.0.4" }
1616
libcrux-kem = { version = "0.0.4", default-features = false }
17-
libcrux-chacha20poly1305 = { version = "0.0.4" }
17+
libcrux-aead = { version = "0.0.4" }
18+
libcrux-traits = { version = "0.0.4", default-features = false }
1819
# Randomness
1920
rand = { version = "0.9", default-features = false }
2021
rand_core = { version = "0.9", features = ["os_rng"] }

libcrux_provider/src/lib.rs

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,28 @@ impl HpkeCrypto for HpkeLibcrux {
144144
aad: &[u8],
145145
msg: &[u8],
146146
) -> Result<Vec<u8>, Error> {
147-
// only chacha20poly1305 is supported
148-
if !matches!(alg, AeadAlgorithm::ChaCha20Poly1305) {
149-
return Err(Error::UnknownAeadAlgorithm);
150-
}
147+
let alg = aead_alg(alg)?;
148+
149+
use libcrux_traits::aead::typed_refs::Aead as _;
151150

152-
let iv = <&[u8; 12]>::try_from(nonce).map_err(|_| Error::AeadInvalidNonce)?;
151+
// set up buffer for ctxt and tag
152+
let mut msg_ctx: Vec<u8> = alloc::vec![0; msg.len() + alg.tag_len()];
153+
let (ctxt, tag) = msg_ctx.split_at_mut(msg.len());
153154

154-
// TODO: instead, use key conversion from the libcrux-chacha20poly1305 crate, when available,
155-
let key = <&[u8; 32]>::try_from(key)
155+
// set up nonce
156+
let nonce = alg.new_nonce(nonce).map_err(|_| Error::AeadInvalidNonce)?;
157+
158+
// set up key
159+
let key = alg
160+
.new_key(key)
156161
.map_err(|_| Error::CryptoLibraryError("AEAD invalid key length".into()))?;
157162

158-
let mut msg_ctx: Vec<u8> = alloc::vec![0; msg.len() + 16];
159-
libcrux_chacha20poly1305::encrypt(key, msg, &mut msg_ctx, aad, iv)
163+
// set up tag
164+
let tag = alg
165+
.new_tag_mut(tag)
166+
.map_err(|_| Error::CryptoLibraryError("Invalid tag length".into()))?;
167+
168+
key.encrypt(ctxt, tag, nonce, aad, msg)
160169
.map_err(|_| Error::CryptoLibraryError("Invalid configuration".into()))?;
161170

162171
Ok(msg_ctx)
@@ -169,31 +178,40 @@ impl HpkeCrypto for HpkeLibcrux {
169178
aad: &[u8],
170179
cipher_txt: &[u8],
171180
) -> Result<Vec<u8>, Error> {
172-
// only chacha20poly1305 is supported
173-
if !matches!(alg, AeadAlgorithm::ChaCha20Poly1305) {
174-
return Err(Error::UnknownAeadAlgorithm);
175-
}
176-
if cipher_txt.len() < 16 {
181+
let alg = aead_alg(alg)?;
182+
183+
use libcrux_traits::aead::typed_refs::{Aead as _, DecryptError};
184+
185+
if cipher_txt.len() < alg.tag_len() {
177186
return Err(Error::AeadInvalidCiphertext);
178187
}
179188

180-
let boundary = cipher_txt.len() - 16;
189+
let boundary = cipher_txt.len() - alg.tag_len();
181190

191+
// set up buffers for ptext, ctext, and tag
182192
let mut ptext = alloc::vec![0; boundary];
193+
let (ctext, tag) = cipher_txt.split_at(boundary);
183194

184-
let iv = <&[u8; 12]>::try_from(nonce).map_err(|_| Error::AeadInvalidNonce)?;
195+
// set up nonce
196+
let nonce = alg.new_nonce(nonce).map_err(|_| Error::AeadInvalidNonce)?;
185197

186-
// TODO: instead, use key conversion from the libcrux-chacha20poly1305 crate, when available,
187-
let key = <&[u8; 32]>::try_from(key)
198+
// set up key
199+
let key = alg
200+
.new_key(key)
188201
.map_err(|_| Error::CryptoLibraryError("AEAD invalid key length".into()))?;
189-
libcrux_chacha20poly1305::decrypt(key, &mut ptext, cipher_txt, aad, iv).map_err(
190-
|e| match e {
191-
libcrux_chacha20poly1305::AeadError::InvalidCiphertext => {
202+
203+
// set up tag
204+
let tag = alg
205+
.new_tag(tag)
206+
.map_err(|_| Error::CryptoLibraryError("Invalid tag length".into()))?;
207+
208+
key.decrypt(&mut ptext, nonce, aad, ctext, tag)
209+
.map_err(|e| match e {
210+
DecryptError::InvalidTag => {
192211
Error::CryptoLibraryError(format!("AEAD decryption error: {:?}", e))
193212
}
194213
_ => Error::CryptoLibraryError("Invalid configuration".into()),
195-
},
196-
)?;
214+
})?;
197215

198216
Ok(ptext)
199217
}
@@ -237,8 +255,7 @@ impl HpkeCrypto for HpkeLibcrux {
237255
/// Returns an error if the AEAD algorithm is not supported by this crypto provider.
238256
fn supports_aead(alg: AeadAlgorithm) -> Result<(), Error> {
239257
match alg {
240-
// Don't support Aes
241-
AeadAlgorithm::Aes128Gcm | AeadAlgorithm::Aes256Gcm => Err(Error::UnknownAeadAlgorithm),
258+
AeadAlgorithm::Aes128Gcm | AeadAlgorithm::Aes256Gcm => Ok(()),
242259
AeadAlgorithm::ChaCha20Poly1305 => Ok(()),
243260
AeadAlgorithm::HpkeExport => Ok(()),
244261
}
@@ -295,6 +312,16 @@ fn kem_key_type_to_ecdh_alg(alg: KemAlgorithm) -> Result<libcrux_ecdh::Algorithm
295312
}
296313
}
297314

315+
#[inline(always)]
316+
fn aead_alg(alg_type: AeadAlgorithm) -> Result<libcrux_aead::Aead, Error> {
317+
match alg_type {
318+
AeadAlgorithm::ChaCha20Poly1305 => Ok(libcrux_aead::Aead::ChaCha20Poly1305),
319+
AeadAlgorithm::Aes128Gcm => Ok(libcrux_aead::Aead::AesGcm128),
320+
AeadAlgorithm::Aes256Gcm => Ok(libcrux_aead::Aead::AesGcm256),
321+
_ => Err(Error::UnknownAeadAlgorithm),
322+
}
323+
}
324+
298325
impl hpke_rs_crypto::RngCore for HpkeLibcruxPrng {
299326
fn next_u32(&mut self) -> u32 {
300327
self.rng.next_u32()

src/test_aead.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,36 @@ fn test_aes_gcm_128_self() {
2121
HpkeRustCrypto::aead_open(AeadAlgorithm::Aes128Gcm, &key, &nonce, &aad, &ctxt).unwrap();
2222
assert_eq!(&ptxt, msg);
2323

24-
// assert error on Libcrux provider
25-
HpkeLibcrux::aead_seal(AeadAlgorithm::Aes128Gcm, &key, &nonce, &aad, msg)
26-
.expect_err("Unsupported algorithm");
27-
HpkeLibcrux::aead_open(AeadAlgorithm::Aes128Gcm, &key, &nonce, &aad, &ctxt)
28-
.expect_err("Unsupported algorithm");
24+
// test libcrux crypto provider
25+
let ctxt = HpkeLibcrux::aead_seal(AeadAlgorithm::Aes128Gcm, &key, &nonce, &aad, msg).unwrap();
26+
let ptxt = HpkeLibcrux::aead_open(AeadAlgorithm::Aes128Gcm, &key, &nonce, &aad, &ctxt).unwrap();
27+
assert_eq!(&ptxt, msg);
28+
}
29+
30+
#[test]
31+
fn test_aes_gcm_256_self() {
32+
let key = [
33+
0x5b, 0x96, 0x04, 0xfe, 0x14, 0xea, 0xdb, 0xa9, 0x31, 0xb0, 0xcc, 0xf3, 0x48, 0x43, 0xda,
34+
0xb9, 0x5b, 0x96, 0x04, 0xfe, 0x14, 0xea, 0xdb, 0xa9, 0x31, 0xb0, 0xcc, 0xf3, 0x48, 0x43,
35+
0xda, 0xb9,
36+
];
37+
let nonce = [
38+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
39+
];
40+
let aad = [0x03, 0x04, 0x05];
41+
let msg = b"test message";
42+
43+
// test rust crypto provider
44+
let ctxt =
45+
HpkeRustCrypto::aead_seal(AeadAlgorithm::Aes256Gcm, &key, &nonce, &aad, msg).unwrap();
46+
let ptxt =
47+
HpkeRustCrypto::aead_open(AeadAlgorithm::Aes256Gcm, &key, &nonce, &aad, &ctxt).unwrap();
48+
assert_eq!(&ptxt, msg);
49+
50+
// test libcrux crypto provider
51+
let ctxt = HpkeLibcrux::aead_seal(AeadAlgorithm::Aes256Gcm, &key, &nonce, &aad, msg).unwrap();
52+
let ptxt = HpkeLibcrux::aead_open(AeadAlgorithm::Aes256Gcm, &key, &nonce, &aad, &ctxt).unwrap();
53+
assert_eq!(&ptxt, msg);
2954
}
3055

3156
#[test]

0 commit comments

Comments
 (0)