Bug 1928997: Update tabs icon in Unified Search popup r=desktop-theme-reviewers,daleh...
[gecko.git] / security / manager / tools / crtshToIdentifyingStruct / crtshToIdentifyingStruct.py
blob05e0842e2a45444afc0609ead1516e1915a4e47a
1 #!/usr/bin/env python3
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 """
8 This utility takes a series of https://crt.sh/ identifiers and writes to
9 stdout all of those certs' distinguished name or SPKI fields in hex, with an
10 array of all those. You'll need to post-process this list to handle any
11 duplicates.
13 Requires Python 3.
14 """
15 import argparse
16 import io
17 import re
18 import sys
20 import requests
21 from cryptography import x509
22 from cryptography.hazmat.backends import default_backend
23 from cryptography.hazmat.primitives import hashes
24 from cryptography.x509.oid import NameOID
25 from pyasn1.codec.der import decoder, encoder
26 from pyasn1_modules import pem, rfc5280
28 assert sys.version_info >= (3, 2), "Requires Python 3.2 or later"
31 def hex_string_for_struct(bytes):
32 return ["0x{:02X}".format(x) for x in bytes]
35 def hex_string_human_readable(bytes):
36 return ["{:02X}".format(x) for x in bytes]
39 def nameOIDtoString(oid):
40 if oid == NameOID.COUNTRY_NAME:
41 return "C"
42 if oid == NameOID.COMMON_NAME:
43 return "CN"
44 if oid == NameOID.LOCALITY_NAME:
45 return "L"
46 if oid == NameOID.ORGANIZATION_NAME:
47 return "O"
48 if oid == NameOID.ORGANIZATIONAL_UNIT_NAME:
49 return "OU"
50 raise Exception("Unknown OID: {}".format(oid))
53 def print_block(pemData, identifierType="DN", crtshId=None):
54 substrate = pem.readPemFromFile(io.StringIO(pemData.decode("utf-8")))
55 cert, _ = decoder.decode(substrate, asn1Spec=rfc5280.Certificate())
56 octets = None
58 if identifierType == "DN":
59 der_subject = encoder.encode(cert["tbsCertificate"]["subject"])
60 octets = hex_string_for_struct(der_subject)
61 elif identifierType == "SPKI":
62 der_spki = encoder.encode(cert["tbsCertificate"]["subjectPublicKeyInfo"])
63 octets = hex_string_for_struct(der_spki)
64 else:
65 raise Exception("Unknown identifier type: " + identifierType)
67 cert = x509.load_pem_x509_certificate(pemData, default_backend())
68 common_name = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0]
69 block_name = "CA{}{}".format(
70 re.sub(r"[-:=_. ]", "", common_name.value), identifierType
73 fingerprint = hex_string_human_readable(cert.fingerprint(hashes.SHA256()))
75 dn_parts = [
76 "/{id}={value}".format(id=nameOIDtoString(part.oid), value=part.value)
77 for part in cert.subject
79 distinguished_name = "".join(dn_parts)
81 print("// {dn}".format(dn=distinguished_name))
82 print("// SHA256 Fingerprint: " + ":".join(fingerprint[:16]))
83 print("// " + ":".join(fingerprint[16:]))
84 if crtshId:
85 print("// https://crt.sh/?id={crtsh} (crt.sh ID={crtsh})".format(crtsh=crtshId))
86 print("static const uint8_t {}[{}] = ".format(block_name, len(octets)) + "{")
88 while len(octets) > 0:
89 print(" " + ", ".join(octets[:13]) + ",")
90 octets = octets[13:]
92 print("};")
93 print()
95 return block_name
98 if __name__ == "__main__":
99 parser = argparse.ArgumentParser()
100 parser.add_argument(
101 "-spki",
102 action="store_true",
103 help="Create a list of subject public key info fields",
105 parser.add_argument(
106 "-dn",
107 action="store_true",
108 help="Create a list of subject distinguished name fields",
110 parser.add_argument("-listname", help="Name of the final DataAndLength block")
111 parser.add_argument(
112 "certId", nargs="+", help="A list of PEM files on disk or crt.sh IDs"
114 args = parser.parse_args()
116 if not args.dn and not args.spki:
117 parser.print_help()
118 raise Exception("You must select either DN or SPKI matching")
120 blocks = []
122 print(
123 "// Script from security/manager/tools/crtshToIdentifyingStruct/"
124 + "crtshToIdentifyingStruct.py"
126 print("// Invocation: {}".format(" ".join(sys.argv)))
127 print()
129 identifierType = None
130 if args.dn:
131 identifierType = "DN"
132 else:
133 identifierType = "SPKI"
135 for certId in args.certId:
136 # Try a local file first, then crt.sh
137 try:
138 with open(certId, "rb") as pemFile:
139 blocks.append(
140 print_block(pemFile.read(), identifierType=identifierType)
142 except OSError:
143 r = requests.get("https://crt.sh/?d={}".format(certId))
144 r.raise_for_status()
145 blocks.append(
146 print_block(r.content, crtshId=certId, identifierType=identifierType)
149 print("static const DataAndLength " + args.listname + "[]= {")
150 for structName in blocks:
151 if len(structName) < 33:
152 print(" { " + "{name}, sizeof({name}) ".format(name=structName) + "},")
153 else:
154 print(" { " + "{},".format(structName))
155 print(" sizeof({})".format(structName) + " },")
156 print("};")