diff --git a/cryptoparser/tls/extension.py b/cryptoparser/tls/extension.py index 806e44c..bedbf83 100644 --- a/cryptoparser/tls/extension.py +++ b/cryptoparser/tls/extension.py @@ -1191,6 +1191,101 @@ def compose(self): return header_bytes + payload_composer.composed_bytes +class TlsEncryptedClientHelloType(enum.IntEnum): + OUTER = 0 + INNER = 1 + + +@attr.s +class TlsExtensionEncryptedClientHelloBase(TlsExtensionParsed): + @classmethod + def get_extension_type(cls): + return TlsExtensionType.ENCRYPTED_CLIENT_HELLO + + @classmethod + @abc.abstractmethod + def _parse(cls, parsable): + raise NotImplementedError() + + @abc.abstractmethod + def compose(self): + raise NotImplementedError() + + @classmethod + @abc.abstractmethod + def get_encrypted_client_hello_type(cls): + raise NotImplementedError() + + @classmethod + def _parse_header(cls, parsable): + parser = super(TlsExtensionEncryptedClientHelloBase, cls)._parse_header(parsable) + + parser.parse_numeric('client_hello_type', 1, TlsEncryptedClientHelloType) + if parser['client_hello_type'] != cls.get_encrypted_client_hello_type(): + raise InvalidType() + + return parser + + def compose_type(self): + composer = ComposerBinary() + + composer.compose_numeric(self.get_encrypted_client_hello_type(), 1) + + return composer + + +@attr.s +class TlsExtensionEncryptedClientHelloInner(TlsExtensionEncryptedClientHelloBase): + @classmethod + def get_encrypted_client_hello_type(cls): + return TlsEncryptedClientHelloType.INNER + + @classmethod + def _parse(cls, parsable): + parser = cls._parse_header(parsable) + + return cls(), parser.parsed_length + + def compose(self): + payload_composer = self.compose_type() + + header_bytes = self._compose_header(payload_composer.composed_length) + + return header_bytes + payload_composer.composed_bytes + + +@attr.s +class TlsExtensionEncryptedClientHelloOuter(TlsExtensionEncryptedClientHelloBase): + data = attr.ib(validator=attr.validators.instance_of((bytes, bytearray))) + + @classmethod + def get_encrypted_client_hello_type(cls): + return TlsEncryptedClientHelloType.OUTER + + @classmethod + def _parse(cls, parsable): + parser = cls._parse_header(parsable) + + parser.parse_raw('data', parser.unparsed_length) + + return cls(parser['data']), parser.parsed_length + + def compose(self): + payload_composer = self.compose_type() + + payload_composer.compose_raw(self.data) + + header_bytes = self._compose_header(payload_composer.composed_length) + + return header_bytes + payload_composer.composed_bytes + + +class TlsExtensionPostHandshakeAuthentication(TlsExtensionUnusedData): + @classmethod + def get_extension_type(cls): + return TlsExtensionType.POST_HANDSHAKE_AUTH + + class TlsExtensionVariantBase(VariantParsable): @classmethod @abc.abstractmethod @@ -1233,6 +1328,7 @@ def get_parsed_extensions(cls): (TlsExtensionType.EC_POINT_FORMATS, [TlsExtensionECPointFormats, ]), (TlsExtensionType.KEY_SHARE, [TlsExtensionKeyShareClient, ]), (TlsExtensionType.KEY_SHARE_RESERVED, [TlsExtensionKeyShareReservedClient, ]), + (TlsExtensionType.POST_HANDSHAKE_AUTH, [TlsExtensionPostHandshakeAuthentication, ]), (TlsExtensionType.PSK_KEY_EXCHANGE_MODES, [TlsExtensionPskKeyExchangeModes, ]), (TlsExtensionType.RECORD_SIZE_LIMIT, [TlsExtensionRecordSizeLimit, ]), (TlsExtensionType.SHORT_RECORD_HEADER, [TlsExtensionShortRecordHeader, ]), @@ -1240,6 +1336,8 @@ def get_parsed_extensions(cls): (TlsExtensionType.SIGNATURE_ALGORITHMS_CERT, [TlsExtensionSignatureAlgorithmsCert, ]), (TlsExtensionType.SIGNED_CERTIFICATE_TIMESTAMP, [TlsExtensionSignedCertificateTimestampClient, ]), (TlsExtensionType.SUPPORTED_VERSIONS, [TlsExtensionSupportedVersionsClient, ]), + (TlsExtensionType.ENCRYPTED_CLIENT_HELLO, [TlsExtensionEncryptedClientHelloInner, + TlsExtensionEncryptedClientHelloOuter]), (TlsExtensionType.TOKEN_BINDING, [TlsExtensionTokenBinding, ]), ]) diff --git a/submodules/cryptodatahub b/submodules/cryptodatahub index 6481248..ed6b4d2 160000 --- a/submodules/cryptodatahub +++ b/submodules/cryptodatahub @@ -1 +1 @@ -Subproject commit 6481248363f96e5f3d1560533020f90d3613e8ac +Subproject commit ed6b4d24f44ce57fd7974790adead6d03099b552 diff --git a/test/tls/test_extension.py b/test/tls/test_extension.py index 7ff422b..bd942ee 100644 --- a/test/tls/test_extension.py +++ b/test/tls/test_extension.py @@ -36,6 +36,8 @@ TlsExtensionCompressCertificate, TlsExtensionECPointFormats, TlsExtensionEllipticCurves, + TlsExtensionEncryptedClientHelloInner, + TlsExtensionEncryptedClientHelloOuter, TlsExtensionEncryptThenMAC, TlsExtensionExtendedMasterSecret, TlsExtensionKeyShareClient, @@ -45,6 +47,7 @@ TlsExtensionNextProtocolNegotiationClient, TlsExtensionNextProtocolNegotiationServer, TlsExtensionPadding, + TlsExtensionPostHandshakeAuthentication, TlsExtensionPskKeyExchangeModes, TlsExtensionRecordSizeLimit, TlsExtensionRenegotiationInfo, @@ -884,3 +887,71 @@ def test_parse(self): extension_padding_with_data = TlsExtensionPadding.parse_exact_size(extension_padding_with_data_bytes) self.assertEqual(extension_padding_with_data.compose(), extension_padding_with_data_bytes) + + +class ExtensionEncryptedClientHelloBase(unittest.TestCase): + def test_parse(self): + extension_encrypted_client_hello_dict = collections.OrderedDict([ + ('extension_type', b'\xfe\x0d'), + ('extension_length', b'\x00\x01'), + ('hello_type', b'\x00'), + ]) + extension_encrypted_client_hello_bytes = b''.join(extension_encrypted_client_hello_dict.values()) + + with self.assertRaises(InvalidType): + # pylint: disable=expression-not-assigned + TlsExtensionEncryptedClientHelloInner.parse_exact_size(extension_encrypted_client_hello_bytes) + + +class ExtensionEncryptedClientHelloInner(unittest.TestCase): + def test_parse(self): + extension_encrypted_client_hello_dict = collections.OrderedDict([ + ('extension_type', b'\xfe\x0d'), + ('extension_length', b'\x00\x01'), + ('hello_type', b'\x01'), + ]) + extension_encrypted_client_hello_bytes = b''.join(extension_encrypted_client_hello_dict.values()) + + extension_encrypted_client_hello = TlsExtensionEncryptedClientHelloInner.parse_exact_size( + extension_encrypted_client_hello_bytes + ) + self.assertEqual( + extension_encrypted_client_hello.compose(), + extension_encrypted_client_hello_bytes + ) + + +class ExtensionEncryptedClientHelloOuter(unittest.TestCase): + def test_parse(self): + extension_encrypted_client_hello_dict = collections.OrderedDict([ + ('extension_type', b'\xfe\x0d'), + ('extension_length', b'\x00\x11'), + ('hello_type', b'\x00'), + ('hello_data', b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'), + ]) + extension_encrypted_client_hello_bytes = b''.join(extension_encrypted_client_hello_dict.values()) + + extension_encrypted_client_hello = TlsExtensionEncryptedClientHelloOuter.parse_exact_size( + extension_encrypted_client_hello_bytes + ) + self.assertEqual( + extension_encrypted_client_hello.compose(), + extension_encrypted_client_hello_bytes + ) + + +class TestExtensionPostHandshakeAuthentication(unittest.TestCase): + def test_parse(self): + extension_post_handshake_authentication_dict = collections.OrderedDict([ + ('extension_type', b'\x00\x31'), + ('extension_length', b'\x00\x00'), + ]) + extension_post_handshake_authentication_bytes = b''.join(extension_post_handshake_authentication_dict.values()) + + extension_post_handshake_authentication = TlsExtensionPostHandshakeAuthentication.parse_exact_size( + extension_post_handshake_authentication_bytes + ) + self.assertEqual( + extension_post_handshake_authentication.compose(), + extension_post_handshake_authentication_bytes + )