[PATCH] MINOR: crypto: Add digest and hmac converters
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
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
> 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
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
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
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
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
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
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