]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: jws: introduce jws_b64_hmac_signature() function for HMAC signing
authorMia Kanashi <chad@redpilled.dev>
Wed, 6 May 2026 21:17:41 +0000 (00:17 +0300)
committerWilliam Lallemand <wlallemand@haproxy.com>
Thu, 7 May 2026 13:19:15 +0000 (15:19 +0200)
New jws_b64_hmac_signature() duplicates the same functionality as
jws_b64_signature(), but for the use case of HMAC signing.
Intended to be used for ACME EAB.

OpenSSL allows to use EVP_PKEY for HMAC functionality, so
jws_b64_signature() could be reused, but the problem is that 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. Another alternative
would be to use EVP_MD API, but it was introduced in OpenSSL 3.0,
so not as widely supported.

include/haproxy/jws.h
src/jws.c

index f68147cff2a0050de8f202d3cfcd22cbe83f114c..4f5fb8c409ee5abfad67a737f7655095049cc084 100644 (file)
@@ -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);
index 31808a4e30566143e14197518dff12daf16cc6ef..f8fb4738f621c1f1ee20bb2795c20ec69760f9d0 100644 (file)
--- a/src/jws.c
+++ b/src/jws.c
@@ -452,6 +452,52 @@ out:
        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
  *