diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/certtools.py | 48 | ||||
-rwxr-xr-x | tools/merge.py | 76 | ||||
-rwxr-xr-x | tools/testcase1.py | 9 | ||||
-rw-r--r-- | tools/testcerts/pre1.txt | 79 | ||||
-rw-r--r-- | tools/testcerts/pre2.txt | 106 | ||||
-rw-r--r-- | tools/testcerts/roots/root4.pem | 19 | ||||
-rw-r--r-- | tools/testcerts/roots/root5.pem | 29 |
7 files changed, 319 insertions, 47 deletions
diff --git a/tools/certtools.py b/tools/certtools.py index 2fb1492..0e639f2 100644 --- a/tools/certtools.py +++ b/tools/certtools.py @@ -6,6 +6,8 @@ import json import base64 import urllib import urllib2 +import ssl +import urlparse import struct import sys import hashlib @@ -78,7 +80,7 @@ def get_root_cert(issuer): return root_cert def get_sth(baseurl): - result = urllib2.urlopen(baseurl + "ct/v1/get-sth").read() + result = urllib2.urlopen(baseurl + "ct/v1/get-sth", context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() return json.loads(result) def get_proof_by_hash(baseurl, hash, tree_size): @@ -86,7 +88,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).read() + urllib2.urlopen(baseurl + "ct/v1/get-proof-by-hash?" + params, context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() return json.loads(result) except urllib2.HTTPError, e: print "ERROR:", e.read() @@ -97,7 +99,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).read() + urllib2.urlopen(baseurl + "ct/v1/get-sth-consistency?" + params, context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() return json.loads(result)["consistency"] except urllib2.HTTPError, e: print "ERROR:", e.read() @@ -120,8 +122,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)).read() + result = urllib2.urlopen(baseurl + "ct/v1/add-chain", json.dumps(submission), context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() return json.loads(result) except urllib2.HTTPError, e: print "ERROR", e.code,":", e.read() @@ -139,7 +140,7 @@ def add_chain(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).read() + result = urllib2.urlopen(baseurl + "ct/v1/get-entries?" + params, context=ssl.SSLContext(ssl.PROTOCOL_TLSv1)).read() return json.loads(result) except urllib2.HTTPError, e: print "ERROR:", e.read() @@ -182,10 +183,35 @@ def check_signature(baseurl, signature, data): vk.verify(unpacked_signature, data, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der) -def create_signature(privatekey, data): +def http_request(url, data=None, key=None): + req = urllib2.Request(url, data) + (keyname, keyfile) = key + privatekey = get_eckey_from_file(keyfile) sk = ecdsa.SigningKey.from_der(privatekey) - unpacked_signature = sk.sign(data, hashfunc=hashlib.sha256, - sigencode=ecdsa.util.sigencode_der) + parsed_url = urlparse.urlparse(url) + if data == None: + data = parsed_url.query + method = "GET" + else: + method = "POST" + 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 + +def get_signature(baseurl, data, key=None): + try: + params = json.dumps({"plop_version":1, "data": base64.b64encode(data)}) + result = http_request(baseurl + "ct/signing/sth", params, key=key) + parsed_result = json.loads(result) + return base64.b64decode(parsed_result.get(u"result")) + except urllib2.HTTPError, e: + print "ERROR: get_signature", e.read() + sys.exit(1) + +def create_signature(baseurl, data, key=None): + unpacked_signature = get_signature(baseurl, data, key) return encode_signature(4, 3, unpacked_signature) def check_sth_signature(baseurl, sth): @@ -200,14 +226,14 @@ def check_sth_signature(baseurl, sth): check_signature(baseurl, signature, tree_head) -def create_sth_signature(tree_size, timestamp, root_hash, privatekey): +def create_sth_signature(tree_size, timestamp, root_hash, baseurl, key=None): version = struct.pack(">b", 0) signature_type = struct.pack(">b", 1) timestamp_packed = struct.pack(">Q", timestamp) tree_size_packed = struct.pack(">Q", tree_size) tree_head = version + signature_type + timestamp_packed + tree_size_packed + root_hash - return create_signature(privatekey, tree_head) + return create_signature(baseurl, tree_head, key=key) def check_sct_signature(baseurl, leafcert, sct): publickey = base64.decodestring(publickeys[baseurl]) diff --git a/tools/merge.py b/tools/merge.py index 6becf7e..1b94581 100755 --- a/tools/merge.py +++ b/tools/merge.py @@ -14,14 +14,15 @@ import time import ecdsa import hashlib import urlparse -from certtools import build_merkle_tree, create_sth_signature, check_sth_signature, get_eckey_from_file, timing_point +import os +from certtools import build_merkle_tree, create_sth_signature, check_sth_signature, get_eckey_from_file, timing_point, http_request parser = argparse.ArgumentParser(description="") parser.add_argument("--baseurl", metavar="url", help="Base URL for CT server", required=True) parser.add_argument("--frontend", action="append", metavar="url", help="Base URL for frontend server", required=True) parser.add_argument("--storage", action="append", metavar="url", help="Base URL for storage server", required=True) parser.add_argument("--mergedb", metavar="dir", help="Merge database directory", required=True) -parser.add_argument("--keyfile", metavar="keyfile", help="File containing log key", required=True) +parser.add_argument("--signing", metavar="url", help="Base URL for signing server", required=True) parser.add_argument("--own-keyname", metavar="keyname", help="The key name of the merge node", required=True) parser.add_argument("--own-keyfile", metavar="keyfile", help="The file containing the private key of the merge node", required=True) parser.add_argument("--nomerge", action='store_true', help="Don't actually do merge") @@ -34,6 +35,10 @@ storagenodes = args.storage chainsdir = args.mergedb + "/chains" logorderfile = args.mergedb + "/logorder" +own_key = (args.own_keyname, args.own_keyfile) + +hashed_dir = True + def parselogrow(row): return base64.b16decode(row) @@ -42,12 +47,26 @@ def get_logorder(): return [parselogrow(row.rstrip()) for row in f] def write_chain(key, value): - f = open(chainsdir + "/" + base64.b16encode(key), "w") + filename = base64.b16encode(key) + if hashed_dir: + path = chainsdir + "/" + filename[0:2] + "/" + filename[2:4] + "/" + filename[4:6] + try: + os.makedirs(path) + except Exception, e: + print e + else: + path = chainsdir + f = open(path + "/" + filename, "w") f.write(value) f.close() def read_chain(key): - f = open(chainsdir + "/" + base64.b16encode(key), "r") + filename = base64.b16encode(key) + path = chainsdir + "/" + filename[0:2] + "/" + filename[2:4] + "/" + filename[4:6] + try: + f = open(path + "/" + filename, "r") + except IOError, e: + f = open(chainsdir + "/" + filename, "r") value = f.read() f.close() return value @@ -57,26 +76,9 @@ def add_to_logorder(key): f.write(base64.b16encode(key) + "\n") f.close() -def http_request(url, data=None): - req = urllib2.Request(url, data) - keyname = args.own_keyname - privatekey = get_eckey_from_file(args.own_keyfile) - sk = ecdsa.SigningKey.from_der(privatekey) - parsed_url = urlparse.urlparse(url) - if data == None: - data = parsed_url.query - method = "GET" - else: - method = "POST" - 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).read() - return result - def get_new_entries(baseurl): try: - result = http_request(baseurl + "ct/storage/fetchnewentries") + result = http_request(baseurl + "ct/storage/fetchnewentries", key=own_key) parsed_result = json.loads(result) if parsed_result.get(u"result") == u"ok": return [base64.b64decode(entry) for entry in parsed_result[u"entries"]] @@ -89,7 +91,7 @@ def get_new_entries(baseurl): def get_entries(baseurl, hashes): try: params = urllib.urlencode({"hash":[base64.b64encode(hash) for hash in hashes]}, doseq=True) - result = http_request(baseurl + "ct/storage/getentry?" + params) + result = http_request(baseurl + "ct/storage/getentry?" + params, key=own_key) parsed_result = json.loads(result) if parsed_result.get(u"result") == u"ok": entries = dict([(base64.b64decode(entry["hash"]), base64.b64decode(entry["entry"])) for entry in parsed_result[u"entries"]]) @@ -104,7 +106,7 @@ def get_entries(baseurl, hashes): def get_curpos(baseurl): try: - result = http_request(baseurl + "ct/frontend/currentposition") + result = http_request(baseurl + "ct/frontend/currentposition", key=own_key) parsed_result = json.loads(result) if parsed_result.get(u"result") == u"ok": return parsed_result[u"position"] @@ -117,7 +119,7 @@ def get_curpos(baseurl): def sendlog(baseurl, submission): try: result = http_request(baseurl + "ct/frontend/sendlog", - json.dumps(submission)) + json.dumps(submission), key=own_key) return json.loads(result) except urllib2.HTTPError, e: print "ERROR: sendlog", e.read() @@ -133,7 +135,7 @@ def sendlog(baseurl, submission): def sendentry(baseurl, entry, hash): try: result = http_request(baseurl + "ct/frontend/sendentry", - json.dumps({"entry":base64.b64encode(entry), "treeleafhash":base64.b64encode(hash)})) + json.dumps({"entry":base64.b64encode(entry), "treeleafhash":base64.b64encode(hash)}), key=own_key) return json.loads(result) except urllib2.HTTPError, e: print "ERROR: sendentry", e.read() @@ -149,7 +151,7 @@ def sendentry(baseurl, entry, hash): def sendsth(baseurl, submission): try: result = http_request(baseurl + "ct/frontend/sendsth", - json.dumps(submission)) + json.dumps(submission), key=own_key) return json.loads(result) except urllib2.HTTPError, e: print "ERROR: sendsth", e.read() @@ -164,7 +166,7 @@ def sendsth(baseurl, submission): def get_missingentries(baseurl): try: - result = http_request(baseurl + "ct/frontend/missingentries") + result = http_request(baseurl + "ct/frontend/missingentries", key=own_key) parsed_result = json.loads(result) if parsed_result.get(u"result") == u"ok": return parsed_result[u"entries"] @@ -230,10 +232,9 @@ tree = build_merkle_tree(logorder) tree_size = len(logorder) root_hash = tree[-1][0] timestamp = int(time.time() * 1000) -privatekey = get_eckey_from_file(args.keyfile) tree_head_signature = create_sth_signature(tree_size, timestamp, - root_hash, privatekey) + root_hash, args.signing, key=own_key) sth = {"tree_size": tree_size, "timestamp": timestamp, "sha256_root_hash": base64.b64encode(root_hash), @@ -255,7 +256,10 @@ for frontendnode in frontendnodes: print "current position", curpos entries = [base64.b64encode(entry) for entry in logorder[curpos:]] for chunk in chunks(entries, 1000): - sendlog(frontendnode, {"start": curpos, "hashes": chunk}) + sendlogresult = sendlog(frontendnode, {"start": curpos, "hashes": chunk}) + if sendlogresult["result"] != "ok": + print "sendlog:", sendlogresult + sys.exit(1) curpos += len(chunk) print curpos, sys.stdout.flush() @@ -266,8 +270,14 @@ for frontendnode in frontendnodes: print "missing entries:", len(missingentries) for missingentry in missingentries: hash = base64.b64decode(missingentry) - sendentry(frontendnode, read_chain(hash), hash) + sendentryresult = sendentry(frontendnode, read_chain(hash), hash) + if sendentryresult["result"] != "ok": + print "send sth:", sendentryresult + sys.exit(1) timing_point(timing, "send missing") - sendsth(frontendnode, sth) + sendsthresult = sendsth(frontendnode, sth) + if sendsthresult["result"] != "ok": + print "send sth:", sendsthresult + sys.exit(1) timing_point(timing, "send sth") print timing["deltatimes"] diff --git a/tools/testcase1.py b/tools/testcase1.py index c87e8eb..73613fb 100755 --- a/tools/testcase1.py +++ b/tools/testcase1.py @@ -136,13 +136,13 @@ def get_and_check_entry(timestamp, chain, leaf_index): len(submittedcertchain)) def merge(): - return subprocess.call(["./merge.py", "--baseurl", "https://127.0.0.1:8080/", "--frontend", "https://127.0.0.1:8082/", "--storage", "https://127.0.0.1:8081/", "--mergedb", "../rel/mergedb", "--keyfile", "../rel/test/eckey.pem", "--own-keyname", "merge-1", "--own-keyfile", "../rel/privatekeys/merge-1-private.pem"]) - -print_and_check_tree_size(0) + return subprocess.call(["./merge.py", "--baseurl", "https://127.0.0.1:8080/", "--frontend", "https://127.0.0.1:8082/", "--storage", "https://127.0.0.1:8081/", "--mergedb", "../rel/mergedb", "--signing", "https://127.0.0.1:8088/", "--own-keyname", "merge-1", "--own-keyfile", "../rel/privatekeys/merge-1-private.pem"]) mergeresult = merge() assert_equal(mergeresult, 0, "merge", quiet=True) +print_and_check_tree_size(0) + testgroup("cert1") result1 = do_add_chain(cc1) @@ -230,6 +230,9 @@ get_and_validate_proof(result4["timestamp"], cc3, 2, 3) get_and_validate_proof(result5["timestamp"], cc4, 3, 3) get_and_validate_proof(result6["timestamp"], cc5, 4, 1) +mergeresult = merge() +assert_equal(mergeresult, 0, "merge", quiet=True) + for first_size in range(1, 5): for second_size in range(first_size + 1, 6): get_and_validate_consistency_proof(size_sth[first_size], size_sth[second_size], first_size, second_size) diff --git a/tools/testcerts/pre1.txt b/tools/testcerts/pre1.txt new file mode 100644 index 0000000..776c38e --- /dev/null +++ b/tools/testcerts/pre1.txt @@ -0,0 +1,79 @@ +Timestamp: 1383337821156 +Leafhash: A4892155FE9929177BCA785A73C15351A3EE2AF6F163DE40C15802BDE0F41302 +-----BEGIN PRECERTIFICATE----- +MIIGqDCCBZCgAwIBAgIQCxvJV1NZEuon0JIojHqH+DANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5EaWdpQ2VydCBTSEEyIFNlY3Vy +ZSBTZXJ2ZXIgQ0EwHhcNMTMxMTAxMDAwMDAwWhcNMTQxMTA2MTIwMDAwWjBkMQswCQYDVQQGEwJV +UzENMAsGA1UECBMEVXRhaDENMAsGA1UEBxMETGVoaTEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x +HjAcBgNVBAMTFWVtYmVkLmN0LmRpZ2ljZXJ0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANExEGl1kTCQJNWXQuTH3m4DWx7xh9Tq+EXHlhorVtgUmHLmBPn7FGC3MH51q0MXN6K7 +huQVXa9LRmCdPRNlNPSkWUqpCVTEqBZrTPuAGEs01+XgXsyhP3uwBxWZkkKJ0FJ4tu7RVHXXgmSC ++JQkSgI4MUNuMaIHvWEpEKsmov9kcQZGUTPnwEg90PyVLlbKypRoFM0dynpslh6FUH4OEAuCx4h1 +tsAN2KHk/ajYE0ND+FN0gBf5qXuY+njUEsDaGiAVKgAb16wOk//0xWy4cTWeHnyLObrsZ3F11GVl +8cK1x0dNGxgeVfH6yTB8BJu/2wqaQSAdzf14Cie5D8YUXf0CAwEAAaOCA2swggNnMB8GA1UdIwQY +MBaAFA+AYRyCMWHVLyjnjUY4tCzhxtniMB0GA1UdDgQWBBT8yxF+UXTw/RIW5igB3ZSRrSSkFzAg +BgNVHREEGTAXghVlbWJlZC5jdC5kaWdpY2VydC5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW +MBQGCCsGAQUFBwMBBggrBgEFBQcDAjBrBgNVHR8EZDBiMC+gLaArhilodHRwOi8vY3JsMy5kaWdp +Y2VydC5jb20vc3NjYS1zaGEyLWcxLmNybDAvoC2gK4YpaHR0cDovL2NybDQuZGlnaWNlcnQuY29t +L3NzY2Etc2hhMi1nMS5jcmwwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwBATCCAaQwOgYI +KwYBBQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0w +ggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBl +AHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQA +YQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABh +AG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUA +bgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg +AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIA +ZQBmAGUAcgBlAG4AYwBlAC4wfAYIKwYBBQUHAQEEcDBuMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz +cC5kaWdpY2VydC5jb20wRgYIKwYBBQUHMAKGOmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9E +aWdpQ2VydFNIQTJTZWN1cmVTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADATBgorBgEEAdZ5AgQD +AQH/BAIFADANBgkqhkiG9w0BAQsFAAOCAQEAbHgFxzrmkXjRdQdlHj4Ey2U8rTOetMqjddrXR1DZ +9E12vp8yWB+LkSVASutpgzxNawj/rv1w1ODdJWMTra12R1MnxqoVytSEmbE0gjgxahdWWiV8yTFB +4tMFRHvCCwmIJqhRwjufnRs1q1+9YMxZ6reCG4kg29qgtQhh8V9vCrGfQja/4cBHa6O7w407FPra +b2NIqtJB/47fOdACkVdFjbOVSWielDtTv7QNPi3OUfNwNE/Qqh1k5MOBDP1gif1AFzl5Z7plUos5 +3533VCBjrcOWp8WXUtNlIedlxjarUaTKSRpZVdRzY9ugvou9JLVF1SuDIAXQ3+tN44bjAjERug== +-----END PRECERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgx +MjAwMDBaME0xCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRp +Z2lDZXJ0IFNIQTIgU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83nf36QYSv +x6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bdKpPDkC55gIDvEwRqFDu1 +m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f/ld0Uzs1gN2ujkSYs58O09rg1/RrKatE +p0tYhG2SS4HD2nOLEpdIkARFdRrdNzGXkujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJ +TvOX6+guqw9ypzAO+sf0/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQI +MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0 +cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj +ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1oDOGMWh0dHA6Ly9jcmw0LmRpZ2lj +ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYI +KwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHV +LyjnjUY4tCzhxtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB +CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl5TlPHoOlblyY +oiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA8MXW5dRNJ2Srm8c+cftIl7gz +bckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8s +jX7tN8Cp1Tm5gr8ZDOo0rwAhaPitc+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopY +JeS4d60tbvVS3bR0j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + diff --git a/tools/testcerts/pre2.txt b/tools/testcerts/pre2.txt new file mode 100644 index 0000000..4c86537 --- /dev/null +++ b/tools/testcerts/pre2.txt @@ -0,0 +1,106 @@ +Timestamp: 1399629239033 +Leafhash: 758B8612DFED6A3321215C0586C0AC9F43137CD2BBF043C86301D66DC7D1205A +-----BEGIN PRECERTIFICATE----- +MIIFFzCCBAGgAwIBAgIgd+115NyVfYOnRINB2wJy2eaQRbJ6j8Zau5IdwBNpmzowCwYJKoZIhvcN +AQELMGYxLDAqBgNVBAMMI1ByZS1jZXJ0aWZpY2F0ZSBTaWduaW5nIENlcnRpZmljYXRlMRAwDgYD +VQQLDAdDQSBUZWFtMRcwFQYDVQQKDA5UQUlXQU4tQ0EgSU5DLjELMAkGA1UEBhMCVFcwHhcNMTQw +NTA5MDk1MzU3WhcNMTQwNTE2MTU1OTU5WjB0MR0wGwYDVQQDDBRjdHRlc3QwNS50d2NhLmNvbS50 +dzELMAkGA1UECwwCUkQxFzAVBgNVBAoMDlRBSVdBTi1DQSBJTkMuMQ8wDQYDVQQHDAZUYWlwZWkx +DzANBgNVBAgMBlRhaXdhbjELMAkGA1UEBhMCVFcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDSgb3MYpsqjkNqcOJHIlEmy8ldCzXtmJfoLfvW1g8JyaGgKR6B98ceg1whThF1tPy8aqJv +fEXGivb+2El1BmxTNvSZ+bOkOT0UsD2hiIgUppD6b/ICWQrIvwrBTNnfJtrwvGD/rygpVTZQoekX +IVdapI95Cfn+36YXqjX7ixgItEx3t/nzOqBxJNI0p52m9l1sowi2/hGmvc/xqC0Cti4m177c8gP0 +u4oKQRJVF2690F748KfzIMcbS7KbDDDVhtWqwgKaRLvqD+gJAUZ1QYEyzDr5Xhhi1O0FXfhyeeCj +mRUJBENmhqElt9C1HugaBsno37JP1AQdsuVg776qQQ1PAgMBAAGjggGlMIIBoTArBgNVHSMEJDAi +gCCVnLtVYCn+QZohG69CSwl1Y2OhEQ7LbPhnh353anz2ezApBgNVHQ4EIgQgt6NL2avrK2PUt8X1 +oG0rd0Wd2ZVDVuJru2T6Z4/eJUEwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2N0dGVzdC50d2Nh +LmNvbS50dy9zc2xzZXJ2ZXIvY3R0ZXN0LmNybDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwID +qDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwJQYDVR0RBB4wHIIUY3R0ZXN0MDUudHdj +YS5jb20udHeHBMCoAckwOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vY3R0ZXN0 +LnR3Y2EuY29tLnR3L29jc3AwUQYDVR0gBEowSDBGBgdggR4DAQgFMDswIgYIKwYBBQUHAgEWFmh0 +dHA6Ly93d3cudHdjYS5jb20udHcwFQYIKwYBBQUHAgIwCRoHMC4xLjEuMzATBgorBgEEAdZ5AgQD +AQH/BAIFADALBgkqhkiG9w0BAQsDggEBAIkN6er89ss6KAZOH/ZpTPbXhO/J8NNq7vJBxhD4z56R +aRTJpr7Fla9zr8K3aD7bbBUpVeMqER3YA7eeOR8ITBqzMN9SpjdpDlBLcI/6S+7iUVRw4+UvEVqL +0xlCfdxftGLX+T77y7/qqLxyH+QVuSS4sKzTCfspqAaxteK32A5nWKAiJFUI/ise67o3T9f015hR +7rHj+U2AomLQwnyiMg4u3D2mYzK9q7VDGJfKIW6wrFYS/lQsFKyb4sUTyGG9VuzgSDIjCXJag7fs +MZ+/shgsVOTzHUVeHGuKsPcpps0Yvu2W3DybsVoBwtS/vePPnfNfCrDqM9vZCTurvG4KaS4= +-----END PRECERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEUTCCAzugAwIBAgIEATNR3TALBgkqhkiG9w0BAQswVDELMAkGA1UEBhMCVFcxFzAVBgNVBAoT +DlRBSVdBTi1DQSBJTkMuMRAwDgYDVQQLEwdDQSBUZWFtMRowGAYDVQQDExFSRCBUV0NBIENUVEVT +VCBDQTAeFw0xNDA1MDkwOTQzMjZaFw0xNTA1MDkxNTU5NTlaMGYxLDAqBgNVBAMMI1ByZS1jZXJ0 +aWZpY2F0ZSBTaWduaW5nIENlcnRpZmljYXRlMRAwDgYDVQQLDAdDQSBUZWFtMRcwFQYDVQQKDA5U +QUlXQU4tQ0EgSU5DLjELMAkGA1UEBhMCVFcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCtFIow0xs7VQ42AEck0o+D8pDDOvIclTPJG7j5+wc7lz1wOwbqP8w06Qa/18tg3sdk16dYFg9k +pIeOU7suaWgeHifBjjj9iXTELH4U0RP3HwxlM23WArt9a5OKM5KJlA2T9obppnfsN9fm6ZGX4TTY +JqV8x2vgXSkHhVwxl8wnZoywHlHlgThvVVi+/DzZUD8FIXz2/dPeMtSTfHQ6LqIhee9YMIVgqg/f +tPb5lOhrJEmAl56mJWi1haVYmxZDSa4+1XCJkOxEzQDPpAvIrXVgAQzr6A5jIHZ7VucTEQ5U/9lx +Gckzv6CFDRxYyjSpBZsxML/d4A1P9nKdWcABqO9PAgMBAAGjggEbMIIBFzArBgNVHSMEJDAigCCE +xPSrbrwoBcYxPScQhJ7WOGJB5N3Efkav81dvue7NsjApBgNVHQ4EIgQglZy7VWAp/kGaIRuvQksJ +dWNjoREOy2z4Z4d+d2p89nswPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2N0dGVzdC50d2NhLmNv +bS50dy9zc2xzZXJ2ZXIvY3R0ZXN0LmNybDASBgNVHRMBAf8ECDAGAQH/AgEAMBUGA1UdJQQOMAwG +CisGAQQB1nkCBAQwUQYDVR0gBEowSDBGBgdggR4DAQgFMDswIgYIKwYBBQUHAgEWFmh0dHA6Ly93 +d3cudHdjYS5jb20udHcwFQYIKwYBBQUHAgIwCRoHMC4xLjEuMzALBgkqhkiG9w0BAQsDggEBAN8v +hr/zNUNSSikqAtRVZVgcJTuN3yTlaXX4hMJFAWrbBqJuN++cE6A2BBTkaLpEZajVqPKL5AxL5KWM +dMFNkpF3i0UKbf4vnpfrQprsamDX5tKqPCAOKa8yL82CBkimOCmLx24WN+VtNitYzh/MqspApNM7 +7wCO8ncvFtS4sC1Gj5M9CjVhxKmMe15O4SZr9aZpGP7raT4CE3X95APKX5yyiAVwPcOPdPkfRRLQ +gHko60NbxaeayH5sfWa2dNPEjbOkz0SKaXurV9pzrj/2FZNhgsnRsGIJhx2BLm7FoeUC45RarDJD +YrscJ6DBR83YwJXsaFCyB7l5CP7L13Wr98E= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEvjCCAqagAwIBAgIQQAEzUd0AAAAAAAAAFzPdqzANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJUVzEbMBkGA1UEChMSVFdDQSBSRCBEZXBhcnRtZW50MRAwDgYDVQQLEwdDQSBUZWFtMSQwIgYD +VQQDExtSRCBUV0NBIFJvb3QgQ0EgNDA5NiBTaGEyNTYwHhcNMTQwNTA5MDMyMDUyWhcNMTUwNTA5 +MTU1OTU5WjBUMQswCQYDVQQGEwJUVzEXMBUGA1UEChMOVEFJV0FOLUNBIElOQy4xEDAOBgNVBAsT +B0NBIFRlYW0xGjAYBgNVBAMTEVJEIFRXQ0EgQ1RURVNUIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA6xAMprFXHtOkmXIgL63aTx4S20IzdrcdDTMQvwnyYLBhJf8lWz5qeOY37SaC +4VXILP54qVdML+YBa2BAQfgu6kS+/f73Bp3pSHx8LIHQCt5jdgkNS3OVYK8nQdbWXWeA64bCFdE/ +tlelHSTHtIKXE+v7ug+P5Q/RRBf0Dzi/M1fXTXqXeAga3LaPGPT7o6lZZJh7hp25aJxChIa/1X8x +99sPx/BqO/WHyYKBCU9Ym05yQFel8mpCgzSbqscKTbKPkvm0ejDANX/WCEziJ3IzR5G9kPoL/zYZ +ofIqYJMIYRsQRlD/n1ILnMxwdhN3EFlZ0e5xkyIm9TaCqeCZsdFJWQIDAQABo34wfDArBgNVHSME +JDAigCCwvM16BvA51cl2uO30/ohdOMPVrVBVG5BZ4teNnteYnTApBgNVHQ4EIgQghMT0q268KAXG +MT0nEISe1jhiQeTdxH5Gr/NXb7nuzbIwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C +AQAwDQYJKoZIhvcNAQEFBQADggIBABDkaI3GMCKBfJSfnpmxmiU1ht3cfq/9/hpJSzE6k+of5esV +D3bYW9nnKScCcBy7poeOoc3C7p9fQtsLZbNfhYpG4/Aq0aVYGtZxw/FCWnXi9rUXpSLZh1yW1uV9 +KBj2D8yzGIx99mpHifjjeoCWG0TW/aaHeIolJm2DhkPTLWjd/urN1TP8YqhEiKMHZI3SFWeeH/BV +WJKE5rX8vtLW1FPnlRPE+Z/FAa52qOyN4ie0A9IhNPs3gtc+bnhdcZaHnxkySqUvWxqQxkzAGaiO +VnPlnSlnMCn5I2KOT0XVWYOyU9PP1//V/baDftv7VpT5AOtIaz8mQ6Lp4AIcoPFeU8cgJNZhXgmp +NOv/dW8lWXH6RYxdM7NFmv98Wk3rKLCzOzR6kuXnARKOJghZf4FV+6Fvjgjf/8wLnzhSdCEbyL7A +znkOSKc9wzUcZCxF8aTWtRT8HYIu8diZo0CzPxN8OyDl5mPsYexhguPHOXyLv/EljZ8yCdy/SsgQ +JPzuqKu2a3RD4des15EzbnJOxn4DSeqoUfSfaU/KVfmUKpBEJ3ouD2SLAZ7L+4F6NPCte3HEE2kN +tOmQIwe65htXmLJxDB+dwMjE4dkA2sETaN2dQ9DqpCNkpNxuNdis/uacAAXnDNddPIlR2moCtUx8 ++Y7wlcqBHdmmg5rbFBuBN+esL8J8 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +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 +7bTu +-----END CERTIFICATE----- + diff --git a/tools/testcerts/roots/root4.pem b/tools/testcerts/roots/root4.pem new file mode 100644 index 0000000..3fdb770 --- /dev/null +++ b/tools/testcerts/roots/root4.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- diff --git a/tools/testcerts/roots/root5.pem b/tools/testcerts/roots/root5.pem new file mode 100644 index 0000000..096fd18 --- /dev/null +++ b/tools/testcerts/roots/root5.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +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 +7bTu +-----END CERTIFICATE----- |