]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: Add aes_gcm_enc converter
authorNenad Merdanovic <nmerdan@haproxy.com>
Tue, 5 Mar 2024 12:03:51 +0000 (13:03 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 8 Mar 2024 16:20:43 +0000 (17:20 +0100)
The converter can be used to encrypt the raw byte input using the
AES-GCM algorithm, using provided nonce and key.

Co-authored-by: Dragan Dosen (ddosen@haproxy.com)
doc/configuration.txt
src/ssl_sample.c

index 787103fe0083b17f74981a9f23d4b738da893bb9..f807c35a63a35622ee770a6913aa3d7036817891 100644 (file)
@@ -18676,6 +18676,7 @@ The following keywords are supported:
 add(value)                                         integer      integer
 add_item(delim,[var][,suff]])                      string       string
 aes_gcm_dec(bits,nonce,key,aead_tag)               binary       binary
+aes_gcm_enc(bits,nonce,key,aead_tag)               binary       binary
 and(value)                                         integer      integer
 b64dec                                             string       binary
 base64                                             binary       string
@@ -18874,6 +18875,18 @@ aes_gcm_dec(<bits>,<nonce>,<key>,<aead_tag>)
     http-response set-header X-Decrypted-Text %[var(txn.enc),\
       aes_gcm_dec(128,txn.nonce,Zm9vb2Zvb29mb29wZm9vbw==,txn.aead_tag)]
 
+aes_gcm_enc(<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. <nonce> and <key>
+  parameters must be base64 encoded. Last parameter, <aead_tag>, must be a
+  variable. The AEAD tag will be stored base64 encoded into that variable.
+  The returned result is in raw byte format. The <nonce> and <key> can either
+  be strings or variables. This converter requires at least OpenSSL 1.0.1.
+
+  Example:
+    http-response set-header X-Encrypted-Text %[var(txn.plain),\
+      aes_gcm_enc(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
index 21949cce040448a2663a5b614f66297b3df8ff54..42d60ac9dad86143a1534101b56aab07dfda685c 100644 (file)
@@ -219,6 +219,10 @@ static inline int sample_check_arg_base64(struct arg *arg, char **err)
 static int check_aes_gcm(struct arg *args, struct sample_conv *conv,
                                                  const char *file, int line, char **err)
 {
+       if (conv->kw[8] == 'd')
+               /* flag it as "aes_gcm_dec" */
+               args[0].type_flags = 1;
+
        switch(args[0].data.sint) {
        case 128:
        case 192:
@@ -238,7 +242,8 @@ static int check_aes_gcm(struct arg *args, struct sample_conv *conv,
                memprintf(err, "failed to parse key : %s", *err);
                return 0;
        }
-       if (!sample_check_arg_base64(&args[3], err)) {
+       if ((args[0].type_flags && !sample_check_arg_base64(&args[3], err)) ||
+           (!args[0].type_flags && !vars_check_arg(&args[3], err))) {
                memprintf(err, "failed to parse aead_tag : %s", *err);
                return 0;
        }
@@ -246,13 +251,37 @@ static int check_aes_gcm(struct arg *args, struct sample_conv *conv,
        return 1;
 }
 
+#define sample_conv_aes_gcm_init(a, b, c, d, e, f)     \
+       ({                                              \
+               int _ret = (a) ?                        \
+                   EVP_DecryptInit_ex(b, c, d, e, f) : \
+                   EVP_EncryptInit_ex(b, c, d, e, f);  \
+               _ret;                                   \
+       })
+
+#define sample_conv_aes_gcm_update(a, b, c, d, e, f)   \
+       ({                                              \
+               int _ret = (a) ?                        \
+                   EVP_DecryptUpdate(b, c, d, e, f) :  \
+                   EVP_EncryptUpdate(b, c, d, e, f);   \
+               _ret;                                   \
+       })
+
+#define sample_conv_aes_gcm_final(a, b, c, d)          \
+       ({                                              \
+               int _ret = (a) ?                        \
+                   EVP_DecryptFinal_ex(b, c, d) :      \
+                   EVP_EncryptFinal_ex(b, c, d);       \
+               _ret;                                   \
+       })
+
 /* Arguments: 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)
+static int sample_conv_aes_gcm(const struct arg *arg_p, struct sample *smp, void *private)
 {
        struct sample nonce, key, aead_tag;
        struct buffer *smp_trash = NULL, *smp_trash_alloc = NULL;
        EVP_CIPHER_CTX *ctx;
-       int dec_size, ret;
+       int size, ret, dec;
 
        smp_trash_alloc = alloc_trash_chunk();
        if (!smp_trash_alloc)
@@ -278,30 +307,33 @@ static int sample_conv_aes_gcm_dec(const struct arg *arg_p, struct sample *smp,
                goto err;
 
        if (arg_p[1].type == ARGT_VAR) {
-               dec_size = base64dec(nonce.data.u.str.area, nonce.data.u.str.data, smp_trash->area, smp_trash->size);
-               if (dec_size < 0)
+               size = base64dec(nonce.data.u.str.area, nonce.data.u.str.data, smp_trash->area, smp_trash->size);
+               if (size < 0)
                        goto err;
-               smp_trash->data = dec_size;
+               smp_trash->data = size;
                nonce.data.u.str = *smp_trash;
        }
 
+       /* encrypt (0) or decrypt (1) */
+       dec = (arg_p[0].type_flags == 1);
+
        /* Set cipher type and mode */
        switch(arg_p[0].data.sint) {
        case 128:
-               EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL);
+               sample_conv_aes_gcm_init(dec, ctx, EVP_aes_128_gcm(), NULL, NULL, NULL);
                break;
        case 192:
-               EVP_DecryptInit_ex(ctx, EVP_aes_192_gcm(), NULL, NULL, NULL);
+               sample_conv_aes_gcm_init(dec, ctx, EVP_aes_192_gcm(), NULL, NULL, NULL);
                break;
        case 256:
-               EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
+               sample_conv_aes_gcm_init(dec, ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
                break;
        }
 
        EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce.data.u.str.data, NULL);
 
        /* Initialise IV */
-       if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, (unsigned char *) nonce.data.u.str.area))
+       if(!sample_conv_aes_gcm_init(dec, ctx, NULL, NULL, NULL, (unsigned char *) nonce.data.u.str.area))
                goto err;
 
        smp_set_owner(&key, smp->px, smp->sess, smp->strm, smp->opt);
@@ -309,42 +341,67 @@ static int sample_conv_aes_gcm_dec(const struct arg *arg_p, struct sample *smp,
                goto err;
 
        if (arg_p[2].type == ARGT_VAR) {
-               dec_size = base64dec(key.data.u.str.area, key.data.u.str.data, smp_trash->area, smp_trash->size);
-               if (dec_size < 0)
+               size = base64dec(key.data.u.str.area, key.data.u.str.data, smp_trash->area, smp_trash->size);
+               if (size < 0)
                        goto err;
-               smp_trash->data = dec_size;
+               smp_trash->data = size;
                key.data.u.str = *smp_trash;
        }
 
        /* Initialise key */
-       if (!EVP_DecryptInit_ex(ctx, NULL, NULL, (unsigned char *) key.data.u.str.area, NULL))
+       if (!sample_conv_aes_gcm_init(dec, ctx, NULL, NULL, (unsigned char *) key.data.u.str.area, NULL))
                goto err;
 
-       if (!EVP_DecryptUpdate(ctx, (unsigned char *) smp_trash->area, (int *) &smp_trash->data,
-                              (unsigned char *) smp_trash_alloc->area, (int) smp_trash_alloc->data))
+       if (!sample_conv_aes_gcm_update(dec, ctx, (unsigned char *) smp_trash->area, (int *) &smp_trash->data,
+                                       (unsigned char *) smp_trash_alloc->area, (int) smp_trash_alloc->data))
                goto err;
 
        smp_set_owner(&aead_tag, smp->px, smp->sess, smp->strm, smp->opt);
-       if (!sample_conv_var2smp_str(&arg_p[3], &aead_tag))
-               goto err;
-
-       if (arg_p[3].type == ARGT_VAR) {
-               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)
+       if (dec) {
+               if (!sample_conv_var2smp_str(&arg_p[3], &aead_tag))
                        goto err;
-               smp_trash_alloc->data = dec_size;
-               aead_tag.data.u.str = *smp_trash_alloc;
-       }
 
-       dec_size = smp_trash->data;
+               if (arg_p[3].type == ARGT_VAR) {
+                       size = base64dec(aead_tag.data.u.str.area, aead_tag.data.u.str.data, smp_trash_alloc->area,
+                                        smp_trash_alloc->size);
+                       if (size < 0)
+                               goto err;
+                       smp_trash_alloc->data = size;
+                       aead_tag.data.u.str = *smp_trash_alloc;
+               }
 
-       EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, aead_tag.data.u.str.data, (void *) aead_tag.data.u.str.area);
-       ret = EVP_DecryptFinal_ex(ctx, (unsigned char *) smp_trash->area + smp_trash->data, (int *) &smp_trash->data);
+               EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, aead_tag.data.u.str.data,
+                                   (void *) aead_tag.data.u.str.area);
+       }
 
+       size = smp_trash->data;
+
+       ret = sample_conv_aes_gcm_final(dec, 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;
+       if (!dec) {
+               struct buffer *trash = get_trash_chunk();
+
+               EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, (void *) trash->area);
+
+               aead_tag.data.u.str = *smp_trash_alloc;
+               ret = a2base64(trash->area, 16, aead_tag.data.u.str.area, aead_tag.data.u.str.size);
+               if (ret < 0)
+                       goto err;
+
+               aead_tag.data.u.str.data = ret;
+               aead_tag.data.type = SMP_T_STR;
+               aead_tag.flags &= ~SMP_F_CONST;
+
+               if (!var_set(arg_p[3].data.var.name_hash, arg_p[3].data.var.scope, &aead_tag,
+                            (arg_p[3].data.var.scope == SCOPE_PROC) ? VF_COND_IFEXISTS : 0)) {
+                       goto err;
+               }
+       }
+
+       smp->data.u.str.data = size + smp_trash->data;
        smp->data.u.str.area = smp_trash->area;
        smp->data.type = SMP_T_BIN;
        smp_dup(smp);
@@ -2363,7 +2420,8 @@ INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
 static struct sample_conv_kw_list sample_conv_kws = {ILH, {
        { "sha2",               sample_conv_sha2,             ARG1(0, SINT),            smp_check_sha2,          SMP_T_BIN,  SMP_T_BIN  },
 #ifdef EVP_CIPH_GCM_MODE
-       { "aes_gcm_dec",        sample_conv_aes_gcm_dec,      ARG4(4,SINT,STR,STR,STR), check_aes_gcm,           SMP_T_BIN,  SMP_T_BIN  },
+       { "aes_gcm_enc",        sample_conv_aes_gcm,          ARG4(4,SINT,STR,STR,STR), check_aes_gcm,           SMP_T_BIN,  SMP_T_BIN  },
+       { "aes_gcm_dec",        sample_conv_aes_gcm,          ARG4(4,SINT,STR,STR,STR), check_aes_gcm,           SMP_T_BIN,  SMP_T_BIN  },
 #endif
        { "x509_v_err_str",     sample_conv_x509_v_err,       0,                        NULL,                    SMP_T_SINT, SMP_T_STR },
        { "digest",             sample_conv_crypto_digest,    ARG1(1,STR),              check_crypto_digest,     SMP_T_BIN,  SMP_T_BIN  },