Le 07/05/2020 à 18:54, Patrick Gansterer a écrit :
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.

Hi Patrick,

Thanks. It looks good for me. I've only a comment. I discussed it with Willy and William. Instead of creating a new file, we prefer to move these converters in sample.c. There are already some other hash functions in this file (sha1, sha2...). So it is a good place for these ones too and it avoids to add a new file. I also changed the required version in the reg-test scripts to set it to 2.2.

Sorry, I would have liked to be faster and save you from unnecessary work. I don't want to ask you to rework your patches again, thus I've amended them. You'll find them in attachment. If it is ok for you, I'll merge it. Otherwise, feel free to rework it according to your taste.

--
Christopher Faulet
>From 546605a62872c9fa8b6a26b1ea33750c3b92b510 Mon Sep 17 00:00:00 2001
From: Patrick Gansterer <[email protected]>
Date: Sun, 17 Jun 2018 11:21:11 +0200
Subject: [PATCH 1/2] MINOR: sample: Move aes_gcm_dec implementation into
 sample.c

aes_gcm_dec is independent of the TLS implementation and fits better
in sample.c file with others hash functions.

[Cf: I slightly updated this patch to move aes_gcm_dec converter in sample.c
     instead the new file crypto.c]

Reviewed-by: Tim Duesterhus <[email protected]>
---
 src/sample.c   | 138 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/ssl_sock.c | 142 -------------------------------------------------
 2 files changed, 137 insertions(+), 143 deletions(-)

diff --git a/src/sample.c b/src/sample.c
index 12724f73d..1be945993 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -1653,7 +1653,140 @@ static int sample_conv_sha2(const struct arg *arg_p, struct sample *smp, void *p
 	smp->flags &= ~SMP_F_CONST;
 	return 1;
 }
-#endif
+
+#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 /* HA_OPENSSL_VERSION_NUMBER */
+
+#endif /* USE_OPENSSL */
 
 static int sample_conv_bin2hex(const struct arg *arg_p, struct sample *smp, void *private)
 {
@@ -3496,6 +3629,9 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
 	{ "sha1",   sample_conv_sha1,      0,            NULL, SMP_T_BIN,  SMP_T_BIN  },
 #ifdef USE_OPENSSL
 	{ "sha2",   sample_conv_sha2,      ARG1(0, SINT), smp_check_sha2, SMP_T_BIN,  SMP_T_BIN  },
+#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
 #endif
 	{ "concat", sample_conv_concat,    ARG3(1,STR,STR,STR), smp_check_concat, SMP_T_STR,  SMP_T_STR },
 	{ "strcmp", sample_conv_strcmp,    ARG1(1,STR), smp_check_strcmp, SMP_T_STR,  SMP_T_SINT },
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.25.4

>From d14a1ea8620f10521820b08a7f36de95c2951b9b Mon Sep 17 00:00:00 2001
From: Patrick Gansterer <[email protected]>
Date: Wed, 22 Apr 2020 16:47:57 +0200
Subject: [PATCH 2/2] MINOR: sample: 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.

Reviewed-by: Tim Duesterhus <[email protected]>
---
 doc/configuration.txt          | 16 ++++++
 reg-tests/converter/digest.vtc | 57 +++++++++++++++++++++
 reg-tests/converter/hmac.vtc   | 55 ++++++++++++++++++++
 src/sample.c                   | 93 +++++++++++++++++++++++++++++++++-
 4 files changed, 220 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..a14f1ccfd
--- /dev/null
+++ b/reg-tests/converter/digest.vtc
@@ -0,0 +1,57 @@
+varnishtest "digest converter Test"
+
+#REQUIRE_VERSION=2.2
+#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..f9d9d354a
--- /dev/null
+++ b/reg-tests/converter/hmac.vtc
@@ -0,0 +1,55 @@
+varnishtest "HMAC converter Test"
+
+#REQUIRE_VERSION=2.2
+#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/sample.c b/src/sample.c
index 1be945993..0f0adcd45 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -1654,7 +1654,6 @@ static int sample_conv_sha2(const struct arg *arg_p, struct sample *smp, void *p
 	return 1;
 }
 
-#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000100fL)
 static inline int sample_conv_var2smp_str(const struct arg *arg, struct sample *smp)
 {
 	switch (arg->type) {
@@ -1675,6 +1674,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)
 {
@@ -1786,6 +1786,95 @@ err:
 }
 #endif /* HA_OPENSSL_VERSION_NUMBER */
 
+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;
+}
+
 #endif /* USE_OPENSSL */
 
 static int sample_conv_bin2hex(const struct arg *arg_p, struct sample *smp, void *private)
@@ -3632,6 +3721,8 @@ static struct sample_conv_kw_list sample_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 },
 #endif
 	{ "concat", sample_conv_concat,    ARG3(1,STR,STR,STR), smp_check_concat, SMP_T_STR,  SMP_T_STR },
 	{ "strcmp", sample_conv_strcmp,    ARG1(1,STR), smp_check_strcmp, SMP_T_STR,  SMP_T_SINT },
-- 
2.25.4

Reply via email to