oxicrypto_mac/tls.rs
1//! TLS MAC negotiation: map TLS cipher suite identifiers to MAC implementations.
2//!
3//! In TLS 1.3 (RFC 8446 §7.1 and §4.4.4) the cipher suite's hash function
4//! determines the HKDF and Finished-message HMAC algorithm. The types and
5//! functions in this module let higher-level OxiTLS code select the correct
6//! HMAC without being hard-coded to a particular hash function.
7
8extern crate alloc;
9
10use oxicrypto_core::{CryptoError, Mac};
11
12use crate::{HmacSha256, HmacSha384, HmacSha512};
13
14// ── TlsCipherSuite ────────────────────────────────────────────────────────────
15
16/// TLS cipher suite identifier for MAC negotiation.
17///
18/// Covers all TLS 1.3 cipher suites (RFC 8446 §B.4) and common TLS 1.2
19/// HMAC-based cipher suites.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21#[non_exhaustive]
22pub enum TlsCipherSuite {
23 // ── TLS 1.3 cipher suites (RFC 8446 §B.4) ────────────────────────────────
24 /// TLS_AES_128_GCM_SHA256 (0x1301) — HMAC-SHA-256 for handshake MACs.
25 Aes128GcmSha256,
26 /// TLS_AES_256_GCM_SHA384 (0x1302) — HMAC-SHA-384 for handshake MACs.
27 Aes256GcmSha384,
28 /// TLS_CHACHA20_POLY1305_SHA256 (0x1303) — HMAC-SHA-256 for handshake MACs.
29 Chacha20Poly1305Sha256,
30 /// TLS_AES_128_CCM_SHA256 (0x1304) — HMAC-SHA-256 for handshake MACs.
31 Aes128CcmSha256,
32 /// TLS_AES_128_CCM_8_SHA256 (0x1305) — HMAC-SHA-256 for handshake MACs.
33 Aes128Ccm8Sha256,
34 // ── Common TLS 1.2 HMAC suites ────────────────────────────────────────────
35 /// Any TLS 1.2 suite with SHA-256 PRF (e.g. TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256).
36 Sha256Prf,
37 /// Any TLS 1.2 suite with SHA-384 PRF (e.g. TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384).
38 Sha384Prf,
39 /// Any TLS 1.2 suite with SHA-512 PRF.
40 Sha512Prf,
41}
42
43impl TlsCipherSuite {
44 /// Parse a TLS cipher suite IANA name into a [`TlsCipherSuite`].
45 ///
46 /// Returns `None` if the string is not a recognized cipher suite name.
47 ///
48 /// # Examples
49 ///
50 /// ```
51 /// use oxicrypto_mac::TlsCipherSuite;
52 ///
53 /// assert_eq!(
54 /// TlsCipherSuite::from_iana_name("TLS_AES_256_GCM_SHA384"),
55 /// Some(TlsCipherSuite::Aes256GcmSha384),
56 /// );
57 /// ```
58 pub fn from_iana_name(name: &str) -> Option<Self> {
59 match name {
60 "TLS_AES_128_GCM_SHA256" => Some(Self::Aes128GcmSha256),
61 "TLS_AES_256_GCM_SHA384" => Some(Self::Aes256GcmSha384),
62 "TLS_CHACHA20_POLY1305_SHA256" => Some(Self::Chacha20Poly1305Sha256),
63 "TLS_AES_128_CCM_SHA256" => Some(Self::Aes128CcmSha256),
64 "TLS_AES_128_CCM_8_SHA256" => Some(Self::Aes128Ccm8Sha256),
65 _ => None,
66 }
67 }
68}
69
70// ── mac_name_for_suite ────────────────────────────────────────────────────────
71
72/// Return the MAC algorithm name used for a given TLS cipher suite.
73///
74/// In TLS 1.3, the cipher suite hash function determines the HKDF hash and
75/// Finished MAC. This function returns a static string naming the MAC
76/// primitive (e.g. `"HMAC-SHA-256"`). Use [`negotiate_mac`] to obtain a
77/// boxed [`Mac`] implementation.
78pub fn mac_name_for_suite(suite: TlsCipherSuite) -> &'static str {
79 match suite {
80 TlsCipherSuite::Aes128GcmSha256
81 | TlsCipherSuite::Chacha20Poly1305Sha256
82 | TlsCipherSuite::Aes128CcmSha256
83 | TlsCipherSuite::Aes128Ccm8Sha256
84 | TlsCipherSuite::Sha256Prf => "HMAC-SHA-256",
85 TlsCipherSuite::Aes256GcmSha384 | TlsCipherSuite::Sha384Prf => "HMAC-SHA-384",
86 TlsCipherSuite::Sha512Prf => "HMAC-SHA-512",
87 }
88}
89
90// ── negotiate_mac ─────────────────────────────────────────────────────────────
91
92/// Return a boxed [`Mac`] implementation for the hash/MAC function
93/// associated with a TLS cipher suite.
94///
95/// In TLS 1.3 (RFC 8446 §7.1 and §4.4.4), the cipher suite's hash
96/// function determines:
97/// - The HKDF extract/expand function.
98/// - The Finished message HMAC (`HMAC(BaseKey, Transcript-Hash)`).
99///
100/// This function returns the appropriate HMAC primitive so that higher-level
101/// OxiTLS code can compute Finished MAC tags without being hard-coded to a
102/// specific hash function.
103///
104/// # Errors
105///
106/// Currently infallible — all cipher suites map to a supported HMAC.
107/// Returns [`CryptoError::UnsupportedAlgorithm`] if a future variant is
108/// added without a corresponding implementation.
109///
110/// # Example
111///
112/// ```
113/// use oxicrypto_mac::{negotiate_mac, TlsCipherSuite};
114/// use oxicrypto_core::Mac;
115///
116/// let mac = negotiate_mac(TlsCipherSuite::Aes256GcmSha384).expect("negotiate failed");
117/// assert_eq!(mac.name(), "HMAC-SHA-384");
118/// assert_eq!(mac.output_len(), 48);
119/// ```
120pub fn negotiate_mac(
121 suite: TlsCipherSuite,
122) -> Result<alloc::boxed::Box<dyn Mac + Send + Sync>, CryptoError> {
123 let mac: alloc::boxed::Box<dyn Mac + Send + Sync> = match suite {
124 TlsCipherSuite::Aes128GcmSha256
125 | TlsCipherSuite::Chacha20Poly1305Sha256
126 | TlsCipherSuite::Aes128CcmSha256
127 | TlsCipherSuite::Aes128Ccm8Sha256
128 | TlsCipherSuite::Sha256Prf => alloc::boxed::Box::new(HmacSha256),
129 TlsCipherSuite::Aes256GcmSha384 | TlsCipherSuite::Sha384Prf => {
130 alloc::boxed::Box::new(HmacSha384)
131 }
132 TlsCipherSuite::Sha512Prf => alloc::boxed::Box::new(HmacSha512),
133 };
134 Ok(mac)
135}