tls_client_handler module

class tls_client_handler.Certificate(conf=None, content={}, sender=None)

Bases: TLSMsg

Certificate Message handler for teh TLS client

get_public_key()

Returning the corresponding public key

Returns:

the public key object

Return type:

public_key

init_from_conf(certificate_request_context=b'')
class tls_client_handler.CertificateRequest

Bases: TLSMsg

class tls_client_handler.CertificateVerify(conf=None, content={}, sender=None)

Bases: TLSMsg

check_signature(tls_handshake, public_key)
get_cert_from_handshake_msg_list(handshake_msg_list)

parse handshake message list and extract certificates

get_last_exchange(c_register_tickets, is_post_hand_auth_proposed) bool

returns the last_message tag

last_exchange is set to False in the following cases:

  1. post handshake: post_hand_auth_proposed (post handshake)

  2. session resumption: method is ‘cs_generated’ or PSK in use in CS

this corresponds to the value c_register_tickets

handle_c_client_finished(lurk_client, ks, handshake_msg_list, c_register_tickets)

generates certificate_verify and updates ks

handle_c_init_client_finished(lurk_client, ks, handshake_msg_list, c_register_tickets)

generates certificate_verify and updates ks

init_content_from_lurk_resp(lurk_resp)
class tls_client_handler.ClientHello(conf=None, content={})

Bases: TLSMsg

from_record_layer_bytes(byte_string)

initiates TLSMsg from TLS record layer bytes

init(lurk_client=None, tls_handshake=None, ks=None, engine_ticket_db=None)
init_from_test_vector(lurk_client=None, tls_handshake=None, ks=None)

init from vector has mostly been done to work with the illustarted TLS 1.3

This function has not done intensive testing. Initially, we expected to test the library against welknown test vectors, but instead we are testing the tls client against with different OpenSSL flavors of TLS.

We leave this part as an initial step for a more extensivve use of test vectors.

Illustrated TLS only works for unauthenticated TLS client. The CS works with freshness set to null.

There is more work to do to integrate the test_vector functionality into the more generic way to handle the client hello.

set_lurk_session_state(has_proposed_psk_in_cs)

determine if a c_init_client_hello is performed

The current policy is to trigger a c_init_client_hello only when an interaction with the CS is needed. The reason is that we want to limit the interactions with the CS. Other policies may be implemented.

to_record_layer_bytes(content_type=None, content=None)

return a byte format TLS Reccord

The inner packet ( self.content ) is wrapped into the TLS Reccord.

class tls_client_handler.EncryptedExtensions

Bases: TLSMsg

class tls_client_handler.EngineTicketDB

Bases: TicketDB

Storing tickets received by the server

Unlike TicketDB, the engine may not be aware of the psk. In addition, the Engine selects tickets that apply to a given TLS server while the CS requires ticket information associated to a specific ticket. Such differences requires different structurre of teh database.

get_ticket_info_list(clt_conf)
key(clt_conf)
register(conf, new_session_ticket, ks, tls_handshake)
class tls_client_handler.ExtClientProtocolVersions

Bases: object

class tls_client_handler.ExtClientSignatureAlgorithms(sig_list)

Bases: object

class tls_client_handler.ExtKeyShare(tls_client_conf, debug)

Bases: object

class tls_client_handler.ExtPostHandshakeAuthentication

Bases: object

class tls_client_handler.ExtPreSharedKey(conf, ticket_info_list)

Bases: object

__init__(conf, ticket_info_list)

generates the pre_shared_key extention

class tls_client_handler.ExtPskKeyExchangeMode(ke_modes)

Bases: object

class tls_client_handler.ExtSupportedGroups(supported_groups)

Bases: object

class tls_client_handler.Finished(conf=None, content={}, sender=None)

Bases: ClientHello

check_verify_data(tls_handshake, ks)
class tls_client_handler.NewSessionTicket(conf=None, content={}, sender=None)

Bases: TLSMsg

handle_c_register_ticket(lurk_client)

generates certificate_verify and updates ks

class tls_client_handler.ServerHello(conf=None, content=None)

Bases: ClientHello

get_shared_secret(client_hello, tls_handshake)
handle_c_server_hello(lurk_client, tls_handshake, ks, client_hello)

performs the c_server_hello and update tls_handshake and ks

handle_server_hello(lurk_client, client_hello, tls_handshake, ks, tls_msg)
set_lurk_session_state(client_hello, tls_handshake)

set the c_server_hello and c_register_tickets staus

Determine if the TLS client needs to perform a c_server_hello or a c_register_tickets LURK exchange.

There different ways to implement it. Our implementation considers these exchanges are only performed when it actually make sense to benefit from the additional security provided by the CS. .

There is a need to interact with the CS if:
    1. the exchange includes ECDHE either with the TLS ECDHE mode or

    TLS PSK-ECDHE mode and the mode is ‘cs_generated’

    1. the TLS mode is psk based and the PSK is in the CS

    1. the client will be authenticated. However, we cannot determine

    it as long as we have not received a certificate request.

In any of these case, the key scheduler cannot be used to generate secrets and these MUST be generated by the CS. In any other case, there is no need to interact with the CS.

It could happen that a c_init_client_hello has initiated a session with the CS, and no more messages are sent. For example, the client may request ECDHE key share being generated by the CS while the CS does not pick that mode. Another case may consider the user using a set of PSKs that are shared between the CS and the Engine. The c_init_client_hello is necessary to generate the binders. However, if the TLS server choses a PSK that is known to the engine and if the Engine has generated the ECDHE key_share - or there are not ECDHE key_share involved, then the Engine may build its own key scheduler. Such example are a bit of a corner case and the CS MUST be able to remove session after some time out.

In the worst case, c_init_client_hello may be sent, c_server_hello is not needed but later on the server request an authentication of the client in which case a c_finished client is needed. Such scenario corresponds to the branch (a) in the LURK specification. This branch is optional and an implementation may chose to consider the ability to skip the c_server_hello or not. In the latter case, a c_init_client_hello will always be followed by a c_server_hello. This would result in the simplified version of this function: ## self.c_server_hello = client_hello.c_init_client_hello ## self.c_register_tickets = self.c_server_hello

The current version of the function considers that a c_server_hello exchange may or may not be performed - considering the various corner cases. When a c_server_hello exchange is expected, the varaible self.c_server_hello is set to True. Note that self.c_server_hello is set to False when there is a c_init_client_hello exchange bu no c_server_hello exchange as well as when no c_init_client_hello has been initated.

Similarly, when a c_register_ticket exchange is expected upon receiving a NewSession Ticket, this function sets the self.c_register_tickets to True. Note that tickets are registered only when these can only be handled by the Engine E and necessarily be handled by the CS.

set_tls_mode(tls_handshake) str

returns the tls_mode ‘ecdhe’, ‘psk_ecdhe’ or ‘psk’

class tls_client_handler.TLSByteStreamParser(socket, debug=None, sender='server')

Bases: object

Parse received bytes and convert them into TLS messages

collect received bytes, reassemble plaintext fragments

socket

the socket the parser is listening to

cipher

the cipher to decrypt the incoming TLS fragments. By default the cipher is set to None for example to handle non encrypted ServerHello.

byte_stream

the received bytes. Anytime there is a read on the socket the raw bytes are aggregated into this variable. By default, it is set to th empty byte stream b’’.

fragment

the bytes associated to fragments that have been received. These fragments are considered as intermediary steps to output a message. Sufficnet bytes my be receievd to get a fragment but not enough to get a TLS message. When sufficient fragments are received, the TLS message is output and the fragment bytes emptied.

debug

indicates if debug message are output (for debuging purpose)

sender

indicates who is sending the received message. In the case of a TLS client, the sender is set to ‘server’

__init__(socket, debug=None, sender='server')

converts received bytes into TLS messages

collect received bytes, reassemble plaintext fragments

Parameters:

sender (str) – the entity from whihc the tls messages are received ‘client’ or ‘server’

fragment_reassembly(plain_text: TLSMsg = None) TLSMsg

returns the first TLS message

returns the first tls (handshake) message from a TLS plaintext fragment or a TLSMsg with its content set to None.

Returning such message MAY require reassembling fragments. Note that the fragment MAY also contain other TLS messages when multiple handshake messages are pipelined.

Parameters:

plain_text

The plain text message which contains:

  1. plain_text_fragment_type corresponds to the TLSPlaintext

’type’. It is extracted from the TLS reccord or the InnerPlaintext. It can be ‘change_cipher’, ‘alert’, ‘handshake’ or ‘application_data’ but in our case, it MUST be set to ‘handshake’ as fragmentation only happens for handshake messages.

  1. plain_text_fragment (bytes): the actual bytes of the fragment.

current_fragment corresponds to the ongoing fragments being reassembled.

The TLS record is a plain text structure as defined below. For ‘change_cipher_text’ and ‘alert’ message, the plaintext contains a full tls message and a single one. In other words, there is no fragmentation. For ‘application_data’ this is transparent to TLS as fragmentation is handled by the application. The case that is of interest to use is the case of handshake message that can be fragmented.

TLSPlaintext = Struct(
  'type' / ContentType,
  'legacy_record_version' /  Const( b'' ),
  'fragment' / Prefixed( BytesInteger(2), Switch( this.type,
     { 'change_cipher_spec' : ChangeCipherSpec,
       'alert' : Alert,
       'handshake' : GreedyBytes,
       'application_data' : GreedyBytes } ) )
)

The function only takes the bytes associated to the fragment as these fragment may be carried inside a TLSPlaintext message as weel as in a InnerPlaintext structure.

fragment MAY be a full handshake message in which case the fragment is returned as the message. The TLSPlaintext structure considers that a full message is sent but does not consider a fragment is set. Fragmentation does not concerns alert messages, nor application data, but only handshake messages.

A fragment will not make possible the parsing. Fragments can be : 1. a starting initial fragment:

  1. a non starting initial fragment:

  1. a non initial fragment

With the current structure, 1) and 3) will generate an error upon parsing as the fragment will not be recognized as handshake message., while 2) will parse the first handshake message and ignore the bytes associatefd to the first fragment.

To do so we need to manually check length.

next_clear_text_fragment() TLSMsg

returns the next clear text fragment

Return type:

tls_msg

The next clear text message corresponds to the fragment (in clear) carried by the next reccord layer. The reccord layer is read from self.byte_stream buffer, this results in a plain text. When self.byte_stream does not have sufficient bytes, complementary bytes are read from the socket. Once we have sufficient reccord layer, the resulting tls message is built. When the plain text is of type ‘application_data’, it contains an inner message that contains the clear text fragment.

parse_record_layer_length() int

returns the record layer length from bytes

note that the len is the len of the plain text which may contain a full message or a fragment.

parse_single_msg() dict
read_bytes(bufsize=4096) int

reads and appends bytes to the byte_stream

returns:

class tls_client_handler.TLSMsg(conf=None, content_type=None, content={}, sender=None)

Bases: object

__init__(conf=None, content_type=None, content={}, sender=None)

basic structure for a TLS message

TLS uses multiple representation for a TLS message. It can be for example a InnerPlaintext or an Plaintext. TLSMsg enables to associate a content_type to a content and provides more abstraction.

Parameters:
  • conf – the configuration of the TLS client

  • content_type – ‘application_data’, ‘handshake’, ‘alert’,…

  • content – the content of the message, it can be a fragment in which case it is commonly a byte or a structure.

  • sender – th eentity sending the message.

add_ext(ext_list)
decrypt_inner_msg(cipher, debug=None)

decyrpt the inner plaintext of the encrypted fragment returns a TLSMsg object

descriptor(sender=None, label='')

return a TLSMsg descriptor (aiming) at uniquely mapping a message

This is mostly useful for test_vector and is currenlty in mapping (sender, content_type, handshake.msg_type). Fragmented messages are not considered.

encrypt_and_send(cipher, socket, sender, debug=None)

encrypt and send the provided innet_tls_msg

The current tls msg is considered as the inner clear text message

from_inner_msg_struct(inner_msg: dict)
from_record_layer_bytes(byte_string)

initiates TLSMsg from TLS record layer bytes

from_record_layer_struct(tls_plain_text)

initiates TLSMsg from a tls_plaintext structure

from_test_vector(test_vector_file, key)
to_inner_msg_bytes()
to_inner_msg_struct()
to_record_layer_bytes(content_type=None, content=None)

return a byte format TLS Reccord

The inner packet ( self.content ) is wrapped into the TLS Reccord.

to_record_layer_struct(content_type=None, content=None) dict

return a plaintext structure representing the record layer