diff options
author | Josef Gustafsson <josef.gson@gmail.com> | 2015-09-17 13:58:51 +0200 |
---|---|---|
committer | Josef Gustafsson <josef.gson@gmail.com> | 2015-09-17 13:58:51 +0200 |
commit | 0a539942e5a94818abedf3adf2090b57f6b92bc2 (patch) | |
tree | a8aa4ce4480e9ea81a1dbf7857ad08b19c26dfd1 | |
parent | a7344504a7af8484eb1811450ad87f899d49d0b1 (diff) |
finding nice script for verifying SCT, requires openssl 1.0.2
-rw-r--r-- | monitor/josef_lib.py | 2 | ||||
-rwxr-xr-x | monitor/josef_monitor.py | 4 | ||||
-rw-r--r-- | monitor/monitor_conf.py | 76 | ||||
-rwxr-xr-x | monitor/verify_sct.py | 239 |
4 files changed, 279 insertions, 42 deletions
diff --git a/monitor/josef_lib.py b/monitor/josef_lib.py index 28ea0c0..24c0c32 100644 --- a/monitor/josef_lib.py +++ b/monitor/josef_lib.py @@ -18,7 +18,7 @@ import zipfile import shutil from certkeys import publickeys -from Crypto.Hash import SHA256 +# from Crypto.Hash import SHA256 import Crypto.PublicKey.RSA as RSA from Crypto.Signature import PKCS1_v1_5 diff --git a/monitor/josef_monitor.py b/monitor/josef_monitor.py index aacc59c..97092bc 100755 --- a/monitor/josef_monitor.py +++ b/monitor/josef_monitor.py @@ -266,11 +266,9 @@ def verify_inclusion_by_hash(base_url, leaf_hash): return True else: print time.strftime('%H:%M:%S') + " ERROR: Could not prove inclusion for entry " + str(proof["leaf_index"]) + " in " + base_url - errors.append(time.strftime('%H:%M:%S') + " ERROR: Could not prove inclusion for entry " + str(proof["leaf_index"]) + " in " + base_url) return False except: print time.strftime('%H:%M:%S') + " ERROR: Could not prove inclusion for hashed entry in " + base_url - errors.append(time.strftime('%H:%M:%S') + " ERROR: Could not prove inclusion for hashed entry in " + base_url) return False def verify_inclusion_by_index(base_url, index): @@ -343,7 +341,7 @@ def main(args): logs = [] try: for item in ctlogs: - logs.append(ctlog(item, ctlogs[item][0], ctlogs[item][1], ctlogs[item][2])) + logs.append(ctlog(item["name"], item["url"], item["key"], item["id"])) print time.strftime('%H:%M:%S') + " Setting up monitor for " + str(len(logs)) + " logs..." # Set up state diff --git a/monitor/monitor_conf.py b/monitor/monitor_conf.py index 38de5ff..326caa8 100644 --- a/monitor/monitor_conf.py +++ b/monitor/monitor_conf.py @@ -28,49 +28,49 @@ MONITORED_DOMAINS = [ ] # CT logs and associated keys -ctlogs = { - # "pilot": - # ["https://ct.googleapis.com/pilot/", - # "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfahLEimAoz2t01p3uMziiLOl/fHTDM0YDOhBRuiBARsV4UvxG2LdNgoIGLrtCzWE0J5APC2em4JlvR8EEEFMoA==", - # "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA="], +ctlogs = [ + # {"name" : "pilot":, + # "url" : "https://ct.googleapis.com/pilot/", + # "key" : "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfahLEimAoz2t01p3uMziiLOl/fHTDM0YDOhBRuiBARsV4UvxG2LdNgoIGLrtCzWE0J5APC2em4JlvR8EEEFMoA==", + # "id" : "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA="}, - # "plausible": - # ["https://plausible.ct.nordu.net/", - # "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9UV9+jO2MCTzkabodO2F7LM03MUBc8MrdAtkcW6v6GA9taTTw9QJqofm0BbdAsbtJL/unyEf0zIkRgXjjzaYqQ==", - # "qucLfzy41WbIbC8Wl5yfRF9pqw60U1WJsvd6AwEE880="], + # {"name" : "plausible", + # "url" : "https://plausible.ct.nordu.net/", + # "key" : "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9UV9+jO2MCTzkabodO2F7LM03MUBc8MrdAtkcW6v6GA9taTTw9QJqofm0BbdAsbtJL/unyEf0zIkRgXjjzaYqQ==", + # "id" : "qucLfzy41WbIbC8Wl5yfRF9pqw60U1WJsvd6AwEE880="}, - # "digicert": - # ["https://ct1.digicert-ct.com/log/", - # "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAkbFvhu7gkAW6MHSrBlpE1n4+HCFRkC5OLAjgqhkTH+/uzSfSl8ois8ZxAD2NgaTZe1M9akhYlrYkes4JECs6A==", - # "VhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0="], + # {"name" : "digicert", + # "url" : "https://ct1.digicert-ct.com/log/", + # "key" : "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAkbFvhu7gkAW6MHSrBlpE1n4+HCFRkC5OLAjgqhkTH+/uzSfSl8ois8ZxAD2NgaTZe1M9akhYlrYkes4JECs6A==", + # "id" : "VhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0="}, - "izenpe": - ["https://ct.izenpe.com/", - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ2Q5DC3cUBj4IQCiDu0s6j51up+TZAkAEcQRF6tczw90rLWXkJMAW7jr9yc92bIKgV8vDXU4lDeZHvYHduDuvg==", - "dGG0oJz7PUHXUVlXWy52SaRFqNJ3CbDMVkpkgrfrQaM="], + {"name" : "izenpe", + "url" : "https://ct.izenpe.com/", + "key" : "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ2Q5DC3cUBj4IQCiDu0s6j51up+TZAkAEcQRF6tczw90rLWXkJMAW7jr9yc92bIKgV8vDXU4lDeZHvYHduDuvg==", + "id" : "dGG0oJz7PUHXUVlXWy52SaRFqNJ3CbDMVkpkgrfrQaM="}, - "certly": - ["https://log.certly.io/", - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2MNvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA==", - "zbUXm3/BwEb+6jETaj+PAC5hgvr4iW/syLL1tatgSQA="], + {"name" : "certly", + "url" : "https://log.certly.io/", + "key" : "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2MNvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA==", + "id" : "zbUXm3/BwEb+6jETaj+PAC5hgvr4iW/syLL1tatgSQA="}, - # "aviator": - # ["https://ct.googleapis.com/aviator/", - # "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q==", - # "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q="], + # {"name" : "aviator", + # "url" : "https://ct.googleapis.com/aviator/", + # "key" : "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q==", + # "id" : "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q="}, - # "rocketeer": - # ["https://ct.googleapis.com/rocketeer/", - # "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg==", - # "7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/cs="], + # {"name" : "rocketeer", + # "url" : "https://ct.googleapis.com/rocketeer/", + # "key" : "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg==", + # "id": "7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/cs="}, - "symantec": - ["https://ct.ws.symantec.com/", - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEluqsHEYMG1XcDfy1lCdGV0JwOmkY4r87xNuroPS2bMBTP01CEDPwWJePa75y9CrsHEKqAy8afig1dpkIPSEUhg==", - "3esdK3oNT6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvsw="], + {"name" : "symantec": + "url" : "https://ct.ws.symantec.com/", + "key" : "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEluqsHEYMG1XcDfy1lCdGV0JwOmkY4r87xNuroPS2bMBTP01CEDPwWJePa75y9CrsHEKqAy8afig1dpkIPSEUhg==", + "id" : "3esdK3oNT6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvsw="}, - "venafi": - ["https://ctlog.api.venafi.com/", - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAolpIHxdSlTXLo1s6H1OCdpSj/4DyHDc8wLG9wVmLqy1lk9fz4ATVmm+/1iN2Nk8jmctUKK2MFUtlWXZBSpym97M7frGlSaQXUWyA3CqQUEuIJOmlEjKTBEiQAvpfDjCHjlV2Be4qTM6jamkJbiWtgnYPhJL6ONaGTiSPm7Byy57iaz/hbckldSOIoRhYBiMzeNoA0DiRZ9KmfSeXZ1rB8y8X5urSW+iBzf2SaOfzBvDpcoTuAaWx2DPazoOl28fP1hZ+kHUYvxbcMjttjauCFx+JII0dmuZNIwjfeG/GBb9frpSX219k1O4Wi6OEbHEr8at/XQ0y7gTikOxBn/s5wQIDAQAB", - "rDua7X+pZ0dXFZ5tfVdWcvnZgQCUHpve/+yhMTt1eC0="], -} + {"name" : "venafi", + "url" : "https://ctlog.api.venafi.com/", + "key" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAolpIHxdSlTXLo1s6H1OCdpSj/4DyHDc8wLG9wVmLqy1lk9fz4ATVmm+/1iN2Nk8jmctUKK2MFUtlWXZBSpym97M7frGlSaQXUWyA3CqQUEuIJOmlEjKTBEiQAvpfDjCHjlV2Be4qTM6jamkJbiWtgnYPhJL6ONaGTiSPm7Byy57iaz/hbckldSOIoRhYBiMzeNoA0DiRZ9KmfSeXZ1rB8y8X5urSW+iBzf2SaOfzBvDpcoTuAaWx2DPazoOl28fP1hZ+kHUYvxbcMjttjauCFx+JII0dmuZNIwjfeG/GBb9frpSX219k1O4Wi6OEbHEr8at/XQ0y7gTikOxBn/s5wQIDAQAB", + "id" : "rDua7X+pZ0dXFZ5tfVdWcvnZgQCUHpve/+yhMTt1eC0="}, +] diff --git a/monitor/verify_sct.py b/monitor/verify_sct.py new file mode 100755 index 0000000..32e460d --- /dev/null +++ b/monitor/verify_sct.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python + +# Signed Certificate Timestamp TLS extension verifier +# Copyright (c) 2015 Pier Carlo Chiodi - http://www.pierky.com +# +# https://github.com/pierky/sct-verify + +import sys +import subprocess +import base64 +import struct + +OPENSSL_PATH="/usr/local/ssl/bin/openssl" + +LOGS = [ + { "Name": "Aviator", + "Key": "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1J\n" + "YP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q==\n" + "-----END PUBLIC KEY-----", + "LogID": "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q=" }, + + { "Name": "Digicert Log", + "Key": "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAkbFvhu7gkAW6MHSrBlpE1n4+HCF\n" + "RkC5OLAjgqhkTH+/uzSfSl8ois8ZxAD2NgaTZe1M9akhYlrYkes4JECs6A==\n" + "-----END PUBLIC KEY-----", + "LogID": "VhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0=" }, + + { "Name": "Pilot", + "Key": "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfahLEimAoz2t01p3uMziiLOl/fHT\n" + "DM0YDOhBRuiBARsV4UvxG2LdNgoIGLrtCzWE0J5APC2em4JlvR8EEEFMoA==\n" + "-----END PUBLIC KEY-----", + "LogID": "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=" }, + + { "Name": "Rocketeer", + "Key": "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1\n" + "aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg==\n" + "-----END PUBLIC KEY-----", + "LogID": "7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/cs=" }, + + { "Name": "Izenpe", + "Key": "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ2Q5DC3cUBj4IQCiDu0s6j51up+T\n" + "ZAkAEcQRF6tczw90rLWXkJMAW7jr9yc92bIKgV8vDXU4lDeZHvYHduDuvg==\n" + "-----END PUBLIC KEY-----", + "LogID": "dGG0oJz7PUHXUVlXWy52SaRFqNJ3CbDMVkpkgrfrQaM=" }, + + { "Name": "Certly", + "Key": "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2M\n" + "NvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA==\n" + "-----END PUBLIC KEY-----", + "LogID": "zbUXm3/BwEb+6jETaj+PAC5hgvr4iW/syLL1tatgSQA=" } + + ] + +if len( sys.argv ) <= 1: + print( "Missing hostname argument." ) + print( "Usage: ./sct-verify hostname" ) + print( "" ) + print( "Example:" ) + print( " ./sct-verify sni.velox.ch" ) + print( "" ) + print( "Known hosts implementing SCT TLS Extensions:" ) + print( " - sni.velox.ch" ) + print( " - ritter.vg" ) + quit() + +HostName = sys.argv[1] + +Args = [ OPENSSL_PATH ] +Args.extend( [ "s_client", "-serverinfo", "18", "-connect", "%s:443" % HostName, "-servername", HostName ]) + +OpenSSL= subprocess.Popen( Args, stdin=open('/dev/null', 'r'), stdout=subprocess.PIPE, stderr=subprocess.PIPE ) +OpenSSL_stdout, OpenSSL_stderr = OpenSSL.communicate() +OpenSSL_exitcode = OpenSSL.wait() + +if OpenSSL_exitcode != 0: + print("OpenSSL can't connect to %s" % HostName) + print(OpenSSL_stderr) + quit() + +ServerInfo18 = "" +ServerInfo18_Add = False +EECert = "" +EECert_Add = False +for L in OpenSSL_stdout.split('\n'): + if L == "-----BEGIN SERVERINFO FOR EXTENSION 18-----": + ServerInfo18_Add = True + elif L == "-----END SERVERINFO FOR EXTENSION 18-----": + ServerInfo18_Add = False + elif L == "-----BEGIN CERTIFICATE-----": + EECert_Add = True + elif L == "-----END CERTIFICATE-----": + EECert_Add = False + elif ServerInfo18_Add: + if ServerInfo18: + ServerInfo18 = ServerInfo18 + '\n' + ServerInfo18 = ServerInfo18 + L + elif EECert_Add: + if EECert: + EECert = EECert + '\n' + EECert = EECert + L + +EECertDER = base64.b64decode( EECert ) + +Data = base64.b64decode( ServerInfo18 ) +DataLen = len(Data) + +def ToHex( v ): + if type(v) is int or type(v) is long: + return hex(v) + else: + return ":".join("{:02x}".format(ord(c)) for c in v) + +def Read( buf, offset, format ): + Values = struct.unpack_from( format, buf, offset ) + NewOffset = offset + struct.calcsize( format ) + + Ret = () + Ret = Ret + ( NewOffset, ) + Ret = Ret + Values + return Ret + +def ReadSCT( SCT ): + print("===========================================================") + Offset = 0 + + Offset, SCTVersion = Read( SCT, Offset, "!B" ) + + Offset, SCTLogID = Read( SCT, Offset, "!32s" ) + Base64LogID = base64.b64encode( SCTLogID ) + + Offset, SCTTimestamp = Read( SCT, Offset, "!Q" ) + + Offset, SCTExtensionsLen = Read( SCT, Offset, "!H" ) + + #FIXME + if SCTExtensionsLen > 0: + print("Extensions length > 0; not implemented") + return + + Offset, SCTSignatureAlgHash = Read( SCT, Offset, "!B" ) + Offset, SCTSignatureAlgSign = Read( SCT, Offset, "!B" ) + + Offset, SCTSignatureLen = Read( SCT, Offset, "!H" ) + Offset, SCTSignature = Read( SCT, Offset, "!%ss" % SCTSignatureLen ) + + # print SCT information + + print( "Version : %s" % ToHex( SCTVersion ) ) + SCTLogID1, SCTLogID2 = struct.unpack( "!16s16s", SCTLogID ) + print( "LogID : %s" % ToHex( SCTLogID1 ) ) + print( " %s" % ToHex( SCTLogID2 ) ) + print( "LogID b64 : %s" % Base64LogID ) + print( "Timestamp : %s (%s)" % ( SCTTimestamp, ToHex( SCTTimestamp ) ) ) + print( "Extensions: %s (%s)" % ( SCTExtensionsLen, ToHex( SCTExtensionsLen )) ) + print( "Algorithms: %s/%s (hash/sign)" % ( ToHex( SCTSignatureAlgHash ), ToHex ( SCTSignatureAlgSign ) )) + + SigOffset = 0 + while SigOffset < len( SCTSignature ): + if len( SCTSignature ) - SigOffset > 16: + SigBytesToRead = 16 + else: + SigBytesToRead = len( SCTSignature ) - SigOffset + SigBytes = struct.unpack_from( "!%ss" % SigBytesToRead, SCTSignature, SigOffset )[0] + + if SigOffset == 0: + print( "Signature : %s" % ToHex( SigBytes ) ) + else: + print( " %s" % ToHex( SigBytes ) ) + + SigOffset = SigOffset + SigBytesToRead + + # look for signing log and its key + + PubKey = None + for Log in LOGS: + if Log["LogID"] == Base64LogID: + print( "Log found : %s" % Log["Name"]) + PubKey = Log["Key"] + + if not PubKey: + print("Log not found") + return + + # signed data + + # 1 version + # 1 signature_type + # 8 timestamp + # 2 entry_type + # 3 DER lenght + # x DER + # 2 extensions length + + EECertDERLen = len( EECertDER ) + _, EECertDERLen1, EECertDERLen2, EECertDERLen3 = struct.unpack( "!4B", struct.pack( "!I", EECertDERLen ) ) + + Data = struct.pack("!BBQhBBB%ssh" % len( EECertDER ), SCTVersion, 0, SCTTimestamp, 0, EECertDERLen1, EECertDERLen2, EECertDERLen3, EECertDER, SCTExtensionsLen ) + + File = open("tmp-signeddata.bin", "wb") + File.write( Data ) + File.close() + + File = open("tmp-pubkey.pem", "w") + File.write( PubKey ) + File.close() + + File = open("tmp-signature.bin", "wb") + File.write( SCTSignature ) + File.close() + + Args = [ OPENSSL_PATH ] + Args.extend( [ "dgst", "-sha256", "-verify", "tmp-pubkey.pem", "-signature", "tmp-signature.bin", "tmp-signeddata.bin" ] ) + + OpenSSL= subprocess.Popen( Args, stdin=open('/dev/null', 'r'), stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + OpenSSL_stdout, OpenSSL_stderr = OpenSSL.communicate() + OpenSSL_exitcode = OpenSSL.wait() + + if OpenSSL_exitcode == 0: + print( "Result : %s" % OpenSSL_stdout ) + else: + print( "OpenSSL error - Exit code %d" % OpenSSL_exitcode ) + print( OpenSSL_stderr ) + +Offset = 0 +Offset, TLS_ExtensionType, TLS_ExtensionLen = Read( Data, Offset, "!HH" ) +Offset, SignedCertificateTimestampListLen = Read( Data, Offset, "!H" ) + +while Offset < DataLen: + Offset, SCTLen = Read( Data, Offset, "!H" ) + Offset, SCT = Read( Data, Offset, "!%ss" % SCTLen ) + # print SCT + ReadSCT( SCT ) + |