macula_rust/seal.rs
1//! End-to-end seal scheme 1 (macula 13, E2E design, amendment A1): what a
2//! provider's advertisement names its KEM key by. A key travels as carried,
3//! ML-KEM-1024's encapsulation key under pq_pure, followed by a P-384 point
4//! under pq_hybrid, and is named by its id, the first 8 bytes of its SHA-384,
5//! as macula_seal's key_id/1. Pinned by tests/vectors/seal/e2e_seal_v1.json.
6
7use sha2::{Digest, Sha384};
8
9use crate::profile::Profile;
10
11/// The bytes of a key id.
12pub const KEY_ID_SIZE: usize = 8;
13
14const MLKEM_EK_BYTES: usize = 1568;
15const P384_POINT_BYTES: usize = 97;
16
17/// The size of a KEM key as carried under `profile`.
18pub fn carried_key_size(profile: Profile) -> usize {
19 match profile {
20 Profile::PqPure => MLKEM_EK_BYTES,
21 Profile::PqHybrid => MLKEM_EK_BYTES + P384_POINT_BYTES,
22 }
23}
24
25/// Whether `len` is a carried KEM key's size under some profile, as
26/// macula_record's kem_key_sizes/0: an advertisement's key is checked by size
27/// alone, whatever the reader's profile.
28pub fn is_carried_key_size(len: usize) -> bool {
29 [Profile::PqPure, Profile::PqHybrid]
30 .into_iter()
31 .any(|p| carried_key_size(p) == len)
32}
33
34/// A carried key's id: the first 8 bytes of its SHA-384.
35pub fn key_id(carried: &[u8]) -> [u8; KEY_ID_SIZE] {
36 let hash = Sha384::digest(carried);
37 let mut id = [0; KEY_ID_SIZE];
38 id.copy_from_slice(&hash[..KEY_ID_SIZE]);
39 id
40}