summaryrefslogtreecommitdiff
path: root/libbb/hash_hmac.c
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2025-07-07 07:44:01 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2025-07-07 07:44:01 +0200
commit1a0913d57ce8287703cfe666d9240e3a147ea30d (patch)
tree165a5ffe29e07ba52ead6666675c4625940057d4 /libbb/hash_hmac.c
parente2091c98425e2e1615db7dcad9556c928ea5123d (diff)
libbb: factor out HMAC code from TLS
function old new delta hmac_block - 88 +88 hmac_peek_hash - 61 +61 hmac_end - 50 +50 hmac_begin 140 177 +37 hmac_hash_v - 30 +30 .rodata 105799 105787 -12 hmac_sha_precomputed 54 - -54 hmac_sha_precomputed_v 69 - -69 hmac 83 - -83 ------------------------------------------------------------------------------ (add/remove: 5/3 grow/shrink: 1/1 up/down: 266/-218) Total: 48 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'libbb/hash_hmac.c')
-rw-r--r--libbb/hash_hmac.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/libbb/hash_hmac.c b/libbb/hash_hmac.c
new file mode 100644
index 000000000..8cf936949
--- /dev/null
+++ b/libbb/hash_hmac.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2025 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+//kbuild:lib-$(CONFIG_TLS) += hash_hmac.o
+//kbuild:lib-$(CONFIG_USE_BB_CRYPT_YES) += hash_hmac.o
+
+#include "libbb.h"
+
+// RFC 2104:
+// HMAC(key, text) based on a hash H (say, sha256) is:
+// ipad = [0x36 x INSIZE]
+// opad = [0x5c x INSIZE]
+// HMAC(key, text) = H((key XOR opad) + H((key XOR ipad) + text))
+//
+// H(key XOR opad) and H(key XOR ipad) can be precomputed
+// if we often need HMAC hmac with the same key.
+//
+// text is often given in disjoint pieces.
+void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, uint8_t *key, unsigned key_size, md5sha_begin_func *begin)
+{
+#if HMAC_ONLY_SHA256
+#define begin sha256_begin
+#endif
+ uint8_t key_xor_ipad[SHA2_INSIZE];
+ uint8_t key_xor_opad[SHA2_INSIZE];
+ unsigned i;
+
+ // "The authentication key can be of any length up to INSIZE, the
+ // block length of the hash function. Applications that use keys longer
+ // than INSIZE bytes will first hash the key using H and then use the
+ // resultant OUTSIZE byte string as the actual key to HMAC."
+ if (key_size > SHA2_INSIZE) {
+ uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE];
+ /* use ctx->hashed_key_xor_ipad as scratch ctx */
+ begin(&ctx->hashed_key_xor_ipad);
+ md5sha_hash(&ctx->hashed_key_xor_ipad, key, key_size);
+ key_size = sha_end(&ctx->hashed_key_xor_ipad, tempkey);
+ key = tempkey;
+ }
+
+ for (i = 0; i < key_size; i++) {
+ key_xor_ipad[i] = key[i] ^ 0x36;
+ key_xor_opad[i] = key[i] ^ 0x5c;
+ }
+ for (; i < SHA2_INSIZE; i++) {
+ key_xor_ipad[i] = 0x36;
+ key_xor_opad[i] = 0x5c;
+ }
+
+ begin(&ctx->hashed_key_xor_ipad);
+ begin(&ctx->hashed_key_xor_opad);
+ md5sha_hash(&ctx->hashed_key_xor_ipad, key_xor_ipad, SHA2_INSIZE);
+ md5sha_hash(&ctx->hashed_key_xor_opad, key_xor_opad, SHA2_INSIZE);
+}
+#undef begin
+
+unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out)
+{
+ unsigned len = sha_end(&ctx->hashed_key_xor_ipad, out);
+ /* out = H((key XOR opad) + out) */
+ md5sha_hash(&ctx->hashed_key_xor_opad, out, len);
+ return sha_end(&ctx->hashed_key_xor_opad, out);
+}
+
+/* TLS helpers */
+
+void FAST_FUNC hmac_hash_v(
+ hmac_ctx_t *ctx,
+ va_list va)
+{
+ uint8_t *in;
+
+ /* ctx->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
+ /* ctx->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
+
+ /* calculate out = H((key XOR ipad) + text) */
+ while ((in = va_arg(va, uint8_t*)) != NULL) {
+ unsigned size = va_arg(va, unsigned);
+ md5sha_hash(&ctx->hashed_key_xor_ipad, in, size);
+ }
+}
+
+/* Using HMAC state, make a copy of it (IOW: without affecting this state!)
+ * hash in the list of (ptr,size) blocks, and finish the HMAC to out[] buffer.
+ * This function is useful for TLS PRF.
+ */
+unsigned FAST_FUNC hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...)
+{
+ hmac_ctx_t tmpctx = *ctx; /* struct copy */
+ va_list va;
+
+ va_start(va, out);
+ hmac_hash_v(&tmpctx, va);
+ va_end(va);
+
+ return hmac_end(&tmpctx, out);
+}