From c31499d747be52d630bf7b3b663639cf09475c3a Mon Sep 17 00:00:00 2001 From: Nenad Merdanovic Date: Sat, 23 Mar 2019 11:00:32 +0100 Subject: [PATCH] MINOR: ssl: Add aes_gcm_dec converter The converter can be used to decrypt the raw byte input using the AES-GCM algorithm, using provided nonce, key and AEAD tag. This can be useful to decrypt encrypted cookies for example and make decisions based on the content. --- doc/configuration.txt | 12 ++++ src/ssl_sock.c | 148 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/doc/configuration.txt b/doc/configuration.txt index e52235e4a..8552d0e23 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -13205,6 +13205,18 @@ add() This prefix is followed by a name. The separator is a '.'. The name may only contain characters 'a-z', 'A-Z', '0-9', '.' and '_'. +aes_gcm_dec(,,,) + Decrypts the raw byte input using the AES128-GCM, AES192-GCM or + AES256-GCM algorithm, depending on the parameter. All other parameters + need to be base64 encoded and the returned result is in raw byte format. + If the validation fails, the converter doesn't return any data. + The , and can either be strings or variables. This + converter requires at least OpenSSL 1.0.1. + + Example: + http-response set-header X-Decrypted-Text %[var(txn.enc),\ + aes_gcm_dec(128,txn.nonce,Zm9vb2Zvb29mb29wZm9vbw==,txn.aead_tag)] + and() Performs a bitwise "AND" between and the input value of type signed integer, and returns the result as an signed integer. can be a diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 47548edc1..e8265622a 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -72,6 +72,11 @@ #define X509_getm_notAfter X509_get_notAfter #endif +#if (OPENSSL_VERSION_NUMBER < 0x1010000fL && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER) +#define EVP_CTRL_AEAD_SET_IVLEN EVP_CTRL_GCM_SET_IVLEN +#define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG +#endif + #include #include @@ -118,6 +123,7 @@ #include #include #include +#include /* Warning, these are bits, not integers! */ #define SSL_SOCK_ST_FL_VERIFY_DONE 0x00000001 @@ -9075,6 +9081,138 @@ static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx } +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER) +static inline int sample_conv_var2smp_str(const struct arg *arg, struct sample *smp) +{ + switch (arg->type) { + case ARGT_STR: + smp->data.type = SMP_T_STR; + smp->data.u.str = arg->data.str; + return 1; + case ARGT_VAR: + if (!vars_get_by_desc(&arg->data.var, smp)) + return 0; + if (!sample_casts[smp->data.type][SMP_T_STR]) + return 0; + if (!sample_casts[smp->data.type][SMP_T_STR](smp)) + return 0; + return 1; + default: + return 0; + } +} + +static int check_aes_gcm(struct arg *args, struct sample_conv *conv, + const char *file, int line, char **err) +{ + switch(args[0].data.sint) { + case 128: + case 192: + case 256: + break; + default: + memprintf(err, "key size must be 128, 192 or 256 (bits)."); + return 0; + } + /* Try to decode a variable. */ + vars_check_arg(&args[1], NULL); + vars_check_arg(&args[2], NULL); + vars_check_arg(&args[3], NULL); + return 1; +} + +/* Arguements: AES size in bits, nonce, key, tag. The last three arguments are base64 encoded */ +static int sample_conv_aes_gcm_dec(const struct arg *arg_p, struct sample *smp, void *private) +{ + struct sample nonce, key, aead_tag; + struct buffer *smp_trash, *smp_trash_alloc; + EVP_CIPHER_CTX *ctx; + int dec_size, ret; + + smp_set_owner(&nonce, smp->px, smp->sess, smp->strm, smp->opt); + if (!sample_conv_var2smp_str(&arg_p[1], &nonce)) + return 0; + + smp_set_owner(&key, smp->px, smp->sess, smp->strm, smp->opt); + if (!sample_conv_var2smp_str(&arg_p[2], &key)) + return 0; + + smp_set_owner(&aead_tag, smp->px, smp->sess, smp->strm, smp->opt); + if (!sample_conv_var2smp_str(&arg_p[3], &aead_tag)) + return 0; + + smp_trash = get_trash_chunk(); + smp_trash_alloc = alloc_trash_chunk(); + if (!smp_trash_alloc) + return 0; + + ctx = EVP_CIPHER_CTX_new(); + + if (!ctx) + goto err; + + dec_size = base64dec(nonce.data.u.str.area, nonce.data.u.str.data, smp_trash->area, smp_trash->size); + if (dec_size < 0) + goto err; + smp_trash->data = dec_size; + + /* Set cipher type and mode */ + switch(arg_p[0].data.sint) { + case 128: + EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL); + break; + case 192: + EVP_DecryptInit_ex(ctx, EVP_aes_192_gcm(), NULL, NULL, NULL); + break; + case 256: + EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL); + break; + } + + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, smp_trash->data, NULL); + + /* Initialise IV */ + if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, (unsigned char *) smp_trash->area)) + goto err; + + dec_size = base64dec(key.data.u.str.area, key.data.u.str.data, smp_trash->area, smp_trash->size); + if (dec_size < 0) + goto err; + smp_trash->data = dec_size; + + /* Initialise key */ + if (!EVP_DecryptInit_ex(ctx, NULL, NULL, (unsigned char *) smp_trash->area, NULL)) + goto err; + + if (!EVP_DecryptUpdate(ctx, (unsigned char *) smp_trash->area, (int *) &smp_trash->data, + (unsigned char *) smp->data.u.str.area, (int) smp->data.u.str.data)) + goto err; + + dec_size = base64dec(aead_tag.data.u.str.area, aead_tag.data.u.str.data, smp_trash_alloc->area, smp_trash_alloc->size); + if (dec_size < 0) + goto err; + smp_trash_alloc->data = dec_size; + dec_size = smp_trash->data; + + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, smp_trash_alloc->data, (void *) smp_trash_alloc->area); + ret = EVP_DecryptFinal_ex(ctx, (unsigned char *) smp_trash->area + smp_trash->data, (int *) &smp_trash->data); + + if (ret <= 0) + goto err; + + smp->data.u.str.data = dec_size + smp_trash->data; + smp->data.u.str.area = smp_trash->area; + smp->data.type = SMP_T_BIN; + smp->flags &= ~SMP_F_CONST; + free_trash_chunk(smp_trash_alloc); + return 1; + +err: + free_trash_chunk(smp_trash_alloc); + return 0; +} +# endif + /* register cli keywords */ static struct cli_kw_list cli_kws = {{ },{ #if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0) @@ -9338,6 +9476,16 @@ static struct cfg_kw_list cfg_kws = {ILH, { INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); +/* Note: must not be declared as its list will be overwritten */ +static struct sample_conv_kw_list conv_kws = {ILH, { +#if (OPENSSL_VERSION_NUMBER >= 0x1000100fL && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER) + { "aes_gcm_dec", sample_conv_aes_gcm_dec, ARG4(4,SINT,STR,STR,STR), check_aes_gcm, SMP_T_BIN, SMP_T_BIN }, +#endif + { NULL, NULL, 0, 0, 0 }, +}}; + +INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws); + /* transport-layer operations for SSL sockets */ static struct xprt_ops ssl_sock = { .snd_buf = ssl_sock_from_buf,