TLS MAC message verification

884 views Asked by At

I'm developing a SSL de-cipher in python but I'm having some problems on HMAC verification:

I've extracted all keyring related material (client IV, MAC, Key and Server IV, MAC, key). When I receive the first Application_Data message (0x17), I am able to decrypt it, but unable to verify message integrity.

On RFC 2246 (https://www.ietf.org/rfc/rfc2246.txt), tells:

The MAC is generated as:

   HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
                 TLSCompressed.version + TLSCompressed.length +
                 TLSCompressed.fragment));

where "+" denotes concatenation.

seq_num The sequence number for this record.

hash The hashing algorithm specified by SecurityParameters.mac_algorithm.

Taking this as an example:

Chosen cipher_suite is TLS_RSA_WITH_AES_256_CBC_SHA256

client_mac = "some random stuff"
message_type = 0x17
message_version = 0x0303
encrypted_message_length = 1184 (IV|Message|MAC|Offset)
decrypted_message_length = 1122 (removing IV, MAC and offset)
message = "some message of length 1122"
  • client_mac is extracted from keyring_material
  • message_type is 0x17, because as an Application_data message type, the correct value should be 0x17
  • message version is 0x0303 as it's TLS 1.2
  • message length is 1122, removing preceding IV, offset and MAC verification, message, gets a final length of 1122
  • seq_number is 1 as it's the first message

HMAC_SHA256 calculation, in python, is as follows:

import hashlib
import hmac
hmac.new(<client_mac>,label+message,hashlib.sha256).digest()

My question is, how do I calculate label? As RFC mentions, "+" denotes concatenation, but concatenation of what

  • HEX values converted to string
    • "1" + "17" + "0303" + "462"
  • INT values converted to strings
    • "1" + "23" + "771" + "1122"

And other thing to mention, TLSCompressed.version means:

  • 0x0303
  • 771
  • "1.2"
  • "12"
  • "TLS 1.2"

In this maillist (http://www.ietf.org/mail-archive/web/tls/current/msg14357.html) I found a supposed clarification of MAC values,

   MAC(MAC_write_key, seq_num +
       TLSCipherText.type +
       TLSCipherText.version +
       length of ENC(content + padding + padding_length) +
       IV +
       ENC(content + padding + padding_length));

where the length is encoded as two bytes in the usual way.

but it makes no sense to me, because it's useless to re-encode decrypted values to check to compute MAC. And from last line "where length is encoded as two bytes in the usual way", does it means that I should use

struct.pack("!H",length)

Then remove "\x" and use this value? or should I encode this value in HEX and then concatenate it?

I'm a bit lost, because RFC are not clear about how values should be used.

I've been trying several combinations (even brute forcing), but none of them worked, I hope you can light my way.

1

There are 1 answers

0
Lolo_Bee On

Well, after diggin' a bit I've managed to solve the issue.

RFC 5246, in section 6.2.3.1 (https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1)

The MAC is generated as:

  MAC(MAC_write_key, seq_num +
                        TLSCompressed.type +
                        TLSCompressed.version +
                        TLSCompressed.length +
                        TLSCompressed.fragment);

where "+" denotes concatenation.

But it does not points the data size, either representation format (hex, string...).

The way every field must be represented is as follows:

  • seq_num:

    • Description: A int counter, starting in 0, which will be incremented every frame received or sended. For a TCP Session, two seq_numbers must be used, one for the server and other for the client, incrementing everytime each of them sends a frame.
    • Representation: This value must be represented as Unsigned Long Long with 8 bytes
    • Representation example: struct.pack("!Q",seq_num)
  • TLSCompressed.type

    • Description: This field is extracted from TLS Record layer (the encrypted payload). For example, if it's an Application Data frame, we must use 0x17.
    • Representation: This value must be represented as Signed Char, with 2 bytes.
    • Representation example: struct.pack("!b",TLSCompressed.type)
  • TLSCompressed.version

    • Description: This field is also extracted from TLS Record layer (the encrypted payload). For example, if the frame is transferred using TLS 1.2, we must use it's hex representation 0x0303.
    • Representation: This value must be represented as Unsigned Short, with 2 bytes.
    • Representation example: struct.pack("!H",TLSCompressed.version)
  • TLSCompressed.length

    • Description: This field represents the actual length of the decrypted payload.
    • Representation: This value must be represented as Unsigned Short, with 2 bytes.
    • Representation example: struct.pack("!H",TLSCompressed.length)
  • TLSCompressed.fragment

    • Description: This field **is the actual decrypted payload.
    • Representation: This value must be represented as a string

As a python example, the HMAC hashing will be as follows for our previous example:

hmac_digest = hmac.new(mac_secret,'',digestmod=hashlib.sha256)
hmac_digest.update(struct.pack('!QbHH',seq_num,TLSCompressed.type,TLSCompressed.version, len(decrypted)))
hmac_digest.update(decrypted)
hmac_digest.digest()