import subprocess, sys, argparse, time, re
from collections import OrderedDict, namedtuple
from enum import Enum
import subprocess, sys, argparse, time, re
from collections import OrderedDict, namedtuple
from enum import Enum
ciphers = subprocess.check_output(["openssl", "ciphers", spec]).decode('UTF-8').strip()
return ciphers.split(':')
ciphers = subprocess.check_output(["openssl", "ciphers", spec]).decode('UTF-8').strip()
return ciphers.split(':')
-def test_cipher(host, port, protocol, cipher = None, options=[]):
+def test_cipher(host, port, protocol, cipher = None, wait_time=0, options=[]):
+ # throttle
+ time.sleep(wait_time/1000)
- subprocess.check_call(["openssl", "s_client", "-"+protocol, "-connect", host+":"+str(port)]+options,
+ subprocess.check_call(["openssl", "s_client", "-"+protocol, "-connect", host+":"+str(port), "-servername", host]+options,
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
return False
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
return False
return True
def test_protocol(host, port, protocol, ciphers, base_frac, wait_time=0, options=[]):
return True
def test_protocol(host, port, protocol, ciphers, base_frac, wait_time=0, options=[]):
- if test_cipher(host, port, protocol, options=options):
+ if test_cipher(host, port, protocol, wait_time=wait_time, options=options):
# the protocol is supported
results = OrderedDict()
for i in range(len(ciphers)):
cipher = ciphers[i]
print_progress(protocol+" "+cipher, base_frac+[(i, len(ciphers))])
# the protocol is supported
results = OrderedDict()
for i in range(len(ciphers)):
cipher = ciphers[i]
print_progress(protocol+" "+cipher, base_frac+[(i, len(ciphers))])
- results[cipher] = test_cipher(host, port, protocol, cipher, options)
- # throttle
- time.sleep(wait_time/1000)
+ results[cipher] = test_cipher(host, port, protocol, cipher=cipher, wait_time=wait_time, options=options)
- def __getProps(self, cipher):
+ def getProps(self, protocol, cipher):
+ # strip the sub-version-number from the protocol
+ pos = protocol.find('_')
+ if pos >= 0:
+ protocol = protocol[:pos]
- cipherInfo = subprocess.check_output(["openssl", "ciphers", "-v", cipher]).decode('UTF-8').strip()
- assert '\n' not in cipherInfo
+ cipherInfo = subprocess.check_output(["openssl", "ciphers", "-v", "-"+protocol, cipher]).decode('UTF-8').strip()
+ assert '\n' not in cipherInfo, "Cipher "+cipher+" produced unexpected output:\n"+cipherInfo
cipherInfoFields = cipherInfo.split()
# get # of bits
encMatch = re.match(r'^Enc=([0-9A-Za-z]+)\(([0-9]+)\)$', cipherInfoFields[4])
cipherInfoFields = cipherInfo.split()
# get # of bits
encMatch = re.match(r'^Enc=([0-9A-Za-z]+)\(([0-9]+)\)$', cipherInfoFields[4])
kx = kxMatch.group(1)
isPfs = kx in ('DH', 'DH(512)', 'ECDH')
# determine security level
kx = kxMatch.group(1)
isPfs = kx in ('DH', 'DH(512)', 'ECDH')
# determine security level
- assert isExp+isLow+isMedium+isHigh <= 1, "Cipher is more than one from EXP, LOW, MEDIUM, HIGH"
- if isExp:
- strength = CipherStrength.exp
- elif isLow:
- strength = CipherStrength.low
- elif isMedium:
+ assert isMedium+isHigh <= 1, "Cipher "+cipher+" is more than one from EXP, LOW, MEDIUM, HIGH"
+ if isMedium:
strength = CipherStrength.unknown
# done!
return CipherProps(bits=bits, strength=strength, isPfs=isPfs)
strength = CipherStrength.unknown
# done!
return CipherProps(bits=bits, strength=strength, isPfs=isPfs)
-
- def getProps(self, cipher):
- if cipher in self.props:
- return self.props[cipher]
- props = self.__getProps(cipher)
- self.props[cipher] = props
- return props
# main program
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Check TLS ciphers supported by a host')
parser.add_argument("--starttls", dest="starttls",
# main program
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Check TLS ciphers supported by a host')
parser.add_argument("--starttls", dest="starttls",
parser.add_argument("--wait-time", "-t", dest="wait_time", default="10",
help="Time (in ms) to wait between two connections to the server. Default is 10ms.")
parser.add_argument("host", metavar='HOST[:PORT]',
parser.add_argument("--wait-time", "-t", dest="wait_time", default="10",
help="Time (in ms) to wait between two connections to the server. Default is 10ms.")
parser.add_argument("host", metavar='HOST[:PORT]',
fsText = ConsoleFormat.color("FS", ConsoleFormat.GREEN) if cipherProps.isPfs else ConsoleFormat.color("no FS", ConsoleFormat.RED)
bitColor = ConsoleFormat.GREEN if cipherProps.bits >= 128 else (ConsoleFormat.YELLOW if cipherProps.bits >= 100 else ConsoleFormat.RED)
print(" {0} ({1}, {2}, {3})".format(cipher.ljust(STATE_WIDTH), cipherProps.strength.colorName(), ConsoleFormat.color(str(cipherProps.bits)+" bits", bitColor), fsText))
fsText = ConsoleFormat.color("FS", ConsoleFormat.GREEN) if cipherProps.isPfs else ConsoleFormat.color("no FS", ConsoleFormat.RED)
bitColor = ConsoleFormat.GREEN if cipherProps.bits >= 128 else (ConsoleFormat.YELLOW if cipherProps.bits >= 100 else ConsoleFormat.RED)
print(" {0} ({1}, {2}, {3})".format(cipher.ljust(STATE_WIDTH), cipherProps.strength.colorName(), ConsoleFormat.color(str(cipherProps.bits)+" bits", bitColor), fsText))