syncsynchalt / illustrated-tls12

The Illustrated TLS 1.2 Connection: Every byte explained
https://tls12.ulfheim.net
MIT License
3.45k stars 208 forks source link

How do i get AAD and IV in AES 128 GCM? #26

Open KevinAdhaikal opened 1 month ago

KevinAdhaikal commented 1 month ago

Hello, how do i get AAD and IV in AES 128 GCM? Cipher: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)

in AES 128 CBC (TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)), the IV is on the Client handshake finished Example like in your website

16 03 03 00 40 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 22 7b c9 ba 81 ef 30 f2 a8 a7 8f f1 df 50 84 4d 58 04 b7 ee b2 e2 14 c3 2b 68 92 ac a3 db 7b 78 07 7f dd 90 06 7c 51 6b ac b3 ba 90 de df 72 0f 

the IV is: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f and no need to use AAD

but how about AES 128 GCM?

Sorry if my grammar is bad, Thanks.

KevinAdhaikal commented 1 month ago

here is my AES 128 GCM decrypt

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#include "hash/hmac_sha256.h"
#include "libtomcrypt/src/headers/tomcrypt.h"

void print_hex(const char* text, uint8_t* data, int len) {
    printf("%s", text);
    for (int a = 0; a < len; a++) printf("%02x", data[a]);
    printf("\n");
}

int main() {
    uint8_t client_random[32] = {0x94, 0x8b, 0x00, 0xd3, 0x83, 0xbb, 0x40, 0x95, 0x37, 0x31, 0xa7, 0x0f, 0x8d, 0x8b, 0x59, 0x40, 0x95, 0x08, 0x38, 0x84, 0xa4, 0xe2, 0x8c, 0xf6, 0x9e, 0x73, 0x9d, 0x35, 0xfa, 0x8c, 0x78, 0x76};
    uint8_t server_random[32] = {0x23, 0x19, 0x4b, 0x74, 0x12, 0xdc, 0x90, 0xf5, 0xb4, 0x89, 0x02, 0x9f, 0xf6, 0x3a, 0xfd, 0x5a, 0x35, 0xb8, 0x5e, 0x37, 0x2d, 0xf7, 0x33, 0xae, 0x19, 0x4f, 0xe5, 0x40, 0x4f, 0x82, 0xfa, 0xcf};
    uint8_t master_secret[48] = {0x5c, 0x05, 0xe8, 0xe6, 0xa6, 0xca, 0x22, 0xfa, 0x3a, 0xfc, 0xd8, 0x32, 0xa0, 0xcd, 0xa6, 0x1a, 0xfd, 0xe7, 0x80, 0xb3, 0xb2, 0x69, 0x29, 0xe0, 0x7c, 0x82, 0x5e, 0xa1, 0xa5, 0x00, 0xd1, 0x6a, 0x10, 0x5a, 0xf6, 0x72, 0x28, 0xdb, 0x9c, 0x09, 0x14, 0x05, 0xc4, 0x1b, 0x2c, 0x78, 0x2c, 0x77};
    uint8_t encrypted_data[40] = {0x1F, 0xDA, 0x89, 0x03, 0xA1, 0x84, 0xBB, 0xB2, 0x07, 0x30, 0xD7, 0xF3, 0x5D, 0xB2, 0x68, 0x6F, 0x1A, 0x7D, 0xB9, 0x80, 0xE3, 0x2C, 0x59, 0xDC, 0x97, 0x96, 0x65, 0x1F, 0xC1, 0x45, 0xEA, 0x2B, 0xED, 0x06, 0x0A, 0x34, 0xD5, 0x99, 0xE8, 0xE2};
    uint8_t decrypted_data[40];

    uint8_t seed[109];

    memcpy(seed + 32, "key expansion", 13);
    memcpy(seed + 45, server_random, 32);
    memcpy(seed + 77, client_random, 32);

    uint8_t a1[32], a2[32], a3[32], a4[32], p[128];
    hmac_sha256(master_secret, 48, seed + 32, 77, a1, 32);
    hmac_sha256(master_secret, 48, a1, 32, a2, 32);
    hmac_sha256(master_secret, 48, a2, 32, a3, 32);
    hmac_sha256(master_secret, 48, a3, 32, a4, 32);

    memcpy(seed, a1, 32);
    hmac_sha256(master_secret, 48, seed, 109, p, 32);

    memcpy(seed, a2, 32);
    hmac_sha256(master_secret, 48, seed, 109, p + 32, 32);

    memcpy(seed, a3, 32);
    hmac_sha256(master_secret, 48, seed, 109, p + 64, 32);

    memcpy(seed, a4, 32);
    hmac_sha256(master_secret, 48, seed, 109, p + 96, 32);

    uint8_t result[104];
    memcpy(result, p, 20);
    memcpy(result + 20, p + 20, 20);
    memcpy(result + 40, p + 40, 16);
    memcpy(result + 56, p + 56, 16);
    memcpy(result + 72, p + 72, 16);
    memcpy(result + 88, p + 88, 16);

    print_hex("Client MAC key: ", result, 20);
    print_hex("Server MAC key: ", result + 20, 20);
    print_hex("Client write key: ", result + 40, 16);
    print_hex("Server write key: ", result + 56, 16);
    print_hex("Client write IV: ", result + 72, 16);
    print_hex("Server write IV: ", result + 88, 16);

    uint8_t iv[12];
    memset(iv + 4, 0, 8);
    memcpy(iv, result + 72, 4);
    register_cipher(&aes_desc);

    uint8_t tag[16];

    gcm_state gcm;
    gcm_init(&gcm, find_cipher("aes"), result + 40, 16);
    gcm_add_iv(&gcm, iv, 12);
    gcm_process(&gcm, encrypted_data, 40, decrypted_data, GCM_DECRYPT);
    gcm_done(&gcm, tag, &(unsigned long){16});

    for (int a = 0; a < 40; a++) printf("%02x ", decrypted_data[a]);

    return 0;
}

i dont know why this is doesnt working.

syncsynchalt commented 1 month ago

Hi Kevin!

The use of GCM in TLS is explained much better on my TLS 1.3 site, https://tls13.xargs.org. That site goes into detail on what is used for IV and AAD, though the exact AAD will probably be different for TLS 1.2 since the record wrappers are different.

My memory is that the IV will be incremented by (xor'd with) with the record sequence number (starting at zero) to create a new IV for each record. The AAD will generally be all the un-encrypted bytes in the record, though I'd have to check for TLS 1.2 specifically.

If you are using a GCM cipher, changing the IV and providing AAD must be performed for each packet encryption/decryption, it is fundamental to the security of GCM. The security guarantees of GCM can disappear if an IV is used twice (therefore it is always modified, for every encryption/decryption), and AAD is also a required input to the GCM algorithm.

I've just checked the TLS 1.2 RFC, here is the spec for AAD:

      additional_data = seq_num + TLSCompressed.type +
                        TLSCompressed.version + TLSCompressed.length;

where + is concatenation and where the TLSCompressed fields are:

          ContentType type;       /* same as TLSPlaintext.type */
          ProtocolVersion version;/* same as TLSPlaintext.version */
          uint16 length;

Since you are most likely not using a compression method length will be the record length, so all together that appears to be the sequence number (a 64-bit number, in big-endian format, counting the number of records sent so far) followed by what I think to be the first five bytes of the packet (the type byte, two bytes for version, and a two-byte length field).

I can't be sure of the correctness of other data in your sample code, but try calling gcm_add_aad() with the appropriate data (the 13 bytes described above) and increment the IV by your record sequence number (that's the same as XOR'ing it) and see if you can get a successful decryption that way.

Good luck!

syncsynchalt commented 1 month ago

(I've made a few edits to the above, not sure if they are sent to you, be sure to refresh the page just in case)

KevinAdhaikal commented 1 month ago

My memory is that the IV will be incremented by (xor'd with) with the record sequence number (starting at zero) to create a new IV for each record. The AAD will generally be all the un-encrypted bytes in the record, though I'd have to check for TLS 1.2 specifically.

Can you give an example of how to get the IV? I'm still dont understand... 😅

KevinAdhaikal commented 1 month ago

Pre-Master key: 5c05e8e6a6ca22fa3afcd832a0cda61afde780b3b26929e07c825ea1a500d16a105af67228db9c091405c41b2c782c77 Client random: 948b00d383bb40953731a70f8d8b594095083884a4e28cf69e739d35fa8c7876 Server random: 23194b7412dc90f5b489029ff63afd5a35b85e372df733ae194fe5404f82facf

Client MAC key: d966c90c69eadfeae377667c305e61c2873f7443 Server MAC key: deb73cfaa5489be7f992bad5c683a9274cb0a416 Client write key: a4d9507245a8bd791f473c13ab9ebe66 Server write key: cf0b718dc53df54ba2acd53830023e4f Client write IV: 721ffb736f49b78bab72aa849a093ac9 Server write IV: de4278bb07ea4bac4026b2f6322dc6c6

So, i must take the first byte of Client write IV, and sequence number and it will like this

IV: 72 1f fb 73 00 00 00 00 00 00 00 00 and then, I must do XOR the IV and I will get the correct IV to decrypt the "Client Handshake Finished" packet?

Sorry if my grammar is bad 🙏

syncsynchalt commented 1 month ago

My understanding is that the IV is 4 bytes generated as client_write_IV / server_write_IV, and 8 bytes from explicit IV sent with the record. Here is the spec:

   The "nonce" SHALL be 12 bytes long consisting
   of two parts as follows: (this is an example of a "partially
   explicit" nonce; see Section 3.2.1 in [RFC5116]).

             struct {
                opaque salt[4];
                opaque nonce_explicit[8];
             } GCMNonce;

   The salt is the "implicit" part of the nonce and is not sent in the
   packet.  Instead, the salt is generated as part of the handshake
   process: it is either the client_write_IV (when the client is
   sending) or the server_write_IV (when the server is sending).  The
   salt length (SecurityParameters.fixed_iv_length) is 4 octets.

   The nonce_explicit is the "explicit" part of the nonce.  It is chosen
   by the sender and is carried in each TLS record in the
   GenericAEADCipher.nonce_explicit field.  The nonce_explicit length
   (SecurityParameters.record_iv_length) is 8 octets.
syncsynchalt commented 1 month ago

IV: 72 1f fb 73 00 00 00 00 00 00 00 00 and then, I must do XOR the IV and I will get the correct IV to decrypt the "Client Handshake Finished" packet?

That sounds right, you'll also need to supply the matching AAD or the encrypt/decrypt operations will fail. The design of an AEAD cipher mode like GCM is that it takes constant time whether successful or failing, and that it can give no indication of which input is incorrect (be it the IV, the key, or the AAD).

syncsynchalt commented 1 month ago

(still reading your questions):

Client write IV: 721ffb736f49b78bab72aa849a093ac9 Server write IV: de4278bb07ea4bac4026b2f6322dc6c6

actually, since you are using a cipher that only wants 4 bytes of IV from this source, it would be a mistake to use this many bytes for each. I believe in this case the client write IV would be 721ffb73 and the server write IV would be the next four bytes, 6f49b78b.

This would be combined with an additional 8 bytes of IV that are sent in the record. Hmm, since GCM requires that an IV is never repeated with the same encryption key, I wonder if this means the same 8 bytes of IV data will be sent each time? That sounds right. Then the record number will keep incrementing the IV by 1 each time.

EDIT: I just re-read the spec and I think you'll see the the explicit IV, given in each record, already has the number incremented. So you'll probably see something like this:

record 0:

record 1:

record 2:

... etc. I haven't looked deeply at TLS1.2+GCM so haven't seen this yet.

KevinAdhaikal commented 1 month ago

Hmmmmm... This GCM Encryption Makes my head hurt, hahahaha :joy:

KevinAdhaikal commented 1 month ago

opaque nonce_explicit[8];

so, opaque nonce_explicit[8]; is sequence number, is it correct?

syncsynchalt commented 1 month ago

so, opaque nonce_explicit[8]; is sequence number, is it correct?

It doesn't have to be, the spec just says it can't be repeated in two different records. I think the easiest method to do this, and probably what most implementations do, is to just put the sequence number in the IV, yes.

KevinAdhaikal commented 1 month ago

ohhh... btw do you have Discord? so that we can chat about TLS on discord? My discord username is: KevinAdhaikal

if you dont want to chat on the Discord, its okay.

KevinAdhaikal commented 1 month ago

and btw, im sorry if i disturb your time 🙏

KevinAdhaikal commented 1 month ago

okay, after i read https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.3 (section-6.2.3.3) and https://www.rfc-editor.org/rfc/rfc5288.html

the nonce (IV) it generated from Client/Server write IV (4 bytes) and Sequence Number (8 bytes) but, when i try to read carefully again at RFC 5288, section 8, it needs authentication tag (auth tag)

how do i get authentication tag (auth tag)?

is the auth tag is from the last 16 bytes from encrypted data?

example:

0000   16 03 03 00 28 1f da 89 03 a1 84 bb b2 07 30 d7
0010   f3 5d b2 68 6f 1a 7d b9 80 e3 2c 59 dc 97 96 65
0020   1f c1 45 ea 2b ed 06 0a 34 d5 99 e8 e2

so the, auth tag is 9796651fc145ea2bed060a34d599e8e2?

sorry if my grammar is bad 🙏

KevinAdhaikal commented 1 month ago

hmmmm... already try like same in the RFC

const crypto = require('crypto');

/*
Client MAC key: d966c90c69eadfeae377667c305e61c2873f7443
Server MAC key: deb73cfaa5489be7f992bad5c683a9274cb0a416
Client write key: a4d9507245a8bd791f473c13ab9ebe66
Server write key: cf0b718dc53df54ba2acd53830023e4f
Client write IV: 721ffb736f49b78bab72aa849a093ac9
Server write IV: de4278bb07ea4bac4026b2f6322dc6c6
*/

function decrypt(ciphertext, key, iv, aad) {
  const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv);
  decipher.setAAD(aad)
  let decrypted = decipher.update(ciphertext, 'hex', 'hex');
  decrypted += decipher.final('hex');
  return decrypted;
}

const encrypted_text = "1FDA8903A184BBB20730D7F35DB2686F1A7DB980E32C59DC9796651FC145EA2BED060A34D599E8E2";

const decrypted = decrypt(
    encrypted_text,
    Buffer.from("a4d9507245a8bd791f473c13ab9ebe66", "hex"),
    Buffer.from("721ffb730000000000000000", "hex"),
    Buffer.from("00000000000000001603030028", "hex"),
);

console.log('Decrypted:', decrypted);

its still doesnt work

KevinAdhaikal commented 1 month ago

Oops, wrong button :facepalm: