#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum KemId {
DhkemP256HkdfSha256,
DhkemX25519HkdfSha256,
}
impl KemId {
#[must_use]
pub const fn id(self) -> u16 {
match self {
KemId::DhkemP256HkdfSha256 => 0x0010,
KemId::DhkemX25519HkdfSha256 => 0x0020,
}
}
#[must_use]
pub const fn n_secret(self) -> usize {
match self {
KemId::DhkemP256HkdfSha256 | KemId::DhkemX25519HkdfSha256 => 32,
}
}
#[must_use]
pub const fn n_enc(self) -> usize {
match self {
KemId::DhkemP256HkdfSha256 => 65,
KemId::DhkemX25519HkdfSha256 => 32,
}
}
#[must_use]
pub const fn n_pk(self) -> usize {
self.n_enc()
}
#[must_use]
pub const fn n_sk(self) -> usize {
match self {
KemId::DhkemP256HkdfSha256 | KemId::DhkemX25519HkdfSha256 => 32,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KdfId {
HkdfSha256,
HkdfSha384,
HkdfSha512,
}
impl KdfId {
#[must_use]
pub const fn id(self) -> u16 {
match self {
KdfId::HkdfSha256 => 0x0001,
KdfId::HkdfSha384 => 0x0002,
KdfId::HkdfSha512 => 0x0003,
}
}
#[must_use]
pub const fn n_h(self) -> usize {
match self {
KdfId::HkdfSha256 => 32,
KdfId::HkdfSha384 => 48,
KdfId::HkdfSha512 => 64,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AeadId {
Aes128Gcm,
Aes256Gcm,
ChaCha20Poly1305,
ExportOnly,
}
impl AeadId {
#[must_use]
pub const fn id(self) -> u16 {
match self {
AeadId::Aes128Gcm => 0x0001,
AeadId::Aes256Gcm => 0x0002,
AeadId::ChaCha20Poly1305 => 0x0003,
AeadId::ExportOnly => 0xFFFF,
}
}
#[must_use]
pub const fn n_k(self) -> usize {
match self {
AeadId::Aes128Gcm => 16,
AeadId::Aes256Gcm | AeadId::ChaCha20Poly1305 => 32,
AeadId::ExportOnly => 0,
}
}
#[must_use]
pub const fn n_n(self) -> usize {
match self {
AeadId::Aes128Gcm | AeadId::Aes256Gcm | AeadId::ChaCha20Poly1305 => 12,
AeadId::ExportOnly => 0,
}
}
#[must_use]
pub const fn n_t(self) -> usize {
match self {
AeadId::Aes128Gcm | AeadId::Aes256Gcm | AeadId::ChaCha20Poly1305 => 16,
AeadId::ExportOnly => 0,
}
}
}
#[must_use]
pub fn i2osp(n: u128, w: usize) -> Vec<u8> {
let be = n.to_be_bytes();
let mut out = vec![0u8; w];
let take = core::cmp::min(w, be.len());
out[w - take..].copy_from_slice(&be[be.len() - take..]);
out
}
#[must_use]
pub fn kem_suite_id(kem: KemId) -> Vec<u8> {
let mut id = Vec::with_capacity(5);
id.extend_from_slice(b"KEM");
id.extend_from_slice(&i2osp(u128::from(kem.id()), 2));
id
}
#[must_use]
pub fn hpke_suite_id(kem: KemId, kdf: KdfId, aead: AeadId) -> Vec<u8> {
let mut id = Vec::with_capacity(10);
id.extend_from_slice(b"HPKE");
id.extend_from_slice(&i2osp(u128::from(kem.id()), 2));
id.extend_from_slice(&i2osp(u128::from(kdf.id()), 2));
id.extend_from_slice(&i2osp(u128::from(aead.id()), 2));
id
}
#[cfg(test)]
mod ids_tests {
use super::*;
#[test]
fn i2osp_basic_values() {
assert_eq!(i2osp(0, 1), vec![0x00]);
assert_eq!(i2osp(0, 2), vec![0x00, 0x00]);
assert_eq!(i2osp(1, 1), vec![0x01]);
assert_eq!(i2osp(255, 1), vec![0xff]);
assert_eq!(i2osp(256, 2), vec![0x01, 0x00]);
assert_eq!(i2osp(0x0010, 2), vec![0x00, 0x10]);
assert_eq!(i2osp(0x0020, 2), vec![0x00, 0x20]);
assert_eq!(i2osp(0xFFFF, 2), vec![0xff, 0xff]);
assert_eq!(i2osp(32, 2), vec![0x00, 0x20]);
}
#[test]
fn i2osp_wider_than_input() {
assert_eq!(i2osp(1, 4), vec![0x00, 0x00, 0x00, 0x01]);
}
#[test]
fn kem_suite_id_bytes() {
assert_eq!(
kem_suite_id(KemId::DhkemX25519HkdfSha256),
vec![0x4b, 0x45, 0x4d, 0x00, 0x20]
);
assert_eq!(
kem_suite_id(KemId::DhkemP256HkdfSha256),
vec![0x4b, 0x45, 0x4d, 0x00, 0x10]
);
}
#[test]
fn hpke_suite_id_bytes() {
let id = hpke_suite_id(
KemId::DhkemX25519HkdfSha256,
KdfId::HkdfSha256,
AeadId::Aes128Gcm,
);
assert_eq!(
id,
vec![0x48, 0x50, 0x4b, 0x45, 0x00, 0x20, 0x00, 0x01, 0x00, 0x01]
);
}
#[test]
fn identifier_values() {
assert_eq!(KemId::DhkemP256HkdfSha256.id(), 0x0010);
assert_eq!(KemId::DhkemX25519HkdfSha256.id(), 0x0020);
assert_eq!(KdfId::HkdfSha256.id(), 1);
assert_eq!(KdfId::HkdfSha384.id(), 2);
assert_eq!(KdfId::HkdfSha512.id(), 3);
assert_eq!(AeadId::Aes128Gcm.id(), 1);
assert_eq!(AeadId::Aes256Gcm.id(), 2);
assert_eq!(AeadId::ChaCha20Poly1305.id(), 3);
assert_eq!(AeadId::ExportOnly.id(), 0xFFFF);
}
#[test]
fn length_constants() {
assert_eq!(KemId::DhkemX25519HkdfSha256.n_enc(), 32);
assert_eq!(KemId::DhkemX25519HkdfSha256.n_pk(), 32);
assert_eq!(KemId::DhkemX25519HkdfSha256.n_sk(), 32);
assert_eq!(KemId::DhkemX25519HkdfSha256.n_secret(), 32);
assert_eq!(KemId::DhkemP256HkdfSha256.n_enc(), 65);
assert_eq!(KemId::DhkemP256HkdfSha256.n_pk(), 65);
assert_eq!(KemId::DhkemP256HkdfSha256.n_sk(), 32);
assert_eq!(KemId::DhkemP256HkdfSha256.n_secret(), 32);
assert_eq!(KdfId::HkdfSha256.n_h(), 32);
assert_eq!(KdfId::HkdfSha384.n_h(), 48);
assert_eq!(KdfId::HkdfSha512.n_h(), 64);
assert_eq!(AeadId::Aes128Gcm.n_k(), 16);
assert_eq!(AeadId::Aes128Gcm.n_n(), 12);
assert_eq!(AeadId::Aes128Gcm.n_t(), 16);
assert_eq!(AeadId::Aes256Gcm.n_k(), 32);
assert_eq!(AeadId::ChaCha20Poly1305.n_k(), 32);
assert_eq!(AeadId::ExportOnly.n_k(), 0);
assert_eq!(AeadId::ExportOnly.n_n(), 0);
assert_eq!(AeadId::ExportOnly.n_t(), 0);
}
}