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.
This commit is contained in:
Nenad Merdanovic 2019-03-23 11:00:32 +01:00 committed by Willy Tarreau
parent 13d9b0231a
commit c31499d747
2 changed files with 160 additions and 0 deletions

View File

@ -13205,6 +13205,18 @@ add(<value>)
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(<bits>,<nonce>,<key>,<aead_tag>)
Decrypts the raw byte input using the AES128-GCM, AES192-GCM or
AES256-GCM algorithm, depending on the <bits> parameter. All other parameters
need to be base64 encoded and the returned result is in raw byte format.
If the <aead_tag> validation fails, the converter doesn't return any data.
The <nonce>, <key> and <aead_tag> 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(<value>)
Performs a bitwise "AND" between <value> and the input value of type signed
integer, and returns the result as an signed integer. <value> can be a

View File

@ -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 <import/lru.h>
#include <import/xxhash.h>
@ -118,6 +123,7 @@
#include <proto/ssl_sock.h>
#include <proto/stream.h>
#include <proto/task.h>
#include <proto/vars.h>
/* 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 <const> 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,