Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save fulup-bzh/cbca88fc07b3d92cc3e5f3a047ffde7f to your computer and use it in GitHub Desktop.
Save fulup-bzh/cbca88fc07b3d92cc3e5f3a047ffde7f to your computer and use it in GitHub Desktop.
Minimalist TLS-1.3 decrypting with gnutls
/*
* Minimalist sample to decrypt tls-1.3 with gnutls api
*
* Data input:
* crypt_buffers are extracted with wireshark from hello-tls-1.3.pcapng
* master_secret is extracted from hello-tls-1.3.keylog/CLIENT_TRAFFIC_SECRET_0
* cipher_name is extracted from hello_server message
*
* Compilation: gcc -lgnutls tls-decrypt-1.3.c -o tls-decrypt
* Pcap-trace: wireshark hello-tls-1.3.pcapng -o tls.keylog_file:hello-tls-1.3.keylog
*
* Full code decoding pcap: https://github.com/tux-evse/iso15118-simulator-rs/blob/main/pcap-15118/capi/capi-pcap.rs
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
const char *crypt_buffers[] = {
/* toto */ "1703030016b518602289e83c6635cd9f8f630f98cee94398e344ca",
/* titi */ "17030300163062d7d7a8e8fe6b9e8933e723836280a9bca5e59c53",
/* tata */ "1703030016b0852a02d0d41a8598b193ea52d14c548dfa191e7ab9"
};
const char *master_secret =
"af9f4987d68c6ef02dcae968c25e32dbe5925c2d2bbbb78df3aa5427693c88abebd1c7c705724ec00c61c3e2e0678419"
;
const char *cipher_name = "TLS_AES_256_GCM_SHA384";
void get_cipher(
const char *name,
gnutls_kx_algorithm_t * kx,
gnutls_cipher_algorithm_t * cipher,
gnutls_mac_algorithm_t * mac
) {
unsigned char id[2];
size_t idx = 0;
for (;; idx++) {
const char *n = gnutls_cipher_suite_info(idx, id, kx, cipher, mac, NULL);
if (n == NULL) {
fprintf(stderr, "cipher %s not found\n", name);
exit(1);
}
if (strcmp(n, name) == 0) {
if (id[0] == 0x13) {
switch (id[1]) {
case 1: // GNUTLS_AES_128_GCM_SHA256 { 0x13, 0x01 }
case 3: // GNUTLS_CHACHA20_POLY1305_SHA256 { 0x13, 0x03 }
case 4: // GNUTLS_AES_128_CCM_SHA256 { 0x13, 0x04 }
case 5: // GNUTLS_AES_128_CCM_8_SHA256 { 0x13,0x05 }
*mac = GNUTLS_MAC_SHA256;
break;
case 2: // GNUTLS_AES_256_GCM_SHA384 { 0x13, 0x02 }
*mac = GNUTLS_MAC_SHA384;
break;
}
}
return;
}
}
}
void print_datum(const char *tag, gnutls_datum_t *d)
{
unsigned i;
printf("%s: ", tag);
for (i = 0 ; i < d->size ; i++)
printf("%02x", d->data[i]);
printf("\n");
}
void *datum_alloc(gnutls_datum_t *d, size_t sz)
{
return d->data = calloc(1, d->size = (unsigned)sz);
}
unsigned char hexa_to_int(char x)
{
return (unsigned char)(
x >= '0' && x <= '9'
? x - '0'
: x >= 'a' && x <= 'f'
? x - 'a' + 10
: x >= 'A' && x <= 'F'
? x - 'A' + 10
: 0);
}
void hexa_to_datum(const char *h, gnutls_datum_t *d)
{
unsigned len;
for (len = 0 ; h[len] && h[len + 1] ; len += 2);
datum_alloc(d, len >> 1);
for (len = 0 ; h[len] && h[len + 1] ; len += 2)
d->data[len >> 1] = (unsigned char)((hexa_to_int(h[len]) << 4) | hexa_to_int(h[len + 1]));
}
void string_to_datum(const char *s, gnutls_datum_t *d)
{
d->data = (unsigned char*)s;
d->size = strlen(s);
}
void check_status(const char *tag, int rc)
{
if (rc < 0) {
fprintf(stderr, "%s returned error %d=%s: %s\n", tag, rc, gnutls_strerror_name(rc), gnutls_strerror(rc));
}
}
void expand_master_secret(gnutls_datum_t *d, gnutls_datum_t *key, const char *ifo, unsigned outsz, gnutls_mac_algorithm_t mac)
{
int rc;
gnutls_datum_t scratch;
unsigned i, len = (unsigned)strlen(ifo);
unsigned char buffer[20];
buffer[0] = (unsigned char)(outsz >> 8);
buffer[1] = (unsigned char)outsz;
buffer[2] = (unsigned char)(6 + len); // TBF Fulup
buffer[3] = (unsigned char)'t';
buffer[4] = (unsigned char)'l';
buffer[5] = (unsigned char)'s';
buffer[6] = (unsigned char)'1';
buffer[7] = (unsigned char)'3';
buffer[8] = (unsigned char)' ';
memcpy(buffer+9, ifo, len);
len += 9;
buffer[len++] = 0; // extra msg is ""(Null)
datum_alloc(d, outsz);
scratch.size = len;
scratch.data = buffer;
rc = gnutls_hkdf_expand(mac, key, &scratch, d->data, outsz);
check_status("gnutls_hkdf_expand", rc);
}
int decrypt(gnutls_datum_t *in, gnutls_cipher_algorithm_t cipher, gnutls_aead_cipher_hd_t aead, gnutls_datum_t *iv, gnutls_datum_t *out)
{
int rc;
size_t size= out->size;
rc = gnutls_aead_cipher_decrypt(
aead,
iv->data, iv->size,
in->data, 5,
gnutls_cipher_get_tag_size(cipher),
in->data + 5, in->size - 5,
out->data, &size);
check_status("gnutls_aead_cipher_decrypt", rc);
if (rc < 0) return rc;
return (int) size;
}
void main()
{
gnutls_kx_algorithm_t kx;
gnutls_cipher_algorithm_t cipher;
gnutls_mac_algorithm_t mac;
gnutls_datum_t dmaster_secret;
gnutls_datum_t scratch;
gnutls_datum_t div;
gnutls_datum_t dkey;
gnutls_datum_t dsam;
gnutls_datum_t ddec;
unsigned sequence_num, ipkt;
gnutls_aead_cipher_hd_t aead;
int rc;
get_cipher(cipher_name, &kx, &cipher, &mac);
printf("---- gnutls config ----\n");
printf(" cipher_name %s\n", gnutls_cipher_get_name(cipher));
printf(" cipher_block_size %u\n", gnutls_cipher_get_block_size(cipher));
printf(" cipher_iv_size %u\n", gnutls_cipher_get_iv_size(cipher));
printf(" cipher_tag_size %u\n", gnutls_cipher_get_tag_size(cipher));
printf(" cipher_key_size %u\n", gnutls_cipher_get_key_size(cipher));
printf(" mac_name %s\n", gnutls_mac_get_name(mac));
printf(" mac_key_size %u\n", gnutls_mac_get_key_size(mac));
printf(" mac_nonce_size %u\n", gnutls_mac_get_nonce_size(mac));
printf("\n---- extracted from pcap file ----\n");
hexa_to_datum(master_secret, &dmaster_secret);
print_datum(" master_secret", &dmaster_secret);
expand_master_secret(&div, &dmaster_secret, "iv", gnutls_cipher_get_iv_size(cipher), mac);
print_datum(" iv_nonce", &div);
expand_master_secret(&dkey, &dmaster_secret, "key", gnutls_cipher_get_key_size(cipher), mac);
print_datum(" iv_data", &dkey);
rc = gnutls_aead_cipher_init(&aead, cipher, &dkey);
check_status("gnutls_aead_cipher_init", rc);
// allocate buffer output (max 100 bytes)
datum_alloc(&ddec, 100);
printf("\n---- decrypt data buffer ----\n");
for (sequence_num = 0 ; sequence_num < sizeof crypt_buffers / sizeof *crypt_buffers ; sequence_num++) {
hexa_to_datum(crypt_buffers[sequence_num], &dsam);
div.data[div.size - 1] ^= (unsigned char)sequence_num; // xor on sequence number (as we only have few sample on fill last byte)
int size = decrypt(&dsam, cipher, aead, &div, &ddec);
if (size >= 0 ) {
// do not print two last char from decrypted text to avoid trailing "\n\0"
printf(" seq:%u -> text:'%.*s' (crypt:'%s')\n", sequence_num, size - 2, ddec.data, crypt_buffers[sequence_num]);
}
div.data[div.size - 1] ^= (unsigned char)sequence_num; // restore iv to original value (was modified with previous xor)
}
printf("---- decrypting end ----\n");
gnutls_aead_cipher_deinit(aead);
}
@fulup-bzh
Copy link
Author

Minimalist C sample on how to decrypt TLS-1.3 with gnutls from from SSLKEYLOGFILE+pcap file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment