aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/transport/tls_cert.py
blob: 8d680ea26b1ae0255c38e6a93af0cd003fa26c63 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""
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 logging
import os
from pathlib import Path
import datetime
import ipaddress

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.timezone.utc))
        .not_valid_after(datetime.datetime.now(datetime.timezone.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.