Skip to content

Commit 9912a51

Browse files
authored
Merge pull request #398 from inikolcev/clean_key_mat_extractors
Move keying material extractors to one place
2 parents c32629e + 3e94cef commit 9912a51

File tree

8 files changed

+255
-134
lines changed

8 files changed

+255
-134
lines changed

tlslite/keyexchange.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import ecdsa
88
from .mathtls import goodGroupParameters, makeK, makeU, makeX, \
9-
calcMasterSecret, paramStrength, RFC7919_GROUPS
9+
paramStrength, RFC7919_GROUPS, calc_key
1010
from .errors import TLSInsufficientSecurity, TLSUnknownPSKIdentity, \
1111
TLSIllegalParameterException, TLSDecryptionFailed, TLSInternalError, \
1212
TLSDecodeError
@@ -260,11 +260,11 @@ def calcVerifyBytes(version, handshakeHashes, signatureAlg,
260260
prf_name = None, peer_tag=b'client', key_type="rsa"):
261261
"""Calculate signed bytes for Certificate Verify"""
262262
if version == (3, 0):
263-
masterSecret = calcMasterSecret(version,
264-
0,
265-
premasterSecret,
266-
clientRandom,
267-
serverRandom)
263+
masterSecret = calc_key(version, premasterSecret,
264+
0, b"master secret",
265+
client_random=clientRandom,
266+
server_random=serverRandom,
267+
output_length=48)
268268
verifyBytes = handshakeHashes.digestSSL(masterSecret, b"")
269269
elif version in ((3, 1), (3, 2)):
270270
if key_type != "ecdsa":

tlslite/mathtls.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .constants import CipherSuite
1414
from .utils import tlshashlib as hashlib
1515
from .utils import tlshmac as hmac
16+
from .utils.deprecations import deprecated_method
1617

1718
# 1024, 1536, 2048, 3072, 4096, 6144, and 8192 bit groups
1819
# Formatted to match lines in RFC
@@ -509,6 +510,7 @@ def PRF_SSL(secret, seed, length):
509510
index += 1
510511
return bytes
511512

513+
@deprecated_method("Please use calcKey method instead.")
512514
def calcExtendedMasterSecret(version, cipherSuite, premasterSecret,
513515
handshakeHashes):
514516
"""Derive Extended Master Secret from premaster and handshake msgs"""
@@ -532,6 +534,7 @@ def calcExtendedMasterSecret(version, cipherSuite, premasterSecret,
532534
return masterSecret
533535

534536

537+
@deprecated_method("Please use calcKey method instead.")
535538
def calcMasterSecret(version, cipherSuite, premasterSecret, clientRandom,
536539
serverRandom):
537540
"""Derive Master Secret from premaster secret and random values"""
@@ -556,6 +559,7 @@ def calcMasterSecret(version, cipherSuite, premasterSecret, clientRandom,
556559
raise AssertionError()
557560
return masterSecret
558561

562+
@deprecated_method("Please use calcKey method instead.")
559563
def calcFinished(version, masterSecret, cipherSuite, handshakeHashes,
560564
isClient):
561565
"""Calculate the Handshake protocol Finished value
@@ -595,6 +599,89 @@ def calcFinished(version, masterSecret, cipherSuite, handshakeHashes,
595599

596600
return verifyData
597601

602+
def calc_key(version, secret, cipher_suite, label, handshake_hashes=None,
603+
client_random=None, server_random=None, output_length=None):
604+
"""
605+
Method for calculating different keys depending on input.
606+
It can be used to calculate finished value, master secret,
607+
extended master secret or key expansion.
608+
609+
:param version: TLS protocol version
610+
:type version: tuple(int, int)
611+
:param bytearray secret: master secret or premasterSecret which will be
612+
used in the PRF.
613+
:param int cipher_suite: Negotiated cipher suite of the connection.
614+
:param bytes label: label for the key you want to calculate
615+
(ex. 'master secret', 'extended master secret', etc).
616+
:param handshake_hashes: running hash of the handshake messages
617+
needed for calculating extended master secret or finished value.
618+
:type handshake_hashes: ~tlslite.handshakehashes.HandshakeHashes
619+
:param bytearray client_random: client random needed for calculating
620+
master secret or key expansion.
621+
:param bytearray server_random: server random needed for calculating
622+
master secret or key expansion.
623+
:param int output_length: Number of bytes to output.
624+
"""
625+
626+
627+
# SSL3 calculations.
628+
if version == (3, 0):
629+
# Calculating Finished value, either for message sent
630+
# by server or by client
631+
if label == b"client finished":
632+
senderStr = b"\x43\x4C\x4E\x54"
633+
return handshake_hashes.digestSSL(secret, senderStr)
634+
elif label == b"server finished":
635+
senderStr = b"\x53\x52\x56\x52"
636+
return handshake_hashes.digestSSL(secret, senderStr)
637+
else:
638+
assert label in [b"key expansion", b"master secret"]
639+
func = PRF_SSL
640+
641+
# TLS1.0 or TLS1.1 calculations.
642+
elif version in ((3, 1), (3, 2)):
643+
func = PRF
644+
# Seed needed for calculating extended master secret
645+
if label == b"extended master secret":
646+
seed = handshake_hashes.digest('md5') + \
647+
handshake_hashes.digest('sha1')
648+
# Seed needed for calculating Finished value
649+
elif label in [b"server finished", b"client finished"]:
650+
seed = handshake_hashes.digest()
651+
else:
652+
assert label in [b"key expansion", b"master secret"]
653+
654+
# TLS1.2 calculations.
655+
else:
656+
assert version == (3, 3)
657+
if cipher_suite in CipherSuite.sha384PrfSuites:
658+
func = PRF_1_2_SHA384
659+
# Seed needed for calculating Finished value or extended master
660+
# secret
661+
if label in [b"extended master secret", b"server finished",
662+
b"client finished"]:
663+
seed = handshake_hashes.digest('sha384')
664+
else:
665+
assert label in [b"key expansion", b"master secret"]
666+
else:
667+
# Same as above, just using sha256
668+
func = PRF_1_2
669+
if label in [b"extended master secret", b"server finished",
670+
b"client finished"]:
671+
seed = handshake_hashes.digest('sha256')
672+
else:
673+
assert label in [b"key expansion", b"master secret"]
674+
675+
# Seed needed for calculating key expansion or master secret
676+
if label == b"key expansion":
677+
seed = server_random + client_random
678+
if label == b"master secret":
679+
seed = client_random + server_random
680+
681+
if func == PRF_SSL:
682+
return func(secret, seed, output_length)
683+
return func(secret, label, seed, output_length)
684+
598685
def makeX(salt, username, password):
599686
if len(username)>=256:
600687
raise ValueError("username too long")

tlslite/recordlayer.py

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
from .errors import TLSRecordOverflow, TLSIllegalParameterException,\
3131
TLSAbruptCloseError, TLSDecryptionFailed, TLSBadRecordMAC, \
3232
TLSUnexpectedMessage
33-
from .mathtls import createMAC_SSL, createHMAC, PRF_SSL, PRF, PRF_1_2, \
34-
PRF_1_2_SHA384
33+
from .mathtls import createMAC_SSL, createHMAC, calc_key
3534

3635
class RecordSocket(object):
3736
"""
@@ -1097,34 +1096,6 @@ def _getHMACMethod(version):
10971096

10981097
return createMACFunc
10991098

1100-
def _calcKeyBlock(self, cipherSuite, masterSecret, clientRandom,
1101-
serverRandom, outputLength):
1102-
"""Calculate the overall key to slice up"""
1103-
if self.version == (3, 0):
1104-
keyBlock = PRF_SSL(masterSecret,
1105-
serverRandom + clientRandom,
1106-
outputLength)
1107-
elif self.version in ((3, 1), (3, 2)):
1108-
keyBlock = PRF(masterSecret,
1109-
b"key expansion",
1110-
serverRandom + clientRandom,
1111-
outputLength)
1112-
elif self.version == (3, 3):
1113-
if cipherSuite in CipherSuite.sha384PrfSuites:
1114-
keyBlock = PRF_1_2_SHA384(masterSecret,
1115-
b"key expansion",
1116-
serverRandom + clientRandom,
1117-
outputLength)
1118-
else:
1119-
keyBlock = PRF_1_2(masterSecret,
1120-
b"key expansion",
1121-
serverRandom + clientRandom,
1122-
outputLength)
1123-
else:
1124-
raise AssertionError()
1125-
1126-
return keyBlock
1127-
11281099
def calcSSL2PendingStates(self, cipherSuite, masterSecret, clientRandom,
11291100
serverRandom, implementations):
11301101
"""
@@ -1215,8 +1186,10 @@ def calcPendingStates(self, cipherSuite, masterSecret, clientRandom,
12151186
outputLength = (macLength*2) + (keyLength*2) + (ivLength*2)
12161187

12171188
#Calculate Keying Material from Master Secret
1218-
keyBlock = self._calcKeyBlock(cipherSuite, masterSecret, clientRandom,
1219-
serverRandom, outputLength)
1189+
keyBlock = calc_key(self.version, masterSecret, cipherSuite,
1190+
b"key expansion", client_random=clientRandom,
1191+
server_random=serverRandom,
1192+
output_length=outputLength)
12201193

12211194
#Slice up Keying Material
12221195
clientPendingState = ConnectionState()

tlslite/tlsconnection.py

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,16 +1788,16 @@ def _clientFinished(self, premasterSecret, clientRandom, serverRandom,
17881788
# use the certificate authentication, the hashes are the same
17891789
if not cvhh:
17901790
cvhh = self._handshake_hash
1791-
masterSecret = calcExtendedMasterSecret(self.version,
1792-
cipherSuite,
1793-
premasterSecret,
1794-
cvhh)
1791+
masterSecret = calc_key(self.version, premasterSecret,
1792+
cipherSuite, b"extended master secret",
1793+
handshake_hashes=cvhh,
1794+
output_length=48)
17951795
else:
1796-
masterSecret = calcMasterSecret(self.version,
1797-
cipherSuite,
1798-
premasterSecret,
1799-
clientRandom,
1800-
serverRandom)
1796+
masterSecret = calc_key(self.version, premasterSecret,
1797+
cipherSuite, b"master secret",
1798+
client_random=clientRandom,
1799+
server_random=serverRandom,
1800+
output_length=48)
18011801
self._calcPendingStates(cipherSuite, masterSecret,
18021802
clientRandom, serverRandom,
18031803
cipherImplementations)
@@ -4069,16 +4069,16 @@ def _serverFinished(self, premasterSecret, clientRandom, serverRandom,
40694069
# to regular handshake hash
40704070
if not cvhh:
40714071
cvhh = self._handshake_hash
4072-
masterSecret = calcExtendedMasterSecret(self.version,
4073-
cipherSuite,
4074-
premasterSecret,
4075-
cvhh)
4072+
masterSecret = calc_key(self.version, premasterSecret,
4073+
cipherSuite, b"extended master secret",
4074+
handshake_hashes=cvhh,
4075+
output_length=48)
40764076
else:
4077-
masterSecret = calcMasterSecret(self.version,
4078-
cipherSuite,
4079-
premasterSecret,
4080-
clientRandom,
4081-
serverRandom)
4077+
masterSecret = calc_key(self.version, premasterSecret,
4078+
cipherSuite, b"master secret",
4079+
client_random=clientRandom,
4080+
server_random=serverRandom,
4081+
output_length=48)
40824082

40834083
#Calculate pending connection states
40844084
self._calcPendingStates(cipherSuite, masterSecret,
@@ -4125,12 +4125,16 @@ def _sendFinished(self, masterSecret, cipherSuite=None, nextProto=None,
41254125
for result in self._sendMsg(nextProtoMsg):
41264126
yield result
41274127

4128+
#Figure out the correct label to use
4129+
if self._client:
4130+
label = b"client finished"
4131+
else:
4132+
label = b"server finished"
41284133
#Calculate verification data
4129-
verifyData = calcFinished(self.version,
4130-
masterSecret,
4131-
cipherSuite,
4132-
self._handshake_hash,
4133-
self._client)
4134+
verifyData = calc_key(self.version, masterSecret,
4135+
cipherSuite, label,
4136+
handshake_hashes=self._handshake_hash,
4137+
output_length=12)
41344138
if self.fault == Fault.badFinished:
41354139
verifyData[0] = (verifyData[0]+1)%256
41364140

@@ -4175,12 +4179,17 @@ def _getFinished(self, masterSecret, cipherSuite=None,
41754179
if nextProto:
41764180
self.next_proto = nextProto
41774181

4182+
#Figure out which label to use.
4183+
if self._client:
4184+
label = b"server finished"
4185+
else:
4186+
label = b"client finished"
4187+
41784188
#Calculate verification data
4179-
verifyData = calcFinished(self.version,
4180-
masterSecret,
4181-
cipherSuite,
4182-
self._handshake_hash,
4183-
not self._client)
4189+
verifyData = calc_key(self.version, masterSecret,
4190+
cipherSuite, label,
4191+
handshake_hashes=self._handshake_hash,
4192+
output_length=12)
41844193

41854194
#Get and check Finished message under new state
41864195
for result in self._getMsg(ContentType.handshake,

tlslite/utils/deprecations.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,17 @@ def wrapper(cls):
202202
orig_vars.pop('__weakref__', None)
203203
return metaclass(cls.__name__, cls.__bases__, orig_vars)
204204
return wrapper
205+
206+
def deprecated_method(message):
207+
"""Decorator for deprecating methods.
208+
209+
:param ste message: The message you want to display.
210+
"""
211+
def decorator(func):
212+
@wraps(func)
213+
def wrapper(*args, **kwargs):
214+
warnings.warn("{0} is a deprecated method. {1}".format(func.__name__, message),
215+
DeprecationWarning, stacklevel=2)
216+
return func(*args, **kwargs)
217+
return wrapper
218+
return decorator

0 commit comments

Comments
 (0)