Skip to main content

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}