noxtls 0.2.20

TLS/DTLS protocol and connection state machine for the noxtls Rust stack.
Documentation
// Copyright (c) 2019-2026, Argenox Technologies LLC
// All rights reserved.
//
// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
//
// This file is part of the NoxTLS Library.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by the
// Free Software Foundation; version 2 of the License.
//
// Alternatively, this file may be used under the terms of a commercial
// license from Argenox Technologies LLC.
//
// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
// CONTACT: info@argenox.com

//! TLS 1.2 security hardening helpers (ServerKeyExchange signature verification).

use super::*;
use noxtls_crypto::{
    noxtls_ed25519_public_key_from_subject_public_key_info, noxtls_ed25519_verify,
    noxtls_mldsa_verify, noxtls_p256_ecdsa_verify_sha256, noxtls_p384_ecdsa_verify_sha384,
    noxtls_rsassa_pss_sha256_verify, noxtls_rsassa_pss_sha384_verify, noxtls_rsassa_pss_verify,
    MlDsaPublicKey, P256PublicKey, P384PublicKey, RsaPssHashAlgorithm,
};

/// Parses TLS 1.2 digitally-signed fields from a ServerKeyExchange body tail.
///
/// # Arguments
///
/// * `body` — Full ServerKeyExchange body bytes.
/// * `offset` — Byte offset where the two-byte signature scheme begins.
///
/// # Returns
///
/// On success, `(signature_scheme, signature_bytes)`.
///
/// # Errors
///
/// Returns [`noxtls_core::Error`] when the signature header or length is malformed.
///
/// # Panics
///
/// This function does not panic.
pub(super) fn noxtls_parse_tls12_signature_fields(
    body: &[u8],
    offset: usize,
) -> Result<(u16, &[u8])> {
    if body.len() < offset + 4 {
        return Err(Error::ParseFailure(
            "tls12 server key exchange signature header is truncated",
        ));
    }
    let signature_scheme = u16::from_be_bytes([body[offset], body[offset + 1]]);
    if !noxtls_tls12_signature_scheme_is_modern(signature_scheme) {
        return Err(Error::ParseFailure(
            "tls12 server key exchange uses unsupported signature scheme",
        ));
    }
    let signature_len = u16::from_be_bytes([body[offset + 2], body[offset + 3]]) as usize;
    if signature_len == 0 {
        return Err(Error::ParseFailure(
            "tls12 server key exchange signature must not be empty",
        ));
    }
    if body.len() != offset + 4 + signature_len {
        return Err(Error::ParseFailure(
            "tls12 server key exchange signature length is malformed",
        ));
    }
    Ok((signature_scheme, &body[offset + 4..]))
}

/// Builds RFC 5246 ServerKeyExchange signed input: client_random || server_random || params.
///
/// # Arguments
///
/// * `client_random` — 32-byte ClientHello random.
/// * `server_random` — 32-byte ServerHello random.
/// * `params` — ServerDHParams or ServerECDHParams bytes.
///
/// # Returns
///
/// Concatenated signed input bytes.
///
/// # Panics
///
/// This function does not panic.
#[must_use]
pub(super) fn noxtls_build_tls12_server_key_exchange_signed_input(
    client_random: &[u8; 32],
    server_random: &[u8; 32],
    params: &[u8],
) -> Vec<u8> {
    let mut signed_input = Vec::with_capacity(64 + params.len());
    signed_input.extend_from_slice(client_random);
    signed_input.extend_from_slice(server_random);
    signed_input.extend_from_slice(params);
    signed_input
}

impl Connection {
    /// Verifies a TLS 1.2 ServerKeyExchange signature against the server certificate public key.
    ///
    /// # Arguments
    ///
    /// * `self` — Client connection with TLS 1.2 random values and server leaf SPKI installed.
    /// * `params` — Signed ServerKeyExchange parameter bytes (DHE or ECDHE params).
    /// * `signature_scheme` — Negotiated TLS 1.2 signature scheme identifier.
    /// * `signature` — Signature bytes from ServerKeyExchange.
    ///
    /// # Returns
    ///
    /// `Ok(())` when the signature verifies.
    ///
    /// # Errors
    ///
    /// Returns [`noxtls_core::Error`] when random values, server public key, or signature verification fail.
    ///
    /// # Panics
    ///
    /// This function does not panic.
    pub(super) fn noxtls_verify_tls12_server_key_exchange_signature(
        &self,
        params: &[u8],
        signature_scheme: u16,
        signature: &[u8],
    ) -> Result<()> {
        let client_random = self.tls12_client_random.ok_or(Error::StateError(
            "tls12 client random must be available before server key exchange verification",
        ))?;
        let server_random = self.tls12_server_random.ok_or(Error::StateError(
            "tls12 server random must be available before server key exchange verification",
        ))?;
        let leaf_spki =
            self.tls13_server_leaf_public_key_der
                .as_deref()
                .ok_or(Error::StateError(
                "tls12 server certificate public key is unavailable for key exchange verification",
            ))?;
        let signed_input = noxtls_build_tls12_server_key_exchange_signed_input(
            &client_random,
            &server_random,
            params,
        );
        Self::noxtls_verify_tls12_digitally_signed_signature(
            signature_scheme,
            signature,
            &signed_input,
            leaf_spki,
        )
    }

    /// Verifies one TLS 1.2 digitally-signed blob using a leaf SubjectPublicKeyInfo.
    ///
    /// # Arguments
    ///
    /// * `signature_scheme` — Combined hash/signature scheme identifier.
    /// * `signature` — Signature bytes.
    /// * `signed_input` — RFC 5246 signed octets (randoms plus params).
    /// * `leaf_spki` — DER SubjectPublicKeyInfo for the server leaf certificate.
    ///
    /// # Returns
    ///
    /// `Ok(())` when verification succeeds.
    ///
    /// # Errors
    ///
    /// Returns [`noxtls_core::Error`] when the scheme is unsupported or verification fails.
    ///
    /// # Panics
    ///
    /// This function does not panic.
    fn noxtls_verify_tls12_digitally_signed_signature(
        signature_scheme: u16,
        signature: &[u8],
        signed_input: &[u8],
        leaf_spki: &[u8],
    ) -> Result<()> {
        match signature_scheme {
            TLS13_SIGALG_ECDSA_SECP256R1_SHA256 => {
                let public_key = P256PublicKey::from_uncompressed(leaf_spki)?;
                let (r, s) = if signature.len() == 64 {
                    let mut r = [0_u8; 32];
                    let mut s = [0_u8; 32];
                    r.copy_from_slice(&signature[..32]);
                    s.copy_from_slice(&signature[32..]);
                    (r, s)
                } else {
                    noxtls_parse_ecdsa_signature_der(signature)?
                };
                noxtls_p256_ecdsa_verify_sha256(&public_key, signed_input, &r, &s).map_err(|_| {
                    Error::CryptoFailure("tls12 server key exchange signature validation failed")
                })
            }
            TLS13_SIGALG_ECDSA_SECP384R1_SHA384 => {
                let public_key = P384PublicKey::from_uncompressed(leaf_spki)
                    .map_err(|_| Error::ParseFailure("failed to parse p384 server public key"))?;
                let (r, s) = noxtls_parse_ecdsa_p384_signature_der(signature)
                    .map_err(|_| Error::ParseFailure("failed to parse p384 ecdsa signature"))?;
                noxtls_p384_ecdsa_verify_sha384(&public_key, signed_input, &r, &s).map_err(|_| {
                    Error::CryptoFailure("tls12 server key exchange signature validation failed")
                })
            }
            TLS13_SIGALG_RSA_PSS_RSAE_SHA256 | TLS13_SIGALG_RSA_PSS_PSS_SHA256 => {
                let public_key = noxtls_parse_rsa_public_key_der(leaf_spki)?;
                noxtls_rsassa_pss_sha256_verify(&public_key, signed_input, signature, 32).map_err(
                    |_| {
                        Error::CryptoFailure(
                            "tls12 server key exchange signature validation failed",
                        )
                    },
                )
            }
            TLS13_SIGALG_RSA_PSS_RSAE_SHA384 | TLS13_SIGALG_RSA_PSS_PSS_SHA384 => {
                let public_key = noxtls_parse_rsa_public_key_der(leaf_spki)?;
                noxtls_rsassa_pss_sha384_verify(&public_key, signed_input, signature, 48).map_err(
                    |_| {
                        Error::CryptoFailure(
                            "tls12 server key exchange signature validation failed",
                        )
                    },
                )
            }
            TLS13_SIGALG_RSA_PSS_RSAE_SHA512 | TLS13_SIGALG_RSA_PSS_PSS_SHA512 => {
                let public_key = noxtls_parse_rsa_public_key_der(leaf_spki)?;
                noxtls_rsassa_pss_verify(
                    &public_key,
                    signed_input,
                    signature,
                    RsaPssHashAlgorithm::Sha512,
                    RsaPssHashAlgorithm::Sha512,
                    64,
                )
                .map_err(|_| {
                    Error::CryptoFailure("tls12 server key exchange signature validation failed")
                })
            }
            TLS13_SIGALG_ED25519 => {
                let public_key = noxtls_ed25519_public_key_from_subject_public_key_info(leaf_spki)?;
                noxtls_ed25519_verify(&public_key, signed_input, signature).map_err(|_| {
                    Error::CryptoFailure("tls12 server key exchange signature validation failed")
                })
            }
            TLS13_SIGALG_MLDSA65 => {
                let public_key = MlDsaPublicKey::from_bytes(leaf_spki).map_err(|_| {
                    Error::ParseFailure("failed to parse mldsa server public key bytes")
                })?;
                noxtls_mldsa_verify(&public_key, signed_input, signature).map_err(|_| {
                    Error::CryptoFailure("tls12 server key exchange signature validation failed")
                })
            }
            _ => Err(Error::UnsupportedFeature(
                "unsupported tls12 server key exchange signature scheme",
            )),
        }
    }
}