⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/crypto/
cipher_suite.rs

1//! Unified cipher suite abstraction for protocol crypto
2//!
3//! Provides `CipherSuite` enum and supporting types (`HmacAlgorithm`,
4//! `KdfAlgorithm`) that SSH, QUIC, and WireGuard can use instead of inlining
5//! crypto primitives.
6
7#![allow(dead_code)]
8
9use alloc::vec::Vec;
10
11use super::CryptoResult;
12
13// ============================================================================
14// AEAD Cipher Suites
15// ============================================================================
16
17/// Unified AEAD cipher suite abstraction
18///
19/// Delegates to the existing `SymmetricCipher` implementations in
20/// `crypto::symmetric`.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub(crate) enum CipherSuite {
23    /// ChaCha20-Poly1305 (RFC 8439)
24    ChaCha20Poly1305,
25    /// AES-256-GCM (NIST SP 800-38D)
26    Aes256Gcm,
27}
28
29impl CipherSuite {
30    /// Encrypt with AEAD: returns ciphertext || tag
31    pub(crate) fn encrypt_aead(
32        &self,
33        key: &[u8],
34        nonce: &[u8],
35        _aad: &[u8],
36        plaintext: &[u8],
37    ) -> CryptoResult<Vec<u8>> {
38        use super::symmetric::SymmetricCipher;
39        match self {
40            Self::ChaCha20Poly1305 => {
41                let cipher = super::symmetric::ChaCha20Poly1305::new(key)?;
42                cipher.encrypt(plaintext, nonce)
43            }
44            Self::Aes256Gcm => {
45                let cipher = super::symmetric::Aes256Gcm::new(key)?;
46                cipher.encrypt(plaintext, nonce)
47            }
48        }
49    }
50
51    /// Decrypt with AEAD: input is ciphertext || tag, returns plaintext
52    pub(crate) fn decrypt_aead(
53        &self,
54        key: &[u8],
55        nonce: &[u8],
56        _aad: &[u8],
57        ciphertext_and_tag: &[u8],
58    ) -> CryptoResult<Vec<u8>> {
59        use super::symmetric::SymmetricCipher;
60        match self {
61            Self::ChaCha20Poly1305 => {
62                let cipher = super::symmetric::ChaCha20Poly1305::new(key)?;
63                cipher.decrypt(ciphertext_and_tag, nonce)
64            }
65            Self::Aes256Gcm => {
66                let cipher = super::symmetric::Aes256Gcm::new(key)?;
67                cipher.decrypt(ciphertext_and_tag, nonce)
68            }
69        }
70    }
71
72    /// Key size in bytes
73    pub(crate) fn key_size(&self) -> usize {
74        match self {
75            Self::ChaCha20Poly1305 => 32,
76            Self::Aes256Gcm => 32,
77        }
78    }
79
80    /// Nonce size in bytes
81    pub(crate) fn nonce_size(&self) -> usize {
82        match self {
83            Self::ChaCha20Poly1305 => 12,
84            Self::Aes256Gcm => 12,
85        }
86    }
87
88    /// Authentication tag size in bytes
89    pub(crate) fn tag_size(&self) -> usize {
90        match self {
91            Self::ChaCha20Poly1305 => 16,
92            Self::Aes256Gcm => 16,
93        }
94    }
95
96    /// Algorithm name string
97    pub(crate) fn algorithm_name(&self) -> &'static str {
98        match self {
99            Self::ChaCha20Poly1305 => "ChaCha20-Poly1305",
100            Self::Aes256Gcm => "AES-256-GCM",
101        }
102    }
103}
104
105// ============================================================================
106// HMAC Algorithms
107// ============================================================================
108
109/// HMAC algorithm variants
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub(crate) enum HmacAlgorithm {
112    /// HMAC-SHA-256
113    HmacSha256,
114    /// HMAC-BLAKE2s (RFC 2104 construction with BLAKE2s)
115    HmacBlake2s,
116}
117
118impl HmacAlgorithm {
119    /// Compute HMAC over data with the given key
120    pub(crate) fn compute(&self, key: &[u8], data: &[u8]) -> [u8; 32] {
121        match self {
122            Self::HmacSha256 => hmac_sha256(key, data),
123            Self::HmacBlake2s => hmac_blake2s(key, data),
124        }
125    }
126
127    /// Output size in bytes
128    pub(crate) fn output_size(&self) -> usize {
129        match self {
130            Self::HmacSha256 => 32,
131            Self::HmacBlake2s => 32,
132        }
133    }
134}
135
136/// HMAC-SHA-256 implementation (RFC 2104)
137fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
138    let block_size = 64;
139    let mut padded_key = [0u8; 64];
140
141    if key.len() > block_size {
142        let hash = super::hash::sha256(key);
143        padded_key[..32].copy_from_slice(hash.as_bytes());
144    } else {
145        padded_key[..key.len()].copy_from_slice(key);
146    }
147
148    // Inner hash: H((key XOR ipad) || data)
149    let mut ipad = [0x36u8; 64];
150    for i in 0..64 {
151        ipad[i] ^= padded_key[i];
152    }
153    let mut inner_data = Vec::with_capacity(64 + data.len());
154    inner_data.extend_from_slice(&ipad);
155    inner_data.extend_from_slice(data);
156    let inner_hash = super::hash::sha256(&inner_data);
157
158    // Outer hash: H((key XOR opad) || inner_hash)
159    let mut opad = [0x5cu8; 64];
160    for i in 0..64 {
161        opad[i] ^= padded_key[i];
162    }
163    let mut outer_data = Vec::with_capacity(64 + 32);
164    outer_data.extend_from_slice(&opad);
165    outer_data.extend_from_slice(inner_hash.as_bytes());
166    let outer_hash = super::hash::sha256(&outer_data);
167
168    *outer_hash.as_bytes()
169}
170
171/// HMAC-BLAKE2s implementation (RFC 2104 construction)
172fn hmac_blake2s(key: &[u8], data: &[u8]) -> [u8; 32] {
173    use super::hash::Blake2s;
174
175    let block_size = 64;
176    let mut padded_key = [0u8; 64];
177
178    if key.len() > block_size {
179        let hash = super::hash::blake2s_hash(key, 32);
180        padded_key[..32].copy_from_slice(&hash);
181    } else {
182        padded_key[..key.len()].copy_from_slice(key);
183    }
184
185    // Inner hash: H((key XOR ipad) || data)
186    let mut ipad = [0x36u8; 64];
187    for i in 0..64 {
188        ipad[i] ^= padded_key[i];
189    }
190    let mut inner = Blake2s::new(32);
191    inner.update(&ipad);
192    inner.update(data);
193    let inner_hash = inner.finalize();
194
195    // Outer hash: H((key XOR opad) || inner_hash)
196    let mut opad = [0x5cu8; 64];
197    for i in 0..64 {
198        opad[i] ^= padded_key[i];
199    }
200    let mut outer = Blake2s::new(32);
201    outer.update(&opad);
202    outer.update(&inner_hash);
203    outer.finalize()
204}
205
206// ============================================================================
207// Key Derivation Functions
208// ============================================================================
209
210/// KDF algorithm variants
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub(crate) enum KdfAlgorithm {
213    /// HKDF using BLAKE2s as the underlying HMAC
214    HkdfBlake2s,
215}
216
217impl KdfAlgorithm {
218    /// HKDF-Extract: derive a PRK from salt and input keying material
219    pub(crate) fn extract(&self, salt: &[u8], ikm: &[u8]) -> [u8; 32] {
220        match self {
221            Self::HkdfBlake2s => HmacAlgorithm::HmacBlake2s.compute(salt, ikm),
222        }
223    }
224
225    /// HKDF-Expand: derive output keying material from PRK and info
226    ///
227    /// Returns up to 32 bytes of output keying material.
228    pub(crate) fn expand(&self, prk: &[u8], info: &[u8]) -> [u8; 32] {
229        match self {
230            Self::HkdfBlake2s => HmacAlgorithm::HmacBlake2s.compute(prk, info),
231        }
232    }
233
234    /// Combined extract-and-expand producing two 32-byte outputs
235    ///
236    /// This is the pattern used by WireGuard's Noise handshake:
237    /// PRK = HMAC(chaining_key, input), T1 = HMAC(PRK, 0x01),
238    /// T2 = HMAC(PRK, T1 || 0x02)
239    pub(crate) fn extract_expand2(
240        &self,
241        chaining_key: &[u8; 32],
242        input: &[u8],
243    ) -> ([u8; 32], [u8; 32]) {
244        match self {
245            Self::HkdfBlake2s => {
246                let prk = HmacAlgorithm::HmacBlake2s.compute(chaining_key, input);
247                let t1 = HmacAlgorithm::HmacBlake2s.compute(&prk, &[0x01]);
248                let mut t1_input = [0u8; 33];
249                t1_input[..32].copy_from_slice(&t1);
250                t1_input[32] = 0x02;
251                let t2 = HmacAlgorithm::HmacBlake2s.compute(&prk, &t1_input);
252                (t1, t2)
253            }
254        }
255    }
256
257    /// Combined extract-and-expand producing three 32-byte outputs
258    ///
259    /// Extended pattern for Noise handshake PSK mixing:
260    /// PRK = HMAC(chaining_key, input), T1 = HMAC(PRK, 0x01),
261    /// T2 = HMAC(PRK, T1 || 0x02), T3 = HMAC(PRK, T2 || 0x03)
262    pub(crate) fn extract_expand3(
263        &self,
264        chaining_key: &[u8; 32],
265        input: &[u8],
266    ) -> ([u8; 32], [u8; 32], [u8; 32]) {
267        match self {
268            Self::HkdfBlake2s => {
269                let prk = HmacAlgorithm::HmacBlake2s.compute(chaining_key, input);
270                let t1 = HmacAlgorithm::HmacBlake2s.compute(&prk, &[0x01]);
271                let mut t1_input = [0u8; 33];
272                t1_input[..32].copy_from_slice(&t1);
273                t1_input[32] = 0x02;
274                let t2 = HmacAlgorithm::HmacBlake2s.compute(&prk, &t1_input);
275                let mut t2_input = [0u8; 33];
276                t2_input[..32].copy_from_slice(&t2);
277                t2_input[32] = 0x03;
278                let t3 = HmacAlgorithm::HmacBlake2s.compute(&prk, &t2_input);
279                (t1, t2, t3)
280            }
281        }
282    }
283}