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,
};
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)
}
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)
}
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)
}
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); out.extend_from_slice(bits);
Ok(out)
}
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)
}
pub const NOXTLS_X509_KEY_USAGE_DIGITAL_SIGNATURE: u16 = 1 << 0;
pub const NOXTLS_X509_KEY_USAGE_CONTENT_COMMITMENT: u16 = 1 << 1;
pub const NOXTLS_X509_KEY_USAGE_KEY_ENCIPHERMENT: u16 = 1 << 2;
pub const NOXTLS_X509_KEY_USAGE_DATA_ENCIPHERMENT: u16 = 1 << 3;
pub const NOXTLS_X509_KEY_USAGE_KEY_AGREEMENT: u16 = 1 << 4;
pub const NOXTLS_X509_KEY_USAGE_KEY_CERT_SIGN: u16 = 1 << 5;
pub const NOXTLS_X509_KEY_USAGE_CRL_SIGN: u16 = 1 << 6;
#[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,
}
}
}
pub enum NoxtlsCertificatePublicKey<'a> {
Rsa(&'a RsaPublicKey),
P256(&'a P256PublicKey),
P384(&'a P384PublicKey),
Ed25519(Ed25519PublicKey),
MlDsa65(&'a MlDsaPublicKey),
}
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()),
}
}
}
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(),
)
}
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(),
)
}
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(),
)
}
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,
)
}
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)
}
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,
)
}
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,
)
}
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,
)
}
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)
}
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,
)
}
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,
)
}
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,
)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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(¬_before_der);
body.extend_from_slice(¬_after_der);
noxtls_write_der_sequence(&body)
}
fn write_extensions(is_ca: bool) -> Result<Vec<u8>> {
let ext = write_basic_constraints_extension(is_ca)?;
noxtls_write_der_sequence(&ext)
}
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)
}
fn write_der_null() -> Result<Vec<u8>> {
let mut out = vec![0x05];
out.extend_from_slice(&encode_der_len(0)?);
Ok(out)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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
);
}
}