so I'm writing my own tls 1.2 implementation for learning's sake and because the server I'm connecting to use a modified version of the protocol that ask for a non random client_random anyway (TL;DR: I'm having a bad_record_mac error on finished message with my implementation and can't find where I made a mistake)
I have to precise the only cipher suite I'm concerned about is TLS_RSA_WITH_AES_128_CBC_SHA
so first I create my client random and the handshake begins with the server sending server_random and the RSA public key in the certificate
I generate a random pre_master_secret and encrypt it with the RSA public key (done by a native module of the language, so no mess up by me on this) and send it back in a client_key_exchange message
I then generate the master_secret from: PRF(pre_master_secret, "master secret", client_random+server_random) where PRF is:
class TlsMasterSecret {
constructor(pre_master_secret, client_random, server_random) {
this.master_secret = TlsMasterSecret.PRF(pre_master_secret, Buffer.from("master secret", "ascii"), Buffer.concat([client_random, server_random]), 48);
this.master_secret = this.master_secret.slice(0, 48);
this.key_block = TlsMasterSecret.PRF(this.master_secret, Buffer.from("key expansion", "ascii"),Buffer.concat([server_random, client_random]), 104);
// 20 20 16 16
this.client_write_MAC_key = this.key_block.slice(0, 20);
this.server_write_MAC_key = this.key_block.slice(20, 40);
this.client_write_key = this.key_block.slice(40, 56);
this.server_write_key = this.key_block.slice(56, 72);
}
static PRF(secret, label, seed, neededLength) {
return TlsMasterSecret.P_hash(secret, Buffer.concat([label, seed]), neededLength)
}
static P_hash(secret, seed, neededLength) {
const A = [seed];
let resultHash = Buffer.from("", "hex");
// HMAC_SHA256 generates 32 bytes per update
const iterations = Math.ceil(neededLength / 32);
// generate A
for(let i = 1; i < iterations + 1; i++) {
A[i] = TlsMasterSecret.HMAC_hash(secret, A[i-1]);
}
// calculates the hash
for(let i = 1; i < iterations + 1; i++) {
resultHash = Buffer.concat([resultHash, TlsMasterSecret.HMAC_hash(secret, A[i])]);
}
return resultHash;
}
static HMAC_hash(secret, seed) {
const hmac = crypto.createHmac("sha256", secret);
hmac.update(seed);
return hmac.digest();
}
}
then I create the Finished message from 0x1400000c followed by 12 bytes of verify_data
verify_data is generated like this:
// I strip the record layer headers off the messages
client_hello = 0x0100002f03032668f90d7d3b3420f7712a3004247d233d90cc63fb4b23f77912b31c8c89893b000008002f0035003c003d0100
server_hello = 0x0200002603031296263fba0a7955a83b2bf9c3716c8ee1c52e9065b89ee5649b31391d7ce84700002f00
certificate = 0b00034e00034b00034830820344308202ada00302.......
server_hello_done = 0e000000
client_key_exchange = 100000820080673f1772e35c7565a1e0c2fae5ee1b5bf92211ba9e4de14620f43856d954cf737aa04c48f8b2ffa182f652fedd68523056acda3c7cd101c5a2c3a625655df67ad9fb7ca082cf68476f174ea7b83eab980b7b15657036ad0e26883c9bfe368847a02b052817cb80dabafc6af1dac8064ea0dc676e0f8ea74f84a122135f31b612
handshake_messages = client_hello + server_hello + certificate + server_hello_done + client_key_exchange
verify_data = PRF(master_secret, "client finished", sha256(handshake_messages))[0..12]
then I create the MAC that will go along this message:
// hmac_sha1(secret, seed)
// seq_num is a uint64 @ 0 for the first message, 0x00000000
// type = 0x16
// version = 0x0303
// length = 0x0010
// content = 0x1400000c + verify_data
MAC = hmac_sha1(client_write_MAC_key, seq_num + type + version + length + content)
plaintext = content + MAC
IV = randomBytes(16)
// aes_128_cbc(key, IV, message)
ciphertext = aes_128_cbc(client_write_key, IV, plaintext)
final = 0x16 + 0x0303 + 0x0040 + IV + ciphertext
if you read all of this and still willing to help thank you so much I think I followed the specification but I'm getting a bad_record_mac error in wireshark so there's something wrong somewhere, I just can't find it :(
User contributions licensed under CC BY-SA 3.0