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::{noxtls_hmac_sha256, noxtls_hmac_sha384, noxtls_sha256, noxtls_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 noxtls_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 noxtls_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 noxtls_update(&mut self, message: &[u8]) {
57        self.hasher.noxtls_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 noxtls_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 noxtls_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 noxtls_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 noxtls_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 noxtls_snapshot_hash(&self) -> [u8; 48] {
132        noxtls_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 noxtls_tls12_prf_sha256(
155    secret: &[u8],
156    label: &[u8],
157    seed: &[u8],
158    len: usize,
159) -> Result<Vec<u8>> {
160    if secret.is_empty() {
161        return Err(Error::InvalidLength("tls12 prf secret must not be empty"));
162    }
163    if len == 0 {
164        return Ok(Vec::new());
165    }
166    let mut label_seed = Vec::with_capacity(label.len() + seed.len());
167    label_seed.extend_from_slice(label);
168    label_seed.extend_from_slice(seed);
169
170    let mut a = noxtls_hmac_sha256(secret, &label_seed);
171    let mut out = Vec::with_capacity(len);
172    while out.len() < len {
173        let mut block_input = Vec::with_capacity(a.len() + label_seed.len());
174        block_input.extend_from_slice(&a);
175        block_input.extend_from_slice(&label_seed);
176        out.extend_from_slice(&noxtls_hmac_sha256(secret, &block_input));
177        a = noxtls_hmac_sha256(secret, &a);
178    }
179    out.truncate(len);
180    Ok(out)
181}
182
183/// Computes TLS 1.2 PRF output using SHA-384 and the requested output length.
184///
185/// # Arguments
186/// * `secret`: PRF secret input (typically master secret).
187/// * `label`: TLS PRF label bytes.
188/// * `seed`: Additional seed material.
189/// * `len`: Number of output bytes to derive.
190///
191/// # Returns
192/// Derived PRF output with length `len`.
193///
194/// # Errors
195///
196/// Returns [`Error::InvalidLength`] when `secret` is empty.
197///
198/// # Panics
199///
200/// This function does not panic.
201pub fn noxtls_tls12_prf_sha384(
202    secret: &[u8],
203    label: &[u8],
204    seed: &[u8],
205    len: usize,
206) -> Result<Vec<u8>> {
207    if secret.is_empty() {
208        return Err(Error::InvalidLength("tls12 prf secret must not be empty"));
209    }
210    if len == 0 {
211        return Ok(Vec::new());
212    }
213    let mut label_seed = Vec::with_capacity(label.len() + seed.len());
214    label_seed.extend_from_slice(label);
215    label_seed.extend_from_slice(seed);
216
217    let mut a = noxtls_hmac_sha384(secret, &label_seed);
218    let mut out = Vec::with_capacity(len);
219    while out.len() < len {
220        let mut block_input = Vec::with_capacity(a.len() + label_seed.len());
221        block_input.extend_from_slice(&a);
222        block_input.extend_from_slice(&label_seed);
223        out.extend_from_slice(&noxtls_hmac_sha384(secret, &block_input));
224        a = noxtls_hmac_sha384(secret, &a);
225    }
226    out.truncate(len);
227    Ok(out)
228}
229
230/// Computes TLS 1.2 verify_data for Finished using SHA-256 transcript hash.
231///
232/// # Arguments
233/// * `master_secret`: TLS master secret bytes.
234/// * `finished_label`: Finished label (`client finished` or `server finished`).
235/// * `transcript`: Serialized handshake transcript bytes.
236///
237/// # Returns
238/// 12-byte `verify_data` output for TLS 1.2 Finished.
239///
240/// # Errors
241///
242/// Forwards errors from [`noxtls_tls12_prf_sha256`] (for example empty `master_secret`).
243///
244/// # Panics
245///
246/// This function does not panic.
247pub fn noxtls_tls12_finished_verify_data_sha256(
248    master_secret: &[u8],
249    finished_label: &[u8],
250    transcript: &[u8],
251) -> Result<[u8; 12]> {
252    let hash = noxtls_sha256(transcript);
253    let verify = noxtls_tls12_prf_sha256(master_secret, finished_label, &hash, 12)?;
254    let mut out = [0_u8; 12];
255    out.copy_from_slice(&verify);
256    Ok(out)
257}
258
259/// Computes TLS 1.2 verify_data for Finished using SHA-384 transcript hash.
260///
261/// # Arguments
262/// * `master_secret`: TLS master secret bytes.
263/// * `finished_label`: Finished label (`client finished` or `server finished`).
264/// * `transcript`: Serialized handshake transcript bytes.
265///
266/// # Returns
267/// 12-byte `verify_data` output for TLS 1.2 Finished.
268///
269/// # Errors
270///
271/// Forwards errors from [`noxtls_tls12_prf_sha384`] (for example empty `master_secret`).
272///
273/// # Panics
274///
275/// This function does not panic.
276pub fn noxtls_tls12_finished_verify_data_sha384(
277    master_secret: &[u8],
278    finished_label: &[u8],
279    transcript: &[u8],
280) -> Result<[u8; 12]> {
281    let hash = noxtls_sha384(transcript);
282    let verify = noxtls_tls12_prf_sha384(master_secret, finished_label, &hash, 12)?;
283    let mut out = [0_u8; 12];
284    out.copy_from_slice(&verify);
285    Ok(out)
286}