Skip to main content

tls2/
crypto.rs

1use core::ops::{Deref, DerefMut};
2
3use heapless::Vec;
4
5use crate::{
6    KEY_EXCHANGE_PUBLIC_KEY_MAX_SIZE, KEY_EXCHANGE_SECRET_KEY_MAX_SIZE, KEY_EXCHANGE_SHARED_SECRET_MAX_SIZE,
7    MAX_HASH_SIZE, SIGNATURE_MAX_SIZE, errors::Error,
8};
9
10/// A fixed-capacity byte buffer for cryptographic hash outputs.
11///
12/// Stores up to 48 bytes (enough for SHA-384) and tracks how many are active
13/// (`len`).  `Deref`/`DerefMut` yield a `&[u8]`/`&mut [u8]` of exactly `len`
14/// bytes.
15#[derive(Clone)]
16#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
17pub struct Hash {
18    buf: [u8; Self::MAX_LEN],
19    len: u8,
20}
21
22impl Hash {
23    pub const MAX_LEN: usize = MAX_HASH_SIZE;
24
25    pub fn new_zeroed(len: u8) -> Self {
26        assert!((len as usize) <= Self::MAX_LEN);
27        Self {
28            buf: [0u8; Self::MAX_LEN],
29            len,
30        }
31    }
32
33    pub fn from_slice(data: &[u8]) -> Self {
34        assert!(data.len() <= Self::MAX_LEN);
35        let mut buf = [0u8; Self::MAX_LEN];
36        buf[..data.len()].copy_from_slice(data);
37        Self {
38            buf,
39            len: data.len() as u8,
40        }
41    }
42
43    pub const fn zeroed() -> Self {
44        Self {
45            buf: [0u8; Self::MAX_LEN],
46            len: 0,
47        }
48    }
49
50    pub fn len(&self) -> usize {
51        self.len as usize
52    }
53
54    pub fn is_empty(&self) -> bool {
55        self.len == 0
56    }
57
58    pub fn clear(&mut self) {
59        self.buf = [0u8; Self::MAX_LEN];
60        self.len = 0;
61    }
62}
63
64impl Deref for Hash {
65    type Target = [u8];
66    fn deref(&self) -> &[u8] {
67        &self.buf[..self.len as usize]
68    }
69}
70
71impl DerefMut for Hash {
72    fn deref_mut(&mut self) -> &mut [u8] {
73        &mut self.buf[..self.len as usize]
74    }
75}
76
77pub trait CryptoProvider: Clone + Send + Sync {
78    /// Opaque incremental hash state, must be Clone for transcript checkpointing.
79    type Hasher: Clone + Unpin;
80
81    /// A distinct type is used to avoid computation for ench encrypt / decrypt operation for some
82    /// ciphers (e.g. AES key expanding)
83    type AeadKey: Unpin;
84
85    // Supported cryptographic algorithms.
86    // These are associated functions instead of associated const so crypto providers can perform
87    // runtime detection of the instructions supported by the CPU.
88
89    fn cipher_suites() -> &'static [CipherSuite];
90    fn signature_schemes() -> &'static [SignatureScheme];
91    fn key_exchange_groups() -> &'static [KeyExchangeGroup];
92
93    fn secure_random(&self, buf: &mut [u8]);
94
95    // Hash / HMAC / HKDF
96
97    /// Create a new incremental hash state for the given suite.
98    fn new_hash(&self, suite: CipherSuite) -> Self::Hasher;
99    /// Absorb data into the hash state.
100    fn hash_update(&self, state: &mut Self::Hasher, data: &[u8]);
101    /// Finalize the hash and write the digest into `out`. Consumes the state.
102    fn hash_finalize(&self, state: Self::Hasher) -> Result<Hash, Error>;
103
104    /// One-shot hash (default implementation uses the incremental API).
105    fn hash(&self, suite: CipherSuite, data: &[u8]) -> Result<Hash, Error> {
106        let mut state = self.new_hash(suite);
107        self.hash_update(&mut state, data);
108        self.hash_finalize(state)
109    }
110
111    fn hmac(&self, suite: CipherSuite, key: &Hash, data: &[u8]) -> Result<Hash, Error>;
112    fn hkdf_extract(&self, suite: CipherSuite, salt: &Hash, ikm: &[u8]) -> Result<Hash, Error>;
113    fn hkdf_expand_label(
114        &self,
115        out: &mut [u8],
116        suite: CipherSuite,
117        secret: &Hash,
118        label: &[u8],
119        context: &[u8],
120    ) -> Result<(), Error>;
121
122    // AEAD
123    fn new_aead_key(&self, suite: CipherSuite, key: &[u8]) -> Self::AeadKey;
124    fn aead_encrypt(
125        &self,
126        key: &Self::AeadKey,
127        nonce: &[u8],
128        aad: &[u8],
129        data: &mut [u8],
130        plaintext_len: usize,
131    ) -> Result<usize, Error>;
132    fn aead_decrypt(&self, key: &Self::AeadKey, nonce: &[u8], aad: &[u8], data: &mut [u8]) -> Result<usize, Error>;
133
134    // Key exchange
135    fn key_exchange_generate_keypair(
136        &self,
137        group: KeyExchangeGroup,
138    ) -> Result<(KeyExchangeSecretKey, KeyExchangePublicKey), Error>;
139    fn key_exchange(
140        &self,
141        secret: &KeyExchangeSecretKey,
142        peer_public: &[u8],
143    ) -> Result<Vec<u8, KEY_EXCHANGE_SHARED_SECRET_MAX_SIZE>, Error>;
144
145    // Signatures
146    fn sign(
147        &self,
148        scheme: SignatureScheme,
149        secret_key: &[u8],
150        data: &[u8],
151    ) -> Result<Vec<u8, SIGNATURE_MAX_SIZE>, Error>;
152    fn verify(&self, scheme: SignatureScheme, public_key: &[u8], data: &[u8], signature: &[u8]) -> Result<(), Error>;
153}
154
155/// Cipher suites supported by a [`CryptoProvider`].
156///
157/// In this library the cipher suite prescribes the AEAD cipher, the hash
158/// function used throughout the TLS 1.3 key schedule, and the transcript hash.
159///
160/// TLS 1.3 cipher suites always pair one AEAD with one hash.
161#[derive(Clone, Copy, PartialEq, Eq, Hash)]
162pub enum CipherSuite {
163    /// AES-128-GCM with SHA-256
164    TlsAes128GcmSha256,
165    /// AES-256-GCM with SHA-384
166    TlsAes256GcmSha384,
167    /// ChaCha20-Poly1305 with SHA-256
168    TlsChaCha20Poly1305Sha256,
169}
170
171impl core::fmt::Debug for CipherSuite {
172    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173        match self {
174            Self::TlsAes128GcmSha256 => write!(f, "TLS13_AES_128_GCM_SHA256"),
175            Self::TlsAes256GcmSha384 => write!(f, "TLS13_AES_256_GCM_SHA384"),
176            Self::TlsChaCha20Poly1305Sha256 => write!(f, "TLS13_CHACHA20_POLY1305_SHA256"),
177        }
178    }
179}
180
181impl CipherSuite {
182    /// TLS wire identifier (two bytes, big-endian).
183    pub const fn to_wire(self) -> [u8; 2] {
184        match self {
185            Self::TlsAes128GcmSha256 => [0x13, 0x01],
186            Self::TlsAes256GcmSha384 => [0x13, 0x02],
187            Self::TlsChaCha20Poly1305Sha256 => [0x13, 0x03],
188        }
189    }
190
191    /// Parse a cipher suite from its wire identifier.
192    pub const fn from_wire(bytes: [u8; 2]) -> Option<Self> {
193        match bytes {
194            [0x13, 0x01] => Some(Self::TlsAes128GcmSha256),
195            [0x13, 0x02] => Some(Self::TlsAes256GcmSha384),
196            [0x13, 0x03] => Some(Self::TlsChaCha20Poly1305Sha256),
197            _ => None,
198        }
199    }
200
201    /// Size in bytes of the AEAD key for this suite.
202    pub const fn key_size(self) -> usize {
203        match self {
204            Self::TlsAes128GcmSha256 => 16,
205            Self::TlsAes256GcmSha384 => 32,
206            Self::TlsChaCha20Poly1305Sha256 => 32,
207        }
208    }
209
210    /// Size in bytes of the hash output.
211    pub const fn hash_size(self) -> usize {
212        match self {
213            Self::TlsAes128GcmSha256 => 32,
214            Self::TlsAes256GcmSha384 => 48,
215            Self::TlsChaCha20Poly1305Sha256 => 32,
216        }
217    }
218}
219
220/// Key exchange group identifiers (RFC 8446 §4.2.7).
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
222pub enum KeyExchangeGroup {
223    /// X25519 (ECDHE with Curve25519)
224    X25519,
225    /// X25519MLKEM768 — post-quantum hybrid (X25519 ECDHE + ML-KEM-768 KEM).
226    /// draft-ietf-tls-ecdhe-mlkem
227    X25519MlKem768,
228    /// NIST P-256 (secp256r1) ECDHE
229    Secp256r1,
230    /// NIST P-384 (secp384r1) ECDHE
231    Secp384r1,
232    /// NIST P-521 (secp521r1) ECDHE
233    Secp521r1,
234    /// X448 (ECDHE with Curve448)
235    X448,
236    /// SecP256r1MLKEM768 — post-quantum hybrid (secp256r1 ECDHE + ML-KEM-768 KEM).
237    /// draft-ietf-tls-ecdhe-mlkem
238    Secp256r1MlKem768,
239    /// SecP384r1MLKEM1024 — post-quantum hybrid (secp384r1 ECDHE + ML-KEM-1024 KEM).
240    /// draft-ietf-tls-ecdhe-mlkem
241    Secp384r1MlKem1024,
242}
243
244impl KeyExchangeGroup {
245    /// TLS wire identifier (two bytes, big-endian).
246    pub const fn to_wire(self) -> [u8; 2] {
247        match self {
248            Self::X25519 => [0x00, 0x1D],
249            Self::X25519MlKem768 => [0x11, 0xEC],
250            Self::Secp256r1 => [0x00, 0x17],
251            Self::Secp384r1 => [0x00, 0x18],
252            Self::Secp521r1 => [0x00, 0x19],
253            Self::X448 => [0x00, 0x1E],
254            Self::Secp256r1MlKem768 => [0x11, 0xEB],
255            Self::Secp384r1MlKem1024 => [0x11, 0xED],
256        }
257    }
258
259    /// Parse a key exchange group from its wire identifier.
260    pub const fn from_wire(bytes: [u8; 2]) -> Option<Self> {
261        match bytes {
262            [0x00, 0x1D] => Some(Self::X25519),
263            [0x11, 0xEC] => Some(Self::X25519MlKem768),
264            [0x00, 0x17] => Some(Self::Secp256r1),
265            [0x00, 0x18] => Some(Self::Secp384r1),
266            [0x00, 0x19] => Some(Self::Secp521r1),
267            [0x00, 0x1E] => Some(Self::X448),
268            [0x11, 0xEB] => Some(Self::Secp256r1MlKem768),
269            [0x11, 0xED] => Some(Self::Secp384r1MlKem1024),
270            _ => None,
271        }
272    }
273
274    /// Size of the client-side KeyShare entry for this group, in bytes.
275    pub const fn public_key_size_client(self) -> usize {
276        match self {
277            Self::X25519 => 32,
278            Self::X25519MlKem768 => 1216,
279            Self::Secp256r1 => 65,
280            Self::Secp384r1 => 97,
281            Self::Secp521r1 => 133,
282            Self::X448 => 56,
283            Self::Secp256r1MlKem768 => 1249,
284            Self::Secp384r1MlKem1024 => 1665,
285        }
286    }
287
288    /// Size of the server-side KeyShare entry for this group, in bytes.
289    ///
290    /// For KEM-based groups the server returns a ciphertext (which may
291    /// differ from the client's public key size).
292    pub const fn public_key_size_server(self) -> usize {
293        match self {
294            Self::X25519 => 32,
295            Self::X25519MlKem768 => 1120,
296            Self::Secp256r1 => 65,
297            Self::Secp384r1 => 97,
298            Self::Secp521r1 => 133,
299            Self::X448 => 56,
300            Self::Secp256r1MlKem768 => 1153,
301            Self::Secp384r1MlKem1024 => 1665,
302        }
303    }
304}
305
306pub struct KeyExchangeSecretKey {
307    bytes: Vec<u8, KEY_EXCHANGE_SECRET_KEY_MAX_SIZE>,
308    group: KeyExchangeGroup,
309}
310
311impl KeyExchangeSecretKey {
312    #[inline]
313    pub fn new(group: KeyExchangeGroup, bytes: &[u8]) -> Self {
314        Self {
315            bytes: bytes.try_into().unwrap(),
316            group,
317        }
318    }
319
320    #[inline]
321    pub fn bytes(&self) -> &[u8] {
322        &self.bytes
323    }
324
325    #[inline]
326    pub fn group(&self) -> KeyExchangeGroup {
327        self.group
328    }
329}
330
331#[derive(Debug)]
332pub struct KeyExchangePublicKey {
333    bytes: Vec<u8, KEY_EXCHANGE_PUBLIC_KEY_MAX_SIZE>,
334    group: KeyExchangeGroup,
335}
336
337impl KeyExchangePublicKey {
338    #[inline]
339    pub fn new(group: KeyExchangeGroup, bytes: &[u8]) -> Self {
340        Self {
341            bytes: bytes.try_into().unwrap(),
342            group,
343        }
344    }
345
346    #[inline]
347    pub fn bytes(&self) -> &[u8] {
348        &self.bytes
349    }
350
351    #[inline]
352    pub fn group(&self) -> KeyExchangeGroup {
353        self.group
354    }
355}
356
357/// Signature schemes (RFC 8446 §4.2.3).
358///
359/// Only the schemes needed for raw public key authentication are listed.
360#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
361pub enum SignatureScheme {
362    /// Ed25519
363    Ed25519,
364    /// ECDSA with NIST P-256 and SHA-256
365    EcdsaP256Sha256,
366    /// ECDSA with NIST P-384 and SHA-384
367    EcdsaP384Sha384,
368    /// RSA PKCS#1 v1.5 with SHA-256
369    RsaPkcs1Sha256,
370    /// RSA PKCS#1 v1.5 with SHA-384
371    RsaPkcs1Sha384,
372    /// RSA PKCS#1 v1.5 with SHA-512
373    RsaPkcs1Sha512,
374    /// RSA-PSS with SHA-256 (RFC 8017)
375    RsaPssRsaSha256,
376    /// RSA-PSS with SHA-384 (RFC 8017)
377    RsaPssRsaSha384,
378    /// RSA-PSS with SHA-512 (RFC 8017)
379    RsaPssRsaSha512,
380}
381
382impl SignatureScheme {
383    /// TLS wire identifier (two bytes, big-endian).
384    pub fn to_wire(self) -> [u8; 2] {
385        match self {
386            Self::Ed25519 => [0x08, 0x07],
387            Self::EcdsaP256Sha256 => [0x04, 0x03],
388            Self::EcdsaP384Sha384 => [0x05, 0x03],
389            Self::RsaPkcs1Sha256 => [0x04, 0x01],
390            Self::RsaPkcs1Sha384 => [0x05, 0x01],
391            Self::RsaPkcs1Sha512 => [0x06, 0x01],
392            Self::RsaPssRsaSha256 => [0x08, 0x04],
393            Self::RsaPssRsaSha384 => [0x08, 0x05],
394            Self::RsaPssRsaSha512 => [0x08, 0x06],
395        }
396    }
397
398    /// Parse a signature scheme from its wire identifier.
399    pub fn from_wire(bytes: [u8; 2]) -> Option<Self> {
400        match bytes {
401            [0x08, 0x07] => Some(Self::Ed25519),
402            [0x04, 0x03] => Some(Self::EcdsaP256Sha256),
403            [0x05, 0x03] => Some(Self::EcdsaP384Sha384),
404            [0x04, 0x01] => Some(Self::RsaPkcs1Sha256),
405            [0x05, 0x01] => Some(Self::RsaPkcs1Sha384),
406            [0x06, 0x01] => Some(Self::RsaPkcs1Sha512),
407            [0x08, 0x04] => Some(Self::RsaPssRsaSha256),
408            [0x08, 0x05] => Some(Self::RsaPssRsaSha384),
409            [0x08, 0x06] => Some(Self::RsaPssRsaSha512),
410            _ => None,
411        }
412    }
413
414    /// Expected size of the raw secret key in bytes.
415    pub fn secret_key_size(self) -> usize {
416        match self {
417            Self::Ed25519 => 32,
418            Self::EcdsaP256Sha256 => 32,
419            Self::EcdsaP384Sha384 => 48,
420            Self::RsaPkcs1Sha256 => 256, // typical RSA PKCS#8
421            Self::RsaPkcs1Sha384 => 256,
422            Self::RsaPkcs1Sha512 => 256,
423            Self::RsaPssRsaSha256 => 256,
424            Self::RsaPssRsaSha384 => 256,
425            Self::RsaPssRsaSha512 => 256,
426        }
427    }
428
429    /// Expected size of the raw public key in bytes.
430    pub fn public_key_size(self) -> usize {
431        match self {
432            Self::Ed25519 => 32,
433            Self::EcdsaP256Sha256 => 65,
434            Self::EcdsaP384Sha384 => 97,
435            Self::RsaPkcs1Sha256 => 294, // typical RSA 2048-bit
436            Self::RsaPkcs1Sha384 => 294,
437            Self::RsaPkcs1Sha512 => 294,
438            Self::RsaPssRsaSha256 => 294,
439            Self::RsaPssRsaSha384 => 294,
440            Self::RsaPssRsaSha512 => 294,
441        }
442    }
443
444    /// Signature size in bytes.
445    pub fn signature_size(self) -> usize {
446        match self {
447            Self::Ed25519 => 64,
448            Self::EcdsaP256Sha256 => 64,
449            Self::EcdsaP384Sha384 => 96,
450            Self::RsaPkcs1Sha256 => 256, // RSA 2048-bit
451            Self::RsaPkcs1Sha384 => 256,
452            Self::RsaPkcs1Sha512 => 256,
453            Self::RsaPssRsaSha256 => 256,
454            Self::RsaPssRsaSha384 => 256,
455            Self::RsaPssRsaSha512 => 256,
456        }
457    }
458}
459
460/// Certificate types for `server_certificate_type` / `client_certificate_type`
461/// extension negotiation (RFC 7250 / RFC 9633).
462///
463/// In TLS 1.3 the default is X.509. Raw public keys require explicit
464/// negotiation via the `server_certificate_type` extension.
465#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466#[repr(u8)]
467pub enum CertType {
468    /// X.509 certificate (default).
469    X509 = 0,
470    /// Raw public key (RFC 7250).
471    RawPublicKey = 1,
472}
473
474impl CertType {
475    pub fn from_u8(v: u8) -> Option<Self> {
476        match v {
477            0 => Some(Self::X509),
478            1 => Some(Self::RawPublicKey),
479            _ => None,
480        }
481    }
482
483    pub fn name(&self) -> &'static str {
484        match self {
485            Self::X509 => "X.509",
486            Self::RawPublicKey => "RawPublicKey",
487        }
488    }
489}