From c9e76e5bb150c052dd77b5d48d5957f39028b5d3 Mon Sep 17 00:00:00 2001 From: Mia Kanashi Date: Thu, 7 May 2026 00:17:41 +0300 Subject: [PATCH] MINOR: jws: introduce jws_b64_hmac_signature() function for HMAC signing 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 | 1 + src/jws.c | 46 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) 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/jws.c b/src/jws.c index 31808a4e3..f8fb4738f 100644 --- 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 buffer of size with a jwk thumbprint from a pkey *