#[cfg(feature = "std")]
use std::path::Path;
use crate::{Error, Profile, Result};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ConstantTimePolicy {
BestEffort,
Strict,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SecurityPolicy {
pub constant_time: ConstantTimePolicy,
pub allow_legacy_algorithms: bool,
pub allow_sha1_signatures: bool,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LibraryConfig {
pub profile: Profile,
pub policy: SecurityPolicy,
}
#[must_use]
pub fn compiled_strict_constant_time() -> bool {
cfg!(feature = "policy-strict-constant-time")
}
#[must_use]
pub fn compiled_allow_legacy_algorithms() -> bool {
cfg!(feature = "policy-allow-legacy-algorithms")
}
#[must_use]
pub fn compiled_allow_sha1_signatures() -> bool {
cfg!(feature = "policy-allow-sha1-signatures")
}
impl SecurityPolicy {
#[must_use]
pub fn compiled() -> Self {
let constant_time = if compiled_strict_constant_time() {
ConstantTimePolicy::Strict
} else {
ConstantTimePolicy::BestEffort
};
Self {
constant_time,
allow_legacy_algorithms: compiled_allow_legacy_algorithms(),
allow_sha1_signatures: compiled_allow_sha1_signatures(),
}
}
pub fn validate(self) -> Result<()> {
if self.constant_time == ConstantTimePolicy::Strict && self.allow_legacy_algorithms {
return Err(Error::UnsupportedFeature(
"strict constant-time policy is incompatible with legacy algorithms",
));
}
if self.constant_time == ConstantTimePolicy::Strict && self.allow_sha1_signatures {
return Err(Error::UnsupportedFeature(
"strict constant-time policy is incompatible with sha1 signature mode",
));
}
Ok(())
}
}
impl LibraryConfig {
pub fn compiled() -> Result<Self> {
let config = Self {
profile: Profile::Default,
policy: SecurityPolicy::compiled(),
};
config.validate()?;
Ok(config)
}
pub fn validate(self) -> Result<()> {
self.policy.validate()?;
Ok(())
}
pub fn from_mbedtls_style_str(input: &str) -> Result<Self> {
let mut profile: Option<Profile> = None;
let mut policy = SecurityPolicy {
constant_time: ConstantTimePolicy::BestEffort,
allow_legacy_algorithms: false,
allow_sha1_signatures: false,
};
for (line_idx, raw_line) in input.lines().enumerate() {
let line = strip_inline_comment(raw_line).trim();
if line.is_empty() {
continue;
}
let symbol = match parse_define_symbol(line) {
Some(value) => value,
None => continue,
};
match symbol {
"NOXTLS_PROFILE_DEFAULT" => {
set_profile_once(&mut profile, Profile::Default, line_idx + 1)?
}
"NOXTLS_PROFILE_MINIMAL_TLS_CLIENT" => {
set_profile_once(&mut profile, Profile::MinimalTlsClient, line_idx + 1)?
}
"NOXTLS_PROFILE_TLS_SERVER_PKI" => {
set_profile_once(&mut profile, Profile::TlsServerPki, line_idx + 1)?
}
"NOXTLS_PROFILE_CRYPTO_ONLY" => {
set_profile_once(&mut profile, Profile::CryptoOnly, line_idx + 1)?
}
"NOXTLS_PROFILE_FIPS_LIKE" => {
set_profile_once(&mut profile, Profile::FipsLike, line_idx + 1)?
}
"NOXTLS_PROFILE_UT_ALL_FEATURES" => {
set_profile_once(&mut profile, Profile::UtAllFeatures, line_idx + 1)?
}
"NOXTLS_STRICT_CONSTANT_TIME" => {
policy.constant_time = ConstantTimePolicy::Strict;
}
"NOXTLS_ALLOW_LEGACY_ALGORITHMS" => {
policy.allow_legacy_algorithms = true;
}
"NOXTLS_ALLOW_SHA1_SIGNATURES" => {
policy.allow_sha1_signatures = true;
}
_ => {
return Err(Error::ParseFailure(
"unsupported noxtls configuration symbol",
));
}
}
}
let config = Self {
profile: profile.unwrap_or(Profile::Default),
policy,
};
config.validate()?;
Ok(config)
}
#[cfg(feature = "std")]
pub fn from_mbedtls_style_file(path: &Path) -> Result<Self> {
let content = std::fs::read_to_string(path)
.map_err(|_| Error::ParseFailure("failed to read noxtls configuration file"))?;
Self::from_mbedtls_style_str(&content)
}
}
fn strip_inline_comment(line: &str) -> &str {
if let Some((content, _)) = line.split_once("//") {
return content;
}
if let Some((content, _)) = line.split_once("/*") {
return content;
}
line
}
fn parse_define_symbol(line: &str) -> Option<&str> {
let mut parts = line.split_whitespace();
if parts.next()? != "#define" {
return None;
}
let symbol = parts.next()?;
if symbol.starts_with("NOXTLS_") {
Some(symbol)
} else {
None
}
}
fn set_profile_once(slot: &mut Option<Profile>, value: Profile, _line_number: usize) -> Result<()> {
if slot.is_some() {
return Err(Error::ParseFailure(
"multiple noxtls profile defines found in configuration",
));
}
*slot = Some(value);
Ok(())
}