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 @@ 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


Reply via email to