Bug 1308787 - Certificate Transparency - script for generating the static list of known logs. r=keeler, r=Cykesiopka draft
authorSergei Chernov <sergei.cv@ndivi.com>
Tue, 08 Nov 2016 13:54:39 +0200
changeset 437867 45f7a6aaeb17093c8eb9fc24fde1c0c246aff2b0
parent 434531 c44c01dfd264370c1558b747525d220a9a89b51c
child 536745 e1b70db7d2baccf2c8e09fcb8a82e2f8c04af9b1
push id35532
push usersergei.cv@ndivi.com
push dateFri, 11 Nov 2016 19:25:17 +0000
reviewerskeeler, Cykesiopka
bugs1308787
milestone52.0a1
Bug 1308787 - Certificate Transparency - script for generating the static list of known logs. r=keeler, r=Cykesiopka MozReview-Commit-ID: 9z7Ac5OQqOP
security/certverifier/CTKnownLogs.h
security/manager/tools/getCTKnownLogs.py
--- a/security/certverifier/CTKnownLogs.h
+++ b/security/certverifier/CTKnownLogs.h
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* This file is generated by print_log_list.py from
- * https://github.com/google/certificate-transparency/ */
+/* This file was automatically generated by getCTKnownLogs.py. */
 
 #ifndef CTKnownLogs_h
 #define CTKnownLogs_h
 
 #include <stddef.h>
 
 struct CTLogInfo {
   const char* const logName;
new file mode 100755
--- /dev/null
+++ b/security/manager/tools/getCTKnownLogs.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""
+Parses a JSON file listing the known Certificate Transparency logs
+(as downloaded from https://www.certificate-transparency.org/known-logs)
+and generates a C++ header file to be included in Firefox.
+"""
+
+from __future__ import print_function
+from string import Template
+import argparse
+import base64
+import json
+import os.path
+import sys
+import textwrap
+import urllib2
+
+
+OUTPUT_TEMPLATE = """\
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This file was automatically generated by $prog. */
+
+#ifndef $include_guard
+#define $include_guard
+
+#include <stddef.h>
+
+struct CTLogInfo {
+  const char* const logName;
+  const char* const logUrl;
+  const char* const logKey;
+  const size_t logKeyLength;
+};
+
+const CTLogInfo kCTLogList[] = {
+$logs
+};
+
+#endif // $include_guard
+"""
+
+
+def get_hex_lines(blob, width):
+    """ Convert a binary string to a multiline text of C escape sequences. """
+    text = "".join(["\\x{:02x}".format(ord(c)) for c in blob])
+    # When escaped, a single byte takes 4 chars (e.g. "\x00").
+    # Make sure we don't break an escaped byte between the lines.
+    return textwrap.wrap(text, width - width % 4)
+
+
+def get_log_info_structs(json_data):
+    """ Return array of CTLogInfo initializers for the known logs. """
+    tmpl = Template(textwrap.dedent("""\
+          { $description,
+            $url,
+        $indented_log_key,
+            $log_key_len }"""))
+    initializers = []
+    for log in json_data["logs"]:
+        log_key = base64.decodestring(log["key"])
+        initializers.append(tmpl.substitute(
+            # Use json.dumps for C-escaping strings.
+            # Not perfect but close enough.
+            description=json.dumps(log["description"]),
+            url=json.dumps("https://{0}/".format(log["url"])),
+            # Maximum line width is 80.
+            indented_log_key="\n".
+            join(['    "{0}"'.format(l) for l in get_hex_lines(log_key, 74)]),
+            log_key_len=len(log_key)))
+    return initializers
+
+
+def generate_cpp_header_file(json_data, out_file):
+    """ Generate the C++ header file for the known logs. """
+    filename = os.path.basename(out_file.name)
+    include_guard = filename.replace(".", "_").replace("/", "_")
+    log_info_initializers = get_log_info_structs(json_data)
+    out_file.write(Template(OUTPUT_TEMPLATE).substitute(
+        prog=os.path.basename(sys.argv[0]),
+        include_guard=include_guard,
+        logs=",\n".join(log_info_initializers)))
+
+
+def run(args):
+    """
+    Load the input JSON file and generate the C++ header according to the
+    command line arguments.
+    """
+    if args.file:
+        print("Reading file: ", args.file)
+        with open(args.file, "rb") as json_file:
+            json_text = json_file.read()
+    elif args.url:
+        print("Fetching URL: ", args.url)
+        json_request = urllib2.urlopen(args.url)
+        try:
+            json_text = json_request.read()
+        finally:
+            json_request.close()
+
+    json_data = json.loads(json_text)
+
+    print("Writing output: ", args.out)
+
+    with open(args.out, "w") as out_file:
+        generate_cpp_header_file(json_data, out_file)
+
+    print("Done.")
+
+
+def parse_arguments_and_run():
+    """ Parse the command line arguments and run the program. """
+    arg_parser = argparse.ArgumentParser(
+        description="Parses a JSON file listing the known "
+        "Certificate Transparency logs and generates "
+        "a C++ header file to be included in Firefox.",
+        epilog="Example: python %s --url" % os.path.basename(sys.argv[0]))
+
+    source_group = arg_parser.add_mutually_exclusive_group(required=True)
+    source_group.add_argument("--file",
+                              help="Read the known CT logs JSON file from the "
+                              "specified location on the filesystem.")
+    source_group.add_argument("--url", nargs="?",
+                              const="https://www.certificate-transparency.org/"
+                              "known-logs/log_list.json",
+                              help="Download the known CT logs JSON file "
+                              "from the specified URL. "
+                              "If no URL is given, download the file "
+                              "from %(const)s.")
+
+    arg_parser.add_argument("--out",
+                            default="../../certverifier/CTKnownLogs.h",
+                            help="Path and filename of the header file "
+                            "to be generated. Defaults to %(default)s")
+
+    run(arg_parser.parse_args())
+
+
+if __name__ == "__main__":
+    parse_arguments_and_run()