import base64
import binascii
import codecs
import subprocess
import sys
import urllib.request
import urllib.parse
import urllib.error
import extra_constraints
HEADER = """//!
//! This library is automatically generated from the Mozilla certificate
//! store via mkcert.org. Don't edit it.
//!
//! The generation is done deterministically so you can verify it
//! yourself by inspecting and re-running the generation process.
//!
#![forbid(unsafe_code,
unstable_features)]
#![deny(trivial_casts,
trivial_numeric_casts,
unused_import_braces,
unused_extern_crates,
unused_qualifications)]
"""
CERT = """
%(comment)s
%(code)s,"""
excluded_cas = [
"Buypass Class 2 CA 1",
"China Internet Network Information Center",
"CNNIC",
"RSA Security 2048 v3",
"Root CA Generalitat Valenciana",
"StartCom",
"WoSign",
"TÜRKTRUST",
"TURKTRUST",
]
def fetch_bundle():
proc = subprocess.Popen(['curl',
'https://mkcert.org/generate/all/except/' +
"+".join([urllib.parse.quote(x) for x in excluded_cas])],
stdout = subprocess.PIPE)
stdout, _ = proc.communicate()
return stdout.decode('utf-8')
def split_bundle(bundle):
cert = ''
for line in bundle.splitlines():
if line.strip() != '':
cert += line + '\n'
if '-----END CERTIFICATE-----' in line:
yield cert
cert = ''
def calc_spki_hash(cert):
proc = subprocess.Popen(
['openssl', 'x509', '-noout', '-sha256', '-fingerprint'],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE)
stdout, _ = proc.communicate(cert.encode('utf-8'))
stdout = stdout.decode('utf-8')
assert proc.returncode == 0
assert stdout.startswith('SHA256 Fingerprint=')
hash = stdout.replace('SHA256 Fingerprint=', '').replace(':', '')
hash = hash.strip()
assert len(hash) == 64
return hash.lower()
def extract_header_spki_hash(cert):
line = [ll for ll in cert.splitlines() if ll.startswith('# SHA256 Fingerprint: ')][0]
return line.replace('# SHA256 Fingerprint: ', '').replace(':', '').lower()
def unwrap_pem(cert):
start = '-----BEGIN CERTIFICATE-----\n'
end = '-----END CERTIFICATE-----\n'
body = cert[cert.index(start)+len(start):cert.rindex(end)]
return base64.b64decode(body)
def extract(msg, name):
lines = msg.splitlines()
value = [ll for ll in lines if ll.startswith(name + ': ')][0]
return value[len(name) + 2:].strip()
def convert_cert(cert_der):
proc = subprocess.Popen(
['target/debug/process_cert'],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE)
stdout, _ = proc.communicate(cert_der)
stdout = stdout.decode('utf-8')
assert proc.returncode == 0
return dict(
subject = extract(stdout, 'Subject'),
spki = extract(stdout, 'SPKI'),
name_constraints = extract(stdout, 'Name-Constraints'))
def commentify(cert):
lines = cert.splitlines()
lines = [ll[2:] if ll.startswith('# ') else ll for ll in lines]
return '/*\n * ' + ('\n * '.join(lines)) + '\n */'
def convert_bytes(hex):
bb = binascii.a2b_hex(hex)
encoded, _ = codecs.escape_encode(bb)
return encoded.decode('utf-8').replace('"', '\\"')
def print_root(cert, data):
subject = convert_bytes(data['subject'])
spki = convert_bytes(data['spki'])
nc = data['name_constraints']
nc = ('Some(b"{}")'.format(convert_bytes(nc))) if nc != 'None' else nc
print(""" {}
webpki::TrustAnchor {{
subject: b"{}",
spki: b"{}",
name_constraints: {}
}},
""".format(commentify(cert), subject, spki, nc))
if __name__ == '__main__':
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
bundle = fetch_bundle()
open('fetched.pem', 'w').write(bundle)
certs = {}
for cert in split_bundle(bundle):
our_hash = calc_spki_hash(cert)
their_hash = extract_header_spki_hash(cert)
assert our_hash == their_hash
cert_der = unwrap_pem(cert)
data = convert_cert(cert_der)
imposed_nc = extra_constraints.get_imposed_name_constraints(data['subject'])
if imposed_nc:
data['name_constraints'] = binascii.b2a_hex(imposed_nc)
assert our_hash not in certs, 'duplicate cert'
certs[our_hash] = (cert, data)
print(HEADER)
print("""pub static TLS_SERVER_ROOTS: webpki::TlsServerTrustAnchors = webpki::TlsServerTrustAnchors(&[""")
for hash in sorted(certs):
cert, data = certs[hash]
print_root(cert, data)
print(']);')