Skip to main content

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}