This implements configuration for custom mac alg in EAB.
I don't think there are any reasons to allow that TBH,
but it is something that exists in the spec.

Depends on the EAB impl.
No backport needed
---
 doc/configuration.txt    |  5 +++++
 include/haproxy/acme-t.h |  2 ++
 src/acme.c               | 46 ++++++++++++++++++++++++++++++++++------
 3 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 0cf949ef3..98db0e2fe 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -32674,6 +32674,11 @@ eab-key-id <filename>
   If file is empty HAProxy will ignore it. Whitespace in is not ignored,
   except the trailing newline.
 
+eab-mac-alg { HS256 | HS384 | HS512 }
+  Configure MAC algorithm used for EAB signing. Default is HS256. EAB MAC key
+  must be large enough to support specified MAC algorithm. Not all CAs support
+  algorithms other than HS256.
+
 12.9. Healthchecks
 ------------------
 
diff --git a/include/haproxy/acme-t.h b/include/haproxy/acme-t.h
index 87f3bfffa..fb75399e5 100644
--- a/include/haproxy/acme-t.h
+++ b/include/haproxy/acme-t.h
@@ -5,6 +5,7 @@
 #include <haproxy/acme_resolvers-t.h>
 #include <haproxy/istbuf.h>
 #include <haproxy/buf-t.h>
+#include <haproxy/jwt-t.h>
 #include <haproxy/openssl-compat.h>
 
 #if defined(HAVE_ACME)
@@ -46,6 +47,7 @@ struct acme_cfg {
                char *mac_key_file;    /* base64url encoded EAB hmac key 
filename */
                char *kid;             /* EAB key id */
                struct buffer mac_key; /* raw EAB hmac key */
+               enum jwt_alg mac_alg;  /* MAC algorithm for EAB signature */
        } eab;
        char *challenge;            /* HTTP-01, DNS-01, etc */
        char *profile;              /* ACME profile */
diff --git a/src/acme.c b/src/acme.c
index 637d472b9..f4fd6f14d 100644
--- a/src/acme.c
+++ b/src/acme.c
@@ -214,6 +214,9 @@ struct acme_cfg *new_acme_cfg(const char *name)
        /* default to 2048 bits when using RSA */
        ret->key.bits = 2048;
 
+       /* HS256 is the only sane choice for HMAC */
+       ret->eab.mac_alg = JWS_ALG_HS256;
+
        ret->next = acme_cfgs;
        acme_cfgs = ret;
 
@@ -450,6 +453,26 @@ static int cfg_parse_acme_kws(char **args, int 
section_type, struct proxy *curpx
                        ha_alert("parsing [%s:%d]: out of memory.\n", file, 
linenum);
                        goto out;
                }
+       } else if (strcmp(args[0], "eab-mac-alg") == 0) {
+               if (!*args[1]) {
+                       ha_alert("parsing [%s:%d]: keyword '%s' in '%s' section 
requires an argument\n", file, linenum, args[0], cursection);
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto out;
+               }
+               if (alertif_too_many_args(1, file, linenum, args, &err_code))
+                       goto out;
+
+               if (strcmp(args[1], "HS256") == 0) {
+                       cur_acme->eab.mac_alg = JWS_ALG_HS256;
+               } else if (strcmp(args[1], "HS384") == 0) {
+                       cur_acme->eab.mac_alg = JWS_ALG_HS384;
+               } else if (strcmp(args[1], "HS512") == 0) {
+                       cur_acme->eab.mac_alg = JWS_ALG_HS512;
+               } else {
+                       ha_alert("parsing [%s:%d]: keyword '%s' in '%s' must be 
one of the following: HS256, HS384, HS512\n", file, linenum, args[0], 
cursection);
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto out;
+               }
        } else if (strcmp(args[0], "challenge") == 0) {
                if ((!*args[1]) ||
                    ((strcasecmp("http-01", args[1]) != 0) &&
@@ -882,6 +905,7 @@ static int cfg_postsection_acme()
                if (rv >= 1) {
                        struct buffer *dec_mac = get_trash_chunk();
                        int bytes = 0;
+                       int alg_bytes = 0;
 
                        bytes = base64dec(trash.area, trash.data, 
dec_mac->area, dec_mac->size);
                        if (bytes < 0) {
@@ -891,9 +915,19 @@ static int cfg_postsection_acme()
                        }
                        dec_mac->data = bytes;
 
-                       if (bytes < 32) {
-                               ha_alert("acme: section '%s': EAB MAC key from 
'%s' is only %d bytes long, but at least 32 bytes is required for the specified 
MAC type.\n",
-                                    cur_acme->name, cur_acme->eab.kid_file, 
bytes);
+                       switch (cur_acme->eab.mac_alg) {
+                               case JWS_ALG_HS256: alg_bytes = 32; break;
+                               case JWS_ALG_HS384: alg_bytes = 48; break;
+                               case JWS_ALG_HS512: alg_bytes = 64; break;
+                               default:
+                                       ha_alert("acme: invalid mac alg.\n");
+                                       err_code |= ERR_ALERT | ERR_FATAL | 
ERR_ABORT;
+                                       goto out;
+                       }
+
+                       if (bytes < alg_bytes) {
+                               ha_alert("acme: section '%s': EAB mac key from 
'%s' is only %d bytes long, but at least %d bytes is required for the specified 
mac type.\n",
+                                    cur_acme->name, cur_acme->eab.kid_file, 
bytes, alg_bytes);
                                err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
                                goto out;
                        }
@@ -1115,6 +1149,7 @@ static struct cfg_kw_list cfg_kws_acme = {ILH, {
        { CFG_ACME, "dns-timeout",  cfg_parse_acme_kws },
        { CFG_ACME, "eab-key-id",  cfg_parse_acme_kws },
        { CFG_ACME, "eab-mac-key",  cfg_parse_acme_kws },
+       { CFG_ACME, "eab-mac-alg",  cfg_parse_acme_kws },
        { CFG_ACME, "acme-vars",  cfg_parse_acme_vars_provider },
        { CFG_ACME, "provider-name",  cfg_parse_acme_vars_provider },
        { CFG_GLOBAL, "acme.scheduler", cfg_parse_global_acme_sched },
@@ -1374,13 +1409,12 @@ int acme_jws_payload(struct buffer *req, struct ist 
nonce, struct ist url, EVP_P
        return ret;
 }
 
-int acme_jws_eab_payload(struct ist url, EVP_PKEY *acc_key, struct buffer 
mac_key, char *kid, struct buffer *output, char **errmsg)
+int acme_jws_eab_payload(struct ist url, EVP_PKEY *acc_key, struct buffer 
mac_key, enum jwt_alg alg, char *kid, struct buffer *output, char **errmsg)
 {
        struct buffer *b64payload = NULL;
        struct buffer *b64prot = NULL;
        struct buffer *b64sign = NULL;
        struct buffer *jwk = NULL;
-       enum jwt_alg alg = JWS_ALG_HS256;
        int ret = 1;
 
        b64payload = alloc_trash_chunk();
@@ -2373,7 +2407,7 @@ int acme_req_account(struct task *task, struct acme_ctx 
*ctx, int newaccount, ch
        if (newaccount) {
                chunk_appendf(req_in, "{");
                if (ctx->cfg->eab.mac_key.data > 0 && ctx->cfg->eab.kid != 
NULL) {
-                       if (acme_jws_eab_payload(ctx->resources.newAccount, 
ctx->cfg->account.pkey, ctx->cfg->eab.mac_key, ctx->cfg->eab.kid, eab_req_out, 
errmsg) != 0)
+                       if (acme_jws_eab_payload(ctx->resources.newAccount, 
ctx->cfg->account.pkey, ctx->cfg->eab.mac_key, ctx->cfg->eab.mac_alg, 
ctx->cfg->eab.kid, eab_req_out, errmsg) != 0)
                                goto out;
                        chunk_appendf(req_in, "\"externalAccountBinding\": 
%.*s,", (int)eab_req_out->data, eab_req_out->area);
                }
-- 
2.53.0


Reply via email to