Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PSK ciphers #504

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 103 additions & 6 deletions tlslite/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ class AlertDescription(TLSEnum):
unrecognized_name = 112 # RFC 6066
bad_certificate_status_response = 113 # RFC 6066
bad_certificate_hash_value = 114 # RFC 6066
unknown_psk_identity = 115
unknown_psk_identity = 115 # RFC 4279
certificate_required = 116 # RFC 8446
no_application_protocol = 120 # RFC 7301

Expand Down Expand Up @@ -698,6 +698,12 @@ class CipherSuite:
ietfNames[0x0018] = 'TLS_DH_ANON_WITH_RC4_128_MD5'
TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA = 0x001B
ietfNames[0x001B] = 'TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA'
TLS_PSK_WITH_NULL_SHA = 0x002C
ietfNames[0x002C] = 'TLS_PSK_WITH_NULL_SHA'
TLS_DHE_PSK_WITH_NULL_SHA = 0x002D
ietfNames[0x002D] = 'TLS_DHE_PSK_WITH_NULL_SHA'
TLS_RSA_PSK_WITH_NULL_SHA = 0x002E
ietfNames[0x002E] = 'TLS_RSA_PSK_WITH_NULL_SHA'
TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F
ietfNames[0x002F] = 'TLS_RSA_WITH_AES_128_CBC_SHA'
TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030
Expand Down Expand Up @@ -741,6 +747,20 @@ class CipherSuite:
TLS_DH_ANON_WITH_AES_256_CBC_SHA256 = 0x006D
ietfNames[0x006D] = 'TLS_DH_ANON_WITH_AES_256_CBC_SHA256'

# RFC4279 - Pre-Shared Key Ciphersuites for Transport Layer Security
TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C
ietfNames[0x008C] = 'TLS_PSK_WITH_AES_128_CBC_SHA'
TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D
ietfNames[0x008D] = 'TLS_PSK_WITH_AES_256_CBC_SHA'
TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090
ietfNames[0x0090] = 'TLS_DHE_PSK_WITH_AES_128_CBC_SHA'
TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091
ietfNames[0x0091] = 'TLS_DHE_PSK_WITH_AES_256_CBC_SHA'
TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094
ietfNames[0x0094] = 'TLS_RSA_PSK_WITH_AES_128_CBC_SHA'
TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095
ietfNames[0x0095] = 'TLS_RSA_PSK_WITH_AES_256_CBC_SHA'

# RFC 5288 - AES-GCM ciphers for TLSv1.2
TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C
ietfNames[0x009C] = 'TLS_RSA_WITH_AES_128_GCM_SHA256'
Expand Down Expand Up @@ -999,6 +1019,9 @@ class CipherSuite:
aes128Suites.append(TLS_DH_DSS_WITH_AES_128_CBC_SHA256) # unsupported
aes128Suites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) # unsupported
aes128Suites.append(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA) # unsupported
aes128Suites.append(TLS_PSK_WITH_AES_128_CBC_SHA)
aes128Suites.append(TLS_DHE_PSK_WITH_AES_128_CBC_SHA)
aes128Suites.append(TLS_RSA_PSK_WITH_AES_128_CBC_SHA)

#: AES-256 CBC ciphers
aes256Suites = []
Expand All @@ -1024,6 +1047,9 @@ class CipherSuite:
aes256Suites.append(TLS_DH_DSS_WITH_AES_256_CBC_SHA256) # unsupported
aes256Suites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) # unsupported
aes256Suites.append(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA) # unsupported
aes256Suites.append(TLS_PSK_WITH_AES_256_CBC_SHA)
aes256Suites.append(TLS_DHE_PSK_WITH_AES_256_CBC_SHA)
aes256Suites.append(TLS_RSA_PSK_WITH_AES_256_CBC_SHA)

#: AES-128 GCM ciphers
aes128GcmSuites = []
Expand Down Expand Up @@ -1107,6 +1133,9 @@ class CipherSuite:
nullSuites.append(TLS_RSA_WITH_NULL_MD5)
nullSuites.append(TLS_RSA_WITH_NULL_SHA)
nullSuites.append(TLS_RSA_WITH_NULL_SHA256)
nullSuites.append(TLS_PSK_WITH_NULL_SHA)
nullSuites.append(TLS_DHE_PSK_WITH_NULL_SHA)
nullSuites.append(TLS_RSA_PSK_WITH_NULL_SHA)
nullSuites.append(TLS_ECDHE_ECDSA_WITH_NULL_SHA)
nullSuites.append(TLS_ECDH_ECDSA_WITH_NULL_SHA) # unsupported
nullSuites.append(TLS_ECDH_RSA_WITH_NULL_SHA) # unsupported
Expand Down Expand Up @@ -1141,6 +1170,15 @@ class CipherSuite:
shaSuites.append(TLS_DH_DSS_WITH_AES_128_CBC_SHA) # unsupported
shaSuites.append(TLS_DH_DSS_WITH_AES_256_CBC_SHA) # unsupported
shaSuites.append(TLS_RSA_WITH_NULL_SHA)
shaSuites.append(TLS_PSK_WITH_NULL_SHA)
shaSuites.append(TLS_DHE_PSK_WITH_NULL_SHA)
shaSuites.append(TLS_RSA_PSK_WITH_NULL_SHA)
shaSuites.append(TLS_PSK_WITH_AES_128_CBC_SHA)
shaSuites.append(TLS_PSK_WITH_AES_256_CBC_SHA)
shaSuites.append(TLS_DHE_PSK_WITH_AES_128_CBC_SHA)
shaSuites.append(TLS_DHE_PSK_WITH_AES_256_CBC_SHA)
shaSuites.append(TLS_RSA_PSK_WITH_AES_128_CBC_SHA)
shaSuites.append(TLS_RSA_PSK_WITH_AES_256_CBC_SHA)
shaSuites.append(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
shaSuites.append(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
shaSuites.append(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA)
Expand Down Expand Up @@ -1372,6 +1410,12 @@ def _filterSuites(suites, settings, version=None):
keyExchangeSuites += CipherSuite.anonSuites
if "ecdh_anon" in keyExchangeNames:
keyExchangeSuites += CipherSuite.ecdhAnonSuites
if "psk" in keyExchangeNames:
keyExchangeSuites += CipherSuite.pskSuites
if "dhe_psk" in keyExchangeNames:
keyExchangeSuites += CipherSuite.dhePskSuites
if "rsa_psk" in keyExchangeNames:
keyExchangeSuites += CipherSuite.pskCertSuites

return [s for s in suites if s in macSuites and
s in cipherSuites and s in keyExchangeSuites]
Expand Down Expand Up @@ -1486,9 +1530,6 @@ def getEcdheCertSuites(cls, settings, version=None):
"""Provide authenticated ECDHE ciphersuites matching settings"""
return cls._filterSuites(CipherSuite.ecdheCertSuites, settings, version)

#: RSA authentication
certAllSuites = srpCertSuites + certSuites + dheCertSuites + ecdheCertSuites

#: ECDHE key exchange, ECDSA authentication
ecdheEcdsaSuites = []
ecdheEcdsaSuites.append(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
Expand Down Expand Up @@ -1545,8 +1586,6 @@ def getAnonSuites(cls, settings, version=None):
"""Provide anonymous DH ciphersuites matching settings"""
return cls._filterSuites(CipherSuite.anonSuites, settings, version)

dhAllSuites = dheCertSuites + anonSuites + dheDsaSuites

#: anon ECDHE key exchange
ecdhAnonSuites = []
ecdhAnonSuites.append(TLS_ECDH_ANON_WITH_AES_256_CBC_SHA)
Expand All @@ -1563,6 +1602,64 @@ def getEcdhAnonSuites(cls, settings, version=None):
#: all ciphersuites which use ephemeral ECDH key exchange
ecdhAllSuites = ecdheEcdsaSuites + ecdheCertSuites + ecdhAnonSuites

#: pure PSK key exchange
pskSuites = []
pskSuites.append(TLS_PSK_WITH_NULL_SHA)
pskSuites.append(TLS_PSK_WITH_AES_128_CBC_SHA)
pskSuites.append(TLS_PSK_WITH_AES_256_CBC_SHA)

@classmethod
def getPskSuites(cls, settings, version=None):
"""
Provide ciphersuites that use pure PSK key exchange matching settings
"""
return cls._filterSuites(CipherSuite.pskSuites, settings, version)

#: DHE key exchange with PSK authentication
dhePskSuites = []
dhePskSuites.append(TLS_DHE_PSK_WITH_NULL_SHA)
dhePskSuites.append(TLS_DHE_PSK_WITH_AES_128_CBC_SHA)
dhePskSuites.append(TLS_DHE_PSK_WITH_AES_256_CBC_SHA)

@classmethod
def getDhePskSites(cls, settings, version=None):
"""
Provide ciphersuites that use DHE with PSK key exchange matching
settings
"""
return cls._filterSuites(CipherSuite.dhePskSuites, settings, version)

#: RSA key exchange with PSK authentication
pskCertSuites = []
pskCertSuites.append(TLS_RSA_PSK_WITH_NULL_SHA)
pskCertSuites.append(TLS_RSA_PSK_WITH_AES_128_CBC_SHA)
pskCertSuites.append(TLS_RSA_PSK_WITH_AES_256_CBC_SHA)

#: RSA authentication
certAllSuites = srpCertSuites + certSuites + dheCertSuites + \
ecdheCertSuites + pskCertSuites

#: all ciphersuites that use ephemeral FFDH key exchange
dhAllSuites = dheCertSuites + anonSuites + dheDsaSuites + dhePskSuites

@classmethod
def getPskCertSuites(cls, settings, version=None):
"""
Provide ciphersuites that use RSA with PSK key exchange matching
settings
"""
return cls._filterSuites(CipherSuite.pskCertSuites, settings, version)

#: all suites that use PSK authentication and PSK inside premaster secret
pskAllSuites = pskSuites + dhePskSuites + pskCertSuites

@classmethod
def getPskAllSuites(cls, settings, version=None):
"""
Return all ciphersuites that use any form of PSK that match settings
"""
return cls._filterSuites(CipherSuite.pskAllSuites, settings, version)

@staticmethod
def canonicalCipherName(ciphersuite):
"""Return the canonical name of the cipher whose number is provided."""
Expand Down
6 changes: 4 additions & 2 deletions tlslite/handshakesettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
# Don't allow "md5" by default
MAC_NAMES = ["sha", "sha256", "sha384", "aead"]
ALL_MAC_NAMES = MAC_NAMES + ["md5"]
KEY_EXCHANGE_NAMES = ["ecdhe_ecdsa", "rsa", "dhe_rsa", "ecdhe_rsa", "srp_sha",
"srp_sha_rsa", "ecdh_anon", "dh_anon", "dhe_dsa"]
KEY_EXCHANGE_NAMES = ["ecdhe_ecdsa", "rsa", "dhe_rsa", "ecdhe_rsa",
"psk", "dhe_psk", "rsa_psk",
"srp_sha", "srp_sha_rsa",
"ecdh_anon", "dh_anon", "dhe_dsa"]
CIPHER_IMPLEMENTATIONS = ["openssl", "pycrypto", "python"]
CERTIFICATE_TYPES = ["x509"]
RSA_SIGNATURE_HASHES = ["sha512", "sha384", "sha256", "sha224", "sha1"]
Expand Down
76 changes: 75 additions & 1 deletion tlslite/keyexchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from .utils.x25519 import x25519, x448, X25519_G, X448_G, X25519_ORDER_SIZE, \
X448_ORDER_SIZE
from .utils.compat import int_types
from .utils.codec import DecodeError
from .utils.codec import DecodeError, Writer


class KeyExchange(object):
Expand Down Expand Up @@ -1037,3 +1037,77 @@ def calc_shared_key(self, private, peer_share):
S = ecdhYc * private

return numberToByteArray(S.x(), getPointByteSize(ecdhYc))

class PSKKeyExchange(KeyExchange):
"""Handling of pure PSK key exchange"""

def __init__(self, cipherSuite, clientHello, serverHello, psk_configs):
super(PSKKeyExchange, self).__init__(cipherSuite, clientHello,
serverHello)
self.psk_configs = psk_configs
self.psk_identity_hint = None
self.selected_identity = None

def makeServerKeyExchange(self, sigHash=None):
"""
Don't create a server key exchange message.
"""
# TODO: send psk hint
return None

def _getPSK(self, serverKeyExchange):
if self.psk_identity_hint:
raise ValueError("not finished code")
# iterate over PSKs in self.psk
else:
self.selected_identity = self.psk_configs[0][0]
return self.psk_configs[0][1]

def processServerKeyExchange(self, srvPublicKey, serverKeyExchange):
#self.psk_identity_hint = serverKeyExchange.psk_identity_hint

psk = self._getPSK(serverKeyExchange)

writer = Writer()
other_secret = bytearray(len(psk))
writer.add_var_bytes(other_secret, 2)
writer.add_var_bytes(psk, 2)
return writer.bytes

def makeClientKeyExchange(self):
clientKeyExchange = super(PSKKeyExchange, self).makeClientKeyExchange()
clientKeyExchange.createPSK(self.selected_identity)
return clientKeyExchange

@staticmethod
def verifyServerKeyExchange(serverKeyExchange, publicKey, clientRandom,
serverRandom, validSigAlgs):
# there are no SKE signatures in PSK
pass


class RSA_PSKKeyExchange(PSKKeyExchange):
def __init__(self, cipherSuite, clientHello, serverHello, psk_configs):
super(RSA_PSKKeyExchange, self).__init__(cipherSuite, clientHello,
serverHello, psk_configs)
self.encPremasterSecret = None

def processServerKeyExchange(self, srvPublicKey, serverKeyExchange):
psk = self._getPSK(serverKeyExchange)

rsa_premaster_secret = getRandomBytes(48)
rsa_premaster_secret[0] = self.clientHello.client_version[0]
rsa_premaster_secret[1] = self.clientHello.client_version[1]

self.encPremasterSecret = srvPublicKey.encrypt(rsa_premaster_secret)

writer = Writer()
writer.add_var_bytes(rsa_premaster_secret, 2)
writer.add_var_bytes(psk, 2)
return writer.bytes

def makeClientKeyExchange(self):
clientKeyExchange = super(RSA_PSKKeyExchange, self)\
.makeClientKeyExchange()
clientKeyExchange.createRSA(self.encPremasterSecret)
return clientKeyExchange
43 changes: 40 additions & 3 deletions tlslite/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,8 @@ class ServerKeyExchange(HandshakeMsg):

:vartype cipherSuite: int
:cvar cipherSuite: id of ciphersuite selected in Server Hello message
:vartype psk_identity_hint: bytearray
:cvar psk_identity_hint: PSK identity hint
:vartype srp_N: int
:cvar srp_N: SRP protocol prime
:vartype srp_N_len: int
Expand Down Expand Up @@ -1444,6 +1446,8 @@ def __init__(self, cipherSuite, version):
# signature hash algorithm and signing algorithm for TLSv1.2
self.hashAlg = 0
self.signAlg = 0
# identity hint used in PSK ciphersuites
self.psk_identity_hint = None

def __repr__(self):
ret = "ServerKeyExchange(cipherSuite=CipherSuite.{0}, version={1}"\
Expand All @@ -1458,6 +1462,8 @@ def __repr__(self):
if self.signAlg != 0:
ret += ", hashAlg={0}, signAlg={1}".format(
self.hashAlg, self.signAlg)
if self.psk_identity_hint is not None:
ret += ", psk_identity_hint={0!r}".format(self.psk_identity_hint)
if self.signature != bytearray(0):
ret += ", signature={0!r}".format(self.signature)
ret += ")"
Expand Down Expand Up @@ -1500,6 +1506,11 @@ def parse(self, parser):
:param parser: parser to read data from
"""
parser.startLengthCheck(3)
if self.cipherSuite in CipherSuite.pskAllSuites:
# all PSK SKE's start with hint, and then optionally have
# DHE or ECDHE params
self.psk_identity_hint = parser.getVarBytes(2)

if self.cipherSuite in CipherSuite.srpAllSuites:
self.srp_N_len = parser.get(2)
self.srp_N = bytesToNumber(parser.getFixBytes(self.srp_N_len))
Expand All @@ -1521,12 +1532,18 @@ def parse(self, parser):
assert self.curve_type == 3
self.named_curve = parser.get(2)
self.ecdh_Ys = parser.getVarBytes(1)
elif self.cipherSuite in CipherSuite.pskSuites or \
self.cipherSuite in CipherSuite.pskCertSuites:
# no additional parameters
pass
else:
raise AssertionError()

if self.cipherSuite in CipherSuite.certAllSuites or\
# PSK ciphersuites don't sign SKE, not even the RSA_PSK ones
if self.cipherSuite not in CipherSuite.pskAllSuites and (\
self.cipherSuite in CipherSuite.certAllSuites or\
self.cipherSuite in CipherSuite.ecdheEcdsaSuites or\
self.cipherSuite in CipherSuite.dheDsaSuites:
self.cipherSuite in CipherSuite.dheDsaSuites):
if self.version == (3, 3):
self.hashAlg = parser.get(1)
self.signAlg = parser.get(1)
Expand Down Expand Up @@ -1667,6 +1684,20 @@ def __init__(self, cipherSuite, version=None):
self.dh_Yc = 0
self.ecdh_Yc = bytearray(0)
self.encryptedPreMasterSecret = bytearray(0)
self.psk_identity = bytearray(0)

def createPSK(self, psk_identity):
"""
Set the PSK identity used.

returns self

:type psk_identity: bytearray
:param psk_identity: Used PSK identity
:rtype ClientKeyExchange
"""
self.psk_identity = psk_identity
return self

def createSRP(self, srp_A):
"""
Expand Down Expand Up @@ -1753,9 +1784,13 @@ def write(self):
:rtype: bytearray
"""
w = Writer()
if self.cipherSuite in CipherSuite.pskAllSuites:
w.add_var_bytes(self.psk_identity, 2)

if self.cipherSuite in CipherSuite.srpAllSuites:
w.addVarSeq(numberToByteArray(self.srp_A), 1, 2)
elif self.cipherSuite in CipherSuite.certSuites:
elif self.cipherSuite in CipherSuite.certSuites or \
self.cipherSuite in CipherSuite.pskCertSuites:
if self.version in ((3, 1), (3, 2), (3, 3)):
w.addVarSeq(self.encryptedPreMasterSecret, 1, 2)
elif self.version == (3, 0):
Expand All @@ -1766,6 +1801,8 @@ def write(self):
w.addVarSeq(numberToByteArray(self.dh_Yc), 1, 2)
elif self.cipherSuite in CipherSuite.ecdhAllSuites:
w.addVarSeq(self.ecdh_Yc, 1, 1)
elif self.cipherSuite in CipherSuite.pskSuites:
pass
else:
raise AssertionError()
return self.postWrite(w)
Expand Down
Loading
Loading