Skip to main content

noxtls_core/
lib.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
18#![cfg_attr(not(feature = "std"), no_std)]
19#![forbid(unsafe_code)]
20
21//! Shared error types, wire-format helpers, build-time profile metadata, and library configuration
22//! for the NoxTLS Rust stack. Downstream crates (`noxtls-crypto`, `noxtls`, and others) depend on
23//! this crate for [`Error`], [`Result`], and [`Profile`].
24
25#[cfg(all(not(feature = "std"), feature = "alloc"))]
26extern crate alloc;
27
28use core::fmt::{Display, Formatter};
29
30mod config;
31
32pub use config::{
33    compiled_allow_legacy_algorithms, compiled_allow_sha1_signatures,
34    compiled_strict_constant_time, ConstantTimePolicy, LibraryConfig, SecurityPolicy,
35};
36
37#[cfg(all(
38    feature = "feature-tls",
39    not(any(
40        feature = "feature-tls10",
41        feature = "feature-tls11",
42        feature = "feature-tls12",
43        feature = "feature-tls13"
44    ))
45))]
46compile_error!("feature-tls requires one of feature-tls10/11/12/13");
47
48#[cfg(all(feature = "feature-cert-write", not(feature = "feature-cert")))]
49compile_error!("feature-cert-write requires feature-cert");
50
51#[cfg(all(feature = "feature-dtls", not(feature = "feature-tls")))]
52compile_error!("feature-dtls requires feature-tls");
53
54#[cfg(all(
55    feature = "policy-strict-constant-time",
56    feature = "policy-allow-legacy-algorithms"
57))]
58compile_error!("policy-strict-constant-time is incompatible with policy-allow-legacy-algorithms");
59
60#[cfg(all(
61    feature = "policy-strict-constant-time",
62    feature = "policy-allow-sha1-signatures"
63))]
64compile_error!("policy-strict-constant-time is incompatible with policy-allow-sha1-signatures");
65
66/// Library-wide error type for length, encoding, parse, state, crypto, and feature failures.
67///
68/// Each variant carries a static diagnostic string suitable for logging and user-facing output.
69#[derive(Debug, Clone, Eq, PartialEq)]
70pub enum Error {
71    /// Buffer or value length is outside the allowed range for the operation.
72    InvalidLength(&'static str),
73    /// Encoding rules (for example DER or wire format) were violated.
74    InvalidEncoding(&'static str),
75    /// Parsing failed for structural or syntactic reasons.
76    ParseFailure(&'static str),
77    /// The requested capability is disabled or incompatible with current configuration.
78    UnsupportedFeature(&'static str),
79    /// A cryptographic primitive returned a verification or computation failure.
80    CryptoFailure(&'static str),
81    /// The operation is not valid in the current protocol or object state.
82    StateError(&'static str),
83}
84
85impl Display for Error {
86    /// Writes the embedded static message for this error variant into `f`.
87    ///
88    /// # Arguments
89    ///
90    /// * `self` — Error whose message string is written.
91    /// * `f` — Formatter destination for the human-readable message.
92    ///
93    /// # Returns
94    ///
95    /// `Ok(())` when the message is written successfully.
96    ///
97    /// # Errors
98    ///
99    /// Returns [`core::fmt::Error`] if the formatter rejects output (for example, a full buffer).
100    ///
101    /// # Panics
102    ///
103    /// This function does not panic.
104    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
105        match self {
106            Self::InvalidLength(msg)
107            | Self::InvalidEncoding(msg)
108            | Self::ParseFailure(msg)
109            | Self::UnsupportedFeature(msg)
110            | Self::CryptoFailure(msg)
111            | Self::StateError(msg) => f.write_str(msg),
112        }
113    }
114}
115
116#[cfg(feature = "std")]
117/// Bridges [`Error`] into [`std::error::Error`] for interoperability when the `std` feature is enabled.
118impl std::error::Error for Error {}
119
120/// Convenient [`core::result::Result`] alias using [`Error`] as the error type.
121pub type Result<T> = core::result::Result<T, Error>;
122
123/// Named build or deployment profiles that map to coarse-grained feature sets.
124#[derive(Debug, Copy, Clone, Eq, PartialEq)]
125pub enum Profile {
126    /// Balanced client/server defaults with modern TLS and optional DTLS.
127    Default,
128    /// TLS client-oriented subset without DTLS exposure.
129    MinimalTlsClient,
130    /// TLS/DTLS server profile including certificate issuance helpers.
131    TlsServerPki,
132    /// Cryptographic primitives only (no TLS/DTLS or X.509 stack).
133    CryptoOnly,
134    /// Conservative TLS profile aimed at stricter deployment assumptions.
135    FipsLike,
136    /// Internal or test profile enabling the broadest compiled feature surface.
137    UtAllFeatures,
138}
139
140/// Boolean feature flags describing which protocol and algorithm areas are enabled for a [`Profile`].
141#[derive(Debug, Copy, Clone, Eq, PartialEq)]
142pub struct FeatureSet {
143    /// Any TLS protocol surface is enabled.
144    pub tls: bool,
145    /// TLS 1.0 support flag.
146    pub tls10: bool,
147    /// TLS 1.1 support flag.
148    pub tls11: bool,
149    /// TLS 1.2 support flag.
150    pub tls12: bool,
151    /// TLS 1.3 support flag.
152    pub tls13: bool,
153    /// DTLS protocol surface is enabled.
154    pub dtls: bool,
155    /// X.509 certificate parsing and validation stack is enabled.
156    pub cert: bool,
157    /// Certificate writing / issuance helpers are enabled.
158    pub cert_write: bool,
159    /// Digest and hash primitives are enabled.
160    pub hash: bool,
161    /// Symmetric encryption algorithms are enabled.
162    pub encryption: bool,
163    /// DRBG and entropy-related helpers are enabled.
164    pub drbg: bool,
165    /// Public-key cryptography primitives are enabled.
166    pub pkc: bool,
167}
168
169impl Profile {
170    /// Returns the feature flags implied by this profile for documentation and tooling.
171    ///
172    /// # Arguments
173    ///
174    /// * `self` — Profile variant to expand into concrete flags.
175    ///
176    /// # Returns
177    ///
178    /// A [`FeatureSet`] describing TLS/DTLS, certificate, hash, symmetric, DRBG, and PKC availability.
179    ///
180    /// # Panics
181    ///
182    /// This function does not panic.
183    #[must_use]
184    pub fn features(self) -> FeatureSet {
185        match self {
186            Self::Default => FeatureSet {
187                tls: true,
188                tls10: false,
189                tls11: false,
190                tls12: true,
191                tls13: true,
192                dtls: true,
193                cert: true,
194                cert_write: false,
195                hash: true,
196                encryption: true,
197                drbg: true,
198                pkc: true,
199            },
200            Self::MinimalTlsClient => FeatureSet {
201                tls: true,
202                tls10: false,
203                tls11: false,
204                tls12: true,
205                tls13: true,
206                dtls: false,
207                cert: true,
208                cert_write: false,
209                hash: true,
210                encryption: true,
211                drbg: true,
212                pkc: true,
213            },
214            Self::TlsServerPki => FeatureSet {
215                tls: true,
216                tls10: false,
217                tls11: false,
218                tls12: true,
219                tls13: true,
220                dtls: true,
221                cert: true,
222                cert_write: true,
223                hash: true,
224                encryption: true,
225                drbg: true,
226                pkc: true,
227            },
228            Self::CryptoOnly => FeatureSet {
229                tls: false,
230                tls10: false,
231                tls11: false,
232                tls12: false,
233                tls13: false,
234                dtls: false,
235                cert: false,
236                cert_write: false,
237                hash: true,
238                encryption: true,
239                drbg: true,
240                pkc: true,
241            },
242            Self::FipsLike => FeatureSet {
243                tls: true,
244                tls10: false,
245                tls11: false,
246                tls12: true,
247                tls13: true,
248                dtls: false,
249                cert: true,
250                cert_write: false,
251                hash: true,
252                encryption: true,
253                drbg: true,
254                pkc: true,
255            },
256            Self::UtAllFeatures => FeatureSet {
257                tls: true,
258                tls10: true,
259                tls11: true,
260                tls12: true,
261                tls13: true,
262                dtls: true,
263                cert: true,
264                cert_write: true,
265                hash: true,
266                encryption: true,
267                drbg: true,
268                pkc: true,
269            },
270        }
271    }
272}
273
274/// Reads an unsigned 16-bit big-endian integer from the start of `input`.
275///
276/// # Arguments
277///
278/// * `input` — Byte slice whose first two bytes are interpreted as big-endian `u16`.
279///
280/// # Returns
281///
282/// On success, the parsed 16-bit value; only the first two bytes are read.
283///
284/// # Errors
285///
286/// Returns [`Error::InvalidLength`] when `input` has fewer than two bytes.
287pub fn read_u16_be(input: &[u8]) -> Result<u16> {
288    if input.len() < 2 {
289        return Err(Error::InvalidLength("not enough bytes for u16"));
290    }
291    Ok(u16::from_be_bytes([input[0], input[1]]))
292}
293
294/// Reads an unsigned 24-bit big-endian integer from the start of `input`.
295///
296/// # Arguments
297///
298/// * `input` — Byte slice whose first three bytes are interpreted as a 24-bit big-endian integer in a `u32`.
299///
300/// # Returns
301///
302/// On success, the parsed value in the low 24 bits of the returned `u32`.
303///
304/// # Errors
305///
306/// Returns [`Error::InvalidLength`] when `input` has fewer than three bytes.
307pub fn read_u24_be(input: &[u8]) -> Result<u32> {
308    if input.len() < 3 {
309        return Err(Error::InvalidLength("not enough bytes for u24"));
310    }
311    Ok((u32::from(input[0]) << 16) | (u32::from(input[1]) << 8) | u32::from(input[2]))
312}
313
314/// Overwrites `data` with zero bytes to reduce sensitive material lifetime in memory.
315///
316/// # Arguments
317///
318/// * `data` — Mutable buffer cleared in place; length is unchanged.
319///
320/// # Returns
321///
322/// Returns unit; the buffer is always fully zeroed.
323///
324/// # Panics
325///
326/// This function does not panic.
327pub fn secure_zero(data: &mut [u8]) {
328    data.fill(0);
329}