summaryrefslogtreecommitdiffstats
path: root/support/ctauditscts
diff options
context:
space:
mode:
Diffstat (limited to 'support/ctauditscts')
-rwxr-xr-xsupport/ctauditscts160
1 files changed, 160 insertions, 0 deletions
diff --git a/support/ctauditscts b/support/ctauditscts
new file mode 100755
index 0000000000..6f4c12d12d
--- /dev/null
+++ b/support/ctauditscts
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import binascii
+import os
+import sqlite3
+import ssl
+import struct
+import sys
+import tempfile
+
+from contextlib import closing
+
+SERVER_START = 1
+KEY_START = 2
+CERT_START = 3
+SCT_START = 4
+
+
+def usage():
+ print >> sys.stderr, ('Usage: %s /path/to/audit/files ' +
+ '[/path/to/log-config-db]') % sys.argv[0]
+ sys.exit(1)
+
+
+def audit(fn, tmp, already_checked, cur):
+ print 'Auditing %s...' % fn
+
+ # First, parse the audit file into a series of related
+ #
+ # 1. PEM file with certificate chain
+ # 2. Individual SCT files
+ #
+ # Next, for each SCT, invoke verify_single_proof to verify.
+ log_bytes = open(fn, 'rb').read()
+ offset = 0
+ while offset < len(log_bytes):
+ print 'Got package from server...'
+ val = struct.unpack_from('>H', log_bytes, offset)
+ assert val[0] == SERVER_START
+ offset += 2
+
+ assert struct.unpack_from('>H', log_bytes, offset)[0] == KEY_START
+ offset += 2
+
+ key_size = struct.unpack_from('>H', log_bytes, offset)[0]
+ assert key_size > 0
+ offset += 2
+
+ key = log_bytes[offset:offset + key_size]
+ offset += key_size
+
+ # at least one certificate
+ assert struct.unpack_from('>H', log_bytes, offset)[0] == CERT_START
+
+ # for each certificate:
+ leaf = None
+ while struct.unpack_from('>H', log_bytes, offset)[0] == CERT_START:
+ offset += 2
+ val = struct.unpack_from('BBB', log_bytes, offset)
+ offset += 3
+ der_size = (val[0] << 16) | (val[1] << 8) | (val[2] << 0)
+ print ' Certificate size:', hex(der_size)
+ if not leaf:
+ leaf = (offset, der_size)
+ offset += der_size
+
+ pem = ssl.DER_cert_to_PEM_cert(log_bytes[leaf[0]:leaf[0] + leaf[1]])
+
+ tmp_leaf_pem = tempfile.mkstemp(text=True)
+ with closing(os.fdopen(tmp_leaf_pem[0], 'w')) as f:
+ f.write(pem)
+
+ # at least one SCT
+ assert struct.unpack_from('>H', log_bytes, offset)[0] == SCT_START
+
+ # for each SCT:
+ while offset < len(log_bytes) and \
+ struct.unpack_from('>H', log_bytes, offset)[0] == SCT_START:
+ offset += 2
+ len_offset = offset
+ sct_size = struct.unpack_from('>H', log_bytes, len_offset)[0]
+ offset += 2
+ print ' SCT size:', hex(sct_size)
+ log_id = log_bytes[offset + 1:offset + 1 + 32]
+ log_id_hex = binascii.hexlify(log_id).upper()
+ print ' Log id: %s' % log_id_hex
+ timestamp_ms = struct.unpack_from('>Q', log_bytes, offset + 33)[0]
+ print ' Timestamp: %s' % timestamp_ms
+
+ # If we ever need the full SCT: sct = (offset, sct_size)
+ offset += sct_size
+
+ if key in already_checked:
+ print ' (SCTs already checked)'
+ continue
+
+ already_checked[key] = True
+
+ log_url_arg = ''
+ if cur:
+ stmt = 'SELECT * FROM loginfo WHERE log_id = ?'
+ cur.execute(stmt, [log_id_hex])
+ recs = list(cur.fetchall())
+ if len(recs) > 0 and recs[0][6] is not None:
+ log_url = recs[0][6]
+
+ # verify_single_proof doesn't accept <scheme>://
+ if '://' in log_url:
+ log_url = log_url.split('://')[1]
+ log_url_arg = '--log_url %s' % log_url
+
+ print ' Log URL: ' + log_url
+
+ cmd = 'verify_single_proof.py --cert %s --timestamp %s %s' % \
+ (tmp_leaf_pem[1], timestamp_ms, log_url_arg)
+ print '>%s<' % cmd
+ os.system(cmd)
+
+ os.unlink(tmp_leaf_pem[1])
+
+
+def main():
+ if len(sys.argv) != 2 and len(sys.argv) != 3:
+ usage()
+
+ top = sys.argv[1]
+ tmp = '/tmp'
+
+ if len(sys.argv) == 3:
+ cxn = sqlite3.connect(sys.argv[2])
+ cur = cxn.cursor()
+ else:
+ cur = None
+
+ # could serialize this between runs to further limit duplicate checking
+ already_checked = dict()
+
+ for dirpath, dnames, fnames in os.walk(top):
+ fnames = [fn for fn in fnames if fn[-4:] == '.out']
+ for fn in fnames:
+ audit(os.path.join(dirpath, fn), tmp, already_checked, cur)
+
+
+if __name__ == "__main__":
+ main()