use crate::internal_alloc::Vec;
use noxtls_core::{Error, Result};
use noxtls_crypto::{
noxtls_mlkem_decapsulate, noxtls_mlkem_generate_keypair_auto, noxtls_named_ecdh_shared_secret,
noxtls_p256_ecdh_shared_secret, noxtls_p256_generate_private_key_auto,
noxtls_p384_ecdh_shared_secret, noxtls_sha256, noxtls_sha384, noxtls_sha512,
noxtls_x25519_generate_private_key_auto, HmacDrbgSha256, MlKemPrivateKey, MlKemPublicKey,
NamedCurve, NamedEcPrivateKey, NamedEcPublicKey, P256PrivateKey, P256PublicKey, P384PrivateKey,
P384PublicKey, X25519PrivateKey, X25519PublicKey,
};
#[cfg(feature = "hazardous-legacy-crypto")]
use noxtls_crypto::{noxtls_x448_shared_secret, X448PrivateKey, X448PublicKey};
const TLS13_KEY_SHARE_GROUP_SECP256R1: u16 = 0x0017;
const TLS13_KEY_SHARE_GROUP_SECP384R1: u16 = 0x0018;
const TLS13_KEY_SHARE_GROUP_SECP521R1: u16 = 0x0019;
const TLS13_KEY_SHARE_GROUP_BRAINPOOLP256R1TLS13: u16 = 0x001F;
const TLS13_KEY_SHARE_GROUP_BRAINPOOLP384R1TLS13: u16 = 0x0020;
const TLS13_KEY_SHARE_GROUP_BRAINPOOLP512R1TLS13: u16 = 0x0021;
const TLS13_KEY_SHARE_GROUP_X25519: u16 = 0x001D;
const TLS13_KEY_SHARE_GROUP_X448: u16 = 0x001E;
const TLS13_KEY_SHARE_GROUP_MLKEM768: u16 = 0x0201;
const TLS13_KEY_SHARE_GROUP_SECP256R1_MLKEM768_HYBRID: u16 = 0x11EB;
const TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID: u16 = 0x11EC;
const TLS13_KEY_SHARE_GROUP_SECP384R1_MLKEM1024_HYBRID: u16 = 0x11ED;
const TLS13_SIGALG_ECDSA_SECP256R1_SHA256: u16 = 0x0403;
const TLS13_SIGALG_ECDSA_SECP384R1_SHA384: u16 = 0x0503;
const TLS13_SIGALG_ECDSA_SECP521R1_SHA512: u16 = 0x0603;
const TLS13_SIGALG_RSA_PSS_RSAE_SHA256: u16 = 0x0804;
const TLS13_SIGALG_RSA_PSS_RSAE_SHA384: u16 = 0x0805;
const TLS13_SIGALG_RSA_PSS_RSAE_SHA512: u16 = 0x0806;
const TLS13_SIGALG_ED25519: u16 = 0x0807;
const TLS13_SIGALG_RSA_PSS_PSS_SHA256: u16 = 0x0809;
const TLS13_SIGALG_RSA_PSS_PSS_SHA384: u16 = 0x080A;
const TLS13_SIGALG_RSA_PSS_PSS_SHA512: u16 = 0x080B;
const TLS13_SIGALG_MLDSA65: u16 = 0x0905;
#[must_use]
pub fn noxtls_derive_deterministic_x25519_private(seed: &[u8], label: &[u8]) -> X25519PrivateKey {
let mut material = Vec::with_capacity(seed.len() + label.len());
material.extend_from_slice(seed);
material.extend_from_slice(label);
X25519PrivateKey::from_bytes(noxtls_sha256(&material))
}
#[cfg(feature = "hazardous-legacy-crypto")]
#[must_use]
pub fn noxtls_derive_deterministic_x448_private(seed: &[u8], label: &[u8]) -> X448PrivateKey {
let mut material = Vec::with_capacity(seed.len() + label.len() + 1);
material.extend_from_slice(seed);
material.extend_from_slice(label);
material.push(0);
let first = noxtls_sha256(&material);
*material.last_mut().expect("counter byte exists") = 1;
let second = noxtls_sha256(&material);
let mut scalar = [0_u8; 56];
scalar[..32].copy_from_slice(&first);
scalar[32..].copy_from_slice(&second[..24]);
X448PrivateKey::from_bytes(scalar)
}
fn derive_deterministic_p256_private_bytes(seed: &[u8], label: &[u8]) -> Result<P256PrivateKey> {
for counter in 0_u32..256 {
let mut material = Vec::with_capacity(seed.len() + label.len() + 4);
material.extend_from_slice(seed);
material.extend_from_slice(label);
material.extend_from_slice(&counter.to_be_bytes());
let candidate: [u8; 32] = noxtls_sha256(&material);
if let Ok(key) = P256PrivateKey::from_bytes(candidate) {
return Ok(key);
}
}
Err(Error::CryptoFailure(
"tls13 deterministic p-256 private key derivation exhausted retry budget",
))
}
pub fn noxtls_derive_deterministic_p256_private(
seed: &[u8],
label: &[u8],
) -> Result<P256PrivateKey> {
derive_deterministic_p256_private_bytes(seed, label)
}
pub fn noxtls_derive_deterministic_p384_private(
seed: &[u8],
label: &[u8],
) -> Result<P384PrivateKey> {
for counter in 0_u32..256 {
let mut material = Vec::with_capacity(seed.len() + label.len() + 4);
material.extend_from_slice(seed);
material.extend_from_slice(label);
material.extend_from_slice(&counter.to_be_bytes());
let candidate: [u8; 48] = noxtls_sha384(&material);
if let Ok(key) = P384PrivateKey::from_bytes(candidate) {
return Ok(key);
}
}
Err(Error::CryptoFailure(
"tls13 deterministic p-384 private key derivation exhausted retry budget",
))
}
pub fn noxtls_derive_deterministic_p521_private(
seed: &[u8],
label: &[u8],
) -> Result<NamedEcPrivateKey> {
for counter in 0_u32..256 {
let mut material = Vec::with_capacity(seed.len() + label.len() + 4);
material.extend_from_slice(seed);
material.extend_from_slice(label);
material.extend_from_slice(&counter.to_be_bytes());
let mut candidate = [0_u8; 66];
candidate[34..].copy_from_slice(&noxtls_sha256(&material));
if let Ok(key) = NamedEcPrivateKey::from_bytes(NamedCurve::Secp521R1, &candidate) {
return Ok(key);
}
}
Err(Error::CryptoFailure(
"tls13 deterministic p-521 private key derivation exhausted retry budget",
))
}
fn noxtls_derive_deterministic_named_ec_private(
curve: NamedCurve,
coordinate_len: usize,
seed: &[u8],
label: &[u8],
hash_material: fn(&[u8]) -> Vec<u8>,
error_message: &'static str,
) -> Result<NamedEcPrivateKey> {
for counter in 0_u32..256 {
let mut material = Vec::with_capacity(seed.len() + label.len() + 4);
material.extend_from_slice(seed);
material.extend_from_slice(label);
material.extend_from_slice(&counter.to_be_bytes());
let candidate = hash_material(&material);
if candidate.len() != coordinate_len {
return Err(Error::CryptoFailure(
"deterministic named-ec hash length mismatch",
));
}
if let Ok(key) = NamedEcPrivateKey::from_bytes(curve, &candidate) {
return Ok(key);
}
}
Err(Error::CryptoFailure(error_message))
}
pub fn noxtls_derive_deterministic_brainpoolp256r1_private(
seed: &[u8],
label: &[u8],
) -> Result<NamedEcPrivateKey> {
noxtls_derive_deterministic_named_ec_private(
NamedCurve::BrainpoolP256R1,
32,
seed,
label,
|material| noxtls_sha256(material).to_vec(),
"tls13 deterministic brainpoolP256r1 private key derivation exhausted retry budget",
)
}
pub fn noxtls_derive_deterministic_brainpoolp384r1_private(
seed: &[u8],
label: &[u8],
) -> Result<NamedEcPrivateKey> {
noxtls_derive_deterministic_named_ec_private(
NamedCurve::BrainpoolP384R1,
48,
seed,
label,
|material| noxtls_sha384(material).to_vec(),
"tls13 deterministic brainpoolP384r1 private key derivation exhausted retry budget",
)
}
pub fn noxtls_derive_deterministic_brainpoolp512r1_private(
seed: &[u8],
label: &[u8],
) -> Result<NamedEcPrivateKey> {
noxtls_derive_deterministic_named_ec_private(
NamedCurve::BrainpoolP512R1,
64,
seed,
label,
|material| noxtls_sha512(material).to_vec(),
"tls13 deterministic brainpoolP512r1 private key derivation exhausted retry budget",
)
}
pub fn noxtls_derive_deterministic_mlkem768_keypair(
seed: &[u8],
label: &[u8],
) -> Result<(MlKemPrivateKey, MlKemPublicKey)> {
let mut material = Vec::with_capacity(seed.len() + label.len());
material.extend_from_slice(seed);
material.extend_from_slice(label);
let entropy = noxtls_sha256(&material);
let mut drbg =
HmacDrbgSha256::noxtls_new(&entropy, b"mlkem768 deterministic nonce", b"tls13 mlkem")
.map_err(|_| Error::CryptoFailure("failed to initialize deterministic mlkem drbg"))?;
noxtls_mlkem_generate_keypair_auto(&mut drbg)
}
#[must_use]
pub fn noxtls_tls13_key_share_group_supported(group: u16) -> bool {
group == TLS13_KEY_SHARE_GROUP_X25519
|| group == TLS13_KEY_SHARE_GROUP_SECP256R1
|| group == TLS13_KEY_SHARE_GROUP_SECP384R1
|| group == TLS13_KEY_SHARE_GROUP_SECP521R1
|| group == TLS13_KEY_SHARE_GROUP_BRAINPOOLP256R1TLS13
|| group == TLS13_KEY_SHARE_GROUP_BRAINPOOLP384R1TLS13
|| group == TLS13_KEY_SHARE_GROUP_BRAINPOOLP512R1TLS13
|| cfg!(feature = "hazardous-legacy-crypto") && group == TLS13_KEY_SHARE_GROUP_X448
|| group == TLS13_KEY_SHARE_GROUP_MLKEM768
|| group == TLS13_KEY_SHARE_GROUP_SECP256R1_MLKEM768_HYBRID
|| group == TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID
|| group == TLS13_KEY_SHARE_GROUP_SECP384R1_MLKEM1024_HYBRID
}
#[must_use]
pub fn noxtls_tls13_signature_algorithm_supported(signature_algorithm: u16) -> bool {
signature_algorithm == TLS13_SIGALG_ECDSA_SECP256R1_SHA256
|| signature_algorithm == TLS13_SIGALG_ECDSA_SECP384R1_SHA384
|| signature_algorithm == TLS13_SIGALG_ECDSA_SECP521R1_SHA512
|| signature_algorithm == TLS13_SIGALG_RSA_PSS_RSAE_SHA256
|| signature_algorithm == TLS13_SIGALG_RSA_PSS_RSAE_SHA384
|| signature_algorithm == TLS13_SIGALG_RSA_PSS_RSAE_SHA512
|| signature_algorithm == TLS13_SIGALG_RSA_PSS_PSS_SHA256
|| signature_algorithm == TLS13_SIGALG_RSA_PSS_PSS_SHA384
|| signature_algorithm == TLS13_SIGALG_RSA_PSS_PSS_SHA512
|| signature_algorithm == TLS13_SIGALG_ED25519
|| signature_algorithm == TLS13_SIGALG_MLDSA65
}
#[must_use]
pub fn noxtls_tls13_client_hello_offers_supported_key_exchange(
supported_versions: &[u16],
key_share_groups: &[u16],
signature_algorithms: &[u16],
) -> bool {
let has_tls13 = supported_versions.contains(&0x0304);
let has_supported_key_share = key_share_groups
.iter()
.copied()
.any(noxtls_tls13_key_share_group_supported);
let has_supported_signature_algorithm = signature_algorithms
.iter()
.copied()
.any(noxtls_tls13_signature_algorithm_supported);
has_tls13 && has_supported_key_share && has_supported_signature_algorithm
}
pub fn noxtls_derive_tls13_x25519_shared_secret(
local_private: X25519PrivateKey,
peer_key_exchange: &[u8],
) -> Result<[u8; 32]> {
if peer_key_exchange.len() != 32 {
return Err(Error::ParseFailure(
"tls13 key_share entry must contain 32-byte x25519 key_exchange",
));
}
let peer_bytes: [u8; 32] = peer_key_exchange.try_into().map_err(|_| {
Error::ParseFailure("tls13 key_share entry has invalid key_exchange length")
})?;
let peer = X25519PublicKey::from_bytes(peer_bytes);
local_private.diffie_hellman_checked(peer)
}
#[cfg(feature = "hazardous-legacy-crypto")]
pub fn noxtls_derive_tls13_x448_shared_secret(
local_private: X448PrivateKey,
peer_key_exchange: &[u8],
) -> Result<[u8; 56]> {
if peer_key_exchange.len() != 56 {
return Err(Error::ParseFailure(
"tls13 key_share entry must contain 56-byte x448 key_exchange",
));
}
let peer_bytes: [u8; 56] = peer_key_exchange.try_into().map_err(|_| {
Error::ParseFailure("tls13 key_share entry has invalid key_exchange length")
})?;
noxtls_x448_shared_secret(local_private, X448PublicKey::from_bytes(peer_bytes))
}
pub fn noxtls_derive_tls13_p256_shared_secret(
local_private: &P256PrivateKey,
peer_uncompressed: &[u8],
) -> Result<[u8; 32]> {
let peer = P256PublicKey::from_uncompressed(peer_uncompressed)?;
noxtls_p256_ecdh_shared_secret(local_private, &peer)
}
pub fn noxtls_derive_tls13_p384_shared_secret(
local_private: &P384PrivateKey,
peer_uncompressed: &[u8],
) -> Result<[u8; 48]> {
let peer = P384PublicKey::from_uncompressed(peer_uncompressed)?;
noxtls_p384_ecdh_shared_secret(local_private, &peer)
}
pub fn noxtls_derive_tls13_p521_shared_secret(
local_private: &NamedEcPrivateKey,
peer_uncompressed: &[u8],
) -> Result<Vec<u8>> {
let peer = NamedEcPublicKey::from_uncompressed(NamedCurve::Secp521R1, peer_uncompressed)?;
noxtls_named_ecdh_shared_secret(local_private, &peer)
}
pub fn noxtls_derive_tls13_named_ec_shared_secret(
curve: NamedCurve,
local_private: &NamedEcPrivateKey,
peer_uncompressed: &[u8],
) -> Result<Vec<u8>> {
let peer = NamedEcPublicKey::from_uncompressed(curve, peer_uncompressed)?;
noxtls_named_ecdh_shared_secret(local_private, &peer)
}
pub fn noxtls_derive_tls13_mlkem768_shared_secret(
local_private: &MlKemPrivateKey,
peer_key_exchange: &[u8],
) -> Result<[u8; 32]> {
noxtls_mlkem_decapsulate(local_private, peer_key_exchange)
}
pub fn noxtls_x25519_generate_private_key_with_entropy(
entropy: &mut dyn noxtls_platform::EntropySource,
) -> Result<X25519PrivateKey> {
let mut drbg =
crate::platform_bridge::noxtls_hmac_drbg_from_entropy(entropy, b"x25519 keygen")?;
noxtls_x25519_generate_private_key_auto(&mut drbg)
}
pub fn noxtls_p256_generate_private_key_with_entropy(
entropy: &mut dyn noxtls_platform::EntropySource,
) -> Result<P256PrivateKey> {
let mut drbg = crate::platform_bridge::noxtls_hmac_drbg_from_entropy(entropy, b"p256 keygen")?;
noxtls_p256_generate_private_key_auto(&mut drbg)
}