102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
# mypy: allow-untyped-defs
|
|
|
|
import argparse
|
|
import base64
|
|
import logging
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# TODO(Issue #24180): Regenerate SXG fingerprint too.
|
|
CHROME_SPKI_CERTS_CONTENT = """\
|
|
# This file is automatically generated by 'wpt regen-certs'
|
|
# DO NOT EDIT MANUALLY.
|
|
|
|
# tools/certs/web-platform.test.pem
|
|
WPT_FINGERPRINT = '{wpt_fingerprint}'
|
|
|
|
# signed-exchange/resources/127.0.0.1.sxg.pem
|
|
SXG_WPT_FINGERPRINT = '0Rt4mT6SJXojEMHTnKnlJ/hBKMBcI4kteBlhR1eTTdk='
|
|
|
|
IGNORE_CERTIFICATE_ERRORS_SPKI_LIST = [
|
|
WPT_FINGERPRINT,
|
|
SXG_WPT_FINGERPRINT
|
|
]
|
|
"""
|
|
|
|
|
|
def get_parser():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--checkend-seconds", type=int, default=5184000,
|
|
help="The number of seconds the certificates must be valid for")
|
|
parser.add_argument("--force", action="store_true",
|
|
help="Regenerate certificates even if not reaching expiry")
|
|
return parser
|
|
|
|
|
|
def check_cert(certificate, checkend_seconds):
|
|
"""Checks whether an x509 certificate will expire within a set period.
|
|
|
|
Returns 0 if the certificate will not expire, non-zero otherwise."""
|
|
cmd = [
|
|
"openssl", "x509",
|
|
"-checkend", str(checkend_seconds),
|
|
"-noout",
|
|
"-in", certificate
|
|
]
|
|
logger.info("Running '%s'" % " ".join(cmd))
|
|
return subprocess.call(cmd)
|
|
|
|
|
|
def regen_certs():
|
|
"""Regenerate the wpt openssl certificates, by delegating to wptserve."""
|
|
cmd = [
|
|
sys.executable, "wpt", "serve",
|
|
"--config", "tools/certs/config.json",
|
|
"--exit-after-start",
|
|
]
|
|
logger.info("Running '%s'" % " ".join(cmd))
|
|
subprocess.check_call(cmd)
|
|
|
|
|
|
def regen_chrome_spki():
|
|
"""Regenerate the SPKI fingerprints for Chrome's ignore-cert list.
|
|
|
|
Chrome requires us to explicitly list which certificates are ignored by its
|
|
security-checking, by listing a base64 hash of the public key. This will
|
|
change every time we replace our certificates, so we store the hashes in a
|
|
file and regenerate it here.
|
|
"""
|
|
wpt_spki = calculate_spki("tools/certs/web-platform.test.pem")
|
|
with open("tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py", "w") as f:
|
|
f.write(CHROME_SPKI_CERTS_CONTENT.format(wpt_fingerprint=wpt_spki))
|
|
|
|
|
|
def calculate_spki(cert_path):
|
|
"""Calculate the SPKI fingerprint for a given x509 certificate."""
|
|
# We use shell=True as we control the input |cert_path|, and piping
|
|
# correctly across processes is non-trivial in Python.
|
|
cmd = (f"openssl x509 -noout -pubkey -in {cert_path} | " +
|
|
"openssl pkey -pubin -outform der | " +
|
|
"openssl dgst -sha256 -binary")
|
|
dgst_output = subprocess.check_output(cmd, shell=True)
|
|
|
|
return base64.b64encode(dgst_output).decode('utf-8')
|
|
|
|
|
|
def run(**kwargs):
|
|
logging.basicConfig()
|
|
|
|
if kwargs["force"]:
|
|
logger.info("Force regenerating WPT certificates")
|
|
checkend_seconds = kwargs["checkend_seconds"]
|
|
if (kwargs["force"] or
|
|
check_cert("tools/certs/cacert.pem", checkend_seconds) or
|
|
check_cert("tools/certs/web-platform.test.pem", checkend_seconds)):
|
|
regen_certs()
|
|
regen_chrome_spki()
|
|
else:
|
|
logger.info("Certificates are still valid for at least %s seconds, skipping regeneration" % checkend_seconds)
|