[PATCH] MINOR: crypto: Add digest and hmac converters

2018-06-17 Thread Patrick Gansterer
Make the digest and HMAC function of OpenSSL accesable to the user via
converters. e.g. They can be used to sign and validate cookies.
---
 Makefile |  2 +-
 src/crypto.c | 84 
 2 files changed, 85 insertions(+), 1 deletion(-)
 create mode 100644 src/crypto.c

diff --git a/Makefile b/Makefile
index 5d170041..9a3a5024 100644
--- a/Makefile
+++ b/Makefile
@@ -609,7 +609,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 ..dcb343dc
--- /dev/null
+++ b/src/crypto.c
@@ -0,0 +1,84 @@
+/*
+ * Crypto converters
+ *
+ * Copyright 2018 Patrick Gansterer 
+ *
+ * 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 
+
+#include 
+#include 
+
+#include 
+#include 
+
+static int sample_conv_crypto_digest(const struct arg *args, struct sample 
*smp, void *private)
+{
+   struct chunk *trash = get_trash_chunk();
+   EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+   const EVP_MD *evp = EVP_get_digestbyname(args[0].data.str.str);
+   unsigned char *md = (unsigned char*) trash->str;
+   unsigned int md_len = trash->size;
+
+   if (!ctx)
+   return 0;
+   if (!evp)
+   return 0;
+
+   if (!EVP_DigestInit(ctx, evp) ||
+   !EVP_DigestUpdate(ctx, smp->data.u.str.str, smp->data.u.str.len) ||
+   !EVP_DigestFinal(ctx, md, _len)) {
+   EVP_MD_CTX_free(ctx);
+   return 0;
+   }
+
+   EVP_MD_CTX_free(ctx);
+
+   trash->len = md_len;
+   smp->data.u.str = *trash;
+   smp->data.type = SMP_T_BIN;
+   smp->flags &= ~SMP_F_CONST;
+   return 1;
+}
+
+static int sample_conv_crypto_hmac(const struct arg *args, struct sample *smp, 
void *private)
+{
+   struct chunk *trash = get_trash_chunk();
+   const EVP_MD *evp = EVP_get_digestbyname(args[0].data.str.str);
+   const char* key = args[1].data.str.str;
+   int key_len = args[1].data.str.len;
+   unsigned char *md = (unsigned char*) trash->str;
+   unsigned int md_len = trash->size;
+
+   trash->len = 0;
+
+   if (!evp)
+   return 0;
+
+   if (!HMAC(evp, key, key_len, (const unsigned char*) 
smp->data.u.str.str, smp->data.u.str.len, md, _len))
+   return 0;
+
+   trash->len = md_len;
+   smp->data.u.str = *trash;
+   smp->data.type = SMP_T_BIN;
+   smp->flags &= ~SMP_F_CONST;
+   return 1;
+}
+
+static struct sample_conv_kw_list sample_conv_kws = {ILH, {
+   { "digest", sample_conv_crypto_digest, ARG1(1,STR), NULL, 
SMP_T_BIN, SMP_T_BIN },
+   { "hmac",   sample_conv_crypto_hmac,   ARG2(2,STR,STR), NULL, 
SMP_T_BIN, SMP_T_BIN },
+   { /* END */ },
+}};
+
+__attribute__((constructor))
+static void __crypto_init(void)
+{
+   sample_register_convs(_conv_kws);
+}
-- 
2.17.1


[PATCH] MINOR: crypto: Add digest and hmac converters

2018-06-17 Thread Patrick Gansterer
Make the digest and HMAC function of OpenSSL accesable to the user via
converters. e.g. They can be used to sign and validate cookies.
---
 Makefile  |  2 +-
 doc/configuration.txt |  9 +
 src/crypto.c  | 84 +++
 3 files changed, 94 insertions(+), 1 deletion(-)
 create mode 100644 src/crypto.c

diff --git a/Makefile b/Makefile
index 5d170041..9a3a5024 100644
--- a/Makefile
+++ b/Makefile
@@ -609,7 +609,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/doc/configuration.txt b/doc/configuration.txt
index e901d7ee..7c9eb55c 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -12909,6 +12909,10 @@ debug
   type of the input sample. The sample is returned as is on its output. This
   converter only exists when haproxy was built with debugging enabled.
 
+digest()
+  Converts a binary input sample to a message digest. The result is a binary
+  sample. The alorithm must a OpenSSL message digest name (e.g sha256).
+
 div()
   Divides the input value of type signed integer by , and returns the
   result as an signed integer. If  is null, the largest unsigned
@@ -12963,6 +12967,11 @@ hex2i
   Converts a hex string containing two hex digits per input byte to an
   integer. If the input value can not be converted, then zero is returned.
 
+hmac(, )
+  Converts a binary input sample to a message authentication code with the 
given
+  key. The result is  a binary sample. The alorithm must be of the registered
+  OpenSSL message digest names (e.g sha256).
+
 http_date([])
   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/src/crypto.c b/src/crypto.c
new file mode 100644
index ..e4a65557
--- /dev/null
+++ b/src/crypto.c
@@ -0,0 +1,84 @@
+/*
+ * Crypto converters
+ *
+ * Copyright 2018 Patrick Gansterer 
+ *
+ * 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 
+
+#include 
+#include 
+
+#include 
+#include 
+
+static int sample_conv_crypto_digest(const struct arg *args, struct sample 
*smp, void *private)
+{
+ struct chunk *trash = get_trash_chunk();
+ EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+ const EVP_MD *evp = EVP_get_digestbyname(args[0].data.str.str);
+ unsigned char *md = (unsigned char*) trash->str;
+ unsigned int md_len = trash->size;
+
+ if (!ctx)
+   return 0;
+ if (!evp)
+   return 0;
+
+ if (!EVP_DigestInit(ctx, evp) ||
+ !EVP_DigestUpdate(ctx, smp->data.u.str.str, smp->data.u.str.len) ||
+ !EVP_DigestFinal(ctx, md, _len)) {
+   EVP_MD_CTX_free(ctx);
+   return 0;
+ }
+
+ EVP_MD_CTX_free(ctx);
+
+ trash->len = md_len;
+ smp->data.u.str = *trash;
+ smp->data.type = SMP_T_BIN;
+ smp->flags &= ~SMP_F_CONST;
+ return 1;
+}
+
+static int sample_conv_crypto_hmac(const struct arg *args, struct sample *smp, 
void *private)
+{
+ struct chunk *trash = get_trash_chunk();
+ const EVP_MD *evp = EVP_get_digestbyname(args[0].data.str.str);
+ const char* key = args[1].data.str.str;
+ int key_len = args[1].data.str.len;
+ unsigned char *md = (unsigned char*) trash->str;
+ unsigned int md_len = trash->size;
+
+ trash->len = 0;
+
+ if (!evp)
+   return 0;
+
+ if (!HMAC(evp, key, key_len, (const unsigned char*) smp->data.u.str.str, 
smp->data.u.str.len, md, _len))
+   return 0;
+
+ trash->len = md_len;
+ smp->data.u.str = *trash;
+ smp->data.type = SMP_T_BIN;
+ smp->flags &= ~SMP_F_CONST;
+ return 1;
+}
+
+static struct sample_conv_kw_list sample_conv_kws = {ILH, {
+ { "digest", sample_conv_crypto_digest, ARG1(1,STR), NULL, SMP_T_BIN, 
SMP_T_BIN },
+ { "hmac",   sample_conv_crypto_hmac,   ARG2(2,STR,STR), NULL, SMP_T_BIN, 
SMP_T_BIN },
+ { /* END */ },
+}};
+
+__attribute__((constructor))
+static void __crypto_init(void)
+{
+ sample_register_convs(_conv_kws);
+}
-- 
2.17.1




Re: [PATCH] MINOR: crypto: Add digest and hmac converters

2018-06-17 Thread Patrick Gansterer


> On 17 Jun 2018, at 13:36, Baptiste  wrote:
> 
> Can they be used to validate oauth tokens too?

Depends on the implementation of the tokens, but if they are HMACSHA256 signed 
JWT, it’s very easy to validate them in a lua script now.

> Note: maybe an update for configuration.txt would be helpful too.

Thx for the note. I’ve updated the patch.

- Patrick


Re: [PATCH] MINOR: crypto: Add digest and hmac converters

2020-04-22 Thread Patrick Gansterer
Tim,

thx for the quick review. I attached a new patchset.

On Mittwoch, 22. April 2020 18:01:01 CEST Tim Düsterhus wrote:
> Small nit: It should read 'e.g.' (with a dot at the end).

Argh. Can't believe how many typos I made in this lines. ^^

> I believe you support a variable key now. You should add this to the doc
> (and the reg-test) then.

Done.

- Patrick>From 8f6ce045c80e0f67a485233ee602b57b4c311bde Mon Sep 17 00:00:00 2001
From: Patrick Gansterer 
Date: Sun, 17 Jun 2018 11:21:11 +0200
Subject: [PATCH 1/2] MINOR: crypto: Move aes_gcm_dec implementation into new
 file

---
 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 0..74b92eee5
--- /dev/null
+++ b/src/crypto.c
@@ -0,0 +1,163 @@
+/*
+ * Crypto converters
+ *
+ * Copyright 2020 Nenad Merdanovic 
+ *
+ * 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 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include 
+
+#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(>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([1], NULL);
+	vars_check_arg([2], NULL);
+	vars_check_arg([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(, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(_p[1], ))
+		return 0;
+
+	smp_set_owner(, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(_p[2], ))
+		return 0;
+
+	smp_set_owner(_tag, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(_p[3], _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 *) _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->

Re: [PATCH] MINOR: crypto: Add digest and hmac converters

2020-04-22 Thread Patrick Gansterer
Tim,

sorry for the troubles. My mail program added automatic line breaks. :-(

I attached the two files now.

- Patrick
>From 8f6ce045c80e0f67a485233ee602b57b4c311bde Mon Sep 17 00:00:00 2001
From: Patrick Gansterer 
Date: Sun, 17 Jun 2018 11:21:11 +0200
Subject: [PATCH 1/2] MINOR: crypto: Move aes_gcm_dec implementation into new
 file

---
 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 0..74b92eee5
--- /dev/null
+++ b/src/crypto.c
@@ -0,0 +1,163 @@
+/*
+ * Crypto converters
+ *
+ * Copyright 2020 Nenad Merdanovic 
+ *
+ * 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 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include 
+
+#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(>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([1], NULL);
+	vars_check_arg([2], NULL);
+	vars_check_arg([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(, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(_p[1], ))
+		return 0;
+
+	smp_set_owner(, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(_p[2], ))
+		return 0;
+
+	smp_set_owner(_tag, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(_p[3], _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 *) _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_

Re: [PATCH] MINOR: crypto: Add digest and hmac converters

2020-04-22 Thread Patrick Gansterer
Tim,

thanks for the review. I just rebased my old patch today and didn't check what 
changed in the meantime in the codebase. I created a separate patch to move 
aes_gcm_dec out of ssl_sock.c since it seams to fit better to my new file.

- Patrick

>From 8f6ce045c80e0f67a485233ee602b57b4c311bde Mon Sep 17 00:00:00 2001
From: Patrick Gansterer 
Date: Sun, 17 Jun 2018 11:21:11 +0200
Subject: [PATCH 1/2] MINOR: crypto: Move aes_gcm_dec implementation into new
 file

---
 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 0..74b92eee5
--- /dev/null
+++ b/src/crypto.c
@@ -0,0 +1,163 @@
+/*
+ * Crypto converters
+ *
+ * Copyright 2020 Nenad Merdanovic 
+ *
+ * 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 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include 
+
+#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(>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([1], NULL);
+   vars_check_arg([2], NULL);
+   vars_check_arg([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(, smp->px, smp->sess, smp->strm, smp->opt);
+   if (!sample_conv_var2smp_str(_p[1], ))
+   return 0;
+
+   smp_set_owner(, smp->px, smp->sess, smp->strm, smp->opt);
+   if (!sample_conv_var2smp_str(_p[2], ))
+   return 0;
+
+   smp_set_owner(_tag, smp->px, smp->sess, smp->strm, smp->opt);
+   if (!sample_conv_var2smp_str(_p[3], _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.

[PATCH] MINOR: crypto: Add digest and hmac converters

2020-04-22 Thread Patrick Gansterer
Make the digest and HMAC function of OpenSSL accessible to the user via
converters. They can be used to sign and validate content.
---
 Makefile|  2 +-
 doc/configuration.txt   |  9 
 reg-tests/sample_fetches/hashes.vtc | 22 
 src/crypto.c| 84 +
 4 files changed, 116 insertions(+), 1 deletion(-)
 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/doc/configuration.txt b/doc/configuration.txt
index 2e548b66c..17b2debe5 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -13918,6 +13918,10 @@ debug([])
   Example:
 tcp-request connection track-sc0 src,debug(track-sc)
 
+digest()
+  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).
+
 div()
   Divides the input value of type signed integer by , and returns the
   result as an signed integer. If  is null, the largest unsigned
@@ -13972,6 +13976,11 @@ hex2i
   Converts a hex string containing two hex digits per input byte to an
   integer. If the input value cannot be converted, then zero is returned.
 
+hmac(, )
+  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).
+
 http_date([])
   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/sample_fetches/hashes.vtc 
b/reg-tests/sample_fetches/hashes.vtc
index 874f81e41..ca641f86c 100644
--- a/reg-tests/sample_fetches/hashes.vtc
+++ b/reg-tests/sample_fetches/hashes.vtc
@@ -38,6 +38,19 @@ haproxy h1 -conf {
 #http-response set-header x-sha2-384 "%[var(res.key),sha2(384),hex]"
 #http-response set-header x-sha2-512 "%[var(res.key),sha2(512),hex]"
 
+# OpenSSL Digest
+#http-response set-header x-digest-sha1 
"%[var(res.key),digest(sha1),hex]"
+#http-response set-header x-digest-sha224 
"%[var(res.key),digest(sha224),hex]"
+#http-response set-header x-digest-sha256 
"%[var(res.key),digest(sha256),hex]"
+#http-response set-header x-digest-sha384 
"%[var(res.key),digest(sha384),hex]"
+#http-response set-header x-digest-sha512 
"%[var(res.key),digest(sha512),hex]"
+
+# OpenSSL HMAC
+#http-response set-header x-hmac-sha1-short 
"%[var(res.key),hmac(sha1,key),hex]"
+#http-response set-header x-hmac-sha1-long 
"%[var(res.key),hmac(sha1,my_super_secret_long_key),hex]"
+#http-response set-header x-hmac-sha256-short 
"%[var(res.key),hmac(sha256,key),hex]"
+#http-response set-header x-hmac-sha256-long 
"%[var(res.key),hmac(sha256,my_super_secret_long_key),hex]"
+
 # 32-bit hashes, and their avalanche variants
 http-response set-header x-crc32   "%[var(res.key),crc32]"
 http-response set-header x-crc32-1 "%[var(res.key),crc32(1)]"
@@ -80,6 +93,15 @@ client c1 -connect ${h1_fe_sock} {
 #expect resp.http.x-sha2-256 == 
"40AFF2E9D2D8922E47AFD4648E6967497158785FBD1DA870E7110266BF944880"
 #expect resp.http.x-sha2-384 == 
"FFDAEBFF65ED05CF400F0221C4CCFB4B2104FB6A51F87E40BE6C4309386BFDEC2892E9179B34632331A59592737DB5C5"
 #expect resp.http.x-sha2-512 == 
"1E7B80BC8EDC552C8FEEB2780E111477E5BC70465FAC1A77B29B35980C3F0CE4A036A6C9462036824BD56801E62AF7E9FEBA5C22ED8A5AF877BF7DE117DCAC6D"
+#expect resp.http.x-digest-sha1 == resp.http.x-digest-sha1
+#expect resp.http.x-digest-sha224 == resp.http.x-sha2-224
+#expect resp.http.x-digest-sha256 == resp.http.x-sha2-256
+#expect resp.http.x-digest-sha384 == resp.http.x-sha2-384
+#expect resp.http.x-digest-sha512 == resp.http.x-sha2-512
+#expect resp.http.x-hmac-sha1-short == 
"98C6C3B2F2701E0C7B0AC31C09C44EFF006C802C"
+#expect resp.http.x-hmac-sha1-long == 
"0E153DC06F81DEC1352EA9394B12754C718E2600"
+#expect resp.http.x-hmac-sha256-short == 
"6AD0A89813F79E827359742225B46DC811D35E920192CFDF60F4955F14A93680"
+#expect resp.http.x-hmac-sha256-long == 
"C8E39024773AB08D937265FFAF22231F851CF00C96C6EE98DF9E0B66FFE7C089"
 expect resp.http.x-crc32 == "688229491"
 expect resp.http.x-crc32-1 == "4230317029"
 expect resp.http.x-crc32c =

Re: [PATCH] MINOR: crypto: Add digest and hmac converters

2020-05-07 Thread Patrick Gansterer
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 
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 0..74b92eee5
--- /dev/null
+++ b/src/crypto.c
@@ -0,0 +1,163 @@
+/*
+ * Crypto converters
+ *
+ * Copyright 2020 Nenad Merdanovic 
+ *
+ * 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 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include 
+
+#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(>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([1], NULL);
+	vars_check_arg([2], NULL);
+	vars_check_arg([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(, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(_p[1], ))
+		return 0;
+
+	smp_set_owner(, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(_p[2], ))
+		return 0;
+
+	smp_set_owner(_tag, smp->px, smp->sess, smp->strm, smp->opt);
+	if (!sample_conv_var2smp_str(_p[3], _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);

Re: [PATCH] MINOR: crypto: Add digest and hmac converters

2020-05-07 Thread Patrick Gansterer

On 07.05.20 17:35, Willy Tarreau wrote:

Indeed. I encourage to ping again after one week because usually when you
restart with a new week of work, the previous one is definitely in old
history and will only be revisited by pure luck.


I don't want to look impatient, so I waited 2 weeks. ;-)


With this said, I remember having noticed Tim's ack and was about to take
the series until I noticed there was a first patch with no commit message
to justify the change and postponed the reply because I had other things
to do than to rehash what's already in CONTRIBUTING again and again :-/


I'm not sure how I should read this. I wrote an explanation into the 
commit message and tried to match already existing messages. If I look 
at commits in the last days like 0e9d87bf06 or de80201460 there are no 
very long commit messages either.


If I should write an essay about my use case into the commit message I 
could do that, but that's something a "first level reviewer" could 
demand already.


I read the CONTRIBUTING text before submitting my patch, since I don't 
like to repeat myself also, but it's hard to get everything right in the 
first patch. Sorry for that.


I really like haproxy and want to give something back, but I'm not sure 
if I want to do that in the future with the experience I had so far. :-(


- Patrick