Created
June 5, 2024 12:41
-
-
Save fulup-bzh/cbca88fc07b3d92cc3e5f3a047ffde7f to your computer and use it in GitHub Desktop.
Minimalist TLS-1.3 decrypting with gnutls
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Minimalist C sample on how to decrypt TLS-1.3 with gnutls from from SSLKEYLOGFILE+pcap file.