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}