3 # Licensed to the Apache Software Foundation (ASF) under one or more
4 # contributor license agreements. See the NOTICE file distributed with
5 # this work for additional information regarding copyright ownership.
6 # The ASF licenses this file to You under the Apache License, Version 2.0
7 # (the "License"); you may not use this file except in compliance with
8 # the License. You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
26 from contextlib import closing
35 print >> sys.stderr, ('Usage: %s /path/to/audit/files ' +
36 '[/path/to/log-config-db]') % sys.argv[0]
40 def audit(fn, tmp, already_checked, cur):
41 print 'Auditing %s...' % fn
43 # First, parse the audit file into a series of related
45 # 1. PEM file with certificate chain
46 # 2. Individual SCT files
48 # Next, for each SCT, invoke verify_single_proof to verify.
49 log_bytes = open(fn, 'rb').read()
51 while offset < len(log_bytes):
52 print 'Got package from server...'
53 val = struct.unpack_from('>H', log_bytes, offset)
54 assert val[0] == SERVER_START
57 assert struct.unpack_from('>H', log_bytes, offset)[0] == KEY_START
60 key_size = struct.unpack_from('>H', log_bytes, offset)[0]
64 key = log_bytes[offset:offset + key_size]
67 # at least one certificate
68 assert struct.unpack_from('>H', log_bytes, offset)[0] == CERT_START
70 # for each certificate:
72 while struct.unpack_from('>H', log_bytes, offset)[0] == CERT_START:
74 val = struct.unpack_from('BBB', log_bytes, offset)
76 der_size = (val[0] << 16) | (val[1] << 8) | (val[2] << 0)
77 print ' Certificate size:', hex(der_size)
79 leaf = (offset, der_size)
82 pem = ssl.DER_cert_to_PEM_cert(log_bytes[leaf[0]:leaf[0] + leaf[1]])
84 tmp_leaf_pem = tempfile.mkstemp(text=True)
85 with closing(os.fdopen(tmp_leaf_pem[0], 'w')) as f:
89 assert struct.unpack_from('>H', log_bytes, offset)[0] == SCT_START
92 while offset < len(log_bytes) and \
93 struct.unpack_from('>H', log_bytes, offset)[0] == SCT_START:
96 sct_size = struct.unpack_from('>H', log_bytes, len_offset)[0]
98 print ' SCT size:', hex(sct_size)
99 log_id = log_bytes[offset + 1:offset + 1 + 32]
100 log_id_hex = binascii.hexlify(log_id).upper()
101 print ' Log id: %s' % log_id_hex
102 timestamp_ms = struct.unpack_from('>Q', log_bytes, offset + 33)[0]
103 print ' Timestamp: %s' % timestamp_ms
105 # If we ever need the full SCT: sct = (offset, sct_size)
108 if key in already_checked:
109 print ' (SCTs already checked)'
112 already_checked[key] = True
116 stmt = 'SELECT * FROM loginfo WHERE log_id = ?'
117 cur.execute(stmt, [log_id_hex])
118 recs = list(cur.fetchall())
119 if len(recs) > 0 and recs[0][6] is not None:
122 # verify_single_proof doesn't accept <scheme>://
124 log_url = log_url.split('://')[1]
125 log_url_arg = '--log_url %s' % log_url
127 print ' Log URL: ' + log_url
129 cmd = 'verify_single_proof.py --cert %s --timestamp %s %s' % \
130 (tmp_leaf_pem[1], timestamp_ms, log_url_arg)
134 os.unlink(tmp_leaf_pem[1])
138 if len(sys.argv) != 2 and len(sys.argv) != 3:
144 if len(sys.argv) == 3:
145 cxn = sqlite3.connect(sys.argv[2])
150 # could serialize this between runs to further limit duplicate checking
151 already_checked = dict()
153 for dirpath, dnames, fnames in os.walk(top):
154 fnames = [fn for fn in fnames if fn[-4:] == '.out']
156 audit(os.path.join(dirpath, fn), tmp, already_checked, cur)
159 if __name__ == "__main__":