Skip to content

Commit

Permalink
Merge a3736ee into 322f5ff
Browse files Browse the repository at this point in the history
  • Loading branch information
NoName115 committed Nov 19, 2019
2 parents 322f5ff + a3736ee commit 1d4e885
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 12 deletions.
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,21 @@ endif
pylint --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" tlslite > pylint_report.txt || :
diff-quality --violations=pylint --fail-under=90 pylint_report.txt
ifdef COVERAGE2
coverage2 combine --append
coverage2 combine --append .coverage .coverage.2.server .coverage.2.client
coverage2 report -m
coverage2 xml
diff-cover --fail-under=90 coverage.xml
endif
ifdef COVERAGE3
coverage2 combine --append .coverage .coverage.3.server .coverage.3.client
coverage3 report -m
coverage3 xml
diff-cover --fail-under=90 coverage.xml
endif
ifndef COVERAGE2
ifndef COVERAGE3
ifdef COVERAGE
coverage combine --append
coverage combine --append .coverage .coverage.server .coverage.client
coverage report -m
coverage xml
diff-cover --fail-under=90 coverage.xml
Expand Down
16 changes: 14 additions & 2 deletions scripts/tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,10 +530,22 @@ def serverCmd(argv):
settings.maxVersion = max_ver
settings.virtual_hosts = virtual_hosts

class MySimpleHTTPHandler(SimpleHTTPRequestHandler):
class MySimpleHTTPHandler(SimpleHTTPRequestHandler, object):
"""Buffer the header and body of HTTP message."""
wbufsize = -1

def do_GET(self):
"""Simple override to send KeyUpdate to client."""
if self.path.startswith('/keyupdate'):
for i in self.connection.send_keyupdate_request(
eyUpdateMessageType.update_requested):
if i in (0, 1):
continue
else:
raise ValueError("Invalid return from "
"send_keyupdate_request")
return super(MySimpleHTTPHandler, self).do_GET()

class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, HTTPServer):
def handshake(self, connection):
print("About to handshake...")
Expand Down Expand Up @@ -588,7 +600,7 @@ def handshake(self, connection):
return False
else:
raise

connection.ignoreAbruptClose = True
printGoodConnection(connection, stop-start)
printExporter(connection, expLabel, expLength)
Expand Down
108 changes: 103 additions & 5 deletions tests/tlstest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from xmlrpc import client as xmlrpclib
import ssl
from tlslite import *
from tlslite.constants import KeyUpdateMessageType

try:
from tack.structures.Tack import Tack
Expand Down Expand Up @@ -77,10 +78,18 @@ def testConnClient(conn):
conn.write(b10)
conn.write(b100)
conn.write(b1000)
assert(conn.read(min=1, max=1) == b1)
assert(conn.read(min=10, max=10) == b10)
assert(conn.read(min=100, max=100) == b100)
assert(conn.read(min=1000, max=1000) == b1000)
r1 = conn.read(min=1, max=1)
assert len(r1) == 1
assert r1 == b1
r10 = conn.read(min=10, max=10)
assert len(r10) == 10
assert r10 == b10
r100 = conn.read(min=100, max=100)
assert len(r100) == 100
assert r100 == b100
r1000 = conn.read(min=1000, max=1000)
assert len(r1000) == 1000
assert r1000 == b1000

def clientTestCmd(argv):

Expand Down Expand Up @@ -1203,6 +1212,50 @@ def heartbeat_response_check(message):

test_no += 1

print("Test {0} - KeyUpdate from client in TLSv1.3".format(test_no))
assert synchro.recv(1) == b'R'
connection = connect()
settings = HandshakeSettings()
settings.maxVersion = (3, 4)
connection.handshakeClientCert(serverName=address[0], settings=settings)
assert synchro.recv(1) == b'K'
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
assert i in (0, 1)
assert synchro.recv(1) == b'K'
testConnClient(connection)
connection.close()

test_no += 1

print("Test {0} - mutual KeyUpdates in TLSv1.3".format(test_no))
assert synchro.recv(1) == b'R'
connection = connect()
settings = HandshakeSettings()
settings.maxVersion = (3, 4)
connection.handshakeClientCert(serverName=address[0], settings=settings)
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
assert i in (0, 1)
testConnClient(connection)
connection.close()

test_no += 1

print("Test {0} - multiple mutual KeyUpdates in TLSv1.3".format(test_no))
assert synchro.recv(1) == b'R'
connection = connect()
settings = HandshakeSettings()
settings.maxVersion = (3, 4)
connection.handshakeClientCert(serverName=address[0], settings=settings)
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
assert i in (0, 1)
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
assert i in (0, 1)
testConnClient(connection)
synchro.send(b'R')
connection.close()

test_no += 1

print('Test {0} - good standard XMLRPC https client'.format(test_no))
address = address[0], address[1]+1
synchro.recv(1)
Expand Down Expand Up @@ -2274,8 +2327,52 @@ def heartbeat_response_check(message):

test_no += 1

print("Test {0} - KeyUpdate from client in TLSv1.3".format(test_no))
synchro.send(b'R')
connection = connect()
settings = HandshakeSettings()
settings.maxVersion = (3, 4)
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key,
settings=settings)
synchro.send(b'K')
synchro.send(b'K')
testConnServer(connection)
connection.close()

test_no += 1

print("Test {0} - mutual KeyUpdates in TLSv1.3".format(test_no))
synchro.send(b'R')
connection = connect()
settings = HandshakeSettings()
settings.maxVersion = (3, 4)
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key,
settings=settings)
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
assert i in (0, 1)
testConnServer(connection)
connection.close()

test_no += 1

print("Test {0} - multiple mutual KeyUpdates in TLSv1.3".format(test_no))
synchro.send(b'R')
connection = connect()
settings = HandshakeSettings()
settings.maxVersion = (3, 4)
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key,
settings=settings)
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
assert i in (0, 1)
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
assert i in (0, 1)
testConnServer(connection)
assert synchro.recv(1) == b'R'
connection.close()

test_no += 1

print("Tests {0}-{1} - XMLRPXC server".format(test_no, test_no + 2))
test_no += 2

address = address[0], address[1]+1
class Server(TLSXMLRPCServer):
Expand Down Expand Up @@ -2306,6 +2403,7 @@ def add(self, x, y): return x + y

synchro.close()
synchroSocket.close()
test_no += 2

print("Test succeeded")

Expand Down
8 changes: 8 additions & 0 deletions tlslite/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class HandshakeType(TLSEnum):
client_key_exchange = 16
finished = 20
certificate_status = 22
key_update = 24 # TLS 1.3
next_protocol = 67
message_hash = 254 # TLS 1.3

Expand Down Expand Up @@ -409,6 +410,13 @@ class HeartbeatMessageType(TLSEnum):
heartbeat_response = 2


class KeyUpdateMessageType(TLSEnum):
"""Types of keyupdate messages from RFC 8446"""

update_not_requested = 0
update_requested = 1


class AlertLevel(TLSEnum):
"""Enumeration of TLS Alert protocol levels"""

Expand Down
32 changes: 32 additions & 0 deletions tlslite/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2329,3 +2329,35 @@ def _message_type(self):
def __str__(self):
"""Return human readable representation of heartbeat message."""
return "heartbeat {0}".format(self._message_type)


class KeyUpdate(HandshakeMsg):
"""
Handling KeyUpdate message from RFC 8446
@type message_type: int
@ivar message_type: type of message (update_not_requested or
update_requested)
"""

def __init__(self):
super(KeyUpdate, self).__init__(HandshakeType.key_update)
self.message_type = 0

def create(self, message_type):
"""Create KeyUpdate message with selected parameter."""
self.message_type = message_type
return self

def parse(self, p):
"""Deserialize keyupdate message from parser."""
p.startLengthCheck(3)
self.message_type = p.get(1)
p.stopLengthCheck()
return self

def write(self):
"""Serialise keyupdate message."""
writer = Writer()
writer.add(self.message_type, 1)
return self.postWrite(writer)
52 changes: 52 additions & 0 deletions tlslite/recordlayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1316,3 +1316,55 @@ def calcTLS1_3PendingState(self, cipherSuite, cl_traffic_secret,
else:
self._pendingWriteState = serverPendingState
self._pendingReadState = clientPendingState

def _calcTLS1_3KeyUpdate(self, cipherSuite, app_secret):
prf_name, prf_length = ('sha384', 48) if cipherSuite \
in CipherSuite.sha384PrfSuites \
else ('sha256', 32)
key_length, iv_length, cipher_func = \
self._getCipherSettings(cipherSuite)
iv_length = 12

new_app_secret = HKDF_expand_label(app_secret,
b"traffic upd", b"",
prf_length,
prf_name)
new_state = ConnectionState()
new_state.macContext = None
new_state.encContext = \
cipher_func(HKDF_expand_label(new_app_secret,
b"key", b"",
key_length,
prf_name),
None)
new_state.fixedNonce = HKDF_expand_label(new_app_secret,
b"iv", b"",
iv_length,
prf_name)
return new_app_secret, new_state

def calcTLS1_3KeyUpdate_sender(self, cipherSuite, cl_app_secret,
sr_app_secret):
if self.client:
new_sr_app_secret, server_state = self._calcTLS1_3KeyUpdate(
cipherSuite, sr_app_secret)
self._readState = server_state
return cl_app_secret, new_sr_app_secret
else:
new_cl_app_secret, client_state = self._calcTLS1_3KeyUpdate(
cipherSuite, cl_app_secret)
self._readState = client_state
return new_cl_app_secret, sr_app_secret

def calcTLS1_3KeyUpdate_reciever(self, cipherSuite, cl_app_secret,
sr_app_secret):
if self.client:
new_cl_app_secret, client_state = self._calcTLS1_3KeyUpdate(
cipherSuite, cl_app_secret)
self._writeState = client_state
return new_cl_app_secret, sr_app_secret
else:
new_sr_app_secret, server_state = self._calcTLS1_3KeyUpdate(
cipherSuite, sr_app_secret)
self._writeState = server_state
return cl_app_secret, new_sr_app_secret
56 changes: 55 additions & 1 deletion tlslite/tlsrecordlayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ def readAsync(self, max=None, min=1):
if self.version > (3, 3):
allowedTypes = (ContentType.application_data,
ContentType.handshake)
allowedHsTypes = HandshakeType.new_session_ticket
allowedHsTypes = (HandshakeType.new_session_ticket,
HandshakeType.key_update)
else:
allowedTypes = ContentType.application_data
allowedHsTypes = None
Expand All @@ -315,6 +316,10 @@ def readAsync(self, max=None, min=1):
result.time = time.time()
self.tickets.append(result)
continue
if isinstance(result, KeyUpdate):
for result in self._handle_keyupdate_request(result):
yield result
continue
applicationData = result
self._readBuffer += applicationData.write()
except TLSRemoteAlert as alert:
Expand Down Expand Up @@ -947,6 +952,8 @@ def _getMsg(self, expectedType, secondaryType=None, constructorType=None):
yield EncryptedExtensions().parse(p)
elif subType == HandshakeType.new_session_ticket:
yield NewSessionTicket().parse(p)
elif subType == HandshakeType.key_update:
yield KeyUpdate().parse(p)
else:
raise AssertionError()

Expand Down Expand Up @@ -1128,3 +1135,50 @@ def send_heartbeat_request(self, payload, padding_length):
"""
for _ in self.write_heartbeat(payload, padding_length):
pass

def _handle_keyupdate_request(self, request):
"""Process the KeyUpdate request.
@type request: KeyUpdate
@param request: Recieved KeyUpdate message.
"""
if request.message_type == KeyUpdateMessageType.update_not_requested or\
request.message_type == KeyUpdateMessageType.update_requested:
self.session.cl_app_secret, self.session.sr_app_secret = self._recordLayer.\
calcTLS1_3KeyUpdate_sender(
self.session.cipherSuite,
self.session.cl_app_secret,
self.session.sr_app_secret)
if request.message_type == KeyUpdateMessageType.update_requested:
for result in self.send_keyupdate_request(
KeyUpdateMessageType.update_not_requested):
yield result
else:
for result in self._sendError(
AlertDescription.illegal_parameter,
"Received KeyUpdate request with unknown message_type"):
yield result

def send_keyupdate_request(self, message_type):
"""Send a KeyUpdate message.
@type payload: int
@param payload: Type of KeyUpdate message.
@raise socket.error: If a socket error occurs.
"""
if self.closed:
raise TLSClosedConnectionError(
"attempt to write to closed connection")
if self.version != (3, 4):
raise TLSIllegalParameterException("KeyUpdate is a TLS 1.3 specific"
" feature")

keyupdate_request = KeyUpdate().create(message_type)
for result in self._sendMsg(keyupdate_request):
yield result
self.session.cl_app_secret, self.session.sr_app_secret = \
self._recordLayer.calcTLS1_3KeyUpdate_reciever(
self.session.cipherSuite,
self.session.cl_app_secret,
self.session.sr_app_secret)
Loading

0 comments on commit 1d4e885

Please sign in to comment.