libcrux/hpke/
kdf.rs

1#![doc = include_str!("KDF_Readme.md")]
2#![allow(non_snake_case, non_camel_case_types)]
3
4use crate::hkdf::Algorithm;
5
6use super::errors::*;
7
8/// ## Key Derivation Functions (KDFs)
9///
10/// | Value  | KDF         | Nh  | Reference |
11/// | :----- | :---------- | --- | :-------- |
12/// | 0x0000 | (reserved)  | N/A | N/A       |
13/// | 0x0001 | HKDF-SHA256 | 32  | [RFC5869] |
14/// | 0x0002 | HKDF-SHA384 | 48  | [RFC5869] |
15/// | 0x0003 | HKDF-SHA512 | 64  | [RFC5869] |
16///
17/// ### KDF Identifiers
18///
19/// The "HPKE KDF Identifiers" registry lists identifiers for key derivation
20/// functions defined for use with HPKE. These identifiers are two-byte values,
21/// so the maximum possible value is 0xFFFF = 65535.
22///
23/// Template:
24///
25/// * Value: The two-byte identifier for the algorithm
26/// * KDF: The name of the algorithm
27/// * Nh: The output size of the Extract function in bytes
28/// * Reference: Where this algorithm is defined
29///
30/// [RFC5869]: https://www.rfc-editor.org/info/rfc5869
31#[derive(Clone, Copy, PartialEq, Debug)]
32pub enum KDF {
33    /// 0x0001
34    HKDF_SHA256,
35    /// 0x0002
36    HKDF_SHA384,
37    /// 0x0003
38    HKDF_SHA512,
39}
40
41pub type InputKeyMaterial = [u8];
42pub type Info = [u8];
43
44/// Get the numeric value of the `kdf_id`.
45///
46/// See [`KDF`] for details.
47pub fn kdf_value(kdf_id: KDF) -> u16 {
48    match kdf_id {
49        KDF::HKDF_SHA256 => 0x0001u16,
50        KDF::HKDF_SHA384 => 0x0002u16,
51        KDF::HKDF_SHA512 => 0x0003u16,
52    }
53}
54
55/// The output size of the `Extract()` function in bytes.
56///
57/// See [`KDF`] for details.
58pub fn Nh(kdf_id: KDF) -> usize {
59    match kdf_id {
60        KDF::HKDF_SHA256 => 32,
61        KDF::HKDF_SHA384 => 48,
62        KDF::HKDF_SHA512 => 64,
63    }
64}
65
66/// The string literal "HPKE-v1" used in [`LabeledExtract()`] and [`LabeledExpand()`]
67/// ensures that any secrets derived in HPKE are bound to the scheme's name
68/// and version, even when possibly derived from the same Diffie-Hellman or
69/// KEM shared secret as in another scheme or version.
70fn hpke_version_label() -> Vec<u8> {
71    vec![0x48u8, 0x50u8, 0x4bu8, 0x45u8, 0x2du8, 0x76u8, 0x31u8]
72}
73
74fn hkdf_algorithm(alg: KDF) -> Algorithm {
75    match alg {
76        KDF::HKDF_SHA256 => Algorithm::Sha256,
77        KDF::HKDF_SHA384 => Algorithm::Sha384,
78        KDF::HKDF_SHA512 => Algorithm::Sha512,
79    }
80}
81
82/// LabeledExtract
83///
84/// ```text
85/// def LabeledExtract(salt, label, ikm):
86///   labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
87///   return Extract(salt, labeled_ikm)
88/// ```
89pub fn LabeledExtract(
90    alg: KDF,
91    suite_id: Vec<u8>,
92    salt: &[u8],
93    label: Vec<u8>,
94    ikm: &InputKeyMaterial,
95) -> HpkeBytesResult {
96    let mut labeled_ikm = hpke_version_label();
97    labeled_ikm.extend_from_slice(&suite_id);
98    labeled_ikm.extend_from_slice(&label);
99    labeled_ikm.extend_from_slice(ikm);
100
101    Ok(crate::hkdf::extract(
102        hkdf_algorithm(alg),
103        salt,
104        &labeled_ikm,
105    ))
106}
107
108/// KDF: Labeled Expand
109///
110/// ```text
111/// def LabeledExpand(prk, label, info, L):
112///   labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id,
113///                         label, info)
114///   return Expand(prk, labeled_info, L)
115/// ```
116pub fn LabeledExpand(
117    alg: KDF,
118    suite_id: Vec<u8>,
119    prk: &[u8],
120    label: Vec<u8>,
121    info: &Info,
122    L: usize,
123) -> HpkeBytesResult {
124    if L > (255 * Nh(alg)) {
125        // This check is mentioned explicitly in the spec because because it
126        // must be adhered to when exporting secrets.
127        // The check comes from HKDF and will be performed there again.
128        Err(HpkeError::InvalidParameters)
129    } else {
130        let mut labeled_info = (L as u16).to_be_bytes().to_vec();
131        labeled_info.extend_from_slice(&hpke_version_label());
132        labeled_info.extend_from_slice(&suite_id);
133        labeled_info.extend_from_slice(&label);
134        labeled_info.extend_from_slice(info);
135
136        match crate::hkdf::expand(hkdf_algorithm(alg), prk, &labeled_info, L) {
137            Ok(r) => Ok(r),
138            Err(_) => Err(HpkeError::CryptoError),
139        }
140    }
141}