dcerpc-nt: add UNION_ALIGN_TO... helpers
[wireshark-sm.git] / tools / make-tls-ct-logids.py
blob0b74c51344c3e5b9ac5a05853fcec4bc6d4777b2
1 #!/usr/bin/env python3
2 # Generate the array of Certificate Transparency Log ID to description mappings
3 # for the TLS dissector.
5 # To update the TLS dissector source file, run this from the source directory:
7 # python3 tools/make-tls-ct-logids.py --update
10 import argparse
11 from base64 import b64decode, b64encode
12 from enum import Enum
13 import itertools
14 import os
15 import requests
16 from hashlib import sha256
19 # Begin of comment, followed by the actual array definition
20 HEADER = "/* Generated by tools/make-tls-ct-logids.py\n"
21 # See also https://www.certificate-transparency.org/known-logs
22 CT_JSON_URL = 'https://www.gstatic.com/ct/log_list/v3/all_logs_list.json'
23 # File to be patched
24 SOURCE_FILE = os.path.join('epan', 'dissectors', 'packet-tls-utils.c')
26 # Maximum elements per line in the value array. 11 is chosen because it results
27 # in output consistent with clang-format.
28 BYTES_PER_LINE = 11
30 class SourceStage(Enum):
31 BEGIN = 1
32 IN_METAINFO = 2
33 IN_BLOCK = 3
34 END = 4
37 def escape_c(s):
38 return s.replace('\\', '\\\\').replace('"', '\\"')
41 def byteshex(b):
42 return " ".join("0x%02x," % b for b in bytearray(b))
45 def process_json(obj, lastmod):
46 logs = list(itertools.chain(*[op['logs'] for op in obj['operators']]))
47 metainfo, block = HEADER, ''
48 metainfo += " * Last-Modified %s, %s entries. */\n" % (lastmod, len(logs))
49 block += "static const bytes_string ct_logids[] = {\n"
50 for entry in logs:
51 desc = entry["description"]
52 pubkey_der = b64decode(entry["key"])
53 key_id = sha256(pubkey_der).digest()
54 block += ' { (const uint8_t[]){\n'
55 for offset in range(0, len(key_id), BYTES_PER_LINE):
56 block += ' %s\n' % \
57 byteshex(key_id[offset:offset+BYTES_PER_LINE])
58 block += ' },\n'
59 block += ' %d, "%s" },\n' % (len(key_id), escape_c(desc))
60 block += " { NULL, 0, NULL }\n"
61 block += "};\n"
62 return metainfo, block
65 def parse_source(source_path):
66 """
67 Reads the source file and tries to split it in the parts before, inside and
68 after the block.
69 """
70 begin, metainfo, block, end = '', '', '', ''
71 # Stages: BEGIN (before block), IN_METAINFO, IN_BLOCK (skip), END
72 stage = SourceStage.BEGIN
73 with open(source_path) as f:
74 for line in f:
75 if line.startswith('/* Generated by '):
76 stage = SourceStage.IN_METAINFO
79 if stage == SourceStage.BEGIN:
80 begin += line
81 elif stage == SourceStage.IN_METAINFO:
82 metainfo += line
83 elif stage == SourceStage.IN_BLOCK:
84 block += line
85 if line.startswith('}'):
86 stage = SourceStage.END
87 elif stage == SourceStage.END:
88 end += line
90 if line.startswith(' * Last-Modified '):
91 stage = SourceStage.IN_BLOCK
93 if stage != SourceStage.END:
94 raise RuntimeError("Could not parse file (in stage %s)" % stage.name)
95 return begin, metainfo, block, end
98 parser = argparse.ArgumentParser()
99 parser.add_argument("--update", action="store_true",
100 help="Update %s as needed instead of writing to stdout" % SOURCE_FILE)
103 def main():
104 args = parser.parse_args()
105 this_dir = os.path.dirname(__file__)
106 r = requests.get(CT_JSON_URL)
107 j_metainfo, j_block = process_json(r.json(), lastmod=r.headers['Last-Modified'])
108 source_path = os.path.join(this_dir, '..', SOURCE_FILE)
110 if args.update:
111 s_begin, _, s_block, s_end = parse_source(source_path)
112 if s_block == j_block:
113 print("File is up-to-date")
114 else:
115 with open(source_path, "w") as f:
116 f.write(s_begin)
117 f.write(j_metainfo)
118 f.write(j_block)
119 f.write(s_end)
120 print("Updated %s" % source_path)
121 else:
122 print(j_metainfo, j_block)
125 if __name__ == '__main__':
126 main()