Index: third_party/tlslite/patches/alpn.patch |
diff --git a/third_party/tlslite/patches/alpn.patch b/third_party/tlslite/patches/alpn.patch |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a52bf63ad8812f5ee291d57670dde182a0c7bfa9 |
--- /dev/null |
+++ b/third_party/tlslite/patches/alpn.patch |
@@ -0,0 +1,567 @@ |
+diff --git a/third_party/tlslite/patches/alpn.patch b/third_party/tlslite/patches/alpn.patch |
+new file mode 100644 |
+index 0000000..e69de29 |
+diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlslite/constants.py |
+index 715def9..e9743e4 100644 |
+--- a/third_party/tlslite/tlslite/constants.py |
++++ b/third_party/tlslite/tlslite/constants.py |
+@@ -54,6 +54,7 @@ class ExtensionType: # RFC 6066 / 4366 |
+ status_request = 5 # RFC 6066 / 4366 |
+ srp = 12 # RFC 5054 |
+ cert_type = 9 # RFC 6091 |
++ alpn = 16 # RFC 7301 |
+ signed_cert_timestamps = 18 # RFC 6962 |
+ extended_master_secret = 23 # RFC 7627 |
+ token_binding = 24 # draft-ietf-tokbind-negotiation |
+diff --git a/third_party/tlslite/tlslite/handshakesettings.py b/third_party/tlslite/tlslite/handshakesettings.py |
+index d7be5b3..284c474 100644 |
+--- a/third_party/tlslite/tlslite/handshakesettings.py |
++++ b/third_party/tlslite/tlslite/handshakesettings.py |
+@@ -128,6 +128,18 @@ class HandshakeSettings(object): |
+ |
+ Note that TACK support is not standardized by IETF and uses a temporary |
+ TLS Extension number, so should NOT be used in production software. |
++ |
++ @type alpnProtos: list of strings. |
++ @param alpnProtos: A list of supported upper layer protocols to use in the |
++ Application-Layer Protocol Negotiation Extension (RFC 7301). For the |
++ client, the order does not matter. For the server, the list is in |
++ decreasing order of preference. |
++ |
++ @type nextProtos: list of strings. |
++ @param nextProtos: A list of supported upper layer protocols to use in the |
++ Next-Protocol Negotiation Extension. For the server, the order does not |
++ matter. For the client, the list is in decreasing order of preference, with |
++ the first entry used for fallback. |
+ """ |
+ def __init__(self): |
+ self.minKeySize = 1023 |
+@@ -146,6 +158,8 @@ class HandshakeSettings(object): |
+ self.enableChannelID = True |
+ self.enableExtendedMasterSecret = True |
+ self.supportedTokenBindingParams = [] |
++ self.alpnProtos = None |
++ self.nextProtos = None |
+ |
+ # Validates the min/max fields, and certificateTypes |
+ # Filters out unsupported cipherNames and cipherImplementations |
+@@ -166,6 +180,8 @@ class HandshakeSettings(object): |
+ other.enableChannelID = self.enableChannelID |
+ other.enableExtendedMasterSecret = self.enableExtendedMasterSecret |
+ other.supportedTokenBindingParams = self.supportedTokenBindingParams |
++ other.alpnProtos = self.alpnProtos; |
++ other.nextProtos = self.nextProtos; |
+ |
+ if not cipherfactory.tripleDESPresent: |
+ other.cipherNames = [e for e in self.cipherNames if e != "3des"] |
+diff --git a/third_party/tlslite/tlslite/messages.py b/third_party/tlslite/tlslite/messages.py |
+index 5762ac6..651492b 100644 |
+--- a/third_party/tlslite/tlslite/messages.py |
++++ b/third_party/tlslite/tlslite/messages.py |
+@@ -99,6 +99,27 @@ class HandshakeMsg(object): |
+ headerWriter.add(len(w.bytes), 3) |
+ return headerWriter.bytes + w.bytes |
+ |
++ def parse_next_protos(self, b): |
++ protos = [] |
++ while True: |
++ if len(b) == 0: |
++ break |
++ l = b[0] |
++ b = b[1:] |
++ if len(b) < l: |
++ raise BadNextProtos(len(b)) |
++ protos.append(b[:l]) |
++ b = b[l:] |
++ return protos |
++ |
++ def next_protos_encoded(self, protocol_list): |
++ b = bytearray() |
++ for e in protocol_list: |
++ if len(e) > 255 or len(e) == 0: |
++ raise BadNextProtos(len(e)) |
++ b += bytearray( [len(e)] ) + bytearray(e) |
++ return b |
++ |
+ class ClientHello(HandshakeMsg): |
+ def __init__(self, ssl2=False): |
+ HandshakeMsg.__init__(self, HandshakeType.client_hello) |
+@@ -111,6 +132,7 @@ class ClientHello(HandshakeMsg): |
+ self.compression_methods = [] # a list of 8-bit values |
+ self.srp_username = None # a string |
+ self.tack = False |
++ self.alpn_protos_advertised = None |
+ self.supports_npn = False |
+ self.server_name = bytearray(0) |
+ self.channel_id = False |
+@@ -120,8 +142,8 @@ class ClientHello(HandshakeMsg): |
+ self.status_request = False |
+ |
+ def create(self, version, random, session_id, cipher_suites, |
+- certificate_types=None, srpUsername=None, |
+- tack=False, supports_npn=False, serverName=None): |
++ certificate_types=None, srpUsername=None, tack=False, |
++ alpn_protos_advertised=None, supports_npn=False, serverName=None): |
+ self.client_version = version |
+ self.random = random |
+ self.session_id = session_id |
+@@ -131,6 +153,7 @@ class ClientHello(HandshakeMsg): |
+ if srpUsername: |
+ self.srp_username = bytearray(srpUsername, "utf-8") |
+ self.tack = tack |
++ self.alpn_protos_advertised = alpn_protos_advertised |
+ self.supports_npn = supports_npn |
+ if serverName: |
+ self.server_name = bytearray(serverName, "utf-8") |
+@@ -171,6 +194,11 @@ class ClientHello(HandshakeMsg): |
+ self.certificate_types = p.getVarList(1, 1) |
+ elif extType == ExtensionType.tack: |
+ self.tack = True |
++ elif extType == ExtensionType.alpn: |
++ structLength = p.get(2) |
++ if (structLength + 2 != extLength): |
++ raise SyntaxError() |
++ self.alpn_protos_advertised = self.parse_next_protos(p.getFixBytes(structLength)) |
+ elif extType == ExtensionType.supports_npn: |
+ self.supports_npn = True |
+ elif extType == ExtensionType.server_name: |
+@@ -243,6 +271,12 @@ class ClientHello(HandshakeMsg): |
+ w2.add(ExtensionType.srp, 2) |
+ w2.add(len(self.srp_username)+1, 2) |
+ w2.addVarSeq(self.srp_username, 1, 1) |
++ if self.alpn_protos_advertised is not None: |
++ encoded_alpn_protos_advertised = self.next_protos_encoded(self.alpn_protos_advertised) |
++ w2.add(ExtensionType.alpn, 2) |
++ w2.add(len(encoded_alpn_protos_advertised) + 2, 2) |
++ w2.add(len(encoded_alpn_protos_advertised), 2) |
++ w2.addFixSeq(encoded_alpn_protos_advertised, 1) |
+ if self.supports_npn: |
+ w2.add(ExtensionType.supports_npn, 2) |
+ w2.add(0, 2) |
+@@ -267,6 +301,13 @@ class BadNextProtos(Exception): |
+ def __str__(self): |
+ return 'Cannot encode a list of next protocols because it contains an element with invalid length %d. Element lengths must be 0 < x < 256' % self.length |
+ |
++class InvalidAlpnResponse(Exception): |
++ def __init__(self, l): |
++ self.length = l |
++ |
++ def __str__(self): |
++ return 'ALPN server response protocol list has invalid length %d. It must be of length one.' % self.length |
++ |
+ class ServerHello(HandshakeMsg): |
+ def __init__(self): |
+ HandshakeMsg.__init__(self, HandshakeType.server_hello) |
+@@ -277,6 +318,7 @@ class ServerHello(HandshakeMsg): |
+ self.certificate_type = CertificateType.x509 |
+ self.compression_method = 0 |
+ self.tackExt = None |
++ self.alpn_proto_selected = None |
+ self.next_protos_advertised = None |
+ self.next_protos = None |
+ self.channel_id = False |
+@@ -286,7 +328,8 @@ class ServerHello(HandshakeMsg): |
+ self.status_request = False |
+ |
+ def create(self, version, random, session_id, cipher_suite, |
+- certificate_type, tackExt, next_protos_advertised): |
++ certificate_type, tackExt, alpn_proto_selected, |
++ next_protos_advertised): |
+ self.server_version = version |
+ self.random = random |
+ self.session_id = session_id |
+@@ -294,6 +337,7 @@ class ServerHello(HandshakeMsg): |
+ self.certificate_type = certificate_type |
+ self.compression_method = 0 |
+ self.tackExt = tackExt |
++ self.alpn_proto_selected = alpn_proto_selected |
+ self.next_protos_advertised = next_protos_advertised |
+ return self |
+ |
+@@ -316,35 +360,22 @@ class ServerHello(HandshakeMsg): |
+ self.certificate_type = p.get(1) |
+ elif extType == ExtensionType.tack and tackpyLoaded: |
+ self.tackExt = TackExtension(p.getFixBytes(extLength)) |
++ elif extType == ExtensionType.alpn: |
++ structLength = p.get(2) |
++ if structLength + 2 != extLength: |
++ raise SyntaxError() |
++ alpn_protos = self.parse_next_protos(p.getFixBytes(structLength)) |
++ if alpn_protos.len() != 1: |
++ raise InvalidAlpnResponse(alpn_protos.len()); |
++ self.alpn_proto_selected = alpn_protos[0] |
+ elif extType == ExtensionType.supports_npn: |
+- self.next_protos = self.__parse_next_protos(p.getFixBytes(extLength)) |
++ self.next_protos = self.parse_next_protos(p.getFixBytes(extLength)) |
+ else: |
+ p.getFixBytes(extLength) |
+ soFar += 4 + extLength |
+ p.stopLengthCheck() |
+ return self |
+ |
+- def __parse_next_protos(self, b): |
+- protos = [] |
+- while True: |
+- if len(b) == 0: |
+- break |
+- l = b[0] |
+- b = b[1:] |
+- if len(b) < l: |
+- raise BadNextProtos(len(b)) |
+- protos.append(b[:l]) |
+- b = b[l:] |
+- return protos |
+- |
+- def __next_protos_encoded(self): |
+- b = bytearray() |
+- for e in self.next_protos_advertised: |
+- if len(e) > 255 or len(e) == 0: |
+- raise BadNextProtos(len(e)) |
+- b += bytearray( [len(e)] ) + bytearray(e) |
+- return b |
+- |
+ def write(self): |
+ w = Writer() |
+ w.add(self.server_version[0], 1) |
+@@ -365,8 +396,15 @@ class ServerHello(HandshakeMsg): |
+ w2.add(ExtensionType.tack, 2) |
+ w2.add(len(b), 2) |
+ w2.bytes += b |
++ if self.alpn_proto_selected is not None: |
++ alpn_protos_single_element_list = [self.alpn_proto_selected] |
++ encoded_alpn_protos_advertised = self.next_protos_encoded(alpn_protos_single_element_list) |
++ w2.add(ExtensionType.alpn, 2) |
++ w2.add(len(encoded_alpn_protos_advertised) + 2, 2) |
++ w2.add(len(encoded_alpn_protos_advertised), 2) |
++ w2.addFixSeq(encoded_alpn_protos_advertised, 1) |
+ if self.next_protos_advertised is not None: |
+- encoded_next_protos_advertised = self.__next_protos_encoded() |
++ encoded_next_protos_advertised = self.next_protos_encoded(self.next_protos_advertised) |
+ w2.add(ExtensionType.supports_npn, 2) |
+ w2.add(len(encoded_next_protos_advertised), 2) |
+ w2.addFixSeq(encoded_next_protos_advertised, 1) |
+diff --git a/third_party/tlslite/tlslite/session.py b/third_party/tlslite/tlslite/session.py |
+index 6aadf58..7758b7c 100644 |
+--- a/third_party/tlslite/tlslite/session.py |
++++ b/third_party/tlslite/tlslite/session.py |
+@@ -54,11 +54,13 @@ class Session(object): |
+ self.tackExt = None |
+ self.tackInHelloExt = False |
+ self.serverName = "" |
++ self.alpn_proto_selected = None |
+ self.resumable = False |
+ |
+ def create(self, masterSecret, sessionID, cipherSuite, |
+ srpUsername, clientCertChain, serverCertChain, |
+- tackExt, tackInHelloExt, serverName, resumable=True): |
++ tackExt, tackInHelloExt, alpn_proto_selected, serverName, |
++ resumable=True): |
+ self.masterSecret = masterSecret |
+ self.sessionID = sessionID |
+ self.cipherSuite = cipherSuite |
+@@ -68,6 +70,7 @@ class Session(object): |
+ self.tackExt = tackExt |
+ self.tackInHelloExt = tackInHelloExt |
+ self.serverName = serverName |
++ self.alpn_proto_selected = alpn_proto_selected |
+ self.resumable = resumable |
+ |
+ def _clone(self): |
+@@ -81,6 +84,7 @@ class Session(object): |
+ other.tackExt = self.tackExt |
+ other.tackInHelloExt = self.tackInHelloExt |
+ other.serverName = self.serverName |
++ other.alpn_proto_selected = self.alpn_proto_selected |
+ other.resumable = self.resumable |
+ return other |
+ |
+diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/tlslite/tlsconnection.py |
+index 41aab85..e062940 100644 |
+--- a/third_party/tlslite/tlslite/tlsconnection.py |
++++ b/third_party/tlslite/tlslite/tlsconnection.py |
+@@ -337,8 +337,7 @@ class TLSConnection(TLSRecordLayer): |
+ |
+ def handshakeClientCert(self, certChain=None, privateKey=None, |
+ session=None, settings=None, checker=None, |
+- nextProtos=None, reqTack=True, serverName="", |
+- async=False): |
++ reqTack=True, serverName="", async=False): |
+ """Perform a certificate-based handshake in the role of client. |
+ |
+ This function performs an SSL or TLS handshake. The server |
+@@ -383,11 +382,7 @@ class TLSConnection(TLSRecordLayer): |
+ @param checker: A Checker instance. This instance will be |
+ invoked to examine the other party's authentication |
+ credentials, if the handshake completes succesfully. |
+- |
+- @type nextProtos: list of strings. |
+- @param nextProtos: A list of upper layer protocols ordered by |
+- preference, to use in the Next-Protocol Negotiation Extension. |
+- |
++ |
+ @type reqTack: bool |
+ @param reqTack: Whether or not to send a "tack" TLS Extension, |
+ requesting the server return a TackExtension if it has one. |
+@@ -417,7 +412,7 @@ class TLSConnection(TLSRecordLayer): |
+ handshaker = self._handshakeClientAsync(certParams=(certChain, |
+ privateKey), session=session, settings=settings, |
+ checker=checker, serverName=serverName, |
+- nextProtos=nextProtos, reqTack=reqTack) |
++ reqTack=reqTack) |
+ # The handshaker is a Python Generator which executes the handshake. |
+ # It allows the handshake to be run in a "piecewise", asynchronous |
+ # fashion, returning 1 when it is waiting to able to write, 0 when |
+@@ -433,7 +428,7 @@ class TLSConnection(TLSRecordLayer): |
+ |
+ def _handshakeClientAsync(self, srpParams=(), certParams=(), anonParams=(), |
+ session=None, settings=None, checker=None, |
+- nextProtos=None, serverName="", reqTack=True): |
++ serverName="", reqTack=True): |
+ |
+ handshaker = self._handshakeClientAsyncHelper(srpParams=srpParams, |
+ certParams=certParams, |
+@@ -441,14 +436,13 @@ class TLSConnection(TLSRecordLayer): |
+ session=session, |
+ settings=settings, |
+ serverName=serverName, |
+- nextProtos=nextProtos, |
+ reqTack=reqTack) |
+ for result in self._handshakeWrapperAsync(handshaker, checker): |
+ yield result |
+ |
+ |
+ def _handshakeClientAsyncHelper(self, srpParams, certParams, anonParams, |
+- session, settings, serverName, nextProtos, reqTack): |
++ session, settings, serverName, reqTack): |
+ |
+ self._handshakeStart(client=True) |
+ |
+@@ -485,16 +479,19 @@ class TLSConnection(TLSRecordLayer): |
+ reqTack = False |
+ if not settings or not settings.useExperimentalTackExtension: |
+ reqTack = False |
+- if nextProtos is not None: |
+- if len(nextProtos) == 0: |
+- raise ValueError("Caller passed no nextProtos") |
+- |
+ # Validates the settings and filters out any unsupported ciphers |
+ # or crypto libraries that were requested |
+ if not settings: |
+ settings = HandshakeSettings() |
+ settings = settings._filter() |
+ |
++ if settings.alpnProtos is not None: |
++ if len(settings.alpnProtos) == 0: |
++ raise ValueError("Caller passed no alpnProtos") |
++ if settings.nextProtos is not None: |
++ if len(settings.nextProtos) == 0: |
++ raise ValueError("Caller passed no nextProtos") |
++ |
+ if clientCertChain: |
+ if not isinstance(clientCertChain, X509CertChain): |
+ raise ValueError("Unrecognized certificate type") |
+@@ -530,8 +527,7 @@ class TLSConnection(TLSRecordLayer): |
+ # Send the ClientHello. |
+ for result in self._clientSendClientHello(settings, session, |
+ srpUsername, srpParams, certParams, |
+- anonParams, serverName, nextProtos, |
+- reqTack): |
++ anonParams, serverName, reqTack): |
+ if result in (0,1): yield result |
+ else: break |
+ clientHello = result |
+@@ -543,8 +539,6 @@ class TLSConnection(TLSRecordLayer): |
+ serverHello = result |
+ cipherSuite = serverHello.cipher_suite |
+ |
+- # Choose a matching Next Protocol from server list against ours |
+- # (string or None) |
+ nextProto = self._clientSelectNextProto(nextProtos, serverHello) |
+ |
+ #If the server elected to resume the session, it is handled here. |
+@@ -615,13 +609,12 @@ class TLSConnection(TLSRecordLayer): |
+ self.session = Session() |
+ self.session.create(masterSecret, serverHello.session_id, cipherSuite, |
+ srpUsername, clientCertChain, serverCertChain, |
+- tackExt, serverHello.tackExt!=None, serverName) |
++ tackExt, serverHello.tackExt!=None, None, serverName) |
+ self._handshakeDone(resumed=False) |
+ |
+ |
+- def _clientSendClientHello(self, settings, session, srpUsername, |
+- srpParams, certParams, anonParams, |
+- serverName, nextProtos, reqTack): |
++ def _clientSendClientHello(self, settings, session, srpUsername, srpParams, |
++ certParams, anonParams, serverName, reqTack): |
+ #Initialize acceptable ciphersuites |
+ cipherSuites = [CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] |
+ if srpParams: |
+@@ -649,9 +642,10 @@ class TLSConnection(TLSRecordLayer): |
+ clientHello = ClientHello() |
+ clientHello.create(settings.maxVersion, getRandomBytes(32), |
+ session.sessionID, cipherSuites, |
+- certificateTypes, |
++ certificateTypes, |
+ session.srpUsername, |
+- reqTack, nextProtos is not None, |
++ reqTack, settings.alpnProtos, |
++ settings.nextProtos is not None, |
+ session.serverName) |
+ |
+ #Or send ClientHello (without) |
+@@ -659,10 +653,10 @@ class TLSConnection(TLSRecordLayer): |
+ clientHello = ClientHello() |
+ clientHello.create(settings.maxVersion, getRandomBytes(32), |
+ bytearray(0), cipherSuites, |
+- certificateTypes, |
++ certificateTypes, |
+ srpUsername, |
+- reqTack, nextProtos is not None, |
+- serverName) |
++ reqTack, settings.alpnProtos, |
++ settings.nextProtos is not None, serverName) |
+ for result in self._sendMsg(clientHello): |
+ yield result |
+ yield clientHello |
+@@ -714,6 +708,16 @@ class TLSConnection(TLSRecordLayer): |
+ AlertDescription.illegal_parameter, |
+ "Server responded with unrequested Tack Extension"): |
+ yield result |
++ if serverHello.alpn_proto_selected and not clientHello.alpn_protos_advertised: |
++ for result in self._sendError(\ |
++ AlertDescription.illegal_parameter, |
++ "Server responded with unrequested ALPN Extension"): |
++ yield result |
++ if serverHello.alpn_proto_selected and serverHello.next_protos: |
++ for result in self._sendError(\ |
++ AlertDescription.illegal_parameter, |
++ "Server responded with both ALPN and NPN extension"): |
++ yield result |
+ if serverHello.next_protos and not clientHello.supports_npn: |
+ for result in self._sendError(\ |
+ AlertDescription.illegal_parameter, |
+@@ -1101,8 +1105,7 @@ class TLSConnection(TLSRecordLayer): |
+ certChain=None, privateKey=None, reqCert=False, |
+ sessionCache=None, settings=None, checker=None, |
+ reqCAs = None, reqCertTypes = None, |
+- tacks=None, activationFlags=0, |
+- nextProtos=None, anon=False, |
++ tacks=None, activationFlags=0, anon=False, |
+ signedCertTimestamps=None, |
+ fallbackSCSV=False, ocspResponse=None): |
+ """Perform a handshake in the role of server. |
+@@ -1172,11 +1175,6 @@ class TLSConnection(TLSRecordLayer): |
+ @param reqCertTypes: A list of certificate_type values to be sent |
+ along with a certificate request. This does not affect verification. |
+ |
+- @type nextProtos: list of strings. |
+- @param nextProtos: A list of upper layer protocols to expose to the |
+- clients through the Next-Protocol Negotiation Extension, |
+- if they support it. |
+- |
+ @type signedCertTimestamps: str |
+ @param signedCertTimestamps: A SignedCertificateTimestampList (as a |
+ binary 8-bit string) that will be sent as a TLS extension whenever |
+@@ -1206,9 +1204,8 @@ class TLSConnection(TLSRecordLayer): |
+ """ |
+ for result in self.handshakeServerAsync(verifierDB, |
+ certChain, privateKey, reqCert, sessionCache, settings, |
+- checker, reqCAs, reqCertTypes, |
+- tacks=tacks, activationFlags=activationFlags, |
+- nextProtos=nextProtos, anon=anon, |
++ checker, reqCAs, reqCertTypes, tacks=tacks, |
++ activationFlags=activationFlags, anon=anon, |
+ signedCertTimestamps=signedCertTimestamps, |
+ fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse): |
+ pass |
+@@ -1218,8 +1215,7 @@ class TLSConnection(TLSRecordLayer): |
+ certChain=None, privateKey=None, reqCert=False, |
+ sessionCache=None, settings=None, checker=None, |
+ reqCAs=None, reqCertTypes=None, |
+- tacks=None, activationFlags=0, |
+- nextProtos=None, anon=False, |
++ tacks=None, activationFlags=0, anon=False, |
+ signedCertTimestamps=None, |
+ fallbackSCSV=False, |
+ ocspResponse=None |
+@@ -1238,10 +1234,9 @@ class TLSConnection(TLSRecordLayer): |
+ handshaker = self._handshakeServerAsyncHelper(\ |
+ verifierDB=verifierDB, certChain=certChain, |
+ privateKey=privateKey, reqCert=reqCert, |
+- sessionCache=sessionCache, settings=settings, |
++ sessionCache=sessionCache, settings=settings, |
+ reqCAs=reqCAs, reqCertTypes=reqCertTypes, |
+- tacks=tacks, activationFlags=activationFlags, |
+- nextProtos=nextProtos, anon=anon, |
++ tacks=tacks, activationFlags=activationFlags, anon=anon, |
+ signedCertTimestamps=signedCertTimestamps, |
+ fallbackSCSV=fallbackSCSV, |
+ ocspResponse=ocspResponse) |
+@@ -1256,8 +1251,7 @@ class TLSConnection(TLSRecordLayer): |
+ def _handshakeServerAsyncHelper(self, verifierDB, |
+ certChain, privateKey, reqCert, sessionCache, |
+ settings, reqCAs, reqCertTypes, |
+- tacks, activationFlags, |
+- nextProtos, anon, |
++ tacks, activationFlags, anon, |
+ signedCertTimestamps, fallbackSCSV, |
+ ocspResponse): |
+ |
+@@ -1314,9 +1308,18 @@ class TLSConnection(TLSRecordLayer): |
+ sessionID = getRandomBytes(32) |
+ else: |
+ sessionID = bytearray(0) |
+- |
++ |
++ alpn_proto_selected = None |
++ if clientHello.alpn_protos_advertised is not None: |
++ if settings.alpnProtos is not None: |
++ for proto in settings.alpnProtos: |
++ if proto in clientHello.alpn_protos_advertised: |
++ alpn_proto_selected = proto |
++ settings.nextProtos = None |
++ break; |
++ |
+ if not clientHello.supports_npn: |
+- nextProtos = None |
++ settings.nextProtos = None |
+ |
+ # If not doing a certificate-based suite, discard the TACK |
+ if not cipherSuite in CipherSuite.certAllSuites: |
+@@ -1330,7 +1333,7 @@ class TLSConnection(TLSRecordLayer): |
+ serverHello = ServerHello() |
+ serverHello.create(self.version, getRandomBytes(32), sessionID, \ |
+ cipherSuite, CertificateType.x509, tackExt, |
+- nextProtos) |
++ alpn_proto_selected, settings.nextProtos) |
+ serverHello.channel_id = \ |
+ clientHello.channel_id and settings.enableChannelID |
+ serverHello.extended_master_secret = \ |
+@@ -1397,7 +1400,7 @@ class TLSConnection(TLSRecordLayer): |
+ for result in self._serverFinished(premasterSecret, |
+ clientHello.random, serverHello.random, |
+ cipherSuite, settings.cipherImplementations, |
+- nextProtos, serverHello.channel_id, |
++ settings.nextProtos, serverHello.channel_id, |
+ serverHello.extended_master_secret): |
+ if result in (0,1): yield result |
+ else: break |
+@@ -1420,7 +1423,7 @@ class TLSConnection(TLSRecordLayer): |
+ serverName = clientHello.server_name.decode("utf-8") |
+ self.session.create(masterSecret, serverHello.session_id, cipherSuite, |
+ srpUsername, clientCertChain, serverCertChain, |
+- tackExt, serverHello.tackExt!=None, serverName) |
++ tackExt, serverHello.tackExt!=None, alpn_proto_selected, serverName) |
+ |
+ #Add the session object to the session cache |
+ if sessionCache and sessionID: |
+@@ -1540,7 +1543,8 @@ class TLSConnection(TLSRecordLayer): |
+ serverHello = ServerHello() |
+ serverHello.create(self.version, getRandomBytes(32), |
+ session.sessionID, session.cipherSuite, |
+- CertificateType.x509, None, None) |
++ CertificateType.x509, None, |
++ session.alpn_proto_selected, None) |
+ serverHello.extended_master_secret = \ |
+ clientHello.extended_master_secret and \ |
+ settings.enableExtendedMasterSecret |