1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! HKDF-SHA256 and HKDF-SHA512 key schedule with domain separation.
//!
//! # Responsibility scope
//! This module derives session keys from KEM shared secrets using HKDF (RFC 5869).
//! It owns the domain-separation label constants and the `derive_session_key` function.
//! Input validation and type conversion stay in this module; the caller never sees raw
//! arrays or output bytes — only the `SessionKey` newtype.
//!
//! # Key types exported
//! - [`derive_session_key`] — primary derivation function (SHA-256 backed)
//! - [`derive_session_key_sha512`] — SHA-512 backed variant
//! - Label constants: [`LABEL_XCHACHA20POLY1305`], [`LABEL_AESGCMSIV`], [`LABEL_GENERIC`]
//!
//! # Concurrency
//! All functions are pure (no shared state). `Send + Sync` trivially.
//!
//! # Errors
//! Returns [`CryptError::CustomError`] if the HKDF output length is invalid (>255 * HashLen).
//! In practice this never occurs because the output is always 32 bytes.
//!
//! # Examples
//! ```rust,no_run
//! use crypt_guard::kdf::{derive_session_key, LABEL_XCHACHA20POLY1305};
//! use crypt_guard::kdf::types::HkdfSalt;
//!
//! let shared_secret = vec![0u8; 32];
//! let salt = HkdfSalt::zero(32);
//! let session_key = derive_session_key(&shared_secret, &salt, LABEL_XCHACHA20POLY1305).unwrap();
//! assert_eq!(session_key.as_ref().len(), 32);
//! ```
use crateCryptError;
use crate;
use Hkdf;
use ;
/// Domain separation label for XChaCha20-Poly1305 AEAD.
///
/// # Description
/// Used as the `info` parameter to HKDF-Expand. Ensures that session keys derived for
/// different AEAD algorithms are independent even from the same shared secret.
pub const LABEL_XCHACHA20POLY1305: & = b"crypt_guard:v2:aead:xchacha20poly1305";
/// Domain separation label for AES-256-GCM-SIV AEAD.
pub const LABEL_AESGCMSIV: & = b"crypt_guard:v2:aead:aes-gcm-siv";
/// Domain separation label for AES-256-CBC (legacy HMAC mode).
pub const LABEL_AES: & = b"crypt_guard:v2:aead:aes-cbc-hmac";
/// Domain separation label for XChaCha20 (stream, no authentication).
pub const LABEL_XCHACHA20: & = b"crypt_guard:v2:aead:xchacha20";
/// Generic domain separation label for unnamed AEAD algorithms.
pub const LABEL_GENERIC: & = b"crypt_guard:v2:aead:generic";
/// Derive a 32-byte session key from a KEM shared secret using HKDF-SHA256.
///
/// # Description
/// Implements the key schedule: `HKDF(salt, shared_secret, label) → 32-byte SessionKey`.
/// The `label` parameter provides algorithm-level domain separation so that session keys
/// for different AEAD algorithms derived from the same shared secret are independent.
///
/// Use one of the provided label constants (`LABEL_*`) for known algorithm names, or
/// supply an arbitrary `b"crypt_guard:v2:aead:<your-alg>"` prefix for custom algorithms.
///
/// # Arguments
/// - `shared_secret` (`&[u8]`): the KEM shared secret bytes (IKM in HKDF terms).
/// - `salt` (`&HkdfSalt`): the HKDF salt (use `HkdfSalt::zero(32)` if no random salt is
/// available; HKDF handles the zero-salt case as specified in RFC 5869 §2.2).
/// - `label` (`&[u8]`): domain-separation context string (the `info` parameter).
///
/// # Returns
/// `Ok(SessionKey)` — a 32-byte zeroizing session key.
///
/// # Errors
/// - [`CryptError::CustomError`]: HKDF output length was invalid. Cannot occur in practice
/// because the output length is always 32 bytes.
///
/// # Concurrency
/// Pure function; no shared state. Safe to call concurrently.
///
/// # Examples
/// ```rust,no_run
/// use crypt_guard::kdf::{derive_session_key, LABEL_XCHACHA20POLY1305};
/// use crypt_guard::kdf::types::HkdfSalt;
///
/// let ss = vec![42u8; 32];
/// let salt = HkdfSalt::zero(32);
/// let key = derive_session_key(&ss, &salt, LABEL_XCHACHA20POLY1305).unwrap();
/// assert_eq!(key.as_ref().len(), 32);
/// ```
/// Derive a 32-byte session key from a KEM shared secret using HKDF-SHA512.
///
/// # Description
/// SHA-512 backed variant of [`derive_session_key`]. Preferred when the hash of the
/// shared secret must be 512-bit before truncation (e.g. for suite-level compliance).
/// The output is still 32 bytes (the first 256 bits of the 512-bit PRK).
///
/// # Arguments
/// - `shared_secret` (`&[u8]`): the KEM shared secret bytes.
/// - `salt` (`&HkdfSalt`): the HKDF salt.
/// - `label` (`&[u8]`): domain-separation context string.
///
/// # Returns
/// `Ok(SessionKey)` — a 32-byte zeroizing session key.
///
/// # Errors
/// - [`CryptError::CustomError`]: HKDF output length was invalid.
///
/// # Concurrency
/// Pure function; no shared state. Safe to call concurrently.
///
/// # Examples
/// ```rust,no_run
/// use crypt_guard::kdf::{derive_session_key_sha512, LABEL_AESGCMSIV};
/// use crypt_guard::kdf::types::HkdfSalt;
///
/// let ss = vec![42u8; 32];
/// let salt = HkdfSalt::zero(64);
/// let key = derive_session_key_sha512(&ss, &salt, LABEL_AESGCMSIV).unwrap();
/// assert_eq!(key.as_ref().len(), 32);
/// ```