On 07.05.20 18:23, Tim Düsterhus wrote:> This is about your "[PATCH 1/2] MINOR: crypto: Move aes_gcm_dec
implementation into new file". It does not contain a body, but instead
just a subject.

Ok, agree.

I skipped the patch (that's why I did not ACK the first, but only the
second one), because it was a very large change of code that was just
moved. Nonetheless I should have noticed the missing body and noted that
during my review.

IMHO, that's a patch you usually do not want review.

Please note that I review patches on a voluntary basis. I'm not an
"employed first level reviewer".

That's not what I meant. I thought that when regular participants on this list do not spot the errors of first time contributes, it can't be that obvious and directing to CONTRIBUTING might not be enough.

Liking HAProxy and wanting to give something back is my motivation as
well. I am very sorry to see how this experience went for you. If it is
of any help to you: This is definitely not how it usually goes.

Then here is my next try. ;-)

I've rebased my changes to reflect the recent changes and added the missing description to the first patch.

- Patrick
>From 9125c6df81e135275c450a0be32bcd0b58ef2099 Mon Sep 17 00:00:00 2001
From: Patrick Gansterer <par...@paroga.com>
Date: Sun, 17 Jun 2018 11:21:11 +0200
Subject: [PATCH 1/2] MINOR: crypto: Move aes_gcm_dec implementation into new
 file

aes_gcm_dec is independent of the TLS implementation and fits better
in a separate file dedicated to crypto functionality.
This gives converters which depend on OpenSSL a clear home.
---
 Makefile       |   2 +-
 src/crypto.c   | 163 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/ssl_sock.c | 142 ------------------------------------------
 3 files changed, 164 insertions(+), 143 deletions(-)
 create mode 100644 src/crypto.c

diff --git a/Makefile b/Makefile
index 1e4213989..2dea46368 100644
--- a/Makefile
+++ b/Makefile
@@ -542,7 +542,7 @@ OPTIONS_LDFLAGS += $(if $(SSL_LIB),-L$(SSL_LIB)) -lssl -lcrypto
 ifneq ($(USE_DL),)
 OPTIONS_LDFLAGS += -ldl
 endif
-OPTIONS_OBJS  += src/ssl_sock.o
+OPTIONS_OBJS  += src/crypto.o src/ssl_sock.o
 endif
 
 # The private cache option affect the way the shctx is built
diff --git a/src/crypto.c b/src/crypto.c
new file mode 100644
index 000000000..74b92eee5
--- /dev/null
+++ b/src/crypto.c
@@ -0,0 +1,163 @@
+/*
+ * Crypto converters
+ *
+ * Copyright 2020 Nenad Merdanovic <nmer...@haproxy.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <common/base64.h>
+#include <common/chunk.h>
+#include <common/standard.h>
+
+#include <proto/arg.h>
+#include <proto/sample.h>
+#include <proto/vars.h>
+
+#include <openssl/evp.h>
+
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000100fL)
+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;
+}
+
+/* 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)
+{
+	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
+
+/* Note: must not be declared <const> as its list will be overwritten */
+static struct sample_conv_kw_list conv_kws = {ILH, {
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000100fL)
+	{ "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);
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 8b02a1d66..442863e71 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -12694,138 +12694,6 @@ static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx
 
 }
 
-#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000100fL)
-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;
-}
-
-/* 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)
-{
-	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
-
 /* Argument validation functions */
 
 /* This function is used to validate the arguments passed to any "x_dn" ssl
@@ -13128,16 +12996,6 @@ 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 (HA_OPENSSL_VERSION_NUMBER >= 0x1000100fL)
-	{ "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,
-- 
2.26.2

>From 1df76e534518084cfb9c0f8296cda8e2f758396c Mon Sep 17 00:00:00 2001
From: Patrick Gansterer <par...@paroga.com>
Date: Wed, 22 Apr 2020 16:47:57 +0200
Subject: [PATCH 2/2] MINOR: crypto: Add digest and hmac converters

Make the digest and HMAC function of OpenSSL accessible to the user via
converters. They can be used to sign and validate content.
---
 doc/configuration.txt          | 16 ++++++
 reg-tests/converter/digest.vtc | 57 ++++++++++++++++++++
 reg-tests/converter/hmac.vtc   | 55 ++++++++++++++++++++
 src/crypto.c                   | 95 +++++++++++++++++++++++++++++++++-
 4 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 reg-tests/converter/digest.vtc
 create mode 100644 reg-tests/converter/hmac.vtc

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 40dae3fd7..4af9f00c5 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -14499,6 +14499,13 @@ debug([<prefix][,<destination>])
   Example:
     tcp-request connection track-sc0 src,debug(track-sc)
 
+digest(<algorithm>)
+  Converts a binary input sample to a message digest. The result is a binary
+  sample. The <algorithm> must be an OpenSSL message digest name (e.g. sha256).
+
+  Please note that this converter is only available when haproxy has been
+  compiled with USE_OPENSSL.
+
 div(<value>)
   Divides the input value of type signed integer by <value>, and returns the
   result as an signed integer. If <value> is null, the largest unsigned
@@ -14559,6 +14566,15 @@ htonl
   this converter is used, the input integer value is first casted to an
   unsigned 32-bit integer.
 
+hmac(<algorithm>, <key>)
+  Converts a binary input sample to a message authentication code with the given
+  key. The result is a binary sample. The <algorithm> must be one of the
+  registered OpenSSL message digest names (e.g. sha256). The <key> parameter must
+  be base64 encoded and can either be a string or a variable.
+
+  Please note that this converter is only available when haproxy has been
+  compiled with USE_OPENSSL.
+
 http_date([<offset],[<unit>])
   Converts an integer supposed to contain a date since epoch to a string
   representing this date in a format suitable for use in HTTP header fields. If
diff --git a/reg-tests/converter/digest.vtc b/reg-tests/converter/digest.vtc
new file mode 100644
index 000000000..39525a82b
--- /dev/null
+++ b/reg-tests/converter/digest.vtc
@@ -0,0 +1,57 @@
+varnishtest "digest converter Test"
+
+#REQUIRE_VERSION=2.1
+#REQUIRE_OPTION=OPENSSL
+
+feature ignore_unknown_macro
+
+server s1 {
+	rxreq
+	txresp
+} -repeat 2 -start
+
+haproxy h1 -conf {
+    defaults
+	mode http
+	timeout connect 1s
+	timeout client  1s
+	timeout server  1s
+
+    frontend fe
+	bind "fd@${fe}"
+
+	#### requests
+	http-request  set-var(txn.hash) req.hdr(hash)
+
+	http-response set-header SHA1   "%[var(txn.hash),digest(sha1),hex,lower]"
+	http-response set-header SHA224   "%[var(txn.hash),digest(sha224),hex,lower]"
+	http-response set-header SHA256   "%[var(txn.hash),digest(sha256),hex,lower]"
+	http-response set-header SHA384   "%[var(txn.hash),digest(sha384),hex,lower]"
+	http-response set-header SHA512   "%[var(txn.hash),digest(sha512),hex,lower]"
+
+	default_backend be
+
+    backend be
+	server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+	txreq -url "/" \
+	  -hdr "Hash: 1"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.sha1 == "356a192b7913b04c54574d18c28d46e6395428ab"
+	expect resp.http.sha224 == "e25388fde8290dc286a6164fa2d97e551b53498dcbf7bc378eb1f178"
+	expect resp.http.sha256 == "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b"
+	expect resp.http.sha384 == "47f05d367b0c32e438fb63e6cf4a5f35c2aa2f90dc7543f8a41a0f95ce8a40a313ab5cf36134a2068c4c969cb50db776"
+	expect resp.http.sha512 == "4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a"
+	txreq -url "/" \
+	  -hdr "Hash: 2"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.sha1 == "da4b9237bacccdf19c0760cab7aec4a8359010b0"
+	expect resp.http.sha224 == "58b2aaa0bfae7acc021b3260e941117b529b2e69de878fd7d45c61a9"
+	expect resp.http.sha256 == "d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35"
+	expect resp.http.sha384 == "d063457705d66d6f016e4cdd747db3af8d70ebfd36badd63de6c8ca4a9d8bfb5d874e7fbd750aa804dcaddae7eeef51e"
+	expect resp.http.sha512 == "40b244112641dd78dd4f93b6c9190dd46e0099194d5a44257b7efad6ef9ff4683da1eda0244448cb343aa688f5d3efd7314dafe580ac0bcbf115aeca9e8dc114"
+} -run
diff --git a/reg-tests/converter/hmac.vtc b/reg-tests/converter/hmac.vtc
new file mode 100644
index 000000000..66de3df00
--- /dev/null
+++ b/reg-tests/converter/hmac.vtc
@@ -0,0 +1,55 @@
+varnishtest "HMAC converter Test"
+
+#REQUIRE_VERSION=2.1
+#REQUIRE_OPTION=OPENSSL
+
+feature ignore_unknown_macro
+
+server s1 {
+	rxreq
+	txresp
+} -repeat 2 -start
+
+haproxy h1 -conf {
+    defaults
+	mode http
+	timeout connect 1s
+	timeout client  1s
+	timeout server  1s
+
+    frontend fe
+	bind "fd@${fe}"
+
+	#### requests
+	http-request  set-var(txn.hash) req.hdr(hash)
+	http-request  set-var(txn.key) str(my_super_secret_long_key),base64
+
+	http-response set-header SHA1-short   "%[var(txn.hash),hmac(sha1,a2V5),hex,lower]"
+	http-response set-header SHA1-long   "%[var(txn.hash),hmac(sha1,txn.key),hex,lower]"
+	http-response set-header SHA256-short   "%[var(txn.hash),hmac(sha256,a2V5),hex,lower]"
+	http-response set-header SHA256-long   "%[var(txn.hash),hmac(sha256,txn.key),hex,lower]"
+
+	default_backend be
+
+    backend be
+	server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+	txreq -url "/" \
+	  -hdr "Hash: 1"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.sha1-short == "e23feb105f9622241bf23db1638cd2b4208b1f53"
+	expect resp.http.sha1-long == "87b10ddcf39e26f6bd7c3b0e38e0125997b255be"
+	expect resp.http.sha256-short == "6da91fb91517be1f5cdcf3af91d7d40c717dd638a306157606fb2e584f7ae926"
+	expect resp.http.sha256-long == "2fb3de6a462c54d1803f946b52202f3a8cd46548ffb3f789b4ac11a4361ffef2"
+	txreq -url "/" \
+	  -hdr "Hash: 2"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.sha1-short == "311219c4a80c5ef81b1cee5505236c1d0ab1922c"
+	expect resp.http.sha1-long == "c5758af565ba4b87b3db49c8b32d4a94d430cb78"
+	expect resp.http.sha256-short == "ae7b3ee87b8c9214f714df1c2042c7a985b9d711e9938a063937ad1636775a88"
+	expect resp.http.sha256-long == "c073191a2ebf29f510444b92c187d62199d84b58f58dceeadb91994c170a9a16"
+} -run
diff --git a/src/crypto.c b/src/crypto.c
index 74b92eee5..7a7231333 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -2,6 +2,7 @@
  * Crypto converters
  *
  * Copyright 2020 Nenad Merdanovic <nmer...@haproxy.com>
+ * Copyright 2020 Patrick Gansterer <par...@paroga.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -19,8 +20,8 @@
 #include <proto/vars.h>
 
 #include <openssl/evp.h>
+#include <openssl/hmac.h>
 
-#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000100fL)
 static inline int sample_conv_var2smp_str(const struct arg *arg, struct sample *smp)
 {
 	switch (arg->type) {
@@ -41,6 +42,7 @@ static inline int sample_conv_var2smp_str(const struct arg *arg, struct sample *
 	}
 }
 
+#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000100fL)
 static int check_aes_gcm(struct arg *args, struct sample_conv *conv,
 						  const char *file, int line, char **err)
 {
@@ -152,11 +154,102 @@ err:
 }
 # endif
 
+static int check_crypto_digest(struct arg *args, struct sample_conv *conv,
+						  const char *file, int line, char **err)
+{
+	const EVP_MD *evp = EVP_get_digestbyname(args[0].data.str.area);
+
+	if (evp)
+		return 1;
+
+	memprintf(err, "algorithm must be a valid OpenSSL message digest name.");
+	return 0;
+}
+
+static int sample_conv_crypto_digest(const struct arg *args, struct sample *smp, void *private)
+{
+	struct buffer *trash = get_trash_chunk();
+	unsigned char *md = (unsigned char*) trash->area;
+	unsigned int md_len = trash->size;
+	EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+	const EVP_MD *evp = EVP_get_digestbyname(args[0].data.str.area);
+
+	if (!ctx)
+		return 0;
+
+	if (!EVP_DigestInit_ex(ctx, evp, NULL) ||
+	    !EVP_DigestUpdate(ctx, smp->data.u.str.area, smp->data.u.str.data) ||
+	    !EVP_DigestFinal_ex(ctx, md, &md_len)) {
+		EVP_MD_CTX_free(ctx);
+		return 0;
+	}
+
+	EVP_MD_CTX_free(ctx);
+
+	trash->data = md_len;
+	smp->data.u.str = *trash;
+	smp->data.type = SMP_T_BIN;
+	smp->flags &= ~SMP_F_CONST;
+	return 1;
+}
+
+static int check_crypto_hmac(struct arg *args, struct sample_conv *conv,
+						  const char *file, int line, char **err)
+{
+	if (!check_crypto_digest(args, conv, file, line, err))
+		return 0;
+
+	vars_check_arg(&args[1], NULL);
+	return 1;
+}
+
+static int sample_conv_crypto_hmac(const struct arg *args, struct sample *smp, void *private)
+{
+	struct sample key;
+	struct buffer *trash, *key_trash;
+	unsigned char *md;
+	unsigned int md_len;
+	const EVP_MD *evp = EVP_get_digestbyname(args[0].data.str.area);
+	int dec_size;
+
+	smp_set_owner(&key, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(&args[1], &key))
+		return 0;
+
+	trash = get_trash_chunk();
+	key_trash = alloc_trash_chunk();
+	if (!key_trash)
+		return 0;
+
+	dec_size = base64dec(key.data.u.str.area, key.data.u.str.data, key_trash->area, key_trash->size);
+	if (dec_size < 0)
+		goto err;
+
+	md = (unsigned char*) trash->area;
+	md_len = trash->size;
+	if (!HMAC(evp, key_trash->area, dec_size, (const unsigned char*) smp->data.u.str.area, smp->data.u.str.data, md, &md_len))
+		goto err;
+
+	free_trash_chunk(key_trash);
+
+	trash->data = md_len;
+	smp->data.u.str = *trash;
+	smp->data.type = SMP_T_BIN;
+	smp->flags &= ~SMP_F_CONST;
+	return 1;
+
+err:
+	free_trash_chunk(key_trash);
+	return 0;
+}
+
 /* Note: must not be declared <const> as its list will be overwritten */
 static struct sample_conv_kw_list conv_kws = {ILH, {
 #if (HA_OPENSSL_VERSION_NUMBER >= 0x1000100fL)
 	{ "aes_gcm_dec", sample_conv_aes_gcm_dec,   ARG4(4,SINT,STR,STR,STR), check_aes_gcm,       SMP_T_BIN, SMP_T_BIN },
 #endif
+	{ "digest",      sample_conv_crypto_digest, ARG1(1,STR),              check_crypto_digest, SMP_T_BIN, SMP_T_BIN },
+	{ "hmac",        sample_conv_crypto_hmac,   ARG2(2,STR,STR),          check_crypto_hmac,   SMP_T_BIN, SMP_T_BIN },
 	{ NULL, NULL, 0, 0, 0 },
 }};
 
-- 
2.26.2

Reply via email to