Skip to main content

gbp_sframe/
kdf.rs

1use gbp_mls::MlsContext;
2
3use crate::error::SFrameError;
4
5/// SFrame ciphersuite selection.
6///
7/// `Aes128Gcm` is the standard choice; `Aes256Gcm` is available for
8/// high-assurance deployments.
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum CipherSuite {
11    /// AES-128-GCM with SHA-256 key expansion (RFC 9605 `AES_128_GCM_SHA256_128`).
12    Aes128Gcm,
13    /// AES-256-GCM with SHA-512 key expansion (RFC 9605 `AES_256_GCM_SHA512_128`).
14    Aes256Gcm,
15}
16
17impl CipherSuite {
18    /// Numeric discriminant used in the FFI (`0` = AES-128, `1` = AES-256).
19    pub fn from_u8(v: u8) -> Option<Self> {
20        match v {
21            0 => Some(Self::Aes128Gcm),
22            1 => Some(Self::Aes256Gcm),
23            _ => None,
24        }
25    }
26
27    /// Numeric discriminant.
28    pub fn as_u8(self) -> u8 {
29        match self {
30            Self::Aes128Gcm => 0,
31            Self::Aes256Gcm => 1,
32        }
33    }
34
35    /// Maps to the corresponding RFC 9605 suite in the `sframe` crate.
36    pub(crate) fn to_sframe(self) -> sframe::CipherSuite {
37        match self {
38            Self::Aes128Gcm => sframe::CipherSuite::AesGcm128Sha256,
39            Self::Aes256Gcm => sframe::CipherSuite::AesGcm256Sha512,
40        }
41    }
42}
43
44/// Derives the 32-byte SFrame base key from the MLS `ExportSecret`.
45///
46/// `label` is the application-defined export label (e.g. `"gbp/sframe v1"`).
47/// `epoch` is passed as an 8-byte big-endian context to bind the key to the
48/// current MLS epoch. Per-sender keys are then expanded from this base key by
49/// the `sframe` crate (RFC 9605 ยง5.2), keyed by the frame's KID.
50pub fn derive_base_key(mls: &MlsContext, label: &str, epoch: u64) -> Result<[u8; 32], SFrameError> {
51    let context = epoch.to_be_bytes();
52    let raw = mls
53        .export_raw(label, &context, 32)
54        .map_err(|e| SFrameError::MlsExport(e.to_string()))?;
55    let mut out = [0u8; 32];
56    out.copy_from_slice(&raw);
57    Ok(out)
58}