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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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)
|