use crate::internal_alloc::Vec;
use noxtls_core::{Error, Result};
use noxtls_crypto::{
mlkem_decapsulate, mlkem_generate_keypair_auto, p256_ecdh_shared_secret, sha256,
HmacDrbgSha256, MlKemPrivateKey, MlKemPublicKey, P256PrivateKey, P256PublicKey,
X25519PrivateKey, X25519PublicKey,
};
const TLS13_KEY_SHARE_GROUP_SECP256R1: u16 = 0x0017;
const TLS13_KEY_SHARE_GROUP_X25519: u16 = 0x001D;
const TLS13_KEY_SHARE_GROUP_MLKEM768: u16 = 0x0201;
const TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID: u16 = 0x11EC;
const TLS13_SIGALG_ECDSA_SECP256R1_SHA256: u16 = 0x0403;
const TLS13_SIGALG_RSA_PSS_RSAE_SHA256: u16 = 0x0804;
const TLS13_SIGALG_RSA_PSS_RSAE_SHA384: u16 = 0x0805;
const TLS13_SIGALG_ED25519: u16 = 0x0807;
const TLS13_SIGALG_MLDSA65: u16 = 0x0905;
#[must_use]
pub fn 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(sha256(&material))
}
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] = 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 derive_deterministic_p256_private(seed: &[u8], label: &[u8]) -> Result<P256PrivateKey> {
derive_deterministic_p256_private_bytes(seed, label)
}
pub fn 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 = sha256(&material);
let mut drbg =
HmacDrbgSha256::new(&entropy, b"mlkem768 deterministic nonce", b"tls13 mlkem")
.map_err(|_| Error::CryptoFailure("failed to initialize deterministic mlkem drbg"))?;
mlkem_generate_keypair_auto(&mut drbg)
}
#[must_use]
pub fn 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_MLKEM768
|| group == TLS13_KEY_SHARE_GROUP_X25519_MLKEM768_HYBRID
}
#[must_use]
pub fn tls13_signature_algorithm_supported(signature_algorithm: u16) -> bool {
signature_algorithm == TLS13_SIGALG_ECDSA_SECP256R1_SHA256
|| signature_algorithm == TLS13_SIGALG_RSA_PSS_RSAE_SHA256
|| signature_algorithm == TLS13_SIGALG_RSA_PSS_RSAE_SHA384
|| signature_algorithm == TLS13_SIGALG_ED25519
|| signature_algorithm == TLS13_SIGALG_MLDSA65
}
#[must_use]
pub fn 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(tls13_key_share_group_supported);
let has_supported_signature_algorithm = signature_algorithms
.iter()
.copied()
.any(tls13_signature_algorithm_supported);
has_tls13 && has_supported_key_share && has_supported_signature_algorithm
}
pub fn 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)
}
pub fn derive_tls13_p256_shared_secret(
local_private: &P256PrivateKey,
peer_uncompressed: &[u8],
) -> Result<[u8; 32]> {
let peer = P256PublicKey::from_uncompressed(peer_uncompressed)?;
p256_ecdh_shared_secret(local_private, &peer)
}
pub fn derive_tls13_mlkem768_shared_secret(
local_private: &MlKemPrivateKey,
peer_key_exchange: &[u8],
) -> Result<[u8; 32]> {
mlkem_decapsulate(local_private, peer_key_exchange)
}