mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2024-12-26 06:32:13 +00:00
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:
parent
13d9b0231a
commit
c31499d747
@ -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
|
||||
|
148
src/ssl_sock.c
148
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 <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,
|
||||
|
Loading…
Reference in New Issue
Block a user