The converted 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 | 11 ++++
src/ssl_sock.c | 140 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 151 insertions(+)
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 88148c3b..a4242843 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -13205,6 +13205,17 @@ 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_decrypt(<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.
+
+ Example:
+ http-response set-header X-Decrypted-Text %[var(txn.enc),\
+ aes_gcm_decrypt(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
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 138b1c58..ba75b94a 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -118,6 +118,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 +9076,137 @@ static int cli_parse_set_ocspresponse(char **args, char
*payload, struct appctx
}
+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_decrypt(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;
+}
+
+
/* 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 +9470,14 @@ 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, {
+ { "aes_gcm_decrypt", sample_conv_aes_gcm_decrypt,
ARG4(4,SINT,STR,STR,STR), check_aes_gcm, SMP_T_BIN, SMP_T_BIN },
+ { 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,
--
2.19.1