From 1075b68f287e957cd73c8cdb9517293b4c920eec Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Mon, 16 Mar 2015 10:06:42 +0100 Subject: Add submission of precerts --- tools/certtools.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'tools/certtools.py') diff --git a/tools/certtools.py b/tools/certtools.py index 0e639f2..b0a1c97 100644 --- a/tools/certtools.py +++ b/tools/certtools.py @@ -61,6 +61,10 @@ def get_certs_from_string(s): f = cStringIO.StringIO(s) return get_pemlike_from_file(f, "CERTIFICATE") +def get_precerts_from_string(s): + f = cStringIO.StringIO(s) + return get_pemlike_from_file(f, "PRECERTIFICATE") + def get_eckey_from_file(keyfile): keys = get_pemlike(keyfile, "EC PRIVATE KEY") assert len(keys) == 1 @@ -137,6 +141,24 @@ def add_chain(baseurl, submission): print "========================" raise e +def add_prechain(baseurl, submission): + try: + result = urllib2.urlopen(baseurl + "ct/v1/add-pre-chain", + json.dumps(submission)).read() + return json.loads(result) + except urllib2.HTTPError, e: + print "ERROR", e.code,":", e.read() + if e.code == 400: + return None + sys.exit(1) + except ValueError, e: + print "==== FAILED REQUEST ====" + print submission + print "======= RESPONSE =======" + print result + print "========================" + raise e + def get_entries(baseurl, start, end): try: params = urllib.urlencode({"start":start, "end":end}) @@ -586,5 +608,6 @@ def verify_consistency_proof(consistency_proof, first, second, oldhash_input): def verify_inclusion_proof(inclusion_proof, index, treesize, leafhash): chain = zip([(index, 0)] + nodes_for_index(index, treesize), [leafhash] + inclusion_proof) + assert len(nodes_for_index(index, treesize)) == len(inclusion_proof) (_, hash) = reduce(lambda e1, e2: combine_two_hashes(e1, e2, treesize), chain) return hash -- cgit v1.1 From 15d5d6fd5cffdea185d18fbd4feb62afa23b9d12 Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Wed, 18 Mar 2015 01:03:18 +0100 Subject: Added validatestore.py --- tools/certtools.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'tools/certtools.py') diff --git a/tools/certtools.py b/tools/certtools.py index b0a1c97..1436863 100644 --- a/tools/certtools.py +++ b/tools/certtools.py @@ -289,6 +289,18 @@ def pack_mtl(timestamp, leafcert): merkle_tree_leaf = version + leaf_type + timestamped_entry return merkle_tree_leaf +def pack_mtl_precert(timestamp, cleanedcert, issuer_key_hash): + entry_type = struct.pack(">H", 1) + extensions = "" + assert len(issuer_key_hash) == 32 + + timestamped_entry = struct.pack(">Q", timestamp) + entry_type + \ + issuer_key_hash + tls_array(cleanedcert, 3) + tls_array(extensions, 2) + version = struct.pack(">b", 0) + leaf_type = struct.pack(">b", 0) + merkle_tree_leaf = version + leaf_type + timestamped_entry + return merkle_tree_leaf + def unpack_mtl(merkle_tree_leaf): version = merkle_tree_leaf[0:1] leaf_type = merkle_tree_leaf[1:2] @@ -375,6 +387,14 @@ def get_hash_from_certfile(cert): return base64.b16decode(line[len("Leafhash: "):]) return None +def get_timestamp_from_certfile(cert): + for line in cert.split("\n"): + if line.startswith("-----"): + return None + if line.startswith("Timestamp: "): + return int(line[len("Timestamp: "):]) + return None + def get_proof(store, tree_size, n): hash = get_hash_from_certfile(get_one_cert(store, n)) return get_proof_by_hash(args.baseurl, hash, tree_size) -- cgit v1.1 From 0a76e4d080a8349456d04434dcb2d4b381eb8ec4 Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Wed, 18 Mar 2015 14:27:18 +0100 Subject: Added precert handling for SCT calculation --- tools/certtools.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'tools/certtools.py') diff --git a/tools/certtools.py b/tools/certtools.py index 1436863..cc423af 100644 --- a/tools/certtools.py +++ b/tools/certtools.py @@ -257,7 +257,7 @@ def create_sth_signature(tree_size, timestamp, root_hash, baseurl, key=None): return create_signature(baseurl, tree_head, key=key) -def check_sct_signature(baseurl, leafcert, sct): +def check_sct_signature(baseurl, signed_entry, sct, precert=False): publickey = base64.decodestring(publickeys[baseurl]) calculated_logid = hashlib.sha256(publickey).digest() received_logid = base64.decodestring(sct["id"]) @@ -271,9 +271,12 @@ def check_sct_signature(baseurl, leafcert, sct): version = struct.pack(">b", sct["sct_version"]) signature_type = struct.pack(">b", 0) timestamp = struct.pack(">Q", sct["timestamp"]) - entry_type = struct.pack(">H", 0) + if precert: + entry_type = struct.pack(">H", 1) + else: + entry_type = struct.pack(">H", 0) signed_struct = version + signature_type + timestamp + \ - entry_type + tls_array(leafcert, 3) + \ + entry_type + signed_entry + \ tls_array(base64.decodestring(sct["extensions"]), 2) check_signature(baseurl, signature, signed_struct) @@ -292,15 +295,22 @@ def pack_mtl(timestamp, leafcert): def pack_mtl_precert(timestamp, cleanedcert, issuer_key_hash): entry_type = struct.pack(">H", 1) extensions = "" - assert len(issuer_key_hash) == 32 timestamped_entry = struct.pack(">Q", timestamp) + entry_type + \ - issuer_key_hash + tls_array(cleanedcert, 3) + tls_array(extensions, 2) + pack_precert(cleanedcert, issuer_key_hash) + tls_array(extensions, 2) version = struct.pack(">b", 0) leaf_type = struct.pack(">b", 0) merkle_tree_leaf = version + leaf_type + timestamped_entry return merkle_tree_leaf +def pack_precert(cleanedcert, issuer_key_hash): + assert len(issuer_key_hash) == 32 + + return issuer_key_hash + tls_array(cleanedcert, 3) + +def pack_cert(cert): + return tls_array(cert, 3) + def unpack_mtl(merkle_tree_leaf): version = merkle_tree_leaf[0:1] leaf_type = merkle_tree_leaf[1:2] -- cgit v1.1 From d645194006bf3c81372073af9784f7d993096444 Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Mon, 23 Mar 2015 16:12:13 +0100 Subject: Generate config from master config. Verify responses in merge.py. --- tools/certtools.py | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'tools/certtools.py') diff --git a/tools/certtools.py b/tools/certtools.py index cc423af..939d9f1 100644 --- a/tools/certtools.py +++ b/tools/certtools.py @@ -70,6 +70,11 @@ def get_eckey_from_file(keyfile): assert len(keys) == 1 return keys[0] +def get_public_key_from_file(keyfile): + keys = get_pemlike(keyfile, "PUBLIC KEY") + assert len(keys) == 1 + return keys[0] + def get_root_cert(issuer): accepted_certs = \ json.loads(open("googlelog-accepted-certs.txt").read())["certificates"] @@ -84,7 +89,7 @@ def get_root_cert(issuer): return root_cert def get_sth(baseurl): - result = urllib2.urlopen(baseurl + "ct/v1/get-sth", context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() + result = urllib2.urlopen(baseurl + "ct/v1/get-sth").read() return json.loads(result) def get_proof_by_hash(baseurl, hash, tree_size): @@ -92,7 +97,7 @@ def get_proof_by_hash(baseurl, hash, tree_size): params = urllib.urlencode({"hash":base64.b64encode(hash), "tree_size":tree_size}) result = \ - urllib2.urlopen(baseurl + "ct/v1/get-proof-by-hash?" + params, context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() + urllib2.urlopen(baseurl + "ct/v1/get-proof-by-hash?" + params).read() return json.loads(result) except urllib2.HTTPError, e: print "ERROR:", e.read() @@ -103,7 +108,7 @@ def get_consistency_proof(baseurl, tree_size1, tree_size2): params = urllib.urlencode({"first":tree_size1, "second":tree_size2}) result = \ - urllib2.urlopen(baseurl + "ct/v1/get-sth-consistency?" + params, context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() + urllib2.urlopen(baseurl + "ct/v1/get-sth-consistency?" + params).read() return json.loads(result)["consistency"] except urllib2.HTTPError, e: print "ERROR:", e.read() @@ -126,7 +131,7 @@ def unpack_tls_array(packed_data, length_len): def add_chain(baseurl, submission): try: - result = urllib2.urlopen(baseurl + "ct/v1/add-chain", json.dumps(submission), context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() + result = urllib2.urlopen(baseurl + "ct/v1/add-chain", json.dumps(submission)).read() return json.loads(result) except urllib2.HTTPError, e: print "ERROR", e.code,":", e.read() @@ -162,7 +167,7 @@ def add_prechain(baseurl, submission): def get_entries(baseurl, start, end): try: params = urllib.urlencode({"start":start, "end":end}) - result = urllib2.urlopen(baseurl + "ct/v1/get-entries?" + params, context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() + result = urllib2.urlopen(baseurl + "ct/v1/get-entries?" + params).read() return json.loads(result) except urllib2.HTTPError, e: print "ERROR:", e.read() @@ -205,7 +210,26 @@ def check_signature(baseurl, signature, data): vk.verify(unpacked_signature, data, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der) -def http_request(url, data=None, key=None): +def parse_auth_header(authheader): + splittedheader = authheader.split(";") + (signature, rawoptions) = (splittedheader[0], splittedheader[1:]) + options = dict([(e.partition("=")[0], e.partition("=")[2]) for e in rawoptions]) + return (base64.b64decode(signature), options) + +def check_auth_header(authheader, expected_key, publickeydir, data, path): + if expected_key == None: + return True + (signature, options) = parse_auth_header(authheader) + keyname = options.get("key") + if keyname != expected_key: + raise Exception("Response claimed to come from %s, expected %s" % (keyname, expected_key)) + publickey = get_public_key_from_file(publickeydir + "/" + keyname + ".pem") + vk = ecdsa.VerifyingKey.from_der(publickey) + vk.verify(signature, "%s\0%s\0%s" % ("REPLY", path, data), hashfunc=hashlib.sha256, + sigdecode=ecdsa.util.sigdecode_der) + return True + +def http_request(url, data=None, key=None, verifynode=None, publickeydir="."): req = urllib2.Request(url, data) (keyname, keyfile) = key privatekey = get_eckey_from_file(keyfile) @@ -219,8 +243,11 @@ def http_request(url, data=None, key=None): signature = sk.sign("%s\0%s\0%s" % (method, parsed_url.path, data), hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der) req.add_header('X-Catlfish-Auth', base64.b64encode(signature) + ";key=" + keyname) - result = urllib2.urlopen(req, context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() - return result + result = urllib2.urlopen(req) + authheader = result.info().get('X-Catlfish-Auth') + data = result.read() + check_auth_header(authheader, verifynode, publickeydir, data, parsed_url.path) + return data def get_signature(baseurl, data, key=None): try: -- cgit v1.1