""" Self-signed TLS certificate generation for the node's QUIC listener. The cert is used for transport confidentiality only. Node identity is verified via Ed25519 PK (from hub), not TLS cert chain. Phase 11.5 note: the certificate hash is also the intended channel-binding anchor for the QUIC handshake proof (11.5.6), since QUIC has no DTLS fingerprint to bind to. Certificate is generated once and cached at ~/.config/meshbay/node_tls.crt/.key. """ import datetime import ipaddress import logging from pathlib import Path from cryptography import x509 from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID from meshbay_node.platform import chmod_private, config_dir log = logging.getLogger(__name__) DEFAULT_CERT = config_dir() / "node_tls.crt" DEFAULT_KEY = config_dir() / "node_tls.key" def generate_self_signed_cert( cert_path: Path = DEFAULT_CERT, key_path: Path = DEFAULT_KEY, ) -> tuple[Path, Path]: """Generate a self-signed RSA-2048 TLS cert valid for 10 years.""" cert_path.parent.mkdir(parents=True, exist_ok=True) rsa_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) subject = issuer = x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "meshbay-node"), ]) cert = ( x509.CertificateBuilder() .subject_name(subject) .issuer_name(issuer) .public_key(rsa_key.public_key()) .serial_number(x509.random_serial_number()) .not_valid_before(datetime.datetime.now(datetime.UTC)) .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=3650)) .add_extension( x509.SubjectAlternativeName([ x509.DNSName("localhost"), x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")), ]), critical=False, ) .sign(rsa_key, hashes.SHA256()) ) cert_path.write_bytes(cert.public_bytes(serialization.Encoding.PEM)) key_path.write_bytes(rsa_key.private_bytes( serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL, serialization.NoEncryption(), )) chmod_private(cert_path, mode=0o644) chmod_private(key_path) log.info("TLS cert generated: %s", cert_path) return cert_path, key_path # `server_ssl_context()` / `client_ssl_context()` were removed in Phase 11.5 along with # the TCP+TLS transport they served. QUIC builds its own QuicConfiguration and calls # generate_self_signed_cert() directly.