+++ /dev/null
-#!/usr/bin/python3
-import subprocess, sys, argparse
-from collections import OrderedDict
-from enum import Enum
-
-# progress bar
-def terminal_size():
- import fcntl, termios, struct
- try:
- result = fcntl.ioctl(1, termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0))
- except OSError:
- # this is not a terminal
- return 0, 0
- h, w, hp, wp = struct.unpack('HHHH', result)
- assert w > 0 and h > 0, "Empty terminal...??"
- return w, h
-
-def compute_frac(fracs):
- frac = 0.0
- last_frac = 1.0
- for complete, total in fracs:
- frac += (complete*last_frac/total)
- last_frac *= 1/total
- return frac
-
-def print_progress(state, fracs):
- STATE_WIDTH = 30
- w, h = terminal_size()
- if w < STATE_WIDTH+10: return # not a (wide enough) terminal
- bar_width = w-STATE_WIDTH-3
- hashes = int(bar_width*compute_frac(fracs))
- sys.stdout.write('\r{0} [{1}{2}]'.format(state[:STATE_WIDTH].ljust(STATE_WIDTH), '#'*hashes, ' '*(bar_width-hashes)))
- sys.stdout.flush()
-def finish_progress():
- w, h = terminal_size()
- sys.stdout.write('\r'+(' '*w)+'\r')
- sys.stdout.flush()
-
-# cipher check
-def list_ciphers():
- ciphers = subprocess.check_output(["openssl", "ciphers", "ALL:COMPLEMENTOFALL"]).decode('UTF-8').strip()
- return ciphers.split(':')
-
-def test_cipher(host, port, protocol, cipher = None, options=[]):
- try:
- if cipher is not None:
- options = ["-cipher", cipher]+options
- subprocess.check_call(["openssl", "s_client", "-"+protocol, "-connect", host+":"+str(port)]+options,
- stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
- except subprocess.CalledProcessError:
- return False
- else:
- return True
-
-def test_protocol(host, port, protocol, ciphers, base_frac, options=[]):
- if test_cipher(host, port, protocol, 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))])
- results[cipher] = test_cipher(host, port, protocol, cipher, options)
- return results
- else:
- # it is not supported
- return None
-
-def test_host(host, port, options=[]):
- ciphers = list_ciphers()
- results = OrderedDict()
- protocols = ('ssl2', 'ssl3', 'tls1', 'tls1_1', 'tls1_2')
- for i in range(len(protocols)):
- protocol = protocols[i]
- print_progress(protocol, [(i, len(protocols))])
- results[protocol] = test_protocol(host, port, protocol, ciphers, [(i, len(protocols))], options)
- finish_progress()
- return results
-
-if __name__ == "__main__":
- parser = argparse.ArgumentParser(description='Check SSL ciphers supported by a host')
- parser.add_argument("--starttls", dest="starttls",
- help="Use a STARTTLS variant to establish the TLS connection. Possible values include smpt, imap, xmpp.")
- parser.add_argument("host", metavar='HOST[:PORT]',
- help="The host to check")
- args = parser.parse_args()
-
- # get host, port
- if ':' in args.host:
- host, port = args.host.split(':')
- else:
- host = args.host
- port = 443
-
- # get options
- options = []
- if args.starttls is not None:
- options += ['-starttls', args.starttls]
-
- # run the test
- results = test_host(host, port, options)
-
- # print the results
- for protocol, ciphers in results.items():
- print(protocol+":")
- if ciphers is None:
- print(" Is not supported by client or server")
- else:
- for cipher, supported in ciphers.items():
- if supported:
- print(" "+cipher)
- print()