| import argparse |
| import base64 |
| import logging |
| import subprocess |
| |
| |
| 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 = [ |
| "python", "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 = ("openssl x509 -noout -pubkey -in {cert_path} | ".format(cert_path=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) |