Hi Mia,

On Fri, May 01, 2026 at 09:06:19PM +0300, Mia Kanashi wrote:
> Subject: [PATCH 3/4] MINOR: acme: implement EAB - external account binding
> This patch implements ACME EAB.
> 
> Patch introduces two new functions related to JWS handling:
> jws_b64_hmac_signature in jws.c, and acme_jws_eab_payload in acme.c,
> which are like jws_b64_signature and acme_jws_payload respectively.
> They duplicate basically the same functionality but for the use case
> of HMAC, OpenSSL allows to use EVP_PKEY for HMAC functionality, but
> although isn't deprecated it was removed in BoringSSL, and was removed
> (due to BoringSSL roots) but then readded back in AWS-LC, because of
> "legacy clients" (citing them), for that reason alone I say that having
> a dedicated function for hmac is better, HMAC() macro seems to be widely
> supported unlike other ways of doing same thing.
> 
> Also touches and rewrites jws_b64_protected() to work for the use case
> of EAB, in EAB JWS nonce must not be set.
> 
> Configuring EAB requires two parts: Key ID and MAC Key.
> Key ID is an ASCII string that specifies the name of the record CA
> should look up. MAC Key is a base64url encoded key that is used
> for the sake of JWS signing, using HS256 or other algorithms.
> 
> It is not clear whether providing a config for algorithms other than
> HS256 makes any sense, standard ACME implementation of Go decided not to
> for example. There are really no benefits, HS256 support is mandatory,
> but not for others, so I decided to split that part into a separate
> commit. Keep in mind that HS256 is only used for initial JWS signing
> of initial account binding process, but not for anything else.
> 
> A thing about EAB is that it is /required only during account creation/
> so it is unexpectedly complex to think about.
> Also some CAs provide EAB credential pair that is reused between
> multiple account order requests, for example ZeroSSL, but others like
> Google Trusted Services require an unique EAB credential for each new
> account creation request.
> 
> There are a lot of ways config could be implemented, I decided to make
> so that Key ID and MAC Key are stored in separate files on disk.
> Other servers that implement ACME, for example stalwart-mail, Caddy,
> allow to specify values directly in the config fields, but then
> they have template directives that allow to expand contents of a file
> into them.
> 
> In Caddy you can do one of the following:
> 
> acme_eab {
>   key_id {file./etc/caddy/eab.key_id}
>   mac_key {file./etc/caddy/eab.mac_key}
> }
> 
> acme_eab {
>   key_id kid-1
>   mac_key HjudV5qnbreN-n9WyFSH
> }
> 
> acme_eab {
>   key_id {env.ACME_EAB_KEY_ID}
>   mac_key {env.ACME_EAB_MAC_KEY}
> }
> 
> HAProxy allows to expand environment variables, but not expand files
> like that.
> 
> So I decided to implement a purely file based approach for now,
> as it provides the most operational flexibility, and in particular works
> well with systemd credentials.
> 
> Another way I though to implement it is by combining multiple creds
> in a single file, for example using a format like: <id>:<key>
> 
> Or using a map file per acme section with contents as following:
> 
> id "kid-1"
> key "whatever"
> 
> But the multi-file approach seemed the most compatible with existing
> setups using placeholders. Although, ID and the Key do come in pairs,
> so it makes not much sense to separate them logically speaking,
> I haven't seen anyone else to do it like that in a single file,
> unless it was a config file.
> 
> I decided to use options like this in an acme section:
> 
> eab-mac-alg HS512
> eab-mac-key pebble.eab.key
> eab-key-id pebble.eab.id
> 
> It could have been instead something like this too:
> 
> eab alg HS512 mac-key pebble.eab.key key-id pebble.eab.id
> 
> The reason I want with the first is just because it was easier to do :)
> 
> I also decided to not error out on empty files, but warn instead,
> as credential existance is always checked for.
> Used read_line_to_trash function from tools.c for reading files,
> that is something that could be replaced by a dedicated function too.
> 
> There is no need to explicitly configure anything in the config,
> If no eab-* keywords are used, filename is implicitly created based
> on a section name, and then haproxy tries to load it. Whether it is a
> good idea or not... I'm not sure.
> 
> There are a lot of decisions here that are slightly tricky,
> even for this simple feature, if there are any ideas on how to change it
> for the better, it should be done. I hope other HAProxy users will
> guide the overall direction here.
> 
> No backport needed :)

You should probably split this commit in two, introduce the HMAC in the jws
first, then use it in the ACME code.

> ---
>  doc/configuration.txt    |  14 ++++
>  include/haproxy/acme-t.h |   7 ++
>  include/haproxy/jws.h    |   1 +
>  src/acme.c               | 163 +++++++++++++++++++++++++++++++++++++--
>  src/jws.c                |  80 ++++++++++++++-----
>  5 files changed, 239 insertions(+), 26 deletions(-)
> 
> diff --git a/doc/configuration.txt b/doc/configuration.txt
> index 8a673c29a..d2b91eed2 100644
> --- a/doc/configuration.txt
> +++ b/doc/configuration.txt
> @@ -32638,6 +32638,20 @@ Example:
>        curves P-384
>        map virt@acme
>  
> +eab-mac-key <filename>
> +eab-key-id <filename>
> +  Configure the path to the EAB MAC key and EAB key id credential pair. You
> +  should get credentials from you CA and place them at the specified path
> +  before launching HAProxy. If not explicitly specified, HAProxy will try to
> +  load a file based on an acme section name similar to "account-key" keyword,
> +  "<name>.eab.id" for the key id, "<name>.eab.key" for the mac key, where
> +  "<name>" is the name of the current acme section. If the file doesn't 
> exist,
> +  or is empty, HAProxy will ignore it. Whitespace in the file is NOT ignored.
> +
> +  EAB key id file should be a plain ASCII string that CA provides as an id.
> +  EAB MAC key file should be a base64url encoded MAC key that CA provides.
> +  EAB credentials are used only during the initial ACME account creation, and
> +  can be removed afterwards.
>  
>  12.9. Healthchecks
>  ------------------
> diff --git a/include/haproxy/acme-t.h b/include/haproxy/acme-t.h
> index 24df7c44a..87f3bfffa 100644
> --- a/include/haproxy/acme-t.h
> +++ b/include/haproxy/acme-t.h
> @@ -4,6 +4,7 @@
>  
>  #include <haproxy/acme_resolvers-t.h>
>  #include <haproxy/istbuf.h>
> +#include <haproxy/buf-t.h>
>  #include <haproxy/openssl-compat.h>
>  
>  #if defined(HAVE_ACME)
> @@ -40,6 +41,12 @@ struct acme_cfg {
>               int bits;                   /* bits for RSA */
>               int curves;                 /* NID of curves */
>       } key;
> +     struct {
> +             char *kid_file;        /* EAB key id filename */
> +             char *mac_key_file;    /* base64url encoded EAB hmac key 
> filename */
> +             char *kid;             /* EAB key id */
> +             struct buffer mac_key; /* raw EAB hmac key */
> +     } eab;
>       char *challenge;            /* HTTP-01, DNS-01, etc */
>       char *profile;              /* ACME profile */
>       char *vars;                 /* variables put in the dpapi sink */
> diff --git a/include/haproxy/jws.h b/include/haproxy/jws.h
> index f68147cff..4f5fb8c40 100644
> --- a/include/haproxy/jws.h
> +++ b/include/haproxy/jws.h
> @@ -11,6 +11,7 @@ size_t EVP_PKEY_to_pub_jwk(EVP_PKEY *pkey, char *dst, 
> size_t dsize);
>  enum jwt_alg EVP_PKEY_to_jws_alg(EVP_PKEY *pkey);
>  size_t jws_b64_payload(char *payload, char *dst, size_t dsize);
>  size_t jws_b64_protected(enum jwt_alg alg, char *kid, char *jwk, char 
> *nonce, char *url, char *dst, size_t dsize);
> +size_t jws_b64_hmac_signature(char *key, size_t key_len, enum jwt_alg alg, 
> char *b64protected, char *b64payload, char *dst, size_t dsize);
>  size_t jws_b64_signature(EVP_PKEY *pkey, enum jwt_alg alg, char 
> *b64protected, char *b64payload, char *dst, size_t dsize);
>  size_t jws_flattened(char *protected, char *payload, char *signature, char 
> *dst, size_t dsize);
>  size_t jws_thumbprint(EVP_PKEY *pkey, char *dst, size_t dsize);
> diff --git a/src/acme.c b/src/acme.c
> index dc038bcab..e7f7d1a77 100644
> --- a/src/acme.c
> +++ b/src/acme.c
> @@ -418,6 +418,38 @@ 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-key-id") == 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;
> +
> +             ha_free(&cur_acme->eab.kid_file);
> +             cur_acme->eab.kid_file = strdup(args[1]);
> +             if (!cur_acme->eab.kid_file) {
> +                     err_code |= ERR_ALERT | ERR_FATAL;
> +                     ha_alert("parsing [%s:%d]: out of memory.\n", file, 
> linenum);
> +                     goto out;
> +             }
> +     } else if (strcmp(args[0], "eab-mac-key") == 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;
> +
> +             ha_free(&cur_acme->eab.mac_key_file);
> +             cur_acme->eab.mac_key_file = strdup(args[1]);
> +             if (!cur_acme->eab.mac_key_file) {
> +                     err_code |= ERR_ALERT | ERR_FATAL;
> +                     ha_alert("parsing [%s:%d]: out of memory.\n", file, 
> linenum);
> +                     goto out;
> +             }
>       } else if (strcmp(args[0], "challenge") == 0) {
>               if ((!*args[1]) ||
>                   ((strcasecmp("http-01", args[1]) != 0) &&
> @@ -799,21 +831,87 @@ static int cfg_postsection_acme()
>       char *errmsg = NULL;
>       char *path;
>       char store_path[PATH_MAX]; /* complete path with crt_base */
> +     int rv = 0;
>       struct stat st;
>  
>       /* if dns-persist-01 is set, add an extra INITIAL_DNS check */
>       if (strcasecmp(cur_acme->challenge, "dns-persist-01") == 0)
>               cur_acme->cond_ready |= ACME_RDY_INITIAL_DNS;
>  
> -     /* if account key filename is unspecified, choose a filename for it */
> -     if (!cur_acme->account.file) {
> -             if (!memprintf(&cur_acme->account.file, "%s.account.key", 
> cur_acme->name)) {
> +     /* if either account key, eab kid, or mac key filename is unspecified, 
> choose a filename for it */
> +     if ((!cur_acme->account.file && !memprintf(&cur_acme->account.file, 
> "%s.account.key", cur_acme->name))
> +        || (!cur_acme->eab.kid_file && !memprintf(&cur_acme->eab.kid_file, 
> "%s.eab.id", cur_acme->name))
> +        || (!cur_acme->eab.mac_key_file && 
> !memprintf(&cur_acme->eab.mac_key_file, "%s.eab.key", cur_acme->name))) {
> +             err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
> +             ha_alert("acme: out of memory.\n");
> +             goto out;
> +     }
> +
> +     if (global_ssl.crt_base && *cur_acme->eab.kid_file != '/')
> +             rv = read_line_to_trash("%s/%s", global_ssl.crt_base, 
> cur_acme->eab.kid_file);
> +     else
> +             rv = read_line_to_trash("%s", cur_acme->eab.kid_file);
> +
> +     /* if read at least one character successfully */
> +     if (rv >= 1) {
> +             const char *p;
> +             cur_acme->eab.kid = my_strndup(trash.area, trash.data);
> +             if (!cur_acme->eab.kid) {
> +                     ha_alert("acme: out of memory.\n");
> +                     err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
> +                     goto out;
> +             }
> +
> +             /* technically ACME RFC allows any ASCII string here,
> +              * but in practice CAs usually provide key id as a base64url 
> encoded secret or an UUID */
> +             for (p = cur_acme->eab.kid; *p; p++) {
> +                     if (!isalnum((uchar)*p) && *p != '-' && *p != '_') {
> +                             ha_warning("acme: section '%s': EAB key id 
> contains strange character '%c'.\n",  cur_acme->name, *p);
> +                             break; /* no need to print this warning many 
> times */
> +                     }
> +             }
> +     } else if (rv == 0) {
> +             /* empty files are allowed, but issue a warning */
> +             ha_warning("acme: section '%s': EAB key id from '%s' is 
> empty.\n", cur_acme->name, cur_acme->eab.kid_file);
> +     }
> +
> +     if (global_ssl.crt_base && *cur_acme->eab.mac_key_file != '/')
> +             rv = read_line_to_trash("%s/%s", global_ssl.crt_base, 
> cur_acme->eab.mac_key_file);
> +     else
> +             rv = read_line_to_trash("%s", cur_acme->eab.mac_key_file);
> +

I don't know if that's a good idea to use crt-base here, if it's not a PEM
certificate we will probably need something else to configure the path. Like an
acme-account-path or something like that.


> +     if (rv >= 1) {
> +             struct buffer *dec_mac = get_trash_chunk();
> +
> +             rv = base64dec(trash.area, trash.data, dec_mac->area, 
> dec_mac->size);
> +             if (rv < 0) {
> +                     ha_alert("acme: section '%s': failed to base64url 
> decode EAB mac key.\n", cur_acme->name);
>                       err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
> +                     goto out;
> +             }
> +             dec_mac->data = rv;
> +
> +             if (rv < 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, rv);
> +                     err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
> +                     goto out;
> +             }
> +
> +             if (chunk_dup(&cur_acme->eab.mac_key, dec_mac) == NULL) {
>                       ha_alert("acme: out of memory.\n");
> +                     err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
>                       goto out;
>               }
> +     } else if (rv == 0) {
> +             ha_warning("acme: section '%s': EAB mac key from '%s' is 
> empty.\n", cur_acme->name, cur_acme->eab.mac_key_file);
>       }
>  
> +     if ((cur_acme->eab.mac_key.data == 0) != (cur_acme->eab.kid == NULL)) {
> +             ha_alert("acme: section '%s': EAB mac key and key id are 
> mutually dependent, specify both or neither.\n", cur_acme->name);
> +             err_code |= ERR_ALERT | ERR_FATAL | ERR_ABORT;
> +             goto out;
> +     }
>  
>       if (global_ssl.crt_base && *cur_acme->account.file != '/') {
>               int rv;
> @@ -988,6 +1086,8 @@ void deinit_acme()
>               ha_free(&acme_cfgs->challenge);
>               ha_free(&acme_cfgs->map);
>               ha_free(&acme_cfgs->profile);
> +             chunk_destroy(&acme_cfgs->eab.mac_key);
> +             ha_free(&acme_cfgs->eab.kid);
>  
>               free(acme_cfgs);
>               acme_cfgs = next;
> @@ -1010,6 +1110,8 @@ static struct cfg_kw_list cfg_kws_acme = {ILH, {
>       { CFG_ACME, "challenge-ready",  cfg_parse_acme_kws },
>       { CFG_ACME, "dns-delay",  cfg_parse_acme_kws },
>       { 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, "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 },
> @@ -1269,6 +1371,46 @@ 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)
> +{
> +     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();
> +     b64prot = alloc_trash_chunk();
> +     jwk = alloc_trash_chunk();
> +     b64sign = alloc_trash_chunk();
> +
> +     if (!b64payload || !b64prot || !jwk || !b64sign || !output) {
> +             memprintf(errmsg, "out of memory");
> +             goto error;
> +     }
> +
> +     jwk->data = EVP_PKEY_to_pub_jwk(acc_key, jwk->area, jwk->size);
> +
> +     b64payload->data = jws_b64_payload(jwk->area, b64payload->area, 
> b64payload->size);
> +     b64prot->data = jws_b64_protected(alg, kid, NULL, NULL, url.ptr, 
> b64prot->area, b64prot->size);
> +     b64sign->data = jws_b64_hmac_signature(mac_key.area, mac_key.data, alg, 
> b64prot->area, b64payload->area, b64sign->area, b64sign->size);
> +     output->data = jws_flattened(b64prot->area, b64payload->area, 
> b64sign->area, output->area, output->size);
> +
> +     if (output->data == 0)
> +             goto error;
> +
> +     ret = 0;
> +
> +error:
> +     free_trash_chunk(b64payload);
> +     free_trash_chunk(b64prot);
> +     free_trash_chunk(jwk);
> +     free_trash_chunk(b64sign);
> +
> +     return ret;
> +}
> +
>  /*
>   * Update every certificate instances for the new store
>   *
> @@ -2211,20 +2353,28 @@ int acme_req_account(struct task *task, struct 
> acme_ctx *ctx, int newaccount, ch
>  {
>       struct buffer *req_in = NULL;
>       struct buffer *req_out = NULL;
> +     struct buffer *eab_req_out = NULL;
>       const struct http_hdr hdrs[] = {
>               { IST("Content-Type"), IST("application/jose+json") },
>               { IST_NULL, IST_NULL }
>       };
>       int ret = 1;
>  
> -        if ((req_in = alloc_trash_chunk()) == NULL)
> +     if ((req_in = alloc_trash_chunk()) == NULL)
>               goto error;
> -        if ((req_out = alloc_trash_chunk()) == NULL)
> +     if ((req_out = alloc_trash_chunk()) == NULL)
> +             goto error;
> +     if ((eab_req_out = alloc_trash_chunk()) == NULL)
>               goto error;
>  
>       if (newaccount) {
>               chunk_appendf(req_in, "{");
> -             if (ctx->cfg->account.contact != NULL)
> +             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)
> +                             goto out;
> +                     chunk_appendf(req_in, "\"externalAccountBinding\": 
> %.*s,", (int)eab_req_out->data, eab_req_out->area);
> +             }
> +             if (ctx->cfg->account.contact)
>                       chunk_appendf(req_in, "\"contact\": [ \"mailto:%s\"; 
> ],", ctx->cfg->account.contact);
>               chunk_appendf(req_in, "\"termsOfServiceAgreed\": true");
>               chunk_appendf(req_in, "}");
> @@ -2246,6 +2396,7 @@ int acme_req_account(struct task *task, struct acme_ctx 
> *ctx, int newaccount, ch
>  out:
>       free_trash_chunk(req_in);
>       free_trash_chunk(req_out);
> +     free_trash_chunk(eab_req_out);
>  
>       return ret;
>  }

Split this part.

> diff --git a/src/jws.c b/src/jws.c
> index e4ea30de6..f8fb4738f 100644
> --- a/src/jws.c
> +++ b/src/jws.c
> @@ -219,6 +219,7 @@ size_t EVP_PKEY_to_pub_jwk(EVP_PKEY *pkey, char *dst, 
> size_t dsize)
>  /*
>   * Generate the JWS payload and converts it to base64url.
>   * Use either <kid> or <jwk>, but won't use both
> + * <nonce> is optional.
>   *
>   * Return the size of the data or 0
>   */
> @@ -226,13 +227,14 @@ size_t EVP_PKEY_to_pub_jwk(EVP_PKEY *pkey, char *dst, 
> size_t dsize)
>  size_t jws_b64_protected(enum jwt_alg alg, char *kid, char *jwk, char 
> *nonce, char *url,
>                           char *dst, size_t dsize)
>  {
> -     char *acc;
> -     char *acctype;
>       int ret = 0;
>       struct buffer *json = NULL;
>       const char *algstr;
>  
>       switch (alg) {
> +             case JWS_ALG_HS256: algstr = "HS256"; break;
> +             case JWS_ALG_HS384: algstr = "HS384"; break;
> +             case JWS_ALG_HS512: algstr = "HS512"; break;
>               case JWS_ALG_RS256: algstr = "RS256"; break;
>               case JWS_ALG_RS384: algstr = "RS384"; break;
>               case JWS_ALG_RS512: algstr = "RS512"; break;
> @@ -246,24 +248,16 @@ size_t jws_b64_protected(enum jwt_alg alg, char *kid, 
> char *jwk, char *nonce, ch
>       if ((json = alloc_trash_chunk()) == NULL)
>               goto out;
>  
> -     /* kid or jwk ? */
> -     acc = kid ? kid : jwk;
> -     acctype = kid ? "kid" : "jwk";
> -
> -     ret = snprintf(json->area, json->size, "{\n"
> -                     "    \"alg\": \"%s\",\n"
> -                     "    \"%s\":  %s%s%s,\n"
> -                     "    \"nonce\":   \"%s\",\n"
> -                     "    \"url\":   \"%s\"\n"
> -                     "}\n",
> -                     algstr, acctype, kid ? "\"" : "", acc, kid ? "\"" : "", 
> nonce, url);
> -     if (ret >= json->size) {
> -             ret = 0;
> -             goto out;
> -     }
> -
> -
> -     json->data = ret;
> +     chunk_appendf(json, "{");
> +     if (kid)
> +             chunk_appendf(json, "\"kid\": \"%s\",", kid);
> +     else
> +             chunk_appendf(json, "\"jwk\": %s,", jwk);
> +     if (nonce)
> +             chunk_appendf(json, "\"nonce\": \"%s\",", nonce);
> +     chunk_appendf(json, "\"alg\": \"%s\",", algstr);
> +     chunk_appendf(json, "\"url\": \"%s\"", url);
> +     chunk_appendf(json, "}");
>  
>       ret = a2base64url(json->area, json->data, dst, dsize);
>  out:
> @@ -458,6 +452,52 @@ size_t jws_b64_signature(EVP_PKEY *pkey, enum jwt_alg 
> alg, char *b64protected, c
>       return 0;
>  }
>  
> +
> +/*
> + * Generate a JWS HMAC signature using the base64url protected buffer and 
> the base64url payload buffer
> + *
> + * Return the size of the data or 0
> + */
> +size_t jws_b64_hmac_signature(char *key, size_t key_len, enum jwt_alg alg, 
> char *b64protected, char *b64payload, char *dst, size_t dsize)
> +{
> +     const EVP_MD *evp_alg = NULL;
> +     int ret = 0;
> +     unsigned char mac[EVP_MAX_MD_SIZE] = {};
> +     unsigned int mac_len = 0;
> +     struct buffer *sig_data = NULL;
> +
> +     if ((sig_data = alloc_trash_chunk()) == NULL)
> +             goto out;
> +
> +     switch (alg) {
> +             case JWS_ALG_HS256: evp_alg = EVP_sha256(); break;
> +             case JWS_ALG_HS384: evp_alg = EVP_sha384(); break;
> +             case JWS_ALG_HS512: evp_alg = EVP_sha512(); break;
> +             default:
> +                     goto out;
> +     }
> +
> +     if (!chunk_memcat(sig_data, b64protected, strlen(b64protected)) ||
> +         !chunk_memcat(sig_data, ".", 1) ||
> +         !chunk_memcat(sig_data, b64payload, strlen(b64payload)))
> +             goto out;
> +
> +     if (HMAC(evp_alg, key, (int)key_len,
> +              (unsigned char*)sig_data->area, sig_data->data,
> +              mac, &mac_len) == NULL)
> +             goto out;
> +
> +     ret = a2base64url((const char *)mac, mac_len, dst, dsize);
> +
> +out:
> +     free_trash_chunk(sig_data);
> +
> +     if (ret > 0)
> +             return ret;
> +     return 0;
> +}
> +
> +
>  /*
>   * Fill a <dst> buffer of <dsize> size with a jwk thumbprint from a pkey
>   *
> -- 
> 2.53.0
> 
> 

-- 
William Lallemand


Reply via email to