mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-26 02:10:59 +00:00
manually read tls clienthello [wip]
This commit is contained in:
parent
8ce0de8bed
commit
3fa65c48dd
5
libmproxy/contrib/tls/__init__.py
Normal file
5
libmproxy/contrib/tls/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
213
libmproxy/contrib/tls/_constructs.py
Normal file
213
libmproxy/contrib/tls/_constructs.py
Normal file
@ -0,0 +1,213 @@
|
||||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from construct import Array, Bytes, Struct, UBInt16, UBInt32, UBInt8, PascalString, Embed, \
|
||||
TunnelAdapter, GreedyRange, Switch
|
||||
|
||||
from .utils import UBInt24
|
||||
|
||||
ProtocolVersion = Struct(
|
||||
"version",
|
||||
UBInt8("major"),
|
||||
UBInt8("minor"),
|
||||
)
|
||||
|
||||
TLSPlaintext = Struct(
|
||||
"TLSPlaintext",
|
||||
UBInt8("type"),
|
||||
ProtocolVersion,
|
||||
UBInt16("length"), # TODO: Reject packets with length > 2 ** 14
|
||||
Bytes("fragment", lambda ctx: ctx.length),
|
||||
)
|
||||
|
||||
TLSCompressed = Struct(
|
||||
"TLSCompressed",
|
||||
UBInt8("type"),
|
||||
ProtocolVersion,
|
||||
UBInt16("length"), # TODO: Reject packets with length > 2 ** 14 + 1024
|
||||
Bytes("fragment", lambda ctx: ctx.length),
|
||||
)
|
||||
|
||||
TLSCiphertext = Struct(
|
||||
"TLSCiphertext",
|
||||
UBInt8("type"),
|
||||
ProtocolVersion,
|
||||
UBInt16("length"), # TODO: Reject packets with length > 2 ** 14 + 2048
|
||||
Bytes("fragment", lambda ctx: ctx.length),
|
||||
)
|
||||
|
||||
Random = Struct(
|
||||
"random",
|
||||
UBInt32("gmt_unix_time"),
|
||||
Bytes("random_bytes", 28),
|
||||
)
|
||||
|
||||
SessionID = Struct(
|
||||
"session_id",
|
||||
UBInt8("length"),
|
||||
Bytes("session_id", lambda ctx: ctx.length),
|
||||
)
|
||||
|
||||
CipherSuites = Struct(
|
||||
"cipher_suites",
|
||||
UBInt16("length"), # TODO: Reject packets of length 0
|
||||
Array(lambda ctx: ctx.length // 2, UBInt16("cipher_suites")),
|
||||
)
|
||||
|
||||
CompressionMethods = Struct(
|
||||
"compression_methods",
|
||||
UBInt8("length"), # TODO: Reject packets of length 0
|
||||
Array(lambda ctx: ctx.length, UBInt8("compression_methods")),
|
||||
)
|
||||
|
||||
ServerName = Struct(
|
||||
"",
|
||||
UBInt8("type"),
|
||||
PascalString("name", length_field=UBInt16("length")),
|
||||
)
|
||||
|
||||
SNIExtension = Struct(
|
||||
"",
|
||||
TunnelAdapter(
|
||||
PascalString("server_names", length_field=UBInt16("length")),
|
||||
TunnelAdapter(
|
||||
PascalString("", length_field=UBInt16("length")),
|
||||
GreedyRange(ServerName)
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
ALPNExtension = Struct(
|
||||
"",
|
||||
TunnelAdapter(
|
||||
PascalString("alpn_protocols", length_field=UBInt16("length")),
|
||||
TunnelAdapter(
|
||||
PascalString("", length_field=UBInt16("length")),
|
||||
GreedyRange(PascalString("name"))
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
UnknownExtension = Struct(
|
||||
"",
|
||||
PascalString("bytes", length_field=UBInt16("extensions_length"))
|
||||
)
|
||||
|
||||
Extension = Struct(
|
||||
"Extension",
|
||||
UBInt16("type"),
|
||||
Embed(
|
||||
Switch(
|
||||
"data", lambda ctx: ctx.type,
|
||||
{
|
||||
0x00: SNIExtension,
|
||||
0x10: ALPNExtension
|
||||
},
|
||||
default=UnknownExtension
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
extensions = TunnelAdapter(
|
||||
PascalString("extensions", length_field=UBInt16("extensions_length")),
|
||||
GreedyRange(Extension)
|
||||
)
|
||||
|
||||
ClientHello = Struct(
|
||||
"ClientHello",
|
||||
ProtocolVersion,
|
||||
Random,
|
||||
SessionID,
|
||||
CipherSuites,
|
||||
CompressionMethods,
|
||||
extensions,
|
||||
)
|
||||
|
||||
ServerHello = Struct(
|
||||
"ServerHello",
|
||||
ProtocolVersion,
|
||||
Random,
|
||||
SessionID,
|
||||
Bytes("cipher_suite", 2),
|
||||
UBInt8("compression_method"),
|
||||
extensions,
|
||||
)
|
||||
|
||||
ClientCertificateType = Struct(
|
||||
"certificate_types",
|
||||
UBInt8("length"), # TODO: Reject packets of length 0
|
||||
Array(lambda ctx: ctx.length, UBInt8("certificate_types")),
|
||||
)
|
||||
|
||||
SignatureAndHashAlgorithm = Struct(
|
||||
"algorithms",
|
||||
UBInt8("hash"),
|
||||
UBInt8("signature"),
|
||||
)
|
||||
|
||||
SupportedSignatureAlgorithms = Struct(
|
||||
"supported_signature_algorithms",
|
||||
UBInt16("supported_signature_algorithms_length"),
|
||||
# TODO: Reject packets of length 0
|
||||
Array(
|
||||
lambda ctx: ctx.supported_signature_algorithms_length / 2,
|
||||
SignatureAndHashAlgorithm,
|
||||
),
|
||||
)
|
||||
|
||||
DistinguishedName = Struct(
|
||||
"certificate_authorities",
|
||||
UBInt16("length"),
|
||||
Bytes("certificate_authorities", lambda ctx: ctx.length),
|
||||
)
|
||||
|
||||
CertificateRequest = Struct(
|
||||
"CertificateRequest",
|
||||
ClientCertificateType,
|
||||
SupportedSignatureAlgorithms,
|
||||
DistinguishedName,
|
||||
)
|
||||
|
||||
ServerDHParams = Struct(
|
||||
"ServerDHParams",
|
||||
UBInt16("dh_p_length"),
|
||||
Bytes("dh_p", lambda ctx: ctx.dh_p_length),
|
||||
UBInt16("dh_g_length"),
|
||||
Bytes("dh_g", lambda ctx: ctx.dh_g_length),
|
||||
UBInt16("dh_Ys_length"),
|
||||
Bytes("dh_Ys", lambda ctx: ctx.dh_Ys_length),
|
||||
)
|
||||
|
||||
PreMasterSecret = Struct(
|
||||
"pre_master_secret",
|
||||
ProtocolVersion,
|
||||
Bytes("random_bytes", 46),
|
||||
)
|
||||
|
||||
ASN1Cert = Struct(
|
||||
"ASN1Cert",
|
||||
UBInt32("length"), # TODO: Reject packets with length not in 1..2^24-1
|
||||
Bytes("asn1_cert", lambda ctx: ctx.length),
|
||||
)
|
||||
|
||||
Certificate = Struct(
|
||||
"Certificate", # TODO: Reject packets with length > 2 ** 24 - 1
|
||||
UBInt32("certificates_length"),
|
||||
Bytes("certificates_bytes", lambda ctx: ctx.certificates_length),
|
||||
)
|
||||
|
||||
Handshake = Struct(
|
||||
"Handshake",
|
||||
UBInt8("msg_type"),
|
||||
UBInt24("length"), # TODO: Reject packets with length > 2 ** 24
|
||||
Bytes("body", lambda ctx: ctx.length),
|
||||
)
|
||||
|
||||
Alert = Struct(
|
||||
"Alert",
|
||||
UBInt8("level"),
|
||||
UBInt8("description"),
|
||||
)
|
64
libmproxy/contrib/tls/alert_message.py
Normal file
64
libmproxy/contrib/tls/alert_message.py
Normal file
@ -0,0 +1,64 @@
|
||||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from characteristic import attributes
|
||||
|
||||
from . import _constructs
|
||||
|
||||
|
||||
class AlertLevel(Enum):
|
||||
WARNING = 1
|
||||
FATAL = 2
|
||||
|
||||
|
||||
class AlertDescription(Enum):
|
||||
CLOSE_NOTIFY = 0
|
||||
UNEXPECTED_MESSAGE = 10
|
||||
BAD_RECORD_MAC = 20
|
||||
DECRYPTION_FAILED_RESERVED = 21
|
||||
RECORD_OVERFLOW = 22
|
||||
DECOMPRESSION_FAILURE = 30
|
||||
HANDSHAKE_FAILURE = 40
|
||||
NO_CERTIFICATE_RESERVED = 41
|
||||
BAD_CERTIFICATE = 42
|
||||
UNSUPPORTED_CERTIFICATE = 43
|
||||
CERTIFICATE_REVOKED = 44
|
||||
CERTIFICATE_EXPIRED = 45
|
||||
CERTIFICATE_UNKNOWN = 46
|
||||
ILLEGAL_PARAMETER = 47
|
||||
UNKNOWN_CA = 48
|
||||
ACCESS_DENIED = 49
|
||||
DECODE_ERROR = 50
|
||||
DECRYPT_ERROR = 51
|
||||
EXPORT_RESTRICTION_RESERVED = 60
|
||||
PROTOCOL_VERSION = 70
|
||||
INSUFFICIENT_SECURITY = 71
|
||||
INTERNAL_ERROR = 80
|
||||
USER_CANCELED = 90
|
||||
NO_RENEGOTIATION = 100
|
||||
UNSUPPORTED_EXTENSION = 110
|
||||
|
||||
|
||||
@attributes(['level', 'description'])
|
||||
class Alert(object):
|
||||
"""
|
||||
An object representing an Alert message.
|
||||
"""
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse an ``Alert`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: Alert object.
|
||||
"""
|
||||
construct = _constructs.Alert.parse(bytes)
|
||||
return cls(
|
||||
level=AlertLevel(construct.level),
|
||||
description=AlertDescription(construct.description)
|
||||
)
|
343
libmproxy/contrib/tls/ciphersuites.py
Normal file
343
libmproxy/contrib/tls/ciphersuites.py
Normal file
@ -0,0 +1,343 @@
|
||||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from .exceptions import UnsupportedCipherException
|
||||
|
||||
|
||||
class CipherSuites(Enum):
|
||||
TLS_NULL_WITH_NULL_NULL = 0x0000
|
||||
TLS_RSA_WITH_NULL_MD5 = 0x0001
|
||||
TLS_RSA_WITH_NULL_SHA = 0x0002
|
||||
TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003
|
||||
TLS_RSA_WITH_RC4_128_MD5 = 0x0004
|
||||
TLS_RSA_WITH_RC4_128_SHA = 0x0005
|
||||
TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006
|
||||
TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007
|
||||
TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008
|
||||
TLS_RSA_WITH_DES_CBC_SHA = 0x0009
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A
|
||||
TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B
|
||||
TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C
|
||||
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D
|
||||
TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E
|
||||
TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F
|
||||
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010
|
||||
TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011
|
||||
TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012
|
||||
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013
|
||||
TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014
|
||||
TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015
|
||||
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016
|
||||
TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017
|
||||
TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018
|
||||
TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019
|
||||
TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A
|
||||
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B
|
||||
TLS_KRB5_WITH_DES_CBC_SHA = 0x001E
|
||||
TLS_KRB5_WITH_3DES_EDE_CBC_SHA = 0x001F
|
||||
TLS_KRB5_WITH_RC4_128_SHA = 0x0020
|
||||
TLS_KRB5_WITH_IDEA_CBC_SHA = 0x0021
|
||||
TLS_KRB5_WITH_DES_CBC_MD5 = 0x0022
|
||||
TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = 0x0023
|
||||
TLS_KRB5_WITH_RC4_128_MD5 = 0x0024
|
||||
TLS_KRB5_WITH_IDEA_CBC_MD5 = 0x0025
|
||||
TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = 0x0026
|
||||
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = 0x0027
|
||||
TLS_KRB5_EXPORT_WITH_RC4_40_SHA = 0x0028
|
||||
TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = 0x0029
|
||||
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = 0x002A
|
||||
TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = 0x002B
|
||||
TLS_PSK_WITH_NULL_SHA = 0x002C
|
||||
TLS_DHE_PSK_WITH_NULL_SHA = 0x002D
|
||||
TLS_RSA_PSK_WITH_NULL_SHA = 0x002E
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F
|
||||
TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030
|
||||
TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031
|
||||
TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033
|
||||
TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035
|
||||
TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036
|
||||
TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037
|
||||
TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038
|
||||
TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039
|
||||
TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A
|
||||
TLS_RSA_WITH_NULL_SHA256 = 0x003B
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D
|
||||
TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E
|
||||
TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F
|
||||
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040
|
||||
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041
|
||||
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042
|
||||
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045
|
||||
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067
|
||||
TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068
|
||||
TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069
|
||||
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A
|
||||
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B
|
||||
TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C
|
||||
TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D
|
||||
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084
|
||||
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085
|
||||
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088
|
||||
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089
|
||||
TLS_PSK_WITH_RC4_128_SHA = 0x008A
|
||||
TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B
|
||||
TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C
|
||||
TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D
|
||||
TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E
|
||||
TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F
|
||||
TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090
|
||||
TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091
|
||||
TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092
|
||||
TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093
|
||||
TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094
|
||||
TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095
|
||||
TLS_RSA_WITH_SEED_CBC_SHA = 0x0096
|
||||
TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097
|
||||
TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098
|
||||
TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099
|
||||
TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A
|
||||
TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D
|
||||
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E
|
||||
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F
|
||||
TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0
|
||||
TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1
|
||||
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2
|
||||
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3
|
||||
TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4
|
||||
TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5
|
||||
TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6
|
||||
TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7
|
||||
TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8
|
||||
TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9
|
||||
TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA
|
||||
TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB
|
||||
TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC
|
||||
TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD
|
||||
TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE
|
||||
TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF
|
||||
TLS_PSK_WITH_NULL_SHA256 = 0x00B0
|
||||
TLS_PSK_WITH_NULL_SHA384 = 0x00B1
|
||||
TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2
|
||||
TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3
|
||||
TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4
|
||||
TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5
|
||||
TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6
|
||||
TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7
|
||||
TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8
|
||||
TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9
|
||||
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA
|
||||
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB
|
||||
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE
|
||||
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF
|
||||
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0
|
||||
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1
|
||||
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4
|
||||
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5
|
||||
TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF
|
||||
TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001
|
||||
TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002
|
||||
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005
|
||||
TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007
|
||||
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A
|
||||
TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B
|
||||
TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C
|
||||
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D
|
||||
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E
|
||||
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F
|
||||
TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010
|
||||
TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014
|
||||
TLS_ECDH_anon_WITH_NULL_SHA = 0xC015
|
||||
TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016
|
||||
TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017
|
||||
TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018
|
||||
TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019
|
||||
TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A
|
||||
TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B
|
||||
TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C
|
||||
TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D
|
||||
TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E
|
||||
TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F
|
||||
TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020
|
||||
TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021
|
||||
TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028
|
||||
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029
|
||||
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030
|
||||
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031
|
||||
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032
|
||||
TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033
|
||||
TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034
|
||||
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035
|
||||
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036
|
||||
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037
|
||||
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038
|
||||
TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039
|
||||
TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A
|
||||
TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B
|
||||
TLS_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC03C
|
||||
TLS_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC03D
|
||||
TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 = 0xC03E
|
||||
TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 = 0xC03F
|
||||
TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC040
|
||||
TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC041
|
||||
TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 = 0xC042
|
||||
TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 = 0xC043
|
||||
TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC044
|
||||
TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC045
|
||||
TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 = 0xC046
|
||||
TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 = 0xC047
|
||||
TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 = 0xC048
|
||||
TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 = 0xC049
|
||||
TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 = 0xC04A
|
||||
TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 = 0xC04B
|
||||
TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC04C
|
||||
TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC04D
|
||||
TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC04E
|
||||
TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC04F
|
||||
TLS_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC050
|
||||
TLS_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC051
|
||||
TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC052
|
||||
TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC053
|
||||
TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC054
|
||||
TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC055
|
||||
TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 = 0xC056
|
||||
TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 = 0xC057
|
||||
TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 = 0xC058
|
||||
TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 = 0xC059
|
||||
TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 = 0xC05A
|
||||
TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 = 0xC05B
|
||||
TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 = 0xC05C
|
||||
TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 = 0xC05D
|
||||
TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 = 0xC05E
|
||||
TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 = 0xC05F
|
||||
TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC060
|
||||
TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC061
|
||||
TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC062
|
||||
TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC063
|
||||
TLS_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC064
|
||||
TLS_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC065
|
||||
TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC066
|
||||
TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC067
|
||||
TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC068
|
||||
TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC069
|
||||
TLS_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06A
|
||||
TLS_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06B
|
||||
TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06C
|
||||
TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06D
|
||||
TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06E
|
||||
TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06F
|
||||
TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC070
|
||||
TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC071
|
||||
TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC072
|
||||
TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC073
|
||||
TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC074
|
||||
TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC075
|
||||
TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC076
|
||||
TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC077
|
||||
TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC078
|
||||
TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC079
|
||||
TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07A
|
||||
TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07B
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07C
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07D
|
||||
TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07E
|
||||
TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07F
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC080
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC081
|
||||
TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC082
|
||||
TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC083
|
||||
TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 = 0xC084
|
||||
TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 = 0xC085
|
||||
TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC086
|
||||
TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC087
|
||||
TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC088
|
||||
TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC089
|
||||
TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08A
|
||||
TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08B
|
||||
TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08C
|
||||
TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08D
|
||||
TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08E
|
||||
TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08F
|
||||
TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC090
|
||||
TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC091
|
||||
TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC092
|
||||
TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC093
|
||||
TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC094
|
||||
TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC095
|
||||
TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC096
|
||||
TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC097
|
||||
TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC098
|
||||
TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC099
|
||||
TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC09A
|
||||
TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC09B
|
||||
TLS_RSA_WITH_AES_128_CCM = 0xC09C
|
||||
TLS_RSA_WITH_AES_256_CCM = 0xC09D
|
||||
TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E
|
||||
TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F
|
||||
TLS_RSA_WITH_AES_128_CCM_8 = 0xC0A0
|
||||
TLS_RSA_WITH_AES_256_CCM_8 = 0xC0A1
|
||||
TLS_DHE_RSA_WITH_AES_128_CCM_8 = 0xC0A2
|
||||
TLS_DHE_RSA_WITH_AES_256_CCM_8 = 0xC0A3
|
||||
TLS_PSK_WITH_AES_128_CCM = 0xC0A4
|
||||
TLS_PSK_WITH_AES_256_CCM = 0xC0A5
|
||||
TLS_DHE_PSK_WITH_AES_128_CCM = 0xC0A6
|
||||
TLS_DHE_PSK_WITH_AES_256_CCM = 0xC0A7
|
||||
TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8
|
||||
TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9
|
||||
TLS_PSK_DHE_WITH_AES_128_CCM_8 = 0xC0AA
|
||||
TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CCM = 0xC0AC
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CCM = 0xC0AD
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 0xC0AE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = 0xC0AF
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC14
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC13
|
||||
|
||||
|
||||
def select_preferred_ciphersuite(client_supported, server_supported):
|
||||
for i in server_supported:
|
||||
assert isinstance(i, CipherSuites)
|
||||
if i in client_supported:
|
||||
return i
|
||||
|
||||
raise UnsupportedCipherException(
|
||||
"Client supported ciphersuites are not supported on the server."
|
||||
)
|
2
libmproxy/contrib/tls/exceptions.py
Normal file
2
libmproxy/contrib/tls/exceptions.py
Normal file
@ -0,0 +1,2 @@
|
||||
class UnsupportedCipherException(Exception):
|
||||
pass
|
178
libmproxy/contrib/tls/hello_message.py
Normal file
178
libmproxy/contrib/tls/hello_message.py
Normal file
@ -0,0 +1,178 @@
|
||||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from characteristic import attributes
|
||||
|
||||
from construct import Container
|
||||
|
||||
from six import BytesIO
|
||||
|
||||
from . import _constructs
|
||||
|
||||
|
||||
@attributes(['major', 'minor'])
|
||||
class ProtocolVersion(object):
|
||||
"""
|
||||
An object representing a ProtocolVersion struct.
|
||||
"""
|
||||
|
||||
|
||||
@attributes(['gmt_unix_time', 'random_bytes'])
|
||||
class Random(object):
|
||||
"""
|
||||
An object representing a Random struct.
|
||||
"""
|
||||
|
||||
|
||||
@attributes(['type', 'data'])
|
||||
class Extension(object):
|
||||
"""
|
||||
An object representing an Extension struct.
|
||||
"""
|
||||
def as_bytes(self):
|
||||
return _constructs.Extension.build(Container(
|
||||
type=self.type.value, length=len(self.data), data=self.data))
|
||||
|
||||
|
||||
@attributes(['client_version', 'random', 'session_id', 'cipher_suites',
|
||||
'compression_methods', 'extensions'])
|
||||
class ClientHello(object):
|
||||
"""
|
||||
An object representing a ClientHello message.
|
||||
"""
|
||||
def as_bytes(self):
|
||||
return _constructs.ClientHello.build(
|
||||
Container(
|
||||
version=Container(major=self.client_version.major,
|
||||
minor=self.client_version.minor),
|
||||
random=Container(
|
||||
gmt_unix_time=self.random.gmt_unix_time,
|
||||
random_bytes=self.random.random_bytes
|
||||
),
|
||||
session_id=Container(length=len(self.session_id),
|
||||
session_id=self.session_id),
|
||||
cipher_suites=Container(length=len(self.cipher_suites) * 2,
|
||||
cipher_suites=self.cipher_suites),
|
||||
compression_methods=Container(
|
||||
length=len(self.compression_methods),
|
||||
compression_methods=self.compression_methods
|
||||
),
|
||||
extensions_length=sum([2 + 2 + len(ext.data)
|
||||
for ext in self.extensions]),
|
||||
extensions_bytes=b''.join(
|
||||
[ext.as_bytes() for ext in self.extensions]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``ClientHello`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: ClientHello object.
|
||||
"""
|
||||
construct = _constructs.ClientHello.parse(bytes)
|
||||
# XXX Is there a better way in Construct to parse an array of
|
||||
# variable-length structs?
|
||||
extensions = []
|
||||
extensions_io = BytesIO(construct.extensions_bytes)
|
||||
while extensions_io.tell() < construct.extensions_length:
|
||||
extension_construct = _constructs.Extension.parse_stream(
|
||||
extensions_io)
|
||||
extensions.append(
|
||||
Extension(type=ExtensionType(extension_construct.type),
|
||||
data=extension_construct.data))
|
||||
return ClientHello(
|
||||
client_version=ProtocolVersion(
|
||||
major=construct.version.major,
|
||||
minor=construct.version.minor,
|
||||
),
|
||||
random=Random(
|
||||
gmt_unix_time=construct.random.gmt_unix_time,
|
||||
random_bytes=construct.random.random_bytes,
|
||||
),
|
||||
session_id=construct.session_id.session_id,
|
||||
# TODO: cipher suites should be enums
|
||||
cipher_suites=construct.cipher_suites.cipher_suites,
|
||||
compression_methods=(
|
||||
construct.compression_methods.compression_methods
|
||||
),
|
||||
extensions=extensions,
|
||||
)
|
||||
|
||||
|
||||
class ExtensionType(Enum):
|
||||
SIGNATURE_ALGORITHMS = 13
|
||||
# XXX: See http://tools.ietf.org/html/rfc5246#ref-TLSEXT
|
||||
|
||||
|
||||
@attributes(['server_version', 'random', 'session_id', 'cipher_suite',
|
||||
'compression_method', 'extensions'])
|
||||
class ServerHello(object):
|
||||
"""
|
||||
An object representing a ServerHello message.
|
||||
"""
|
||||
def as_bytes(self):
|
||||
return _constructs.ServerHello.build(
|
||||
Container(
|
||||
version=Container(major=self.server_version.major,
|
||||
minor=self.server_version.minor),
|
||||
random=Container(
|
||||
gmt_unix_time=self.random.gmt_unix_time,
|
||||
random_bytes=self.random.random_bytes
|
||||
),
|
||||
session_id=Container(length=len(self.session_id),
|
||||
session_id=self.session_id),
|
||||
cipher_suite=self.cipher_suite,
|
||||
compression_method=self.compression_method.value,
|
||||
extensions_length=sum([2 + 2 + len(ext.data)
|
||||
for ext in self.extensions]),
|
||||
extensions_bytes=b''.join(
|
||||
[ext.as_bytes() for ext in self.extensions]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``ServerHello`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: ServerHello object.
|
||||
"""
|
||||
construct = _constructs.ServerHello.parse(bytes)
|
||||
# XXX: Find a better way to parse extensions
|
||||
extensions = []
|
||||
extensions_io = BytesIO(construct.extensions_bytes)
|
||||
while extensions_io.tell() < construct.extensions_length:
|
||||
extension_construct = _constructs.Extension.parse_stream(
|
||||
extensions_io)
|
||||
extensions.append(
|
||||
Extension(type=ExtensionType(extension_construct.type),
|
||||
data=extension_construct.data))
|
||||
return ServerHello(
|
||||
server_version=ProtocolVersion(
|
||||
major=construct.version.major,
|
||||
minor=construct.version.minor,
|
||||
),
|
||||
random=Random(
|
||||
gmt_unix_time=construct.random.gmt_unix_time,
|
||||
random_bytes=construct.random.random_bytes,
|
||||
),
|
||||
session_id=construct.session_id.session_id,
|
||||
cipher_suite=construct.cipher_suite,
|
||||
compression_method=CompressionMethod(construct.compression_method),
|
||||
extensions=extensions,
|
||||
)
|
||||
|
||||
|
||||
class CompressionMethod(Enum):
|
||||
NULL = 0
|
313
libmproxy/contrib/tls/message.py
Normal file
313
libmproxy/contrib/tls/message.py
Normal file
@ -0,0 +1,313 @@
|
||||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from characteristic import attributes
|
||||
|
||||
from construct import Container
|
||||
|
||||
from six import BytesIO
|
||||
|
||||
from . import _constructs
|
||||
|
||||
from .hello_message import (
|
||||
ClientHello, ProtocolVersion, ServerHello
|
||||
)
|
||||
|
||||
|
||||
class ClientCertificateType(Enum):
|
||||
RSA_SIGN = 1
|
||||
DSS_SIGN = 2
|
||||
RSA_FIXED_DH = 3
|
||||
DSS_FIXED_DH = 4
|
||||
RSA_EPHEMERAL_DH_RESERVED = 5
|
||||
DSS_EPHEMERAL_DH_RESERVED = 6
|
||||
FORTEZZA_DMS_RESERVED = 20
|
||||
|
||||
|
||||
class HashAlgorithm(Enum):
|
||||
NONE = 0
|
||||
MD5 = 1
|
||||
SHA1 = 2
|
||||
SHA224 = 3
|
||||
SHA256 = 4
|
||||
SHA384 = 5
|
||||
SHA512 = 6
|
||||
|
||||
|
||||
class SignatureAlgorithm(Enum):
|
||||
ANONYMOUS = 0
|
||||
RSA = 1
|
||||
DSA = 2
|
||||
ECDSA = 3
|
||||
|
||||
|
||||
class HandshakeType(Enum):
|
||||
HELLO_REQUEST = 0
|
||||
CLIENT_HELLO = 1
|
||||
SERVER_HELLO = 2
|
||||
CERTIFICATE = 11
|
||||
SERVER_KEY_EXCHANGE = 12
|
||||
CERTIFICATE_REQUEST = 13
|
||||
SERVER_HELLO_DONE = 14
|
||||
CERTIFICATE_VERIFY = 15
|
||||
CLIENT_KEY_EXCHANGE = 16
|
||||
FINISHED = 20
|
||||
|
||||
|
||||
class HelloRequest(object):
|
||||
"""
|
||||
An object representing a HelloRequest struct.
|
||||
"""
|
||||
def as_bytes(self):
|
||||
return b''
|
||||
|
||||
|
||||
class ServerHelloDone(object):
|
||||
"""
|
||||
An object representing a ServerHelloDone struct.
|
||||
"""
|
||||
def as_bytes(self):
|
||||
return b''
|
||||
|
||||
|
||||
@attributes(['certificate_types', 'supported_signature_algorithms',
|
||||
'certificate_authorities'])
|
||||
class CertificateRequest(object):
|
||||
"""
|
||||
An object representing a CertificateRequest struct.
|
||||
"""
|
||||
def as_bytes(self):
|
||||
return _constructs.CertificateRequest.build(Container(
|
||||
certificate_types=Container(
|
||||
length=len(self.certificate_types),
|
||||
certificate_types=[cert_type.value
|
||||
for cert_type in self.certificate_types]
|
||||
),
|
||||
supported_signature_algorithms=Container(
|
||||
supported_signature_algorithms_length=2 * len(
|
||||
self.supported_signature_algorithms
|
||||
),
|
||||
algorithms=[Container(
|
||||
hash=algorithm.hash.value,
|
||||
signature=algorithm.signature.value,
|
||||
)
|
||||
for algorithm in self.supported_signature_algorithms
|
||||
]
|
||||
),
|
||||
certificate_authorities=Container(
|
||||
length=len(self.certificate_authorities),
|
||||
certificate_authorities=self.certificate_authorities
|
||||
)
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``CertificateRequest`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: CertificateRequest object.
|
||||
"""
|
||||
construct = _constructs.CertificateRequest.parse(bytes)
|
||||
return cls(
|
||||
certificate_types=[
|
||||
ClientCertificateType(cert_type)
|
||||
for cert_type in construct.certificate_types.certificate_types
|
||||
],
|
||||
supported_signature_algorithms=[
|
||||
SignatureAndHashAlgorithm(
|
||||
hash=HashAlgorithm(algorithm.hash),
|
||||
signature=SignatureAlgorithm(algorithm.signature),
|
||||
)
|
||||
for algorithm in (
|
||||
construct.supported_signature_algorithms.algorithms
|
||||
)
|
||||
],
|
||||
certificate_authorities=(
|
||||
construct.certificate_authorities.certificate_authorities
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@attributes(['hash', 'signature'])
|
||||
class SignatureAndHashAlgorithm(object):
|
||||
"""
|
||||
An object representing a SignatureAndHashAlgorithm struct.
|
||||
"""
|
||||
|
||||
|
||||
@attributes(['dh_p', 'dh_g', 'dh_Ys'])
|
||||
class ServerDHParams(object):
|
||||
"""
|
||||
An object representing a ServerDHParams struct.
|
||||
"""
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``ServerDHParams`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: ServerDHParams object.
|
||||
"""
|
||||
construct = _constructs.ServerDHParams.parse(bytes)
|
||||
return cls(
|
||||
dh_p=construct.dh_p,
|
||||
dh_g=construct.dh_g,
|
||||
dh_Ys=construct.dh_Ys
|
||||
)
|
||||
|
||||
|
||||
@attributes(['client_version', 'random'])
|
||||
class PreMasterSecret(object):
|
||||
"""
|
||||
An object representing a PreMasterSecret struct.
|
||||
"""
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``PreMasterSecret`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: CertificateRequest object.
|
||||
"""
|
||||
construct = _constructs.PreMasterSecret.parse(bytes)
|
||||
return cls(
|
||||
client_version=ProtocolVersion(
|
||||
major=construct.version.major,
|
||||
minor=construct.version.minor,
|
||||
),
|
||||
random=construct.random_bytes,
|
||||
)
|
||||
|
||||
|
||||
@attributes(['asn1_cert'])
|
||||
class ASN1Cert(object):
|
||||
"""
|
||||
An object representing ASN.1 Certificate
|
||||
"""
|
||||
def as_bytes(self):
|
||||
return _constructs.ASN1Cert.build(Container(
|
||||
length=len(self.asn1_cert),
|
||||
asn1_cert=self.asn1_cert
|
||||
))
|
||||
|
||||
|
||||
@attributes(['certificate_list'])
|
||||
class Certificate(object):
|
||||
"""
|
||||
An object representing a Certificate struct.
|
||||
"""
|
||||
def as_bytes(self):
|
||||
return _constructs.Certificate.build(Container(
|
||||
certificates_length=sum([4 + len(asn1cert.asn1_cert)
|
||||
for asn1cert in self.certificate_list]),
|
||||
certificates_bytes=b''.join(
|
||||
[asn1cert.as_bytes() for asn1cert in self.certificate_list]
|
||||
)
|
||||
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``Certificate`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: Certificate object.
|
||||
"""
|
||||
construct = _constructs.Certificate.parse(bytes)
|
||||
# XXX: Find a better way to parse an array of variable-length objects
|
||||
certificates = []
|
||||
certificates_io = BytesIO(construct.certificates_bytes)
|
||||
|
||||
while certificates_io.tell() < construct.certificates_length:
|
||||
certificate_construct = _constructs.ASN1Cert.parse_stream(
|
||||
certificates_io
|
||||
)
|
||||
certificates.append(
|
||||
ASN1Cert(asn1_cert=certificate_construct.asn1_cert)
|
||||
)
|
||||
return cls(
|
||||
certificate_list=certificates
|
||||
)
|
||||
|
||||
|
||||
@attributes(['verify_data'])
|
||||
class Finished(object):
|
||||
def as_bytes(self):
|
||||
return self.verify_data
|
||||
|
||||
|
||||
@attributes(['msg_type', 'length', 'body'])
|
||||
class Handshake(object):
|
||||
"""
|
||||
An object representing a Handshake struct.
|
||||
"""
|
||||
def as_bytes(self):
|
||||
if self.msg_type in [
|
||||
HandshakeType.SERVER_HELLO, HandshakeType.CLIENT_HELLO,
|
||||
HandshakeType.CERTIFICATE, HandshakeType.CERTIFICATE_REQUEST,
|
||||
HandshakeType.HELLO_REQUEST, HandshakeType.SERVER_HELLO_DONE,
|
||||
HandshakeType.FINISHED
|
||||
]:
|
||||
_body_as_bytes = self.body.as_bytes()
|
||||
else:
|
||||
_body_as_bytes = b''
|
||||
return _constructs.Handshake.build(
|
||||
Container(
|
||||
msg_type=self.msg_type.value,
|
||||
length=self.length,
|
||||
body=_body_as_bytes
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``Handshake`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: Handshake object.
|
||||
"""
|
||||
construct = _constructs.Handshake.parse(bytes)
|
||||
return cls(
|
||||
msg_type=HandshakeType(construct.msg_type),
|
||||
length=construct.length,
|
||||
body=cls._get_handshake_message(
|
||||
HandshakeType(construct.msg_type), construct.body
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_handshake_message(msg_type, body):
|
||||
_handshake_message_parser = {
|
||||
HandshakeType.CLIENT_HELLO: ClientHello.from_bytes,
|
||||
HandshakeType.SERVER_HELLO: ServerHello.from_bytes,
|
||||
HandshakeType.CERTIFICATE: Certificate.from_bytes,
|
||||
# 12: parse_server_key_exchange,
|
||||
HandshakeType.CERTIFICATE_REQUEST: CertificateRequest.from_bytes,
|
||||
# 15: parse_certificate_verify,
|
||||
# 16: parse_client_key_exchange,
|
||||
}
|
||||
|
||||
try:
|
||||
if msg_type == HandshakeType.HELLO_REQUEST:
|
||||
return HelloRequest()
|
||||
elif msg_type == HandshakeType.SERVER_HELLO_DONE:
|
||||
return ServerHelloDone()
|
||||
elif msg_type == HandshakeType.FINISHED:
|
||||
return Finished(verify_data=body)
|
||||
elif msg_type in [HandshakeType.SERVER_KEY_EXCHANGE,
|
||||
HandshakeType.CERTIFICATE_VERIFY,
|
||||
HandshakeType.CLIENT_KEY_EXCHANGE,
|
||||
]:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
return _handshake_message_parser[msg_type](body)
|
||||
except NotImplementedError:
|
||||
return None # TODO
|
110
libmproxy/contrib/tls/record.py
Normal file
110
libmproxy/contrib/tls/record.py
Normal file
@ -0,0 +1,110 @@
|
||||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from characteristic import attributes
|
||||
|
||||
from construct import Container
|
||||
|
||||
from . import _constructs
|
||||
|
||||
|
||||
@attributes(['major', 'minor'])
|
||||
class ProtocolVersion(object):
|
||||
"""
|
||||
An object representing a ProtocolVersion struct.
|
||||
"""
|
||||
|
||||
|
||||
@attributes(['type', 'version', 'fragment'])
|
||||
class TLSPlaintext(object):
|
||||
"""
|
||||
An object representing a TLSPlaintext struct.
|
||||
"""
|
||||
def as_bytes(self):
|
||||
return _constructs.TLSPlaintext.build(
|
||||
Container(
|
||||
type=self.type.value,
|
||||
version=Container(major=self.version.major,
|
||||
minor=self.version.minor),
|
||||
length=len(self.fragment),
|
||||
fragment=self.fragment
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``TLSPlaintext`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: TLSPlaintext object.
|
||||
"""
|
||||
construct = _constructs.TLSPlaintext.parse(bytes)
|
||||
return cls(
|
||||
type=ContentType(construct.type),
|
||||
version=ProtocolVersion(
|
||||
major=construct.version.major,
|
||||
minor=construct.version.minor
|
||||
),
|
||||
fragment=construct.fragment
|
||||
)
|
||||
|
||||
|
||||
@attributes(['type', 'version', 'fragment'])
|
||||
class TLSCompressed(object):
|
||||
"""
|
||||
An object representing a TLSCompressed struct.
|
||||
"""
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``TLSCompressed`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: TLSCompressed object.
|
||||
"""
|
||||
construct = _constructs.TLSCompressed.parse(bytes)
|
||||
return cls(
|
||||
type=ContentType(construct.type),
|
||||
version=ProtocolVersion(
|
||||
major=construct.version.major,
|
||||
minor=construct.version.minor
|
||||
),
|
||||
fragment=construct.fragment
|
||||
)
|
||||
|
||||
|
||||
@attributes(['type', 'version', 'fragment'])
|
||||
class TLSCiphertext(object):
|
||||
"""
|
||||
An object representing a TLSCiphertext struct.
|
||||
"""
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
"""
|
||||
Parse a ``TLSCiphertext`` struct.
|
||||
|
||||
:param bytes: the bytes representing the input.
|
||||
:return: TLSCiphertext object.
|
||||
"""
|
||||
construct = _constructs.TLSCiphertext.parse(bytes)
|
||||
return cls(
|
||||
type=ContentType(construct.type),
|
||||
version=ProtocolVersion(
|
||||
major=construct.version.major,
|
||||
minor=construct.version.minor
|
||||
),
|
||||
fragment=construct.fragment
|
||||
)
|
||||
|
||||
|
||||
class ContentType(Enum):
|
||||
CHANGE_CIPHER_SPEC = 20
|
||||
ALERT = 21
|
||||
HANDSHAKE = 22
|
||||
APPLICATION_DATA = 23
|
52
libmproxy/contrib/tls/utils.py
Normal file
52
libmproxy/contrib/tls/utils.py
Normal file
@ -0,0 +1,52 @@
|
||||
# This file is dual licensed under the terms of the Apache License, Version
|
||||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
|
||||
# for complete details.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import construct
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class _UBInt24(construct.Adapter):
|
||||
def _encode(self, obj, context):
|
||||
return (
|
||||
six.int2byte((obj & 0xFF0000) >> 16) +
|
||||
six.int2byte((obj & 0x00FF00) >> 8) +
|
||||
six.int2byte(obj & 0x0000FF)
|
||||
)
|
||||
|
||||
def _decode(self, obj, context):
|
||||
obj = bytearray(obj)
|
||||
return (obj[0] << 16 | obj[1] << 8 | obj[2])
|
||||
|
||||
|
||||
def UBInt24(name): # noqa
|
||||
return _UBInt24(construct.Bytes(name, 3))
|
||||
|
||||
|
||||
def LengthPrefixedArray(subcon, length_field=construct.UBInt8("length")):
|
||||
"""
|
||||
An array prefixed by a byte length field.
|
||||
|
||||
In contrast to construct.macros.PrefixedArray,
|
||||
the length field signifies the number of bytes, not the number of elements.
|
||||
"""
|
||||
subcon_with_pos = construct.Struct(
|
||||
subcon.name,
|
||||
construct.Embed(subcon),
|
||||
construct.Anchor("__current_pos")
|
||||
)
|
||||
|
||||
return construct.Embed(
|
||||
construct.Struct(
|
||||
"",
|
||||
length_field,
|
||||
construct.Anchor("__start_pos"),
|
||||
construct.RepeatUntil(
|
||||
lambda obj, ctx: obj.__current_pos == ctx.__start_pos + getattr(ctx, length_field.name),
|
||||
subcon_with_pos
|
||||
),
|
||||
)
|
||||
)
|
@ -1,6 +1,6 @@
|
||||
from __future__ import (absolute_import, print_function, division)
|
||||
|
||||
import traceback
|
||||
from ..contrib.tls._constructs import ClientHello
|
||||
|
||||
from netlib import tcp
|
||||
import netlib.http.http2
|
||||
@ -11,15 +11,15 @@ from .layer import Layer
|
||||
|
||||
class TlsLayer(Layer):
|
||||
def __init__(self, ctx, client_tls, server_tls):
|
||||
self.client_sni = None
|
||||
self.client_alpn_protos = None
|
||||
|
||||
super(TlsLayer, self).__init__(ctx)
|
||||
self._client_tls = client_tls
|
||||
self._server_tls = server_tls
|
||||
self.client_sni = None
|
||||
self._sni_from_server_change = None
|
||||
self.client_alpn_protos = None
|
||||
self.__server_tls_exception = None
|
||||
|
||||
# foo alpn protos = [netlib.http.http1.HTTP1Protocol.ALPN_PROTO_HTTP1, netlib.http.http2.HTTP2Protocol.ALPN_PROTO_H2], # TODO: read this from client_conn first
|
||||
self._sni_from_server_change = None
|
||||
self.__server_tls_exception = None
|
||||
|
||||
def __call__(self):
|
||||
"""
|
||||
@ -45,6 +45,28 @@ class TlsLayer(Layer):
|
||||
https://www.openssl.org/docs/ssl/SSL_CTX_set_cert_cb.html
|
||||
- The original mitmproxy issue is https://github.com/mitmproxy/mitmproxy/issues/427
|
||||
"""
|
||||
import struct
|
||||
|
||||
# Read all records that contain the initial Client Hello message.
|
||||
client_hello = ""
|
||||
client_hello_size = 1
|
||||
offset = 0
|
||||
while len(client_hello) < client_hello_size:
|
||||
record_header = self.client_conn.rfile.peek(offset+5)[offset:]
|
||||
record_size = struct.unpack("!H", record_header[3:])[0] + 5
|
||||
record_body = self.client_conn.rfile.peek(offset+record_size)[offset+5:]
|
||||
client_hello += record_body
|
||||
offset += record_size
|
||||
client_hello_size = struct.unpack("!I", '\x00' + client_hello[1:4])[0] + 4
|
||||
|
||||
client_hello = ClientHello.parse(client_hello[4:])
|
||||
|
||||
for extension in client_hello.extensions:
|
||||
if extension.type == 0x00:
|
||||
host = extension.server_names[0].name
|
||||
if extension.type == 0x10:
|
||||
alpn = extension.alpn_protocols
|
||||
|
||||
client_tls_requires_server_cert = (
|
||||
self._client_tls and self._server_tls and not self.config.no_upstream_cert
|
||||
)
|
||||
@ -60,12 +82,12 @@ class TlsLayer(Layer):
|
||||
def connect(self):
|
||||
if not self.server_conn:
|
||||
self.ctx.connect()
|
||||
if self._server_tls and not self._server_tls_established:
|
||||
if self._server_tls and not self.server_conn.tls_established:
|
||||
self._establish_tls_with_server()
|
||||
|
||||
def reconnect(self):
|
||||
self.ctx.reconnect()
|
||||
if self._server_tls and not self._server_tls_established:
|
||||
if self._server_tls and not self.server_conn.tls_established:
|
||||
self._establish_tls_with_server()
|
||||
|
||||
def set_server(self, address, server_tls, sni, depth=1):
|
||||
@ -74,10 +96,6 @@ class TlsLayer(Layer):
|
||||
self._sni_from_server_change = sni
|
||||
self._server_tls = server_tls
|
||||
|
||||
@property
|
||||
def _server_tls_established(self):
|
||||
return self.server_conn and self.server_conn.tls_established
|
||||
|
||||
@property
|
||||
def sni_for_upstream_connection(self):
|
||||
if self._sni_from_server_change is False:
|
||||
@ -138,6 +156,10 @@ class TlsLayer(Layer):
|
||||
connection.set_context(new_context)
|
||||
|
||||
def __handle_alpn_select(self, conn_, options):
|
||||
"""
|
||||
Once the client signals the alternate protocols it supports,
|
||||
we reconnect upstream with the same list and pass the server's choice down to the client.
|
||||
"""
|
||||
# TODO: change to something meaningful?
|
||||
# alpn_preference = netlib.http.http1.HTTP1Protocol.ALPN_PROTO_HTTP1
|
||||
alpn_preference = netlib.http.http2.HTTP2Protocol.ALPN_PROTO_H2
|
||||
|
Loading…
Reference in New Issue
Block a user