// 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

use crate::internal_alloc::Vec;
use noxtls_core::{Error, Result};
use noxtls_crypto::{
    noxtls_p256_ecdsa_sign_sha256, noxtls_p384_ecdsa_sign_sha384, noxtls_rsassa_sha256_sign,
    Ed25519PrivateKey, Ed25519PublicKey, MlDsaPrivateKey, MlDsaPublicKey, P256PrivateKey,
    P256PublicKey, P384PrivateKey, P384PublicKey, RsaPrivateKey, RsaPublicKey, OID_ID_MLDSA65,
};

use super::{
    noxtls_ed25519_public_key_to_spki_der, noxtls_mldsa_public_key_to_spki_der,
    noxtls_p256_public_key_to_spki_der, noxtls_p384_public_key_to_spki_der,
    noxtls_rsa_public_key_to_spki_der,
};

/// Writes DER INTEGER encoding for a positive integer value.
///
/// # Arguments
/// * `value`: Big-endian integer bytes to encode.
///
/// # Returns
/// DER-encoded INTEGER TLV bytes.
pub fn noxtls_write_der_integer(value: &[u8]) -> Result<Vec<u8>> {
    if value.is_empty() {
        return Err(Error::InvalidLength("der integer value must not be empty"));
    }
    let mut body = value.to_vec();
    while body.len() > 1 && body[0] == 0 {
        body.remove(0);
    }
    if body[0] & 0x80 != 0 {
        body.insert(0, 0x00);
    }
    let mut out = vec![0x02];
    out.extend_from_slice(&encode_der_len(body.len())?);
    out.extend_from_slice(&body);
    Ok(out)
}

/// Writes DER SEQUENCE around already-encoded child elements.
///
/// # Arguments
/// * `encoded_children`: Concatenated DER-encoded child TLVs.
///
/// # Returns
/// DER-encoded SEQUENCE TLV bytes.
pub fn noxtls_write_der_sequence(encoded_children: &[u8]) -> Result<Vec<u8>> {
    let mut out = vec![0x30];
    out.extend_from_slice(&encode_der_len(encoded_children.len())?);
    out.extend_from_slice(encoded_children);
    Ok(out)
}

/// Writes DER OBJECT IDENTIFIER encoding from content octets.
///
/// # Arguments
/// * `oid_content`: BER/DER OID content octets (without tag/length).
///
/// # Returns
/// DER-encoded OBJECT IDENTIFIER TLV bytes.
pub fn noxtls_write_der_oid(oid_content: &[u8]) -> Result<Vec<u8>> {
    if oid_content.is_empty() {
        return Err(Error::InvalidLength("der oid value must not be empty"));
    }
    let mut out = vec![0x06];
    out.extend_from_slice(&encode_der_len(oid_content.len())?);
    out.extend_from_slice(oid_content);
    Ok(out)
}

/// Writes DER BIT STRING encoding with zero unused bits.
///
/// # Arguments
/// * `bits`: Bit-string payload bytes to encode.
///
/// # Returns
/// DER-encoded BIT STRING TLV bytes.
pub fn noxtls_write_der_bit_string(bits: &[u8]) -> Result<Vec<u8>> {
    let mut out = vec![0x03];
    out.extend_from_slice(&encode_der_len(bits.len() + 1)?);
    out.push(0x00); // unused-bit count
    out.extend_from_slice(bits);
    Ok(out)
}

/// Writes a minimal certificate-like DER structure for fixture generation workflows.
///
/// # Arguments
/// * `serial`: Certificate serial number bytes.
/// * `raw_tbs`: Pre-encoded TBSCertificate child bytes to embed.
///
/// # Returns
/// DER bytes containing a minimal certificate-like sequence.
pub fn noxtls_write_minimal_certificate_der(serial: &[u8], raw_tbs: &[u8]) -> Result<Vec<u8>> {
    let serial_der = noxtls_write_der_integer(serial)?;
    let mut tbs_children = Vec::new();
    tbs_children.extend_from_slice(&serial_der);
    tbs_children.extend_from_slice(raw_tbs);
    let tbs_der = noxtls_write_der_sequence(&tbs_children)?;
    noxtls_write_der_sequence(&tbs_der)
}

/// KeyUsage bit for `digitalSignature`.
pub const NOXTLS_X509_KEY_USAGE_DIGITAL_SIGNATURE: u16 = 1 << 0;
/// KeyUsage bit for `contentCommitment` / nonRepudiation.
pub const NOXTLS_X509_KEY_USAGE_CONTENT_COMMITMENT: u16 = 1 << 1;
/// KeyUsage bit for `keyEncipherment`.
pub const NOXTLS_X509_KEY_USAGE_KEY_ENCIPHERMENT: u16 = 1 << 2;
/// KeyUsage bit for `dataEncipherment`.
pub const NOXTLS_X509_KEY_USAGE_DATA_ENCIPHERMENT: u16 = 1 << 3;
/// KeyUsage bit for `keyAgreement`.
pub const NOXTLS_X509_KEY_USAGE_KEY_AGREEMENT: u16 = 1 << 4;
/// KeyUsage bit for `keyCertSign`.
pub const NOXTLS_X509_KEY_USAGE_KEY_CERT_SIGN: u16 = 1 << 5;
/// KeyUsage bit for `cRLSign`.
pub const NOXTLS_X509_KEY_USAGE_CRL_SIGN: u16 = 1 << 6;

/// Common X.509 certificate extension inputs for noxtls certificate writers.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct NoxtlsCertificateExtensions<'a> {
    pub is_ca: bool,
    pub path_len_constraint: Option<u32>,
    pub key_usage: Option<u16>,
    pub dns_names: &'a [&'a str],
    pub extended_key_usage_oids: &'a [&'a [u8]],
    pub subject_key_identifier: Option<&'a [u8]>,
    pub authority_key_identifier: Option<&'a [u8]>,
}

impl<'a> NoxtlsCertificateExtensions<'a> {
    #[must_use]
    pub fn leaf() -> Self {
        Self {
            is_ca: false,
            path_len_constraint: None,
            key_usage: Some(NOXTLS_X509_KEY_USAGE_DIGITAL_SIGNATURE),
            dns_names: &[],
            extended_key_usage_oids: &[],
            subject_key_identifier: None,
            authority_key_identifier: None,
        }
    }

    #[must_use]
    pub fn ca(path_len_constraint: Option<u32>) -> Self {
        Self {
            is_ca: true,
            path_len_constraint,
            key_usage: Some(NOXTLS_X509_KEY_USAGE_KEY_CERT_SIGN | NOXTLS_X509_KEY_USAGE_CRL_SIGN),
            dns_names: &[],
            extended_key_usage_oids: &[],
            subject_key_identifier: None,
            authority_key_identifier: None,
        }
    }
}

/// Public keys supported by noxtls certificate and CSR writers.
pub enum NoxtlsCertificatePublicKey<'a> {
    Rsa(&'a RsaPublicKey),
    P256(&'a P256PublicKey),
    P384(&'a P384PublicKey),
    Ed25519(Ed25519PublicKey),
    MlDsa65(&'a MlDsaPublicKey),
}

/// Signing keys supported by noxtls certificate and CSR writers.
pub enum NoxtlsCertificateSigner<'a> {
    RsaSha256(&'a RsaPrivateKey),
    P256Sha256(&'a P256PrivateKey),
    P384Sha384(&'a P384PrivateKey),
    Ed25519(&'a Ed25519PrivateKey),
    MlDsa65(&'a MlDsaPrivateKey),
}

impl<'a> NoxtlsCertificateSigner<'a> {
    fn algorithm_identifier(&self) -> Result<Vec<u8>> {
        match self {
            Self::RsaSha256(_) => algorithm_identifier_sha256_with_rsa(),
            Self::P256Sha256(_) => algorithm_identifier_ecdsa_sha256(),
            Self::P384Sha384(_) => algorithm_identifier_ecdsa_sha384(),
            Self::Ed25519(_) => algorithm_identifier_ed25519(),
            Self::MlDsa65(_) => algorithm_identifier_mldsa65(),
        }
    }

    fn sign(&self, message: &[u8]) -> Result<Vec<u8>> {
        match self {
            Self::RsaSha256(private) => noxtls_rsassa_sha256_sign(private, message),
            Self::P256Sha256(private) => {
                let (r, s) = noxtls_p256_ecdsa_sign_sha256(private, message)?;
                write_ecdsa_signature_der_scalars(&r, &s)
            }
            Self::P384Sha384(private) => {
                let (r, s) = noxtls_p384_ecdsa_sign_sha384(private, message)?;
                write_ecdsa_signature_der_scalars(&r, &s)
            }
            Self::Ed25519(private) => Ok(private.sign(message).to_vec()),
            Self::MlDsa65(private) => Ok(private.sign(message).to_vec()),
        }
    }
}

/// Writes a self-signed X.509 v3 certificate using P-384 ECDSA with SHA-384.
pub fn noxtls_write_self_signed_certificate_p384_sha384(
    serial: &[u8],
    common_name: &str,
    not_before: &str,
    not_after: &str,
    public_key: &P384PublicKey,
    private_key: &P384PrivateKey,
) -> Result<Vec<u8>> {
    noxtls_write_self_signed_certificate_with_extensions(
        serial,
        common_name,
        not_before,
        not_after,
        NoxtlsCertificatePublicKey::P384(public_key),
        NoxtlsCertificateSigner::P384Sha384(private_key),
        &NoxtlsCertificateExtensions::leaf(),
    )
}

/// Writes a self-signed X.509 v3 certificate using Ed25519.
pub fn noxtls_write_self_signed_certificate_ed25519(
    serial: &[u8],
    common_name: &str,
    not_before: &str,
    not_after: &str,
    public_key: Ed25519PublicKey,
    private_key: &Ed25519PrivateKey,
) -> Result<Vec<u8>> {
    noxtls_write_self_signed_certificate_with_extensions(
        serial,
        common_name,
        not_before,
        not_after,
        NoxtlsCertificatePublicKey::Ed25519(public_key),
        NoxtlsCertificateSigner::Ed25519(private_key),
        &NoxtlsCertificateExtensions::leaf(),
    )
}

/// Writes a self-signed X.509 v3 certificate using ML-DSA-65.
pub fn noxtls_write_self_signed_certificate_mldsa65(
    serial: &[u8],
    common_name: &str,
    not_before: &str,
    not_after: &str,
    public_key: &MlDsaPublicKey,
    private_key: &MlDsaPrivateKey,
) -> Result<Vec<u8>> {
    noxtls_write_self_signed_certificate_with_extensions(
        serial,
        common_name,
        not_before,
        not_after,
        NoxtlsCertificatePublicKey::MlDsa65(public_key),
        NoxtlsCertificateSigner::MlDsa65(private_key),
        &NoxtlsCertificateExtensions::leaf(),
    )
}

/// Writes a self-signed X.509 v3 certificate with caller-specified supported extensions.
pub fn noxtls_write_self_signed_certificate_with_extensions(
    serial: &[u8],
    common_name: &str,
    not_before: &str,
    not_after: &str,
    public_key: NoxtlsCertificatePublicKey<'_>,
    signer: NoxtlsCertificateSigner<'_>,
    extensions: &NoxtlsCertificateExtensions<'_>,
) -> Result<Vec<u8>> {
    noxtls_write_ca_issued_certificate_der(
        serial,
        common_name,
        common_name,
        not_before,
        not_after,
        public_key,
        signer,
        extensions,
    )
}

/// Writes a CA-issued X.509 v3 certificate using a supplied issuer signer and subject public key.
pub fn noxtls_write_ca_issued_certificate_der(
    serial: &[u8],
    issuer_common_name: &str,
    subject_common_name: &str,
    not_before: &str,
    not_after: &str,
    subject_public_key: NoxtlsCertificatePublicKey<'_>,
    issuer_signer: NoxtlsCertificateSigner<'_>,
    extensions: &NoxtlsCertificateExtensions<'_>,
) -> Result<Vec<u8>> {
    let spki_der = public_key_to_spki_der(subject_public_key)?;
    let signature_algorithm = issuer_signer.algorithm_identifier()?;
    let tbs_der = build_certificate_tbs_with_names(
        serial,
        issuer_common_name,
        subject_common_name,
        not_before,
        not_after,
        &signature_algorithm,
        &spki_der,
        extensions,
    )?;
    let signature = issuer_signer.sign(&tbs_der)?;
    build_certificate_der(&tbs_der, &signature_algorithm, &signature)
}

/// Writes a PKCS#10 CSR using P-384 ECDSA with SHA-384.
pub fn noxtls_write_csr_p384_sha384(
    common_name: &str,
    public_key: &P384PublicKey,
    private_key: &P384PrivateKey,
) -> Result<Vec<u8>> {
    noxtls_write_csr_with_extensions(
        common_name,
        NoxtlsCertificatePublicKey::P384(public_key),
        NoxtlsCertificateSigner::P384Sha384(private_key),
        None,
    )
}

/// Writes a PKCS#10 CSR using Ed25519.
pub fn noxtls_write_csr_ed25519(
    common_name: &str,
    public_key: Ed25519PublicKey,
    private_key: &Ed25519PrivateKey,
) -> Result<Vec<u8>> {
    noxtls_write_csr_with_extensions(
        common_name,
        NoxtlsCertificatePublicKey::Ed25519(public_key),
        NoxtlsCertificateSigner::Ed25519(private_key),
        None,
    )
}

/// Writes a PKCS#10 CSR using ML-DSA-65.
pub fn noxtls_write_csr_mldsa65(
    common_name: &str,
    public_key: &MlDsaPublicKey,
    private_key: &MlDsaPrivateKey,
) -> Result<Vec<u8>> {
    noxtls_write_csr_with_extensions(
        common_name,
        NoxtlsCertificatePublicKey::MlDsa65(public_key),
        NoxtlsCertificateSigner::MlDsa65(private_key),
        None,
    )
}

/// Writes a PKCS#10 CSR with an optional extensionRequest attribute.
pub fn noxtls_write_csr_with_extensions(
    common_name: &str,
    public_key: NoxtlsCertificatePublicKey<'_>,
    signer: NoxtlsCertificateSigner<'_>,
    extensions: Option<&NoxtlsCertificateExtensions<'_>>,
) -> Result<Vec<u8>> {
    let spki_der = public_key_to_spki_der(public_key)?;
    let extension_der = match extensions {
        Some(ext) => Some(write_extensions_from_spec(ext)?),
        None => None,
    };
    let cri_der =
        build_csr_info_with_extension_request(common_name, &spki_der, extension_der.as_deref())?;
    let signature_algorithm = signer.algorithm_identifier()?;
    let signature = signer.sign(&cri_der)?;
    build_csr_der(&cri_der, &signature_algorithm, &signature)
}

/// Writes a self-signed X.509 v3 certificate using RSA PKCS#1 v1.5 SHA-256.
///
/// # Arguments
/// * `serial`: Certificate serial number bytes.
/// * `common_name`: Subject/issuer commonName for self-signed identity.
/// * `not_before`: DER time string (`YYMMDDHHMMSSZ` or `YYYYMMDDHHMMSSZ`).
/// * `not_after`: DER time string (`YYMMDDHHMMSSZ` or `YYYYMMDDHHMMSSZ`).
/// * `public_key`: RSA public key for SPKI.
/// * `private_key`: RSA private key for self-signature.
///
/// # Returns
/// DER-encoded X.509 certificate.
pub fn noxtls_write_self_signed_certificate_rsa_sha256(
    serial: &[u8],
    common_name: &str,
    not_before: &str,
    not_after: &str,
    public_key: &RsaPublicKey,
    private_key: &RsaPrivateKey,
) -> Result<Vec<u8>> {
    let spki_der = noxtls_rsa_public_key_to_spki_der(public_key)?;
    let tbs_der = build_certificate_tbs(
        serial,
        common_name,
        not_before,
        not_after,
        &algorithm_identifier_sha256_with_rsa()?,
        &spki_der,
        true,
    )?;
    let signature = noxtls_rsassa_sha256_sign(private_key, &tbs_der)?;
    build_certificate_der(
        &tbs_der,
        &algorithm_identifier_sha256_with_rsa()?,
        &signature,
    )
}

/// Writes a self-signed X.509 v3 certificate using ECDSA P-256 SHA-256.
///
/// # Arguments
/// * `serial`: Certificate serial number bytes.
/// * `common_name`: Subject/issuer commonName for self-signed identity.
/// * `not_before`: DER time string (`YYMMDDHHMMSSZ` or `YYYYMMDDHHMMSSZ`).
/// * `not_after`: DER time string (`YYMMDDHHMMSSZ` or `YYYYMMDDHHMMSSZ`).
/// * `public_key`: P-256 public key for SPKI.
/// * `private_key`: P-256 private key for self-signature.
///
/// # Returns
/// DER-encoded X.509 certificate.
pub fn noxtls_write_self_signed_certificate_p256_sha256(
    serial: &[u8],
    common_name: &str,
    not_before: &str,
    not_after: &str,
    public_key: &P256PublicKey,
    private_key: &P256PrivateKey,
) -> Result<Vec<u8>> {
    let spki_der = noxtls_p256_public_key_to_spki_der(public_key)?;
    let tbs_der = build_certificate_tbs(
        serial,
        common_name,
        not_before,
        not_after,
        &algorithm_identifier_ecdsa_sha256()?,
        &spki_der,
        true,
    )?;
    let (r, s) = noxtls_p256_ecdsa_sign_sha256(private_key, &tbs_der)?;
    let signature_der = write_ecdsa_signature_der(&r, &s)?;
    build_certificate_der(
        &tbs_der,
        &algorithm_identifier_ecdsa_sha256()?,
        &signature_der,
    )
}

/// Writes a PKCS#10 CSR using RSA PKCS#1 v1.5 SHA-256.
///
/// # Arguments
/// * `common_name`: CSR subject commonName.
/// * `public_key`: RSA public key for SubjectPublicKeyInfo.
/// * `private_key`: RSA private key used to sign CertificationRequestInfo.
///
/// # Returns
/// DER-encoded CertificationRequest.
pub fn noxtls_write_csr_rsa_sha256(
    common_name: &str,
    public_key: &RsaPublicKey,
    private_key: &RsaPrivateKey,
) -> Result<Vec<u8>> {
    let spki_der = noxtls_rsa_public_key_to_spki_der(public_key)?;
    let cri_der = build_csr_info(common_name, &spki_der)?;
    let signature = noxtls_rsassa_sha256_sign(private_key, &cri_der)?;
    build_csr_der(
        &cri_der,
        &algorithm_identifier_sha256_with_rsa()?,
        &signature,
    )
}

/// Writes a PKCS#10 CSR using ECDSA P-256 SHA-256.
///
/// # Arguments
/// * `common_name`: CSR subject commonName.
/// * `public_key`: P-256 public key for SubjectPublicKeyInfo.
/// * `private_key`: P-256 private key used to sign CertificationRequestInfo.
///
/// # Returns
/// DER-encoded CertificationRequest.
pub fn noxtls_write_csr_p256_sha256(
    common_name: &str,
    public_key: &P256PublicKey,
    private_key: &P256PrivateKey,
) -> Result<Vec<u8>> {
    let spki_der = noxtls_p256_public_key_to_spki_der(public_key)?;
    let cri_der = build_csr_info(common_name, &spki_der)?;
    let (r, s) = noxtls_p256_ecdsa_sign_sha256(private_key, &cri_der)?;
    let signature_der = write_ecdsa_signature_der(&r, &s)?;
    build_csr_der(
        &cri_der,
        &algorithm_identifier_ecdsa_sha256()?,
        &signature_der,
    )
}

fn public_key_to_spki_der(public_key: NoxtlsCertificatePublicKey<'_>) -> Result<Vec<u8>> {
    match public_key {
        NoxtlsCertificatePublicKey::Rsa(public) => noxtls_rsa_public_key_to_spki_der(public),
        NoxtlsCertificatePublicKey::P256(public) => noxtls_p256_public_key_to_spki_der(public),
        NoxtlsCertificatePublicKey::P384(public) => noxtls_p384_public_key_to_spki_der(public),
        NoxtlsCertificatePublicKey::Ed25519(public) => {
            noxtls_ed25519_public_key_to_spki_der(public)
        }
        NoxtlsCertificatePublicKey::MlDsa65(public) => noxtls_mldsa_public_key_to_spki_der(public),
    }
}

fn build_certificate_tbs_with_names(
    serial: &[u8],
    issuer_common_name: &str,
    subject_common_name: &str,
    not_before: &str,
    not_after: &str,
    signature_algorithm: &[u8],
    spki_der: &[u8],
    extensions: &NoxtlsCertificateExtensions<'_>,
) -> Result<Vec<u8>> {
    let version_ctx = write_der_explicit_context(0, &noxtls_write_der_integer(&[0x02])?)?;
    let serial_der = noxtls_write_der_integer(serial)?;
    let issuer_der = write_common_name(issuer_common_name)?;
    let subject_der = write_common_name(subject_common_name)?;
    let validity = write_validity(not_before, not_after)?;
    let extensions = write_der_explicit_context(3, &write_extensions_from_spec(extensions)?)?;
    let mut tbs_children = Vec::new();
    tbs_children.extend_from_slice(&version_ctx);
    tbs_children.extend_from_slice(&serial_der);
    tbs_children.extend_from_slice(signature_algorithm);
    tbs_children.extend_from_slice(&issuer_der);
    tbs_children.extend_from_slice(&validity);
    tbs_children.extend_from_slice(&subject_der);
    tbs_children.extend_from_slice(spki_der);
    tbs_children.extend_from_slice(&extensions);
    noxtls_write_der_sequence(&tbs_children)
}

fn write_extensions_from_spec(extensions: &NoxtlsCertificateExtensions<'_>) -> Result<Vec<u8>> {
    let mut body = Vec::new();
    body.extend_from_slice(&write_basic_constraints_extension_with_path_len(
        extensions.is_ca,
        extensions.path_len_constraint,
    )?);
    if let Some(key_usage) = extensions.key_usage {
        body.extend_from_slice(&write_key_usage_extension(key_usage, true)?);
    }
    if !extensions.dns_names.is_empty() {
        body.extend_from_slice(&write_subject_alt_name_extension(extensions.dns_names)?);
    }
    if !extensions.extended_key_usage_oids.is_empty() {
        body.extend_from_slice(&write_extended_key_usage_extension(
            extensions.extended_key_usage_oids,
        )?);
    }
    if let Some(key_id) = extensions.subject_key_identifier {
        body.extend_from_slice(&write_subject_key_identifier_extension(key_id)?);
    }
    if let Some(key_id) = extensions.authority_key_identifier {
        body.extend_from_slice(&write_authority_key_identifier_extension(key_id)?);
    }
    noxtls_write_der_sequence(&body)
}

fn build_csr_info_with_extension_request(
    common_name: &str,
    spki_der: &[u8],
    extensions_der: Option<&[u8]>,
) -> Result<Vec<u8>> {
    let version = noxtls_write_der_integer(&[0x00])?;
    let subject = write_common_name(common_name)?;
    let attributes = match extensions_der {
        Some(extensions) => {
            write_der_explicit_context(0, &write_extension_request_attribute(extensions)?)?
        }
        None => write_der_explicit_context(0, &[])?,
    };
    let mut info_children = Vec::new();
    info_children.extend_from_slice(&version);
    info_children.extend_from_slice(&subject);
    info_children.extend_from_slice(spki_der);
    info_children.extend_from_slice(&attributes);
    noxtls_write_der_sequence(&info_children)
}

fn write_extension_request_attribute(extensions_der: &[u8]) -> Result<Vec<u8>> {
    let oid = noxtls_write_der_oid(&[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x0E])?;
    let values = write_der_set(extensions_der)?;
    let mut attribute = Vec::new();
    attribute.extend_from_slice(&oid);
    attribute.extend_from_slice(&values);
    noxtls_write_der_sequence(&attribute)
}

fn write_extension(oid_content: &[u8], critical: bool, value_der: &[u8]) -> Result<Vec<u8>> {
    let oid = noxtls_write_der_oid(oid_content)?;
    let ext_value = write_der_octet_string(value_der)?;
    let mut body = Vec::new();
    body.extend_from_slice(&oid);
    if critical {
        body.extend_from_slice(&write_der_boolean(true)?);
    }
    body.extend_from_slice(&ext_value);
    noxtls_write_der_sequence(&body)
}

fn write_basic_constraints_extension_with_path_len(
    is_ca: bool,
    path_len: Option<u32>,
) -> Result<Vec<u8>> {
    let mut value = Vec::new();
    if is_ca || path_len.is_some() {
        value.extend_from_slice(&write_der_boolean(is_ca)?);
    }
    if let Some(path_len) = path_len {
        value.extend_from_slice(&noxtls_write_der_integer(&path_len.to_be_bytes())?);
    }
    let value_seq = noxtls_write_der_sequence(&value)?;
    write_extension(&[0x55, 0x1D, 0x13], true, &value_seq)
}

fn write_key_usage_extension(bits: u16, critical: bool) -> Result<Vec<u8>> {
    if bits == 0 {
        return Err(Error::InvalidLength(
            "key usage must contain at least one bit",
        ));
    }
    let highest = 15 - bits.leading_zeros() as usize;
    let byte_len = highest / 8 + 1;
    let mut encoded = vec![0_u8; byte_len];
    for bit in 0..=highest {
        if bits & (1_u16 << bit) != 0 {
            encoded[bit / 8] |= 1 << (7 - (bit % 8));
        }
    }
    let unused_bits = (byte_len * 8 - (highest + 1)) as u8;
    let value = write_der_bit_string_with_unused_bits(&encoded, unused_bits)?;
    write_extension(&[0x55, 0x1D, 0x0F], critical, &value)
}

fn write_subject_alt_name_extension(dns_names: &[&str]) -> Result<Vec<u8>> {
    let mut names = Vec::new();
    for dns_name in dns_names {
        if dns_name.is_empty() || !dns_name.bytes().all(|b| b.is_ascii()) {
            return Err(Error::InvalidEncoding("dnsName must be non-empty ASCII"));
        }
        names.extend_from_slice(&write_der_primitive_context(2, dns_name.as_bytes())?);
    }
    let value = noxtls_write_der_sequence(&names)?;
    write_extension(&[0x55, 0x1D, 0x11], false, &value)
}

fn write_extended_key_usage_extension(oids: &[&[u8]]) -> Result<Vec<u8>> {
    let mut value = Vec::new();
    for oid in oids {
        value.extend_from_slice(&noxtls_write_der_oid(oid)?);
    }
    let value = noxtls_write_der_sequence(&value)?;
    write_extension(&[0x55, 0x1D, 0x25], false, &value)
}

fn write_subject_key_identifier_extension(key_id: &[u8]) -> Result<Vec<u8>> {
    if key_id.is_empty() {
        return Err(Error::InvalidLength(
            "subject key identifier must not be empty",
        ));
    }
    let value = write_der_octet_string(key_id)?;
    write_extension(&[0x55, 0x1D, 0x0E], false, &value)
}

fn write_authority_key_identifier_extension(key_id: &[u8]) -> Result<Vec<u8>> {
    if key_id.is_empty() {
        return Err(Error::InvalidLength(
            "authority key identifier must not be empty",
        ));
    }
    let key_identifier = write_der_primitive_context(0, key_id)?;
    let value = noxtls_write_der_sequence(&key_identifier)?;
    write_extension(&[0x55, 0x1D, 0x23], false, &value)
}

/// Builds `TBSCertificate` DER for self-signed, fixture-grade certificates.
///
/// # Arguments
///
/// * `serial` — Serial number body for `noxtls_write_der_integer`.
/// * `common_name` — Subject and issuer common name string.
/// * `not_before` / `not_after` — Validity strings passed to `write_validity`.
/// * `signature_algorithm` — Pre-encoded `AlgorithmIdentifier` DER bytes for the TBS field.
/// * `spki_der` — Subject `SubjectPublicKeyInfo` DER.
/// * `is_ca` — When `true`, writes CA-oriented extensions via `write_extensions`.
///
/// # Returns
///
/// On success, the concatenated `TBSCertificate` SEQUENCE bytes.
///
/// # Errors
///
/// Returns [`noxtls_core::Error`] when any nested DER writer fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic.
fn build_certificate_tbs(
    serial: &[u8],
    common_name: &str,
    not_before: &str,
    not_after: &str,
    signature_algorithm: &[u8],
    spki_der: &[u8],
    is_ca: bool,
) -> Result<Vec<u8>> {
    let version_ctx = write_der_explicit_context(0, &noxtls_write_der_integer(&[0x02])?)?;
    let serial_der = noxtls_write_der_integer(serial)?;
    let name_der = write_common_name(common_name)?;
    let validity = write_validity(not_before, not_after)?;
    let extensions = write_der_explicit_context(3, &write_extensions(is_ca)?)?;
    let mut tbs_children = Vec::new();
    tbs_children.extend_from_slice(&version_ctx);
    tbs_children.extend_from_slice(&serial_der);
    tbs_children.extend_from_slice(signature_algorithm);
    tbs_children.extend_from_slice(&name_der);
    tbs_children.extend_from_slice(&validity);
    tbs_children.extend_from_slice(&name_der);
    tbs_children.extend_from_slice(spki_der);
    tbs_children.extend_from_slice(&extensions);
    noxtls_write_der_sequence(&tbs_children)
}

/// Builds the top-level X.509 `Certificate` SEQUENCE from TBS, noxtls_algorithm identifier, and signature.
///
/// # Arguments
///
/// * `tbs_der` — `TBSCertificate` DER.
/// * `signature_algorithm` — Same `AlgorithmIdentifier` encoding as embedded in the TBS.
/// * `signature` — Raw signature octets (not BIT STRING wrapped).
///
/// # Returns
///
/// On success, the full certificate DER SEQUENCE.
///
/// # Errors
///
/// Returns [`noxtls_core::Error`] when BIT STRING or SEQUENCE encoding fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic.
fn build_certificate_der(
    tbs_der: &[u8],
    signature_algorithm: &[u8],
    signature: &[u8],
) -> Result<Vec<u8>> {
    let signature_bit_string = noxtls_write_der_bit_string(signature)?;
    let mut cert_children = Vec::new();
    cert_children.extend_from_slice(tbs_der);
    cert_children.extend_from_slice(signature_algorithm);
    cert_children.extend_from_slice(&signature_bit_string);
    noxtls_write_der_sequence(&cert_children)
}

/// Builds CertificationRequestInfo DER for PKCS#10.
///
/// # Arguments
///
/// * `common_name` — `&str`.
/// * `spki_der` — `&[u8]`.
///
/// # Returns
///
/// On success, the `Ok` payload from `build_csr_info`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn build_csr_info(common_name: &str, spki_der: &[u8]) -> Result<Vec<u8>> {
    let version = noxtls_write_der_integer(&[0x00])?;
    let subject = write_common_name(common_name)?;
    let attributes = write_der_explicit_context(0, &[])?;
    let mut info_children = Vec::new();
    info_children.extend_from_slice(&version);
    info_children.extend_from_slice(&subject);
    info_children.extend_from_slice(spki_der);
    info_children.extend_from_slice(&attributes);
    noxtls_write_der_sequence(&info_children)
}

/// Builds final PKCS#10 CSR DER from CRI, AlgorithmIdentifier, and raw signature bytes.
///
/// # Arguments
///
/// * `cri_der` — `&[u8]`.
/// * `signature_algorithm` — `&[u8]`.
/// * `signature` — `&[u8]`.
///
/// # Returns
///
/// On success, the `Ok` payload from `build_csr_der`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn build_csr_der(cri_der: &[u8], signature_algorithm: &[u8], signature: &[u8]) -> Result<Vec<u8>> {
    let signature_bit_string = noxtls_write_der_bit_string(signature)?;
    let mut csr_children = Vec::new();
    csr_children.extend_from_slice(cri_der);
    csr_children.extend_from_slice(signature_algorithm);
    csr_children.extend_from_slice(&signature_bit_string);
    noxtls_write_der_sequence(&csr_children)
}

/// Builds AlgorithmIdentifier for sha256WithRSAEncryption.
///
/// # Arguments
///
/// * *(none)* — This function takes no parameters.
///
/// # Returns
///
/// On success, the `Ok` payload from `algorithm_identifier_sha256_with_rsa`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn algorithm_identifier_sha256_with_rsa() -> Result<Vec<u8>> {
    let oid = noxtls_write_der_oid(&[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B])?;
    let null = write_der_null()?;
    let mut body = Vec::new();
    body.extend_from_slice(&oid);
    body.extend_from_slice(&null);
    noxtls_write_der_sequence(&body)
}

/// Builds AlgorithmIdentifier for ecdsa-with-SHA256.
///
/// # Arguments
///
/// * *(none)* — This function takes no parameters.
///
/// # Returns
///
/// On success, the `Ok` payload from `algorithm_identifier_ecdsa_sha256`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn algorithm_identifier_ecdsa_sha256() -> Result<Vec<u8>> {
    let oid = noxtls_write_der_oid(&[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02])?;
    noxtls_write_der_sequence(&oid)
}

fn algorithm_identifier_ecdsa_sha384() -> Result<Vec<u8>> {
    let oid = noxtls_write_der_oid(&[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03])?;
    noxtls_write_der_sequence(&oid)
}

fn algorithm_identifier_ed25519() -> Result<Vec<u8>> {
    let oid = noxtls_write_der_oid(&[0x2B, 0x65, 0x70])?;
    noxtls_write_der_sequence(&oid)
}

fn algorithm_identifier_mldsa65() -> Result<Vec<u8>> {
    let oid = noxtls_write_der_oid(OID_ID_MLDSA65)?;
    noxtls_write_der_sequence(&oid)
}

/// Builds Name ::= SEQUENCE OF RDN containing one commonName UTF8String.
///
/// # Arguments
///
/// * `common_name` — `&str`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_common_name`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_common_name(common_name: &str) -> Result<Vec<u8>> {
    if common_name.is_empty() {
        return Err(Error::InvalidLength("common name must not be empty"));
    }
    let oid_cn = noxtls_write_der_oid(&[0x55, 0x04, 0x03])?;
    let value = write_der_utf8_string(common_name.as_bytes())?;
    let mut atv = Vec::new();
    atv.extend_from_slice(&oid_cn);
    atv.extend_from_slice(&value);
    let atv_seq = noxtls_write_der_sequence(&atv)?;
    let rdn_set = write_der_set(&atv_seq)?;
    noxtls_write_der_sequence(&rdn_set)
}

/// Builds Validity ::= SEQUENCE { notBefore, notAfter } with UTC/generalized time tags.
///
/// # Arguments
///
/// * `not_before` — `&str`.
/// * `not_after` — `&str`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_validity`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_validity(not_before: &str, not_after: &str) -> Result<Vec<u8>> {
    let not_before_der = write_der_time(not_before)?;
    let not_after_der = write_der_time(not_after)?;
    let mut body = Vec::new();
    body.extend_from_slice(&not_before_der);
    body.extend_from_slice(&not_after_der);
    noxtls_write_der_sequence(&body)
}

/// Builds v3 basic constraints extension sequence.
///
/// # Arguments
///
/// * `is_ca` — `bool`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_extensions`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_extensions(is_ca: bool) -> Result<Vec<u8>> {
    let ext = write_basic_constraints_extension(is_ca)?;
    noxtls_write_der_sequence(&ext)
}

/// Writes one BasicConstraints extension with critical flag and cA boolean.
///
/// # Arguments
///
/// * `is_ca` — `bool`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_basic_constraints_extension`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_basic_constraints_extension(is_ca: bool) -> Result<Vec<u8>> {
    let oid = noxtls_write_der_oid(&[0x55, 0x1D, 0x13])?;
    let critical = write_der_boolean(true)?;
    let value_seq = {
        let ca = write_der_boolean(is_ca)?;
        noxtls_write_der_sequence(&ca)?
    };
    let ext_value = write_der_octet_string(&value_seq)?;
    let mut ext_body = Vec::new();
    ext_body.extend_from_slice(&oid);
    ext_body.extend_from_slice(&critical);
    ext_body.extend_from_slice(&ext_value);
    noxtls_write_der_sequence(&ext_body)
}

/// Writes DER NULL value.
///
/// # Arguments
///
/// * *(none)* — This function takes no parameters.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_der_null`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_der_null() -> Result<Vec<u8>> {
    let mut out = vec![0x05];
    out.extend_from_slice(&encode_der_len(0)?);
    Ok(out)
}

/// Writes DER BOOLEAN value.
///
/// # Arguments
///
/// * `value` — `bool`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_der_boolean`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_der_boolean(value: bool) -> Result<Vec<u8>> {
    let mut out = vec![0x01];
    out.extend_from_slice(&encode_der_len(1)?);
    out.push(if value { 0xFF } else { 0x00 });
    Ok(out)
}

/// Writes DER UTF8String value.
///
/// # Arguments
///
/// * `bytes` — `&[u8]`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_der_utf8_string`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_der_utf8_string(bytes: &[u8]) -> Result<Vec<u8>> {
    if bytes.is_empty() {
        return Err(Error::InvalidLength("utf8 string must not be empty"));
    }
    let mut out = vec![0x0C];
    out.extend_from_slice(&encode_der_len(bytes.len())?);
    out.extend_from_slice(bytes);
    Ok(out)
}

/// Writes DER SET value wrapping already-encoded children.
///
/// # Arguments
///
/// * `encoded_children` — `&[u8]`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_der_set`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_der_set(encoded_children: &[u8]) -> Result<Vec<u8>> {
    let mut out = vec![0x31];
    out.extend_from_slice(&encode_der_len(encoded_children.len())?);
    out.extend_from_slice(encoded_children);
    Ok(out)
}

/// Writes DER OCTET STRING value.
///
/// # Arguments
///
/// * `bytes` — `&[u8]`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_der_octet_string`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_der_octet_string(bytes: &[u8]) -> Result<Vec<u8>> {
    let mut out = vec![0x04];
    out.extend_from_slice(&encode_der_len(bytes.len())?);
    out.extend_from_slice(bytes);
    Ok(out)
}

fn write_der_bit_string_with_unused_bits(bits: &[u8], unused_bits: u8) -> Result<Vec<u8>> {
    if unused_bits > 7 {
        return Err(Error::InvalidLength(
            "bit string unused-bit count must be 0..=7",
        ));
    }
    let mut out = vec![0x03];
    out.extend_from_slice(&encode_der_len(bits.len() + 1)?);
    out.push(unused_bits);
    out.extend_from_slice(bits);
    Ok(out)
}

fn write_der_primitive_context(tag_no: u8, value: &[u8]) -> Result<Vec<u8>> {
    if tag_no > 30 {
        return Err(Error::InvalidLength("context-specific tag too large"));
    }
    let mut out = vec![0x80 | tag_no];
    out.extend_from_slice(&encode_der_len(value.len())?);
    out.extend_from_slice(value);
    Ok(out)
}

/// Writes DER explicit context-specific wrapper `[tag_no] EXPLICIT`.
///
/// # Arguments
///
/// * `tag_no` — `u8`.
/// * `encoded_child` — `&[u8]`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_der_explicit_context`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_der_explicit_context(tag_no: u8, encoded_child: &[u8]) -> Result<Vec<u8>> {
    let mut out = vec![0xA0 | tag_no];
    out.extend_from_slice(&encode_der_len(encoded_child.len())?);
    out.extend_from_slice(encoded_child);
    Ok(out)
}

/// Writes DER time node for UTCTime or GeneralizedTime text.
///
/// # Arguments
///
/// * `time` — `&str`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_der_time`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_der_time(time: &str) -> Result<Vec<u8>> {
    let (tag, body) = if time.len() == 13 {
        (0x17, time.as_bytes())
    } else if time.len() == 15 {
        (0x18, time.as_bytes())
    } else {
        return Err(Error::InvalidLength(
            "time must be UTCTime or GeneralizedTime textual length",
        ));
    };
    if !time.ends_with('Z') || !time[..time.len() - 1].bytes().all(|b| b.is_ascii_digit()) {
        return Err(Error::InvalidEncoding(
            "time must end with Z and contain digits",
        ));
    }
    let mut out = vec![tag];
    out.extend_from_slice(&encode_der_len(body.len())?);
    out.extend_from_slice(body);
    Ok(out)
}

/// Writes DER-encoded ECDSA signature sequence from fixed-width r and s values.
///
/// # Arguments
///
/// * `r` — `&[u8; 32]`.
/// * `s` — `&[u8; 32]`.
///
/// # Returns
///
/// On success, the `Ok` payload from `write_ecdsa_signature_der`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn write_ecdsa_signature_der(r: &[u8; 32], s: &[u8; 32]) -> Result<Vec<u8>> {
    write_ecdsa_signature_der_scalars(r, s)
}

fn write_ecdsa_signature_der_scalars(r: &[u8], s: &[u8]) -> Result<Vec<u8>> {
    let r_der = noxtls_write_der_integer(r)?;
    let s_der = noxtls_write_der_integer(s)?;
    let mut body = Vec::new();
    body.extend_from_slice(&r_der);
    body.extend_from_slice(&s_der);
    noxtls_write_der_sequence(&body)
}

/// Encodes a DER definite length prefix in short or long form for the given content length.
///
/// # Arguments
///
/// * `len` — Number of content octets following the length bytes.
///
/// # Returns
///
/// On success, the `Ok` payload from `encode_der_len`; see implementation for value shape.
///
/// # Errors
///
/// Returns `noxtls_core::Error` when parsing or validation fails; see implementation for specific variants.
///
/// # Panics
///
/// This function does not panic unless otherwise noted.
fn encode_der_len(len: usize) -> Result<Vec<u8>> {
    if len < 128 {
        return Ok(vec![len as u8]);
    }
    let len_u32 = u32::try_from(len).map_err(|_| Error::InvalidLength("der length too large"))?;
    let bytes = len_u32.to_be_bytes();
    let first_nonzero = bytes
        .iter()
        .position(|byte| *byte != 0)
        .unwrap_or(bytes.len() - 1);
    let content = &bytes[first_nonzero..];
    let mut out = Vec::with_capacity(content.len() + 1);
    out.push(0x80 | (content.len() as u8));
    out.extend_from_slice(content);
    Ok(out)
}
#[cfg(test)]
mod tests {
    use super::*;
    use noxtls_crypto::{
        noxtls_mldsa_generate_keypair_auto, Ed25519PrivateKey, HmacDrbgSha256, P256PrivateKey,
        P384PrivateKey,
    };

    const SERVER_AUTH: &[u8] = &[0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01];

    fn test_drbg() -> HmacDrbgSha256 {
        HmacDrbgSha256::noxtls_new(
            &[0x55; 32],
            b"x509 writer nonce",
            b"x509 writer personalization",
        )
        .expect("drbg")
    }

    #[test]
    fn noxtls_ca_issued_p384_certificate_writes_rich_extensions() {
        let issuer_private = P256PrivateKey::from_bytes([0x11; 32]).expect("issuer key");
        let subject_private = P384PrivateKey::from_bytes([0x22; 48]).expect("subject key");
        let subject_public = subject_private.public_key().expect("subject public");
        let eku = [SERVER_AUTH];
        let dns = ["example.test", "www.example.test"];
        let extensions = NoxtlsCertificateExtensions {
            is_ca: false,
            path_len_constraint: None,
            key_usage: Some(
                NOXTLS_X509_KEY_USAGE_DIGITAL_SIGNATURE | NOXTLS_X509_KEY_USAGE_KEY_AGREEMENT,
            ),
            dns_names: &dns,
            extended_key_usage_oids: &eku,
            subject_key_identifier: Some(&[0x01, 0x02, 0x03, 0x04]),
            authority_key_identifier: Some(&[0x05, 0x06, 0x07, 0x08]),
        };

        let cert = noxtls_write_ca_issued_certificate_der(
            &[0x01],
            "Noxtls Test CA",
            "example.test",
            "250101000000Z",
            "260101000000Z",
            NoxtlsCertificatePublicKey::P384(&subject_public),
            NoxtlsCertificateSigner::P256Sha256(&issuer_private),
            &extensions,
        )
        .expect("cert");
        let parsed = crate::certs::noxtls_parse_certificate(&cert).expect("parse cert");
        assert_eq!(parsed.basic_constraints_ca, Some(false));
        assert_eq!(parsed.key_usage_bits, extensions.key_usage);
        assert_eq!(
            parsed.subject_alt_dns_names,
            vec!["example.test", "www.example.test"]
        );
        assert_eq!(parsed.extended_key_usage_oids, vec![SERVER_AUTH.to_vec()]);
        assert_eq!(
            parsed.subject_public_key_algorithm_oid,
            vec![0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01]
        );
    }

    #[test]
    fn noxtls_self_signed_ed25519_and_mldsa65_certificates_parse() {
        let ed_private = Ed25519PrivateKey::from_seed(&[0x33; 32]);
        let ed_cert = noxtls_write_self_signed_certificate_ed25519(
            &[0x02],
            "ed25519.example",
            "250101000000Z",
            "260101000000Z",
            ed_private.verifying_key(),
            &ed_private,
        )
        .expect("ed cert");
        let ed_parsed = crate::certs::noxtls_parse_certificate(&ed_cert).expect("parse ed cert");
        assert_eq!(
            ed_parsed.certificate_signature_algorithm_oid,
            vec![0x2B, 0x65, 0x70]
        );

        let mut drbg = test_drbg();
        let (mldsa_private, mldsa_public) =
            noxtls_mldsa_generate_keypair_auto(&mut drbg).expect("mldsa keygen");
        let mldsa_cert = noxtls_write_self_signed_certificate_mldsa65(
            &[0x03],
            "mldsa.example",
            "250101000000Z",
            "260101000000Z",
            &mldsa_public,
            &mldsa_private,
        )
        .expect("mldsa cert");
        let mldsa_parsed =
            crate::certs::noxtls_parse_certificate(&mldsa_cert).expect("parse mldsa cert");
        assert_eq!(
            mldsa_parsed.certificate_signature_algorithm_oid,
            OID_ID_MLDSA65.to_vec()
        );
    }

    #[test]
    fn noxtls_csr_writers_cover_p384_ed25519_and_mldsa65() {
        let p384_private = P384PrivateKey::from_bytes([0x44; 48]).expect("p384 key");
        let p384_public = p384_private.public_key().expect("p384 public");
        let p384_csr = noxtls_write_csr_p384_sha384("p384.example", &p384_public, &p384_private)
            .expect("p384 csr");
        assert_eq!(
            crate::certs::noxtls_parse_der_node(&p384_csr)
                .expect("csr node")
                .0
                .tag,
            0x30
        );

        let ed_private = Ed25519PrivateKey::from_seed(&[0x45; 32]);
        let ed_csr =
            noxtls_write_csr_ed25519("ed.example", ed_private.verifying_key(), &ed_private)
                .expect("ed csr");
        assert_eq!(
            crate::certs::noxtls_parse_der_node(&ed_csr)
                .expect("csr node")
                .0
                .tag,
            0x30
        );

        let mut drbg = test_drbg();
        let (mldsa_private, mldsa_public) =
            noxtls_mldsa_generate_keypair_auto(&mut drbg).expect("mldsa keygen");
        let mldsa_csr = noxtls_write_csr_mldsa65("mldsa.example", &mldsa_public, &mldsa_private)
            .expect("mldsa csr");
        assert_eq!(
            crate::certs::noxtls_parse_der_node(&mldsa_csr)
                .expect("csr node")
                .0
                .tag,
            0x30
        );
    }
}
