diff options
Diffstat (limited to 'testing/web-platform/tests/tools/ci/regen_certs.py')
-rw-r--r-- | testing/web-platform/tests/tools/ci/regen_certs.py | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/testing/web-platform/tests/tools/ci/regen_certs.py b/testing/web-platform/tests/tools/ci/regen_certs.py new file mode 100644 index 0000000000..8f3abdcad6 --- /dev/null +++ b/testing/web-platform/tests/tools/ci/regen_certs.py @@ -0,0 +1,102 @@ +# 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) |