diff --git a/tlslite/constants.py b/tlslite/constants.py index dd958c57..f07de229 100644 --- a/tlslite/constants.py +++ b/tlslite/constants.py @@ -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 @@ -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 @@ -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' @@ -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 = [] @@ -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 = [] @@ -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 @@ -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) @@ -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] @@ -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) @@ -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) @@ -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.""" diff --git a/tlslite/handshakesettings.py b/tlslite/handshakesettings.py index 38e560a2..6545ed40 100644 --- a/tlslite/handshakesettings.py +++ b/tlslite/handshakesettings.py @@ -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"] diff --git a/tlslite/keyexchange.py b/tlslite/keyexchange.py index bea1644a..0c90da15 100644 --- a/tlslite/keyexchange.py +++ b/tlslite/keyexchange.py @@ -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): @@ -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 diff --git a/tlslite/messages.py b/tlslite/messages.py index 1354cd14..5a894585 100644 --- a/tlslite/messages.py +++ b/tlslite/messages.py @@ -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 @@ -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}"\ @@ -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 += ")" @@ -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)) @@ -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) @@ -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): """ @@ -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): @@ -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) diff --git a/tlslite/tlsconnection.py b/tlslite/tlsconnection.py index 582097a7..53cde18f 100644 --- a/tlslite/tlsconnection.py +++ b/tlslite/tlsconnection.py @@ -35,7 +35,8 @@ from .utils.deprecations import deprecated_params from .keyexchange import KeyExchange, RSAKeyExchange, DHE_RSAKeyExchange, \ ECDHE_RSAKeyExchange, SRPKeyExchange, ADHKeyExchange, \ - AECDHKeyExchange, FFDHKeyExchange, ECDHKeyExchange + AECDHKeyExchange, FFDHKeyExchange, ECDHKeyExchange, PSKKeyExchange, \ + RSA_PSKKeyExchange from .handshakehelpers import HandshakeHelpers from .utils.cipherfactory import createAESCCM, createAESCCM_8, \ createAESGCM, createCHACHA20 @@ -611,6 +612,14 @@ def _handshakeClientAsyncHelper(self, srpParams, certParams, anonParams, keyExchange = ECDHE_RSAKeyExchange(cipherSuite, clientHello, serverHello, None, acceptedCurves) + elif cipherSuite in CipherSuite.pskSuites: + keyExchange = PSKKeyExchange(cipherSuite, clientHello, serverHello, + settings.pskConfigs) + + elif cipherSuite in CipherSuite.pskCertSuites: + keyExchange = RSA_PSKKeyExchange(cipherSuite, clientHello, + serverHello, settings.pskConfigs) + #If the server selected a certificate-based RSA ciphersuite, #the client finishes reading the post-ServerHello messages. If @@ -686,6 +695,8 @@ def _clientSendClientHello(self, settings, session, srpUsername, cipherSuites += CipherSuite.getEcdheCertSuites(settings) cipherSuites += CipherSuite.getDheCertSuites(settings) cipherSuites += CipherSuite.getCertSuites(settings) + if settings.pskConfigs: + cipherSuites += CipherSuite.getPskAllSuites(settings) cipherSuites += CipherSuite.getDheDsaSuites(settings) elif anonParams: cipherSuites += CipherSuite.getEcdhAnonSuites(settings) @@ -1737,7 +1748,7 @@ def _clientKeyExchange(self, settings, cipherSuite, self._sigHashesToList(settings, certList=serverCertChain) try: - KeyExchange.verifyServerKeyExchange(serverKeyExchange, + keyExchange.verifyServerKeyExchange(serverKeyExchange, publicKey, clientRandom, serverRandom,