Bug 1308787 - Certificate Transparency - script for generating the static list of known logs. r=Cykesiopka,keeler
authorSergei Chernov <sergei.cv@ndivi.com>
Tue, 08 Nov 2016 13:54:39 +0200
changeset 322459 411dbec99c3f1e683237640fd3f71d5854d383f7
parent 322458 104403755426a634b2dade68a681cdfa5cf6cf77
child 322460 ae6bb745b299211d698513214332a8d67cc13b6f
push id34244
push usercbook@mozilla.com
push dateTue, 15 Nov 2016 08:08:51 +0000
treeherderautoland@411dbec99c3f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersCykesiopka, keeler
bugs1308787
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1308787 - Certificate Transparency - script for generating the static list of known logs. r=Cykesiopka,keeler 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()