OLD | NEW |
(Empty) | |
| 1 diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlsl
ite/constants.py |
| 2 index 715def9..e9743e4 100644 |
| 3 --- a/third_party/tlslite/tlslite/constants.py |
| 4 +++ b/third_party/tlslite/tlslite/constants.py |
| 5 @@ -54,6 +54,7 @@ class ExtensionType: # RFC 6066 / 4366 |
| 6 status_request = 5 # RFC 6066 / 4366 |
| 7 srp = 12 # RFC 5054 |
| 8 cert_type = 9 # RFC 6091 |
| 9 + alpn = 16 # RFC 7301 |
| 10 signed_cert_timestamps = 18 # RFC 6962 |
| 11 extended_master_secret = 23 # RFC 7627 |
| 12 token_binding = 24 # draft-ietf-tokbind-negotiation |
| 13 diff --git a/third_party/tlslite/tlslite/handshakesettings.py b/third_party/tlsl
ite/tlslite/handshakesettings.py |
| 14 index d7be5b3..69fc6f4 100644 |
| 15 --- a/third_party/tlslite/tlslite/handshakesettings.py |
| 16 +++ b/third_party/tlslite/tlslite/handshakesettings.py |
| 17 @@ -128,6 +128,12 @@ class HandshakeSettings(object): |
| 18 |
| 19 Note that TACK support is not standardized by IETF and uses a temporary |
| 20 TLS Extension number, so should NOT be used in production software. |
| 21 + |
| 22 + @type alpnProtos: list of strings. |
| 23 + @param alpnProtos: A list of supported upper layer protocols to use in the |
| 24 + Application-Layer Protocol Negotiation Extension (RFC 7301). For the |
| 25 + client, the order does not matter. For the server, the list is in |
| 26 + decreasing order of preference. |
| 27 """ |
| 28 def __init__(self): |
| 29 self.minKeySize = 1023 |
| 30 @@ -146,6 +152,7 @@ class HandshakeSettings(object): |
| 31 self.enableChannelID = True |
| 32 self.enableExtendedMasterSecret = True |
| 33 self.supportedTokenBindingParams = [] |
| 34 + self.alpnProtos = None |
| 35 |
| 36 # Validates the min/max fields, and certificateTypes |
| 37 # Filters out unsupported cipherNames and cipherImplementations |
| 38 @@ -166,6 +173,7 @@ class HandshakeSettings(object): |
| 39 other.enableChannelID = self.enableChannelID |
| 40 other.enableExtendedMasterSecret = self.enableExtendedMasterSecret |
| 41 other.supportedTokenBindingParams = self.supportedTokenBindingParams |
| 42 + other.alpnProtos = self.alpnProtos; |
| 43 |
| 44 if not cipherfactory.tripleDESPresent: |
| 45 other.cipherNames = [e for e in self.cipherNames if e != "3des"] |
| 46 diff --git a/third_party/tlslite/tlslite/messages.py b/third_party/tlslite/tlsli
te/messages.py |
| 47 index 5762ac6..1ce9320 100644 |
| 48 --- a/third_party/tlslite/tlslite/messages.py |
| 49 +++ b/third_party/tlslite/tlslite/messages.py |
| 50 @@ -18,6 +18,27 @@ from .x509 import X509 |
| 51 from .x509certchain import X509CertChain |
| 52 from .utils.tackwrapper import * |
| 53 |
| 54 +def parse_next_protos(b): |
| 55 + protos = [] |
| 56 + while True: |
| 57 + if len(b) == 0: |
| 58 + break |
| 59 + l = b[0] |
| 60 + b = b[1:] |
| 61 + if len(b) < l: |
| 62 + raise BadNextProtos(len(b)) |
| 63 + protos.append(b[:l]) |
| 64 + b = b[l:] |
| 65 + return protos |
| 66 + |
| 67 +def next_protos_encoded(protocol_list): |
| 68 + b = bytearray() |
| 69 + for e in protocol_list: |
| 70 + if len(e) > 255 or len(e) == 0: |
| 71 + raise BadNextProtos(len(e)) |
| 72 + b += bytearray( [len(e)] ) + bytearray(e) |
| 73 + return b |
| 74 + |
| 75 class RecordHeader3(object): |
| 76 def __init__(self): |
| 77 self.type = 0 |
| 78 @@ -111,6 +132,7 @@ class ClientHello(HandshakeMsg): |
| 79 self.compression_methods = [] # a list of 8-bit values |
| 80 self.srp_username = None # a string |
| 81 self.tack = False |
| 82 + self.alpn_protos_advertised = None |
| 83 self.supports_npn = False |
| 84 self.server_name = bytearray(0) |
| 85 self.channel_id = False |
| 86 @@ -121,7 +143,8 @@ class ClientHello(HandshakeMsg): |
| 87 |
| 88 def create(self, version, random, session_id, cipher_suites, |
| 89 certificate_types=None, srpUsername=None, |
| 90 - tack=False, supports_npn=False, serverName=None): |
| 91 + tack=False, alpn_protos_advertised=None, |
| 92 + supports_npn=False, serverName=None): |
| 93 self.client_version = version |
| 94 self.random = random |
| 95 self.session_id = session_id |
| 96 @@ -131,6 +154,7 @@ class ClientHello(HandshakeMsg): |
| 97 if srpUsername: |
| 98 self.srp_username = bytearray(srpUsername, "utf-8") |
| 99 self.tack = tack |
| 100 + self.alpn_protos_advertised = alpn_protos_advertised |
| 101 self.supports_npn = supports_npn |
| 102 if serverName: |
| 103 self.server_name = bytearray(serverName, "utf-8") |
| 104 @@ -171,6 +195,11 @@ class ClientHello(HandshakeMsg): |
| 105 self.certificate_types = p.getVarList(1, 1) |
| 106 elif extType == ExtensionType.tack: |
| 107 self.tack = True |
| 108 + elif extType == ExtensionType.alpn: |
| 109 + structLength = p.get(2) |
| 110 + if structLength + 2 != extLength: |
| 111 + raise SyntaxError() |
| 112 + self.alpn_protos_advertised = parse_next_protos(p.getFi
xBytes(structLength)) |
| 113 elif extType == ExtensionType.supports_npn: |
| 114 self.supports_npn = True |
| 115 elif extType == ExtensionType.server_name: |
| 116 @@ -243,6 +272,12 @@ class ClientHello(HandshakeMsg): |
| 117 w2.add(ExtensionType.srp, 2) |
| 118 w2.add(len(self.srp_username)+1, 2) |
| 119 w2.addVarSeq(self.srp_username, 1, 1) |
| 120 + if self.alpn_protos_advertised is not None: |
| 121 + encoded_alpn_protos_advertised = next_protos_encoded(self.alpn_prot
os_advertised) |
| 122 + w2.add(ExtensionType.alpn, 2) |
| 123 + w2.add(len(encoded_alpn_protos_advertised) + 2, 2) |
| 124 + w2.add(len(encoded_alpn_protos_advertised), 2) |
| 125 + w2.addFixSeq(encoded_alpn_protos_advertised, 1) |
| 126 if self.supports_npn: |
| 127 w2.add(ExtensionType.supports_npn, 2) |
| 128 w2.add(0, 2) |
| 129 @@ -267,6 +302,13 @@ class BadNextProtos(Exception): |
| 130 def __str__(self): |
| 131 return 'Cannot encode a list of next protocols because it contains an e
lement with invalid length %d. Element lengths must be 0 < x < 256' % self.lengt
h |
| 132 |
| 133 +class InvalidALPNResponse(Exception): |
| 134 + def __init__(self, l): |
| 135 + self.length = l |
| 136 + |
| 137 + def __str__(self): |
| 138 + return 'ALPN server response protocol list has invalid length %d. It m
ust be of length one.' % self.length |
| 139 + |
| 140 class ServerHello(HandshakeMsg): |
| 141 def __init__(self): |
| 142 HandshakeMsg.__init__(self, HandshakeType.server_hello) |
| 143 @@ -277,6 +319,7 @@ class ServerHello(HandshakeMsg): |
| 144 self.certificate_type = CertificateType.x509 |
| 145 self.compression_method = 0 |
| 146 self.tackExt = None |
| 147 + self.alpn_proto_selected = None |
| 148 self.next_protos_advertised = None |
| 149 self.next_protos = None |
| 150 self.channel_id = False |
| 151 @@ -286,7 +329,8 @@ class ServerHello(HandshakeMsg): |
| 152 self.status_request = False |
| 153 |
| 154 def create(self, version, random, session_id, cipher_suite, |
| 155 - certificate_type, tackExt, next_protos_advertised): |
| 156 + certificate_type, tackExt, alpn_proto_selected, |
| 157 + next_protos_advertised): |
| 158 self.server_version = version |
| 159 self.random = random |
| 160 self.session_id = session_id |
| 161 @@ -294,6 +338,7 @@ class ServerHello(HandshakeMsg): |
| 162 self.certificate_type = certificate_type |
| 163 self.compression_method = 0 |
| 164 self.tackExt = tackExt |
| 165 + self.alpn_proto_selected = alpn_proto_selected |
| 166 self.next_protos_advertised = next_protos_advertised |
| 167 return self |
| 168 |
| 169 @@ -316,35 +361,22 @@ class ServerHello(HandshakeMsg): |
| 170 self.certificate_type = p.get(1) |
| 171 elif extType == ExtensionType.tack and tackpyLoaded: |
| 172 self.tackExt = TackExtension(p.getFixBytes(extLength)) |
| 173 + elif extType == ExtensionType.alpn: |
| 174 + structLength = p.get(2) |
| 175 + if structLength + 2 != extLength: |
| 176 + raise SyntaxError() |
| 177 + alpn_protos = parse_next_protos(p.getFixBytes(structLength)
) |
| 178 + if len(alpn_protos) != 1: |
| 179 + raise InvalidALPNResponse(len(alpn_protos)); |
| 180 + self.alpn_proto_selected = alpn_protos[0] |
| 181 elif extType == ExtensionType.supports_npn: |
| 182 - self.next_protos = self.__parse_next_protos(p.getFixBytes(e
xtLength)) |
| 183 + self.next_protos = parse_next_protos(p.getFixBytes(extLengt
h)) |
| 184 else: |
| 185 p.getFixBytes(extLength) |
| 186 soFar += 4 + extLength |
| 187 p.stopLengthCheck() |
| 188 return self |
| 189 |
| 190 - def __parse_next_protos(self, b): |
| 191 - protos = [] |
| 192 - while True: |
| 193 - if len(b) == 0: |
| 194 - break |
| 195 - l = b[0] |
| 196 - b = b[1:] |
| 197 - if len(b) < l: |
| 198 - raise BadNextProtos(len(b)) |
| 199 - protos.append(b[:l]) |
| 200 - b = b[l:] |
| 201 - return protos |
| 202 - |
| 203 - def __next_protos_encoded(self): |
| 204 - b = bytearray() |
| 205 - for e in self.next_protos_advertised: |
| 206 - if len(e) > 255 or len(e) == 0: |
| 207 - raise BadNextProtos(len(e)) |
| 208 - b += bytearray( [len(e)] ) + bytearray(e) |
| 209 - return b |
| 210 - |
| 211 def write(self): |
| 212 w = Writer() |
| 213 w.add(self.server_version[0], 1) |
| 214 @@ -365,8 +397,15 @@ class ServerHello(HandshakeMsg): |
| 215 w2.add(ExtensionType.tack, 2) |
| 216 w2.add(len(b), 2) |
| 217 w2.bytes += b |
| 218 + if self.alpn_proto_selected is not None: |
| 219 + alpn_protos_single_element_list = [self.alpn_proto_selected] |
| 220 + encoded_alpn_protos_advertised = next_protos_encoded(alpn_protos_si
ngle_element_list) |
| 221 + w2.add(ExtensionType.alpn, 2) |
| 222 + w2.add(len(encoded_alpn_protos_advertised) + 2, 2) |
| 223 + w2.add(len(encoded_alpn_protos_advertised), 2) |
| 224 + w2.addFixSeq(encoded_alpn_protos_advertised, 1) |
| 225 if self.next_protos_advertised is not None: |
| 226 - encoded_next_protos_advertised = self.__next_protos_encoded() |
| 227 + encoded_next_protos_advertised = next_protos_encoded(self.next_prot
os_advertised) |
| 228 w2.add(ExtensionType.supports_npn, 2) |
| 229 w2.add(len(encoded_next_protos_advertised), 2) |
| 230 w2.addFixSeq(encoded_next_protos_advertised, 1) |
| 231 diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/
tlslite/tlsconnection.py |
| 232 index 41aab85..de5d580 100644 |
| 233 --- a/third_party/tlslite/tlslite/tlsconnection.py |
| 234 +++ b/third_party/tlslite/tlslite/tlsconnection.py |
| 235 @@ -495,6 +495,10 @@ class TLSConnection(TLSRecordLayer): |
| 236 settings = HandshakeSettings() |
| 237 settings = settings._filter() |
| 238 |
| 239 + if settings.alpnProtos is not None: |
| 240 + if len(settings.alpnProtos) == 0: |
| 241 + raise ValueError("Caller passed no alpnProtos") |
| 242 + |
| 243 if clientCertChain: |
| 244 if not isinstance(clientCertChain, X509CertChain): |
| 245 raise ValueError("Unrecognized certificate type") |
| 246 @@ -651,7 +655,8 @@ class TLSConnection(TLSRecordLayer): |
| 247 session.sessionID, cipherSuites, |
| 248 certificateTypes, |
| 249 session.srpUsername, |
| 250 - reqTack, nextProtos is not None, |
| 251 + reqTack, settings.alpnProtos, |
| 252 + nextProtos is not None, |
| 253 session.serverName) |
| 254 |
| 255 #Or send ClientHello (without) |
| 256 @@ -661,7 +666,8 @@ class TLSConnection(TLSRecordLayer): |
| 257 bytearray(0), cipherSuites, |
| 258 certificateTypes, |
| 259 srpUsername, |
| 260 - reqTack, nextProtos is not None, |
| 261 + reqTack, settings.alpnProtos, |
| 262 + nextProtos is not None, |
| 263 serverName) |
| 264 for result in self._sendMsg(clientHello): |
| 265 yield result |
| 266 @@ -714,6 +720,16 @@ class TLSConnection(TLSRecordLayer): |
| 267 AlertDescription.illegal_parameter, |
| 268 "Server responded with unrequested Tack Extension"): |
| 269 yield result |
| 270 + if serverHello.alpn_proto_selected and not clientHello.alpn_protos_adve
rtised: |
| 271 + for result in self._sendError(\ |
| 272 + AlertDescription.illegal_parameter, |
| 273 + "Server responded with unrequested ALPN Extension"): |
| 274 + yield result |
| 275 + if serverHello.alpn_proto_selected and serverHello.next_protos: |
| 276 + for result in self._sendError(\ |
| 277 + AlertDescription.illegal_parameter, |
| 278 + "Server responded with both ALPN and NPN extension"): |
| 279 + yield result |
| 280 if serverHello.next_protos and not clientHello.supports_npn: |
| 281 for result in self._sendError(\ |
| 282 AlertDescription.illegal_parameter, |
| 283 @@ -1315,6 +1331,15 @@ class TLSConnection(TLSRecordLayer): |
| 284 else: |
| 285 sessionID = bytearray(0) |
| 286 |
| 287 + alpn_proto_selected = None |
| 288 + if (clientHello.alpn_protos_advertised is not None |
| 289 + and settings.alpnProtos is not None): |
| 290 + for proto in settings.alpnProtos: |
| 291 + if proto in clientHello.alpn_protos_advertised: |
| 292 + alpn_proto_selected = proto |
| 293 + nextProtos = None |
| 294 + break; |
| 295 + |
| 296 if not clientHello.supports_npn: |
| 297 nextProtos = None |
| 298 |
| 299 @@ -1330,6 +1355,7 @@ class TLSConnection(TLSRecordLayer): |
| 300 serverHello = ServerHello() |
| 301 serverHello.create(self.version, getRandomBytes(32), sessionID, \ |
| 302 cipherSuite, CertificateType.x509, tackExt, |
| 303 + alpn_proto_selected, |
| 304 nextProtos) |
| 305 serverHello.channel_id = \ |
| 306 clientHello.channel_id and settings.enableChannelID |
| 307 @@ -1500,6 +1526,14 @@ class TLSConnection(TLSRecordLayer): |
| 308 else: |
| 309 assert(False) |
| 310 |
| 311 + alpn_proto_selected = None |
| 312 + if (clientHello.alpn_protos_advertised is not None |
| 313 + and settings.alpnProtos is not None): |
| 314 + for proto in settings.alpnProtos: |
| 315 + if proto in clientHello.alpn_protos_advertised: |
| 316 + alpn_proto_selected = proto |
| 317 + break; |
| 318 + |
| 319 #If resumption was requested and we have a session cache... |
| 320 if clientHello.session_id and sessionCache: |
| 321 session = None |
| 322 @@ -1540,7 +1574,8 @@ class TLSConnection(TLSRecordLayer): |
| 323 serverHello = ServerHello() |
| 324 serverHello.create(self.version, getRandomBytes(32), |
| 325 session.sessionID, session.cipherSuite, |
| 326 - CertificateType.x509, None, None) |
| 327 + CertificateType.x509, None, |
| 328 + alpn_proto_selected, None) |
| 329 serverHello.extended_master_secret = \ |
| 330 clientHello.extended_master_secret and \ |
| 331 settings.enableExtendedMasterSecret |
OLD | NEW |