- defensive ASN.1/DER reader: every length/bound checked; malformation is rejection, never over-read (truncated input KAT-gated) - x509_parse: tbsCertificate span, sig-alg OID, signature, SubjectPublicKeyInfo (RSA n/e or EC P-256 x/y), validity dates - wo_x509_verify_one: one chain link's signature, dispatching to phase-D RSA-PKCS1/PSS + ECDSA-P256 by the issuer key type - wo_x509_parse_spki + wo_x509_check_validity (caller supplies time) - KAT against real python-generated chains (test/gen_x509.py): RSA CA+leaf (SHA256withRSA), EC P-256 CA+leaf (ecdsa-with-SHA256); leaf-vs-CA, self-signed CA, wrong-issuer/tampered/truncated reject, validity window, SPKI extraction. test_crypto 84 pass, ASan/UBSan clean - deferred to phase F: SAN/hostname match + multi-cert chain walk to a system CA bundle (both need the target host / trust store, known at handshake time) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 4ec1c75f889df3612e31b9a1a77c4c927fb546c1)
67 lines
2.9 KiB
Python
67 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate two cert chains (RSA and EC P-256) for the wo_x509 KAT.
|
|
Emits C hex byte arrays: RSA CA + RSA leaf, EC CA + EC leaf."""
|
|
import datetime
|
|
from cryptography import x509
|
|
from cryptography.x509.oid import NameOID
|
|
from cryptography.hazmat.primitives import hashes
|
|
from cryptography.hazmat.primitives.asymmetric import rsa, ec, padding
|
|
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 selfsigned(key, cn, hashalg, sanhost=None):
|
|
b = (x509.CertificateBuilder()
|
|
.subject_name(mkname(cn)).issuer_name(mkname(cn))
|
|
.public_key(key.public_key())
|
|
.serial_number(x509.random_serial_number())
|
|
.not_valid_before(NB).not_valid_after(NA)
|
|
.add_extension(x509.BasicConstraints(ca=True, path_length=None), True))
|
|
if sanhost:
|
|
b = b.add_extension(x509.SubjectAlternativeName([x509.DNSName(sanhost)]), False)
|
|
return b.sign(key, hashalg)
|
|
|
|
def leafcert(leafkey, cakey, ca_cert, cn, hashalg, sanhost):
|
|
return (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)
|
|
.sign(cakey, hashalg))
|
|
|
|
def cbytes(name, der):
|
|
out = "static const uint8_t %s[] = {" % name
|
|
for i, b in enumerate(der):
|
|
if i % 12 == 0:
|
|
out += "\n "
|
|
out += "0x%02x," % b
|
|
out += "\n};\n"
|
|
return out
|
|
|
|
# RSA chain
|
|
rsa_ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
|
rsa_ca = selfsigned(rsa_ca_key, "wo-rsa-ca", hashes.SHA256())
|
|
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",
|
|
hashes.SHA256(), "leaf.example.com")
|
|
|
|
# EC chain
|
|
ec_ca_key = ec.generate_private_key(ec.SECP256R1())
|
|
ec_ca = selfsigned(ec_ca_key, "wo-ec-ca", hashes.SHA256())
|
|
ec_leaf_key = ec.generate_private_key(ec.SECP256R1())
|
|
ec_leaf = leafcert(ec_leaf_key, ec_ca_key, ec_ca, "leaf.example.org",
|
|
hashes.SHA256(), "leaf.example.org")
|
|
|
|
print("/* Generated by scratchpad/gen_x509.py — python cryptography %s.\n"
|
|
" * Two real chains: RSA CA+leaf (SHA256withRSA), EC P-256 CA+leaf\n"
|
|
" * (ecdsa-with-SHA256). Vectors for the wo_x509 phase-E KAT. */" %
|
|
__import__("cryptography").__version__)
|
|
print(cbytes("kat_rsa_ca", rsa_ca.public_bytes(Encoding.DER)))
|
|
print(cbytes("kat_rsa_leaf", rsa_leaf.public_bytes(Encoding.DER)))
|
|
print(cbytes("kat_ec_ca", ec_ca.public_bytes(Encoding.DER)))
|
|
print(cbytes("kat_ec_leaf", ec_leaf.public_bytes(Encoding.DER)))
|