2
0
mirror of https://github.com/mozilla/cipherscan.git synced 2025-04-21 01:03:39 +02:00
cipherscan/cscan/modifiers.py
Hubert Kario 88ae052f8e add more probes
since not all servers support the ciphers supported by Firefox 42,
add other configurations to make sure we can find a way to connect
to server, if anything could

also includes much more elaborate and realistic TLSv1.3 Client Hello
(the Xmas tree)
2016-08-27 23:26:56 +02:00

148 lines
4.9 KiB
Python

# Copyright (c) 2016 Hubert Kario
# Released under Mozilla Public License 2.0
"""Methods for modifying the scan configurations on the fly."""
from __future__ import print_function
from tlslite.constants import CipherSuite
from tlslite.extensions import SNIExtension, PaddingExtension, TLSExtension
import itertools
def no_sni(generator):
if not generator.extensions:
return generator
generator.extensions[:] = (x for x in generator.extensions
if not isinstance(x, SNIExtension))
generator.modifications.append("no SNI")
return generator
proto_versions = {(3, 0): "SSLv3",
(3, 1): "TLSv1.0",
(3, 2): "TLSv1.1",
(3, 3): "TLSv1.2",
(3, 4): "TLSv1.3",
(3, 5): "TLSv1.4",
(3, 6): "TLSv1.5"}
def version_to_str(version):
"""Convert a version tuple to human-readable string."""
version_name = proto_versions.get(version, None)
if version_name is None:
version_name = "{0[0]}.{0[1]}".format(version)
return version_name
def set_hello_version(generator, version):
"""Set client hello version."""
generator.version = version
generator.modifications += [version_to_str(version)]
return generator
def set_record_version(generator, version):
"""Set record version, un-SSLv2-ify"""
generator.record_version = version
generator.ciphers[:] = (i for i in generator.ciphers if i <= 0xffff)
generator.ssl2 = False
generator.modifications += ["r/{0}".format(version_to_str(version))]
return generator
def no_extensions(generator):
"""Remove extensions"""
generator.extensions = None
generator.modifications += ["no ext"]
return generator
def divceil(divident, divisor):
quot, r = divmod(divident, divisor)
return quot + int(bool(r))
def truncate_ciphers_to_size(generator, size):
"""Truncate list of ciphers until client hello is no bigger than size"""
def cb_fun(client_hello, size=size):
hello_len = len(client_hello.write())
bytes_to_remove = hello_len - size
if bytes_to_remove > 0:
ciphers_to_remove = divceil(bytes_to_remove, 2)
client_hello.cipher_suites[:] = \
client_hello.cipher_suites[:-ciphers_to_remove]
return client_hello
generator.callbacks.append(cb_fun)
generator.modifications += ["trunc c/{0}".format(size)]
return generator
def append_ciphers_to_size(generator, size):
"""
Add ciphers from the 0x2000-0xa000 range until size is reached
Increases the size of the Client Hello message until it is at least
`size` bytes long. Uses cipher ID's from the 0x2000-0xc000 range to do
it (0x5600, a.k.a TLS_FALLBACK_SCSV, excluded)
"""
def cb_fun(client_hello, size=size):
ciphers_iter = iter(range(0x2000, 0xc000))
ciphers_present = set(client_hello.cipher_suites)
# we don't want to add a cipher id with special meaning
# and the set is used only internally
ciphers_present.add(CipherSuite.TLS_FALLBACK_SCSV)
bytes_to_add = size - len(client_hello.write())
if bytes_to_add > 0:
ciphers_to_add = divceil(bytes_to_add, 2)
ciphers_gen = (x for x in ciphers_iter
if x not in ciphers_present)
client_hello.cipher_suites.extend(itertools.islice(ciphers_gen,
ciphers_to_add))
return client_hello
generator.callbacks.append(cb_fun)
generator.modifications += ["append c/{0}".format(size)]
return generator
def extend_with_ext_to_size(generator, size):
"""
Add the padding extension so that the Hello is at least `size` bytes
Either adds a padding extension or extends an existing one so that
the specified size is reached
"""
def cb_fun(client_hello, size=size):
if len(client_hello.write()) > size:
return client_hello
if not client_hello.extensions:
client_hello.extensions = []
ext = next((x for x in client_hello.extensions
if isinstance(x, PaddingExtension)), None)
if not ext:
ext = PaddingExtension()
client_hello.extensions.append(ext)
# check if just adding the extension, with no payload, haven't pushed
# us over the limit
bytes_to_add = size - len(client_hello.write())
if bytes_to_add > 0:
ext.paddingData += bytearray(bytes_to_add)
return client_hello
generator.callbacks.append(cb_fun)
generator.modifications += ["append e/{0}".format(size)]
return generator
def add_empty_ext(generator, ext_type):
if generator.extensions is None:
generator.extensions = []
generator.extensions += [TLSExtension(extType=ext_type)
.create(bytearray(0))]
generator.modifications += ["add ext {0}".format(ext_type)]
return generator