]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: acme: allow specifying custom MAC alg for EAB
authorMia Kanashi <chad@redpilled.dev>
Wed, 6 May 2026 21:17:43 +0000 (00:17 +0300)
committerWilliam Lallemand <wlallemand@haproxy.com>
Thu, 7 May 2026 13:19:15 +0000 (15:19 +0200)
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
include/haproxy/acme-t.h
src/acme.c

index 829d46ef399a1a03cca057afbcfd593dc3fbfb32..de877ab590dbb173f563c82b1fc9706f49660288 100644 (file)
@@ -32688,6 +32688,13 @@ eab-mac-key <filename>
 
   See also: "eab-key-id", "eab-mac-alg"
 
+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.
+
+  See also: "eab-key-id", "eab-mac-key"
+
 12.9. Healthchecks
 ------------------
 
index 87f3bfffa3a83cd760f6223ffdf1e789a5a7b687..fb75399e580d7aedc068ae69578013f47c20f3e9 100644 (file)
@@ -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 */
index 289c67343d31b6e6c33bd4597d890e66cae88bb1..26ffc3ab8574ae29dd8588d4d410c131dca09823 100644 (file)
@@ -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 = base64urldec(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.mac_key_file, bytes, alg_bytes);
                                err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
                                goto out;
                        }
@@ -1117,6 +1151,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 },
@@ -1376,13 +1411,12 @@ error:
        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();
@@ -2375,7 +2409,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);
                }