SSL & TLS Encoder & Decoder - HoLyVieR/tls-attack GitHub Wiki

Structure initialization

With data

If you want to automatically fill an object based on serialized data, you can use the method decode of an instance of the object that matches the serialized typed. This will be in almost all cases TLSHeader.

Example :

from tls_attack.structure import *

header = TLSHeader()
header.decode(b'\x16\x03\x03\x00\xdc\x01\x00\x00\xd8\x03\x03\x53\x43\x5b\x90\x9d\x9b\x72\x0b\xbc\x0c\xbc\x2b\x92\xa8\x48\x97\xcf\xbd\x39\x04\xcc\x16\x0a\x85\x03\x90\x9f\x77\x04\x33\xd4\xde\x00\x00\x66\xc0\x14\xc0\x0a\xc0\x22\xc0\x21\x00\x39\x00\x38\x00\x88\x00\x87\xc0\x0f\xc0\x05\x00\x35\x00\x84\xc0\x12\xc0\x08\xc0\x1c\xc0\x1b\x00\x16\x00\x13\xc0\x0d\xc0\x03\x00\x0a\xc0\x13\xc0\x09\xc0\x1f\xc0\x1e\x00\x33\x00\x32\x00\x9a\x00\x99\x00\x45\x00\x44\xc0\x0e\xc0\x04\x00\x2f\x00\x96\x00\x41\xc0\x11\xc0\x07\xc0\x0c\xc0\x02\x00\x05\x00\x04\x00\x15\x00\x12\x00\x09\x00\x14\x00\x11\x00\x08\x00\x06\x00\x03\x00\xff\x01\x00\x00\x49\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x34\x00\x32\x00\x0e\x00\x0d\x00\x19\x00\x0b\x00\x0c\x00\x18\x00\x09\x00\x0a\x00\x16\x00\x17\x00\x08\x00\x06\x00\x07\x00\x14\x00\x15\x00\x04\x00\x05\x00\x12\x00\x13\x00\x01\x00\x02\x00\x03\x00\x0f\x00\x10\x00\x11\x00\x23\x00\x00\x00\x0f\x00\x01\x01')

print(header)

Output :

TLSHeader {
    content_type = 22
    version = TLSVersion.TLS12
    length = 220
    body = TLSHandshake {
[...] Truncating the output [...]
    }
}

Without data

Structure value can be assigned with the constructor.

Example :

header = TLSHeader (
	version = TLSVersion.TLS10,
	body = TLSHeartbeat (
		heartbeat_type = TLSHeartbeatMessageType.HEARTBEAT_REQUEST,
		length = 0x4000,
		payload = b""
	)
)

If you wish to modify field value after the initialization it's possible to do so by changing the attribute value.

Example :

header.version = TLSVersion.TLS11

When manually filling structure data, you can use the magic class TLSAuto to automatically assign the correct value to a length field or a type field. The correct value will be computed when encoding the structure or displaying it.

Example :

header = TLSHeader (
	content_type = TLSAuto(),
	version = TLSVersion.TLS10,
	body = TLSHeartbeat (
		heartbeat_type = TLSHeartbeatMessageType.HEARTBEAT_REQUEST,
		length = 0x4000,
		payload = b""
	)
)

Here content_type will have the value 24 assigned when it's encoded or printed.

Structure definition

Every structure must inherit from the class TLSStructure. You usually want to import two things : TLSStructure and the annotation from TLSAnnotation.

Example :

from tls_attack.structure.TLSStructure import TLSStructure
from tls_attack.structure.TLSAnnotation import *

class TLSEmpty(TLSStructure):
	pass

You must declare field as static value of the class with the annotation from TLSAnnotation. The main annotation is TLSField. The order of the declaration of the field must be the same from the order in which they are serialized.

Example :

from tls_attack.structure.TLSStructure import TLSStructure
from tls_attack.structure.TLSAnnotation import *

class TLSSomething(TLSStructure):
	field_name = TLSField(size = 2,  type = "bytes")

Parameters of TLSField

  • size - Size in byte of the field.
  • type - Type that the byte must be interpreted with. Must be : "bytes", "int", "enum" or the class name of an other TLSStructure.
  • (optional) type_ref - To implement switch case type, this value must be an enum class. This enum class will be used at runtime to replace the type parameter with the correct class value.
  • (optional) type_list - Represents whether the field is a list or not. When set to True, the field is decoded as a list of type.
  • (optional) type_enum - When type is set to "enum", this value must be set to an enum class. This enum class will be used at runtime to replace the decoded value with the corresponding enum element.
  • (optional) encryptable - Represents whether the field becomes encrypted when a ChangeCipherSpec is sent.
  • (optional) optional - If no data is remaining this field will stay empty. This is used for structure which have more value in some version of SSL / TLS.
  • (optional) default - Default value of the field when no value is provided.

For value which depend on previously decoded value, it's possible to use the TLSFieldRef class.

Example :

class TLSHeader(TLSStructure):
	content_type = TLSField(size = 1, type = "int", default = TLSAuto())
	version      = TLSField(size = 2, type = "enum", type_enum = TLSVersion)
	length       = TLSField(size = 2, type = "int", default = TLSAuto())
	body         = TLSField(
		size = TLSFieldRef(name = "length"), 
		type = TLSFieldRef(name = "content_type"),
		type_ref = TLSContentType,
		encryptable = True
	)

In this case, the size and the type will depend on the value of the length field and the content_type field.

To simplify the declaration of the structure, it's recommended to use the annotation TLSAuto for the default value of field which depend on other value. In the previous example, the field length and content_type will be automatically filled with the correct value based on the content of the body.

State

In order to keep state of the connection and have encrypted field properly decoded, you can keep the state of the connection with TLSState. Once initialized, every decoded structure must be fed to the update method. This method takes two parameters :

  • source - Must be either TLSSource.CLIENT or TLSSource.SERVER
  • tls_object - TLSStructure decoded.

This class also has the following attributes which are updated :

  • cipher_suite - Currently negotiated cipher suite. When none are negotiated the value is TLSCipherSuite.TLS_NULL_WITH_NULL_NULL
  • compression - Currently negotiated compression.
  • encrypted - Map that indicates if the structure coming from the specified source is encrypted. Keys to this map are TLSSource.CLIENT and TLSSource.SERVER.