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.
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)
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:
struct.pack("!Q",seq_num)
TLSCompressed.type
struct.pack("!b",TLSCompressed.type)
TLSCompressed.version
struct.pack("!H",TLSCompressed.version)
TLSCompressed.length
struct.pack("!H",TLSCompressed.length)
TLSCompressed.fragment
As a python example, the HMAC hashing will be as follows for our previous example: