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}