- crypto.c: x509_find_ext (generic extension walker) + wo_x509_basic_constraints (cA / pathLenConstraint, absent => not a CA) + wo_x509_eku_serverauth_ok (EKU absent, serverAuth, or anyEKU => usable; else not) - wo_tls_verify_chain enforces decision 6: the leaf must be server-usable (EKU), every server-sent issuer and the signing anchor must be a CA (basicConstraints CA:TRUE) with a pathLenConstraint covering the intermediates below it — stops a leaf masquerading as a CA - gen_x509.py extended (folds in the wildcard leaf, adds EKU clientAuth-only, EKU serverAuth, a non-CA intermediate + a leaf issued under it); vectors regenerated - KATs: extractors (test_crypto 104) + chain enforcement (test_tls 103) — EKU serverAuth accepted, clientAuth-only rejected, leaf-under-non-CA rejected though every signature verifies; existing chains still pass. ASan/UBSan clean Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 3811418014c2c8d9bc3a9464256f52104261b1b9)
122 lines
5.6 KiB
Python
122 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate the wo_x509 KAT certificate vectors.
|
|
|
|
Two real chains (RSA CA+leaf, EC P-256 CA+leaf) for signature/SPKI/validity/SAN,
|
|
a wildcard-SAN leaf for hostname matching, and the phase-6 (decision 6) negatives:
|
|
a leaf whose EKU is clientAuth-only, a leaf with EKU serverAuth, a non-CA
|
|
intermediate, and a leaf issued under that non-CA intermediate.
|
|
|
|
Regenerate with: python3 runtime/test/gen_x509.py > runtime/test/x509_vectors.h
|
|
"""
|
|
import datetime
|
|
from cryptography import x509
|
|
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
|
|
from cryptography.hazmat.primitives import hashes
|
|
from cryptography.hazmat.primitives.asymmetric import rsa, ec
|
|
from cryptography.hazmat.primitives.serialization import Encoding
|
|
|
|
NB = datetime.datetime(2020, 1, 1)
|
|
NA = datetime.datetime(2030, 1, 1)
|
|
|
|
|
|
def mkname(cn):
|
|
return x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, cn)])
|
|
|
|
|
|
def base(subject, issuer, pubkey):
|
|
return (x509.CertificateBuilder()
|
|
.subject_name(mkname(subject)).issuer_name(mkname(issuer))
|
|
.public_key(pubkey)
|
|
.serial_number(x509.random_serial_number())
|
|
.not_valid_before(NB).not_valid_after(NA))
|
|
|
|
|
|
def selfsigned(key, cn, ca=True):
|
|
b = base(cn, cn, key.public_key()).add_extension(
|
|
x509.BasicConstraints(ca=ca, path_length=None), True)
|
|
return b.sign(key, hashes.SHA256())
|
|
|
|
|
|
def leafcert(leafkey, cakey, ca_cert, cn, sanhost, eku=None):
|
|
b = (x509.CertificateBuilder()
|
|
.subject_name(mkname(cn)).issuer_name(ca_cert.subject)
|
|
.public_key(leafkey.public_key())
|
|
.serial_number(x509.random_serial_number())
|
|
.not_valid_before(NB).not_valid_after(NA)
|
|
.add_extension(x509.SubjectAlternativeName([x509.DNSName(sanhost)]), False))
|
|
if eku:
|
|
b = b.add_extension(x509.ExtendedKeyUsage(eku), False)
|
|
return b.sign(cakey, hashes.SHA256())
|
|
|
|
|
|
def intermediate(intkey, cakey, ca_cert, cn, ca):
|
|
"""An intermediate signed by ca; ca flag sets basicConstraints."""
|
|
return (x509.CertificateBuilder()
|
|
.subject_name(mkname(cn)).issuer_name(ca_cert.subject)
|
|
.public_key(intkey.public_key())
|
|
.serial_number(x509.random_serial_number())
|
|
.not_valid_before(NB).not_valid_after(NA)
|
|
.add_extension(x509.BasicConstraints(ca=ca, path_length=None), True)
|
|
.sign(cakey, hashes.SHA256()))
|
|
|
|
|
|
def cbytes(name, der, note=""):
|
|
out = ("/* %s */\n" % note if note else "") + "static const uint8_t %s[] = {" % name
|
|
for i, b in enumerate(der):
|
|
if i % 12 == 0:
|
|
out += "\n "
|
|
out += "0x%02x," % b
|
|
return out + "\n};\n"
|
|
|
|
|
|
def der(c):
|
|
return c.public_bytes(Encoding.DER)
|
|
|
|
|
|
# --- RSA chain ---
|
|
rsa_ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
|
rsa_ca = selfsigned(rsa_ca_key, "wo-rsa-ca")
|
|
rsa_leaf_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
|
rsa_leaf = leafcert(rsa_leaf_key, rsa_ca_key, rsa_ca, "leaf.example.com", "leaf.example.com")
|
|
|
|
# --- EC chain ---
|
|
ec_ca_key = ec.generate_private_key(ec.SECP256R1())
|
|
ec_ca = selfsigned(ec_ca_key, "wo-ec-ca")
|
|
ec_leaf_key = ec.generate_private_key(ec.SECP256R1())
|
|
ec_leaf = leafcert(ec_leaf_key, ec_ca_key, ec_ca, "leaf.example.org", "leaf.example.org")
|
|
|
|
# --- wildcard-SAN leaf (self-signed EC, SAN *.example.com) ---
|
|
wild_key = ec.generate_private_key(ec.SECP256R1())
|
|
wild_leaf = (base("wild", "wild", wild_key.public_key())
|
|
.add_extension(x509.SubjectAlternativeName([x509.DNSName("*.example.com")]), False)
|
|
.sign(wild_key, hashes.SHA256()))
|
|
|
|
# --- decision-6 negatives (all signed by rsa_ca so signatures verify) ---
|
|
# leaf whose EKU is clientAuth only -> must be rejected as a server cert
|
|
eku_client_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
|
leaf_eku_client = leafcert(eku_client_key, rsa_ca_key, rsa_ca, "leaf.example.com",
|
|
"leaf.example.com", eku=[ExtendedKeyUsageOID.CLIENT_AUTH])
|
|
# leaf with EKU serverAuth -> must be accepted
|
|
eku_server_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
|
leaf_eku_server = leafcert(eku_server_key, rsa_ca_key, rsa_ca, "leaf.example.com",
|
|
"leaf.example.com", eku=[ExtendedKeyUsageOID.SERVER_AUTH])
|
|
# non-CA intermediate, and a leaf issued under it -> chain must be rejected
|
|
noca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
|
noca_mid = intermediate(noca_key, rsa_ca_key, rsa_ca, "wo-noca-mid", ca=False)
|
|
under_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
|
leaf_under_noca = leafcert(under_key, noca_key, noca_mid, "leaf.example.com", "leaf.example.com")
|
|
|
|
print("/* Generated by runtime/test/gen_x509.py — python cryptography %s.\n"
|
|
" * RSA + EC chains, a wildcard-SAN leaf, and decision-6 negatives\n"
|
|
" * (EKU clientAuth-only, EKU serverAuth, a non-CA intermediate + a\n"
|
|
" * leaf issued under it). Vectors for the wo_x509 KATs. */" %
|
|
__import__("cryptography").__version__)
|
|
print(cbytes("kat_rsa_ca", der(rsa_ca)))
|
|
print(cbytes("kat_rsa_leaf", der(rsa_leaf)))
|
|
print(cbytes("kat_ec_ca", der(ec_ca)))
|
|
print(cbytes("kat_ec_leaf", der(ec_leaf)))
|
|
print(cbytes("kat_wild_leaf", der(wild_leaf), "wildcard-SAN leaf (*.example.com)"))
|
|
print(cbytes("kat_leaf_eku_client", der(leaf_eku_client), "EKU clientAuth only -> reject as server"))
|
|
print(cbytes("kat_leaf_eku_server", der(leaf_eku_server), "EKU serverAuth -> accept"))
|
|
print(cbytes("kat_noca_mid", der(noca_mid), "intermediate with basicConstraints CA:FALSE"))
|
|
print(cbytes("kat_leaf_under_noca", der(leaf_under_noca), "leaf issued under the non-CA intermediate"))
|