manually read tls clienthello [wip]

This commit is contained in:
Maximilian Hils 2015-08-26 05:39:00 +02:00
parent 8ce0de8bed
commit 3fa65c48dd
11 changed files with 1317 additions and 13 deletions

View 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

View 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"),
)

View 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)
)

View 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."
)

View File

@ -0,0 +1,2 @@
class UnsupportedCipherException(Exception):
pass

View 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

View 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

View 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

View 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
),
)
)

View File

@ -1,6 +1,6 @@
from __future__ import (absolute_import, print_function, division) from __future__ import (absolute_import, print_function, division)
import traceback from ..contrib.tls._constructs import ClientHello
from netlib import tcp from netlib import tcp
import netlib.http.http2 import netlib.http.http2
@ -11,15 +11,15 @@ from .layer import Layer
class TlsLayer(Layer): class TlsLayer(Layer):
def __init__(self, ctx, client_tls, server_tls): def __init__(self, ctx, client_tls, server_tls):
self.client_sni = None
self.client_alpn_protos = None
super(TlsLayer, self).__init__(ctx) super(TlsLayer, self).__init__(ctx)
self._client_tls = client_tls self._client_tls = client_tls
self._server_tls = server_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): def __call__(self):
""" """
@ -45,6 +45,28 @@ class TlsLayer(Layer):
https://www.openssl.org/docs/ssl/SSL_CTX_set_cert_cb.html https://www.openssl.org/docs/ssl/SSL_CTX_set_cert_cb.html
- The original mitmproxy issue is https://github.com/mitmproxy/mitmproxy/issues/427 - 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 = ( client_tls_requires_server_cert = (
self._client_tls and self._server_tls and not self.config.no_upstream_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): def connect(self):
if not self.server_conn: if not self.server_conn:
self.ctx.connect() 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() self._establish_tls_with_server()
def reconnect(self): def reconnect(self):
self.ctx.reconnect() 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() self._establish_tls_with_server()
def set_server(self, address, server_tls, sni, depth=1): def set_server(self, address, server_tls, sni, depth=1):
@ -74,10 +96,6 @@ class TlsLayer(Layer):
self._sni_from_server_change = sni self._sni_from_server_change = sni
self._server_tls = server_tls self._server_tls = server_tls
@property
def _server_tls_established(self):
return self.server_conn and self.server_conn.tls_established
@property @property
def sni_for_upstream_connection(self): def sni_for_upstream_connection(self):
if self._sni_from_server_change is False: if self._sni_from_server_change is False:
@ -138,6 +156,10 @@ class TlsLayer(Layer):
connection.set_context(new_context) connection.set_context(new_context)
def __handle_alpn_select(self, conn_, options): 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? # TODO: change to something meaningful?
# alpn_preference = netlib.http.http1.HTTP1Protocol.ALPN_PROTO_HTTP1 # alpn_preference = netlib.http.http1.HTTP1Protocol.ALPN_PROTO_HTTP1
alpn_preference = netlib.http.http2.HTTP2Protocol.ALPN_PROTO_H2 alpn_preference = netlib.http.http2.HTTP2Protocol.ALPN_PROTO_H2

View File

@ -20,7 +20,9 @@ deps = {
"pyperclip>=1.5.8", "pyperclip>=1.5.8",
"blinker>=1.3", "blinker>=1.3",
"pyparsing>=1.5.2", "pyparsing>=1.5.2",
"html2text>=2015.4.14" "html2text>=2015.4.14",
"construct>=2.5.2",
"six>=1.9.0",
} }
# A script -> additional dependencies dict. # A script -> additional dependencies dict.
scripts = { scripts = {