#!/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"))