noxtls_crypto/hash/mdigest/tls.rs
1// Copyright (c) 2019-2026, Argenox Technologies LLC
2// All rights reserved.
3//
4// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
5//
6// This file is part of the NoxTLS Library.
7//
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by the
10// Free Software Foundation; version 2 of the License.
11//
12// Alternatively, this file may be used under the terms of a commercial
13// license from Argenox Technologies LLC.
14//
15// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
16// CONTACT: info@argenox.com
17
18use crate::internal_alloc::Vec;
19use noxtls_core::{Error, Result};
20
21use super::{hmac_sha256, hmac_sha384, sha256, sha384, Digest, Sha256};
22
23/// Tracks TLS handshake transcript using streaming SHA-256 updates.
24#[derive(Debug, Clone, Default)]
25pub struct TlsTranscriptSha256 {
26 hasher: Sha256,
27}
28
29impl TlsTranscriptSha256 {
30 /// Creates a new transcript hasher with an empty transcript state.
31 ///
32 /// # Returns
33 /// Fresh SHA-256 transcript accumulator.
34 ///
35 /// # Panics
36 ///
37 /// This function does not panic.
38 #[must_use]
39 pub fn new() -> Self {
40 Self::default()
41 }
42
43 /// Appends one handshake message to the transcript hash context.
44 ///
45 /// # Arguments
46 /// * `self` — Running transcript hasher.
47 /// * `message` — Serialized TLS handshake message bytes to append.
48 ///
49 /// # Returns
50 ///
51 /// `()`.
52 ///
53 /// # Panics
54 ///
55 /// This function does not panic.
56 pub fn update(&mut self, message: &[u8]) {
57 self.hasher.update(message);
58 }
59
60 /// Returns a snapshot hash of the transcript without consuming state.
61 ///
62 /// # Arguments
63 ///
64 /// * `self` — Transcript state to clone for hashing.
65 ///
66 /// # Returns
67 /// Current transcript hash as 32-byte SHA-256 digest.
68 ///
69 /// # Panics
70 ///
71 /// This function does not panic.
72 #[must_use]
73 pub fn snapshot_hash(&self) -> [u8; 32] {
74 let digest = self.hasher.clone().finalize();
75 let mut out = [0_u8; 32];
76 out.copy_from_slice(&digest);
77 out
78 }
79}
80
81/// Tracks TLS handshake transcript using buffered SHA-384 snapshots.
82#[derive(Debug, Clone, Default)]
83pub struct TlsTranscriptSha384 {
84 transcript: Vec<u8>,
85}
86
87impl TlsTranscriptSha384 {
88 /// Creates a new SHA-384 transcript hasher with an empty transcript state.
89 ///
90 /// # Returns
91 /// Fresh SHA-384 transcript accumulator.
92 ///
93 /// # Panics
94 ///
95 /// This function does not panic.
96 #[must_use]
97 pub fn new() -> Self {
98 Self::default()
99 }
100
101 /// Appends one handshake message to the transcript buffer.
102 ///
103 /// # Arguments
104 /// * `self` — Running transcript buffer.
105 /// * `message` — Serialized TLS handshake message bytes to append.
106 ///
107 /// # Returns
108 ///
109 /// `()`.
110 ///
111 /// # Panics
112 ///
113 /// This function does not panic.
114 pub fn update(&mut self, message: &[u8]) {
115 self.transcript.extend_from_slice(message);
116 }
117
118 /// Returns a snapshot hash of the transcript without consuming state.
119 ///
120 /// # Arguments
121 ///
122 /// * `self` — Buffered transcript bytes to hash.
123 ///
124 /// # Returns
125 /// Current transcript hash as 48-byte SHA-384 digest.
126 ///
127 /// # Panics
128 ///
129 /// This function does not panic.
130 #[must_use]
131 pub fn snapshot_hash(&self) -> [u8; 48] {
132 sha384(&self.transcript)
133 }
134}
135
136/// Computes TLS 1.2 PRF output using SHA-256 and the requested output length.
137///
138/// # Arguments
139/// * `secret`: PRF secret input (typically master secret).
140/// * `label`: TLS PRF label bytes.
141/// * `seed`: Additional seed material (for example transcript-derived values).
142/// * `len`: Number of output bytes to derive.
143///
144/// # Returns
145/// Derived PRF output with length `len`.
146///
147/// # Errors
148///
149/// Returns [`Error::InvalidLength`] when `secret` is empty.
150///
151/// # Panics
152///
153/// This function does not panic.
154pub fn tls12_prf_sha256(secret: &[u8], label: &[u8], seed: &[u8], len: usize) -> Result<Vec<u8>> {
155 if secret.is_empty() {
156 return Err(Error::InvalidLength("tls12 prf secret must not be empty"));
157 }
158 if len == 0 {
159 return Ok(Vec::new());
160 }
161 let mut label_seed = Vec::with_capacity(label.len() + seed.len());
162 label_seed.extend_from_slice(label);
163 label_seed.extend_from_slice(seed);
164
165 let mut a = hmac_sha256(secret, &label_seed);
166 let mut out = Vec::with_capacity(len);
167 while out.len() < len {
168 let mut block_input = Vec::with_capacity(a.len() + label_seed.len());
169 block_input.extend_from_slice(&a);
170 block_input.extend_from_slice(&label_seed);
171 out.extend_from_slice(&hmac_sha256(secret, &block_input));
172 a = hmac_sha256(secret, &a);
173 }
174 out.truncate(len);
175 Ok(out)
176}
177
178/// Computes TLS 1.2 PRF output using SHA-384 and the requested output length.
179///
180/// # Arguments
181/// * `secret`: PRF secret input (typically master secret).
182/// * `label`: TLS PRF label bytes.
183/// * `seed`: Additional seed material.
184/// * `len`: Number of output bytes to derive.
185///
186/// # Returns
187/// Derived PRF output with length `len`.
188///
189/// # Errors
190///
191/// Returns [`Error::InvalidLength`] when `secret` is empty.
192///
193/// # Panics
194///
195/// This function does not panic.
196pub fn tls12_prf_sha384(secret: &[u8], label: &[u8], seed: &[u8], len: usize) -> Result<Vec<u8>> {
197 if secret.is_empty() {
198 return Err(Error::InvalidLength("tls12 prf secret must not be empty"));
199 }
200 if len == 0 {
201 return Ok(Vec::new());
202 }
203 let mut label_seed = Vec::with_capacity(label.len() + seed.len());
204 label_seed.extend_from_slice(label);
205 label_seed.extend_from_slice(seed);
206
207 let mut a = hmac_sha384(secret, &label_seed);
208 let mut out = Vec::with_capacity(len);
209 while out.len() < len {
210 let mut block_input = Vec::with_capacity(a.len() + label_seed.len());
211 block_input.extend_from_slice(&a);
212 block_input.extend_from_slice(&label_seed);
213 out.extend_from_slice(&hmac_sha384(secret, &block_input));
214 a = hmac_sha384(secret, &a);
215 }
216 out.truncate(len);
217 Ok(out)
218}
219
220/// Computes TLS 1.2 verify_data for Finished using SHA-256 transcript hash.
221///
222/// # Arguments
223/// * `master_secret`: TLS master secret bytes.
224/// * `finished_label`: Finished label (`client finished` or `server finished`).
225/// * `transcript`: Serialized handshake transcript bytes.
226///
227/// # Returns
228/// 12-byte `verify_data` output for TLS 1.2 Finished.
229///
230/// # Errors
231///
232/// Forwards errors from [`tls12_prf_sha256`] (for example empty `master_secret`).
233///
234/// # Panics
235///
236/// This function does not panic.
237pub fn tls12_finished_verify_data_sha256(
238 master_secret: &[u8],
239 finished_label: &[u8],
240 transcript: &[u8],
241) -> Result<[u8; 12]> {
242 let hash = sha256(transcript);
243 let verify = tls12_prf_sha256(master_secret, finished_label, &hash, 12)?;
244 let mut out = [0_u8; 12];
245 out.copy_from_slice(&verify);
246 Ok(out)
247}
248
249/// Computes TLS 1.2 verify_data for Finished using SHA-384 transcript hash.
250///
251/// # Arguments
252/// * `master_secret`: TLS master secret bytes.
253/// * `finished_label`: Finished label (`client finished` or `server finished`).
254/// * `transcript`: Serialized handshake transcript bytes.
255///
256/// # Returns
257/// 12-byte `verify_data` output for TLS 1.2 Finished.
258///
259/// # Errors
260///
261/// Forwards errors from [`tls12_prf_sha384`] (for example empty `master_secret`).
262///
263/// # Panics
264///
265/// This function does not panic.
266pub fn tls12_finished_verify_data_sha384(
267 master_secret: &[u8],
268 finished_label: &[u8],
269 transcript: &[u8],
270) -> Result<[u8; 12]> {
271 let hash = sha384(transcript);
272 let verify = tls12_prf_sha384(master_secret, finished_label, &hash, 12)?;
273 let mut out = [0_u8; 12];
274 out.copy_from_slice(&verify);
275 Ok(out)
276}