Skip to content

Commit

Permalink
Merge pull request #649 from inikolcev/hello-request
Browse files Browse the repository at this point in the history
Add support for HelloRequest
  • Loading branch information
tomato42 committed Feb 27, 2020
2 parents 7a51038 + 49b90be commit 74c65b0
Show file tree
Hide file tree
Showing 8 changed files with 488 additions and 34 deletions.
350 changes: 350 additions & 0 deletions scripts/test-renegotiation-disabled-client-cert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
# Author: Hubert Kario, (c) 2018
# Released under Gnu GPL v2.0, see LICENSE file for details

from __future__ import print_function
import traceback
import sys
import getopt
from itertools import chain
from random import sample

from tlsfuzzer.runner import Runner
from tlsfuzzer.messages import Connect, ClientHelloGenerator, \
ClientKeyExchangeGenerator, ChangeCipherSpecGenerator, \
FinishedGenerator, ApplicationDataGenerator, AlertGenerator, \
fuzz_message, ResetHandshakeHashes, Close, ResetRenegotiationInfo, \
CertificateGenerator, CertificateVerifyGenerator
from tlsfuzzer.expect import ExpectServerHello, ExpectCertificate, \
ExpectServerHelloDone, ExpectChangeCipherSpec, ExpectFinished, \
ExpectAlert, ExpectApplicationData, ExpectClose, ExpectServerKeyExchange,\
ExpectNoMessage, ExpectCertificateRequest, ExpectHelloRequest

from tlslite.constants import CipherSuite, AlertLevel, AlertDescription, \
ExtensionType, SignatureScheme, GroupName
from tlslite.extensions import ALPNExtension, TLSExtension, \
SignatureAlgorithmsExtension, SignatureAlgorithmsCertExtension, \
SupportedGroupsExtension
from tlslite.utils.keyfactory import parsePEMKey
from tlslite.x509 import X509
from tlslite.x509certchain import X509CertChain
from tlsfuzzer.utils.lists import natural_sort_keys


version = 2


def help_msg():
print("Usage: <script-name> [-h hostname] [-p port] [[probe-name] ...]")
print(" -h hostname name of the host to run the test against")
print(" localhost by default")
print(" -p port port number to use for connection, 4433 by default")
print(" probe-name if present, will run only the probes with given")
print(" names and not all of them, e.g \"sanity\"")
print(" -d Use (EC)DHE instead of RSA for key exchange")
print(" -e probe-name exclude the probe from the list of the ones run")
print(" may be specified multiple times")
print(" -n num only run `num` random tests instead of a full set")
print(" (excluding \"sanity\" tests)")
print(" --no-ins-renego expect the insecure renegotiation to be unsupported")
print(" --early-abort server aborts as soon as we ask for restricted resource")
print(" -k file.pem file with private key for client")
print(" -c file.pem file with certificate for client")
print(" --help this message")


def main():
host = "localhost"
port = 4433
num_limit = None
run_exclude = set()
no_ins_renego = False
private_key = None
cert = None
early_abort = False
dhe = False

argv = sys.argv[1:]
opts, args = getopt.getopt(argv, "h:p:e:n:k:c:d", ["help", "no-ins-renego",
"early-abort"])
for opt, arg in opts:
if opt == '-h':
host = arg
elif opt == '-p':
port = int(arg)
elif opt == '-e':
run_exclude.add(arg)
elif opt == '-d':
dhe = True
elif opt == '-n':
num_limit = int(arg)
elif opt == '--no-ins-renego':
no_ins_renego = True
elif opt == '--help':
help_msg()
sys.exit(0)
elif opt == '--early-abort':
early_abort = True
elif opt == '-k':
text_key = open(arg, 'rb').read()
if sys.version_info[0] >= 3:
text_key = str(text_key, 'utf-8')
private_key = parsePEMKey(text_key, private=True)
elif opt == '-c':
text_cert = open(arg, 'rb').read()
if sys.version_info[0] >= 3:
text_cert = str(text_cert, 'utf-8')
cert = X509()
cert.parse(text_cert)
else:
raise ValueError("Unknown option: {0}".format(opt))

if not private_key:
raise ValueError("Specify private key file using -k")
if not cert:
raise ValueError("Specify certificate file using -c")

if args:
run_only = set(args)
else:
run_only = None

conversations = {}

conversation = Connect(host, port)
node = conversation
sig_algs = [SignatureScheme.rsa_pss_rsae_sha256,
SignatureScheme.rsa_pss_pss_sha256,
SignatureScheme.rsa_pkcs1_sha256,
SignatureScheme.ecdsa_secp256r1_sha256]
if dhe:
ext = {}
groups = [GroupName.secp256r1,
GroupName.ffdhe2048]
ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
.create(groups)
ciphers = [CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
else:
ext = {}
ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
.create(sig_algs)
ext[ExtensionType.signature_algorithms_cert] = \
SignatureAlgorithmsCertExtension().create(sig_algs)
node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
node = node.add_child(ExpectServerHello())
node = node.add_child(ExpectCertificate())
if dhe:
node = node.add_child(ExpectServerKeyExchange())
node = node.add_child(ExpectServerHelloDone())
node = node.add_child(ClientKeyExchangeGenerator())
node = node.add_child(ChangeCipherSpecGenerator())
node = node.add_child(FinishedGenerator())
node = node.add_child(ExpectChangeCipherSpec())
node = node.add_child(ExpectFinished())
node = node.add_child(ApplicationDataGenerator(
bytearray(b"GET /?Renegotiation_Test=tlsfuzzer HTTP/1.0\r\n\r\n")))
node = node.add_child(ExpectApplicationData())
node = node.add_child(AlertGenerator(AlertLevel.warning,
AlertDescription.close_notify))
node = node.add_child(ExpectAlert())
node.next_sibling = ExpectClose()
conversations["sanity"] = conversation

# renegotiation
conversation = Connect(host, port)
node = conversation

sig_algs = [SignatureScheme.rsa_pss_rsae_sha256,
SignatureScheme.rsa_pss_pss_sha256,
SignatureScheme.rsa_pkcs1_sha256,
SignatureScheme.ecdsa_secp256r1_sha256]
if dhe:
ext = {ExtensionType.renegotiation_info:None}
groups = [GroupName.secp256r1,
GroupName.ffdhe2048]
ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
.create(groups)
ciphers = [CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA]
else:
ext = {ExtensionType.renegotiation_info:None}
ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
.create(sig_algs)
ext[ExtensionType.signature_algorithms_cert] = \
SignatureAlgorithmsCertExtension().create(sig_algs)
node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
ext = {ExtensionType.renegotiation_info: None}
node = node.add_child(ExpectServerHello(extensions=ext))
node = node.add_child(ExpectCertificate())
if dhe:
node = node.add_child(ExpectServerKeyExchange())
node = node.add_child(ExpectServerHelloDone())
node = node.add_child(ClientKeyExchangeGenerator())
node = node.add_child(ChangeCipherSpecGenerator())
node = node.add_child(FinishedGenerator())
node = node.add_child(ExpectChangeCipherSpec())
node = node.add_child(ExpectFinished())
# send GET request
node = node.add_child(ApplicationDataGenerator(
bytearray(b"GET /secure/test HTTP/1.0\r\n\r\n")))
# 2nd handshake
node = node.add_child(ExpectHelloRequest())
node = node.add_child(ResetHandshakeHashes())
ext = {ExtensionType.renegotiation_info:None}
ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
.create(sig_algs)
ext[ExtensionType.signature_algorithms_cert] = \
SignatureAlgorithmsCertExtension().create(sig_algs)
node = node.add_child(ClientHelloGenerator(ciphers,
session_id=bytearray(0),
extensions=ext))
ext = {ExtensionType.renegotiation_info: None}
node = node.add_child(ExpectServerHello(extensions=ext))
node = node.add_child(ExpectCertificate())
if dhe:
node = node.add_child(ExpectServerKeyExchange())
node = node.add_child(ExpectCertificateRequest())
node = node.add_child(ExpectServerHelloDone())
node = node.add_child(CertificateGenerator(X509CertChain([cert])))
node = node.add_child(ClientKeyExchangeGenerator())
node = node.add_child(CertificateVerifyGenerator(private_key))
node = node.add_child(ChangeCipherSpecGenerator())
node = node.add_child(FinishedGenerator())
node = node.add_child(ExpectChangeCipherSpec())
node = node.add_child(ExpectFinished())
node = node.add_child(ExpectApplicationData())
conversations["try secure renegotiation"] = conversation

# insecure renegotiation
conversation = Connect(host, port)
node = conversation
sig_algs = [SignatureScheme.rsa_pss_rsae_sha256,
SignatureScheme.rsa_pss_pss_sha256,
SignatureScheme.rsa_pkcs1_sha256,
SignatureScheme.ecdsa_secp256r1_sha256]
if dhe:
ext = {ExtensionType.renegotiation_info:None}
groups = [GroupName.secp256r1,
GroupName.ffdhe2048]
ext[ExtensionType.supported_groups] = SupportedGroupsExtension()\
.create(groups)
ciphers = [CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA]
else:
ext = {ExtensionType.renegotiation_info:None}
ciphers = [CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA]
ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
.create(sig_algs)
ext[ExtensionType.signature_algorithms_cert] = \
SignatureAlgorithmsCertExtension().create(sig_algs)
node = node.add_child(ClientHelloGenerator(ciphers, extensions=ext))
node = node.add_child(ExpectServerHello())
node = node.add_child(ExpectCertificate())
if dhe:
node = node.add_child(ExpectServerKeyExchange())
node = node.add_child(ExpectServerHelloDone())
node = node.add_child(ClientKeyExchangeGenerator())
node = node.add_child(ChangeCipherSpecGenerator())
node = node.add_child(FinishedGenerator())
node = node.add_child(ExpectChangeCipherSpec())
node = node.add_child(ExpectFinished())
# send GET request
node = node.add_child(ApplicationDataGenerator(
bytearray(b"GET /secure/test HTTP/1.0\r\n\r\n")))
if early_abort:
node = node.add_child(ExpectAlert(AlertLevel.fatal,
AlertDescription.handshake_failure))
node = node.add_child(ExpectClose())
else:
node = node.add_child(ExpectHelloRequest())
# 2nd handshake
node = node.add_child(ResetHandshakeHashes())
ext = {}
ext[ExtensionType.signature_algorithms] = SignatureAlgorithmsExtension()\
.create(sig_algs)
ext[ExtensionType.signature_algorithms_cert] = \
SignatureAlgorithmsCertExtension().create(sig_algs)
node = node.add_child(ClientHelloGenerator(ciphers,
session_id=bytearray(0),
extensions=ext))
if no_ins_renego:
node = node.add_child(ExpectAlert(AlertLevel.fatal,
AlertDescription.handshake_failure))
node = node.add_child(ExpectClose())
else:
node = node.add_child(ExpectServerHello())
node = node.add_child(ExpectCertificate())
if dhe:
node = node.add_child(ExpectServerKeyExchange())
node = node.add_child(ExpectCertificateRequest())
node = node.add_child(ExpectServerHelloDone())
node = node.add_child(CertificateGenerator(X509CertChain([cert])))
node = node.add_child(ClientKeyExchangeGenerator())
node = node.add_child(CertificateVerifyGenerator(private_key))
node = node.add_child(ChangeCipherSpecGenerator())
node = node.add_child(FinishedGenerator())
node = node.add_child(ExpectChangeCipherSpec())
node = node.add_child(ExpectFinished())
node = node.add_child(ExpectApplicationData())
conversations["try insecure (legacy) renegotiation"] = conversation

# run the conversation
good = 0
bad = 0
failed = []
if not num_limit:
num_limit = len(conversations)

# make sure that sanity test is run first and last
# to verify that server was running and kept running throughout
sanity_tests = [('sanity', conversations['sanity'])]
regular_tests = [(k, v) for k, v in conversations.items() if k != 'sanity']
sampled_tests = sample(regular_tests, min(num_limit, len(regular_tests)))
ordered_tests = chain(sanity_tests, sampled_tests, sanity_tests)

for c_name, c_test in ordered_tests:
if run_only and c_name not in run_only or c_name in run_exclude:
continue
print("{0} ...".format(c_name))

runner = Runner(c_test)

res = True
try:
runner.run()
except Exception:
print("Error while processing")
print(traceback.format_exc())
res = False

if res:
good += 1
print("OK\n")
else:
bad += 1
failed.append(c_name)

print("Verify that the server disabled renegotiation (both legacy")
print("and secure). Use client certificates for the test.\n")
print("Test expects the server to ask for renegotiation after the client")
print("asks for \"/secure/test\" resource\n")
print("version: {0}\n".format(version))

print("Test end")
print("successful: {0}".format(good))
print("failed: {0}".format(bad))
failed_sorted = sorted(failed, key=natural_sort_keys)
print(" {0}".format('\n '.join(repr(i) for i in failed_sorted)))

if bad > 0:
sys.exit(1)

if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions scripts/test-tls13-finished.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ def main():
# waiting for client Finished) but will abort reading of the
# Finished after one record
no_message = node.add_child(ExpectNoMessage(0.001))
nst = ExpectNewSessionTicket(note='first')
nst = ExpectNewSessionTicket(description='first')
no_message.next_sibling = nst
nst.add_child(no_message)
node = no_message
Expand All @@ -405,7 +405,7 @@ def main():
node.next_sibling = close_node

# This message may be sent right after server finished
cycle = ExpectNewSessionTicket(note='second')
cycle = ExpectNewSessionTicket(description='second')
node = node.add_child(cycle)
node.add_child(cycle)

Expand Down

0 comments on commit 74c65b0

Please sign in to comment.