Skip to content

Commit 970d33f

Browse files
authored
Merge pull request #341 from tomato42/key-update-support
Post handshake KeyUpdate support
2 parents 322f5ff + a65c36d commit 970d33f

File tree

8 files changed

+323
-12
lines changed

8 files changed

+323
-12
lines changed

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,20 +92,21 @@ endif
9292
pylint --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" tlslite > pylint_report.txt || :
9393
diff-quality --violations=pylint --fail-under=90 pylint_report.txt
9494
ifdef COVERAGE2
95-
coverage2 combine --append
95+
coverage2 combine --append .coverage .coverage.2.server .coverage.2.client
9696
coverage2 report -m
9797
coverage2 xml
9898
diff-cover --fail-under=90 coverage.xml
9999
endif
100100
ifdef COVERAGE3
101+
coverage2 combine --append .coverage .coverage.3.server .coverage.3.client
101102
coverage3 report -m
102103
coverage3 xml
103104
diff-cover --fail-under=90 coverage.xml
104105
endif
105106
ifndef COVERAGE2
106107
ifndef COVERAGE3
107108
ifdef COVERAGE
108-
coverage combine --append
109+
coverage combine --append .coverage .coverage.server .coverage.client
109110
coverage report -m
110111
coverage xml
111112
diff-cover --fail-under=90 coverage.xml

scripts/tls.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from tlslite.utils.compat import b2a_hex, a2b_hex, time_stamp
3838
from tlslite.utils.dns_utils import is_valid_hostname
3939
from tlslite.utils.cryptomath import getRandomBytes
40+
from tlslite.constants import KeyUpdateMessageType
4041

4142
try:
4243
from tack.structures.Tack import Tack
@@ -530,10 +531,22 @@ def serverCmd(argv):
530531
settings.maxVersion = max_ver
531532
settings.virtual_hosts = virtual_hosts
532533

533-
class MySimpleHTTPHandler(SimpleHTTPRequestHandler):
534+
class MySimpleHTTPHandler(SimpleHTTPRequestHandler, object):
534535
"""Buffer the header and body of HTTP message."""
535536
wbufsize = -1
536537

538+
def do_GET(self):
539+
"""Simple override to send KeyUpdate to client."""
540+
if self.path.startswith('/keyupdate'):
541+
for i in self.connection.send_keyupdate_request(
542+
KeyUpdateMessageType.update_requested):
543+
if i in (0, 1):
544+
continue
545+
else:
546+
raise ValueError("Invalid return from "
547+
"send_keyupdate_request")
548+
return super(MySimpleHTTPHandler, self).do_GET()
549+
537550
class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, HTTPServer):
538551
def handshake(self, connection):
539552
print("About to handshake...")
@@ -588,7 +601,7 @@ def handshake(self, connection):
588601
return False
589602
else:
590603
raise
591-
604+
592605
connection.ignoreAbruptClose = True
593606
printGoodConnection(connection, stop-start)
594607
printExporter(connection, expLabel, expLength)

tests/tlstest.py

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from xmlrpc import client as xmlrpclib
4545
import ssl
4646
from tlslite import *
47+
from tlslite.constants import KeyUpdateMessageType
4748

4849
try:
4950
from tack.structures.Tack import Tack
@@ -77,10 +78,18 @@ def testConnClient(conn):
7778
conn.write(b10)
7879
conn.write(b100)
7980
conn.write(b1000)
80-
assert(conn.read(min=1, max=1) == b1)
81-
assert(conn.read(min=10, max=10) == b10)
82-
assert(conn.read(min=100, max=100) == b100)
83-
assert(conn.read(min=1000, max=1000) == b1000)
81+
r1 = conn.read(min=1, max=1)
82+
assert len(r1) == 1
83+
assert r1 == b1
84+
r10 = conn.read(min=10, max=10)
85+
assert len(r10) == 10
86+
assert r10 == b10
87+
r100 = conn.read(min=100, max=100)
88+
assert len(r100) == 100
89+
assert r100 == b100
90+
r1000 = conn.read(min=1000, max=1000)
91+
assert len(r1000) == 1000
92+
assert r1000 == b1000
8493

8594
def clientTestCmd(argv):
8695

@@ -1203,6 +1212,51 @@ def heartbeat_response_check(message):
12031212

12041213
test_no += 1
12051214

1215+
print("Test {0} - KeyUpdate from client in TLSv1.3".format(test_no))
1216+
assert synchro.recv(1) == b'R'
1217+
connection = connect()
1218+
settings = HandshakeSettings()
1219+
settings.maxVersion = (3, 4)
1220+
connection.handshakeClientCert(serverName=address[0], settings=settings)
1221+
assert synchro.recv(1) == b'K'
1222+
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
1223+
assert i in (0, 1)
1224+
assert synchro.recv(1) == b'K'
1225+
testConnClient(connection)
1226+
connection.close()
1227+
1228+
test_no += 1
1229+
1230+
print("Test {0} - mutual KeyUpdates in TLSv1.3".format(test_no))
1231+
assert synchro.recv(1) == b'R'
1232+
connection = connect()
1233+
settings = HandshakeSettings()
1234+
settings.maxVersion = (3, 4)
1235+
connection.handshakeClientCert(serverName=address[0], settings=settings)
1236+
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
1237+
assert i in (0, 1)
1238+
testConnClient(connection)
1239+
synchro.send(b'R')
1240+
connection.close()
1241+
1242+
test_no += 1
1243+
1244+
print("Test {0} - multiple mutual KeyUpdates in TLSv1.3".format(test_no))
1245+
assert synchro.recv(1) == b'R'
1246+
connection = connect()
1247+
settings = HandshakeSettings()
1248+
settings.maxVersion = (3, 4)
1249+
connection.handshakeClientCert(serverName=address[0], settings=settings)
1250+
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
1251+
assert i in (0, 1)
1252+
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
1253+
assert i in (0, 1)
1254+
testConnClient(connection)
1255+
synchro.send(b'R')
1256+
connection.close()
1257+
1258+
test_no += 1
1259+
12061260
print('Test {0} - good standard XMLRPC https client'.format(test_no))
12071261
address = address[0], address[1]+1
12081262
synchro.recv(1)
@@ -2274,8 +2328,53 @@ def heartbeat_response_check(message):
22742328

22752329
test_no += 1
22762330

2331+
print("Test {0} - KeyUpdate from client in TLSv1.3".format(test_no))
2332+
synchro.send(b'R')
2333+
connection = connect()
2334+
settings = HandshakeSettings()
2335+
settings.maxVersion = (3, 4)
2336+
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key,
2337+
settings=settings)
2338+
synchro.send(b'K')
2339+
synchro.send(b'K')
2340+
testConnServer(connection)
2341+
connection.close()
2342+
2343+
test_no += 1
2344+
2345+
print("Test {0} - mutual KeyUpdates in TLSv1.3".format(test_no))
2346+
synchro.send(b'R')
2347+
connection = connect()
2348+
settings = HandshakeSettings()
2349+
settings.maxVersion = (3, 4)
2350+
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key,
2351+
settings=settings)
2352+
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
2353+
assert i in (0, 1)
2354+
testConnServer(connection)
2355+
assert synchro.recv(1) == b'R'
2356+
connection.close()
2357+
2358+
test_no += 1
2359+
2360+
print("Test {0} - multiple mutual KeyUpdates in TLSv1.3".format(test_no))
2361+
synchro.send(b'R')
2362+
connection = connect()
2363+
settings = HandshakeSettings()
2364+
settings.maxVersion = (3, 4)
2365+
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key,
2366+
settings=settings)
2367+
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
2368+
assert i in (0, 1)
2369+
for i in connection.send_keyupdate_request(KeyUpdateMessageType.update_requested):
2370+
assert i in (0, 1)
2371+
testConnServer(connection)
2372+
assert synchro.recv(1) == b'R'
2373+
connection.close()
2374+
2375+
test_no += 1
2376+
22772377
print("Tests {0}-{1} - XMLRPXC server".format(test_no, test_no + 2))
2278-
test_no += 2
22792378

22802379
address = address[0], address[1]+1
22812380
class Server(TLSXMLRPCServer):
@@ -2306,6 +2405,7 @@ def add(self, x, y): return x + y
23062405

23072406
synchro.close()
23082407
synchroSocket.close()
2408+
test_no += 2
23092409

23102410
print("Test succeeded")
23112411

tlslite/constants.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class HandshakeType(TLSEnum):
125125
client_key_exchange = 16
126126
finished = 20
127127
certificate_status = 22
128+
key_update = 24 # TLS 1.3
128129
next_protocol = 67
129130
message_hash = 254 # TLS 1.3
130131

@@ -409,6 +410,13 @@ class HeartbeatMessageType(TLSEnum):
409410
heartbeat_response = 2
410411

411412

413+
class KeyUpdateMessageType(TLSEnum):
414+
"""Types of keyupdate messages from RFC 8446"""
415+
416+
update_not_requested = 0
417+
update_requested = 1
418+
419+
412420
class AlertLevel(TLSEnum):
413421
"""Enumeration of TLS Alert protocol levels"""
414422

tlslite/messages.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,3 +2329,35 @@ def _message_type(self):
23292329
def __str__(self):
23302330
"""Return human readable representation of heartbeat message."""
23312331
return "heartbeat {0}".format(self._message_type)
2332+
2333+
2334+
class KeyUpdate(HandshakeMsg):
2335+
"""
2336+
Handling KeyUpdate message from RFC 8446
2337+
2338+
@type message_type: int
2339+
@ivar message_type: type of message (update_not_requested or
2340+
update_requested)
2341+
"""
2342+
2343+
def __init__(self):
2344+
super(KeyUpdate, self).__init__(HandshakeType.key_update)
2345+
self.message_type = 0
2346+
2347+
def create(self, message_type):
2348+
"""Create KeyUpdate message with selected parameter."""
2349+
self.message_type = message_type
2350+
return self
2351+
2352+
def parse(self, p):
2353+
"""Deserialize keyupdate message from parser."""
2354+
p.startLengthCheck(3)
2355+
self.message_type = p.get(1)
2356+
p.stopLengthCheck()
2357+
return self
2358+
2359+
def write(self):
2360+
"""Serialise keyupdate message."""
2361+
writer = Writer()
2362+
writer.add(self.message_type, 1)
2363+
return self.postWrite(writer)

tlslite/recordlayer.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,3 +1316,55 @@ def calcTLS1_3PendingState(self, cipherSuite, cl_traffic_secret,
13161316
else:
13171317
self._pendingWriteState = serverPendingState
13181318
self._pendingReadState = clientPendingState
1319+
1320+
def _calcTLS1_3KeyUpdate(self, cipherSuite, app_secret):
1321+
prf_name, prf_length = ('sha384', 48) if cipherSuite \
1322+
in CipherSuite.sha384PrfSuites \
1323+
else ('sha256', 32)
1324+
key_length, iv_length, cipher_func = \
1325+
self._getCipherSettings(cipherSuite)
1326+
iv_length = 12
1327+
1328+
new_app_secret = HKDF_expand_label(app_secret,
1329+
b"traffic upd", b"",
1330+
prf_length,
1331+
prf_name)
1332+
new_state = ConnectionState()
1333+
new_state.macContext = None
1334+
new_state.encContext = \
1335+
cipher_func(HKDF_expand_label(new_app_secret,
1336+
b"key", b"",
1337+
key_length,
1338+
prf_name),
1339+
None)
1340+
new_state.fixedNonce = HKDF_expand_label(new_app_secret,
1341+
b"iv", b"",
1342+
iv_length,
1343+
prf_name)
1344+
return new_app_secret, new_state
1345+
1346+
def calcTLS1_3KeyUpdate_sender(self, cipherSuite, cl_app_secret,
1347+
sr_app_secret):
1348+
if self.client:
1349+
new_sr_app_secret, server_state = self._calcTLS1_3KeyUpdate(
1350+
cipherSuite, sr_app_secret)
1351+
self._readState = server_state
1352+
return cl_app_secret, new_sr_app_secret
1353+
else:
1354+
new_cl_app_secret, client_state = self._calcTLS1_3KeyUpdate(
1355+
cipherSuite, cl_app_secret)
1356+
self._readState = client_state
1357+
return new_cl_app_secret, sr_app_secret
1358+
1359+
def calcTLS1_3KeyUpdate_reciever(self, cipherSuite, cl_app_secret,
1360+
sr_app_secret):
1361+
if self.client:
1362+
new_cl_app_secret, client_state = self._calcTLS1_3KeyUpdate(
1363+
cipherSuite, cl_app_secret)
1364+
self._writeState = client_state
1365+
return new_cl_app_secret, sr_app_secret
1366+
else:
1367+
new_sr_app_secret, server_state = self._calcTLS1_3KeyUpdate(
1368+
cipherSuite, sr_app_secret)
1369+
self._writeState = server_state
1370+
return cl_app_secret, new_sr_app_secret

0 commit comments

Comments
 (0)