[git commit] libbb: factor out HMAC code from TLS
Denys Vlasenko
vda.linux at googlemail.com
Mon Jul 7 05:44:01 UTC 2025
commit: https://git.busybox.net/busybox/commit/?id=1a0913d57ce8287703cfe666d9240e3a147ea30d
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master
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 at googlemail.com>
---
include/libbb.h | 40 +++++++++++---
libbb/hash_hmac.c | 99 +++++++++++++++++++++++++++++++++
networking/tls.c | 161 ++++++++++--------------------------------------------
3 files changed, 161 insertions(+), 139 deletions(-)
diff --git a/include/libbb.h b/include/libbb.h
index b761b1091..3f60acaa0 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -2193,6 +2193,16 @@ int FAST_FUNC i2a64(int i);
int FAST_FUNC a2i64(char c);
char* FAST_FUNC num2str64_lsb_first(char *s, unsigned v, int n);
+enum {
+ /* how many bytes XYZ_end() fills */
+ MD5_OUTSIZE = 16,
+ SHA1_OUTSIZE = 20,
+ SHA256_OUTSIZE = 32,
+ SHA512_OUTSIZE = 64,
+ SHA3_OUTSIZE = 28,
+ /* size of input block */
+ SHA2_INSIZE = 64,
+};
typedef struct md5_ctx_t {
uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */
void (*process_block)(struct md5_ctx_t*) FAST_FUNC;
@@ -2226,18 +2236,32 @@ unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC;
void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC;
void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC;
+void FAST_FUNC sha256_block(const void *in, size_t len, uint8_t hash[32]);
/* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */
typedef struct md5_ctx_t md5sha_ctx_t;
#define md5sha_hash md5_hash
#define sha_end sha1_end
-enum {
- MD5_OUTSIZE = 16,
- SHA1_OUTSIZE = 20,
- SHA256_OUTSIZE = 32,
- SHA512_OUTSIZE = 64,
- SHA3_OUTSIZE = 28,
-};
-void FAST_FUNC sha256_block(const void *in, size_t len, uint8_t hash[32]);
+
+/* RFC 2104 HMAC (hash-based message authentication code) */
+typedef struct hmac_ctx {
+ md5sha_ctx_t hashed_key_xor_ipad;
+ md5sha_ctx_t hashed_key_xor_opad;
+} hmac_ctx_t;
+#define HMAC_ONLY_SHA256 (!ENABLE_FEATURE_TLS_SHA1)
+typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC;
+#if HMAC_ONLY_SHA256
+#define hmac_begin(ctx,key,key_size,begin) \
+ hmac_begin(ctx,key,key_size)
+#endif
+void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, uint8_t *key, unsigned key_size, md5sha_begin_func *begin);
+static ALWAYS_INLINE void hmac_hash(hmac_ctx_t *ctx, const void *in, size_t len)
+{
+ md5sha_hash(&ctx->hashed_key_xor_ipad, in, len);
+}
+unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out);
+/* HMAC helpers for TLS: */
+void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va);
+unsigned FAST_FUNC hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...);
extern uint32_t *global_crc32_table;
uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC;
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);
+}
diff --git a/networking/tls.c b/networking/tls.c
index 8d074c058..b8caf1e4b 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -188,8 +188,6 @@
#define TLS_MAX_OUTBUF (1 << 14)
enum {
- SHA_INSIZE = 64,
-
AES128_KEYSIZE = 16,
AES256_KEYSIZE = 32,
@@ -393,128 +391,6 @@ static void hash_handshake(tls_state_t *tls, const char *fmt, const void *buffer
# define TLS_MAC_SIZE(tls) (tls)->MAC_size
#endif
-// 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.
-typedef struct hmac_precomputed {
- md5sha_ctx_t hashed_key_xor_ipad;
- md5sha_ctx_t hashed_key_xor_opad;
-} hmac_precomputed_t;
-
-typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC;
-#if !ENABLE_FEATURE_TLS_SHA1
-#define hmac_begin(pre,key,key_size,begin) \
- hmac_begin(pre,key,key_size)
-#define begin sha256_begin
-#endif
-static void hmac_begin(hmac_precomputed_t *pre, uint8_t *key, unsigned key_size, md5sha_begin_func *begin)
-{
- uint8_t key_xor_ipad[SHA_INSIZE];
- uint8_t key_xor_opad[SHA_INSIZE];
-// uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE];
- 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 > SHA_INSIZE) {
- bb_simple_error_msg_and_die("HMAC key>64"); //does not happen (yet?)
-// md5sha_ctx_t ctx;
-// begin(&ctx);
-// md5sha_hash(&ctx, key, key_size);
-// key_size = sha_end(&ctx, tempkey);
-// //key = tempkey; - right? RIGHT? why does it work without this?
-// // because SHA_INSIZE is 64, but hmac() is always called with
-// // key_size = tls->MAC_size = SHA1/256_OUTSIZE (20 or 32),
-// // and prf_hmac_sha256() -> hmac_sha256() key sizes are:
-// // - RSA_PREMASTER_SIZE is 48
-// // - CURVE25519_KEYSIZE is 32
-// // - master_secret[] is 48
- }
-
- for (i = 0; i < key_size; i++) {
- key_xor_ipad[i] = key[i] ^ 0x36;
- key_xor_opad[i] = key[i] ^ 0x5c;
- }
- for (; i < SHA_INSIZE; i++) {
- key_xor_ipad[i] = 0x36;
- key_xor_opad[i] = 0x5c;
- }
-
- begin(&pre->hashed_key_xor_ipad);
- begin(&pre->hashed_key_xor_opad);
- md5sha_hash(&pre->hashed_key_xor_ipad, key_xor_ipad, SHA_INSIZE);
- md5sha_hash(&pre->hashed_key_xor_opad, key_xor_opad, SHA_INSIZE);
-}
-#undef begin
-
-static unsigned hmac_sha_precomputed_v(
- hmac_precomputed_t *pre,
- uint8_t *out,
- va_list va)
-{
- uint8_t *text;
- unsigned len;
-
- /* pre->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
- /* pre->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
-
- /* calculate out = H((key XOR ipad) + text) */
- while ((text = va_arg(va, uint8_t*)) != NULL) {
- unsigned text_size = va_arg(va, unsigned);
- md5sha_hash(&pre->hashed_key_xor_ipad, text, text_size);
- }
- len = sha_end(&pre->hashed_key_xor_ipad, out);
-
- /* out = H((key XOR opad) + out) */
- md5sha_hash(&pre->hashed_key_xor_opad, out, len);
- return sha_end(&pre->hashed_key_xor_opad, out);
-}
-
-static unsigned hmac_sha_precomputed(hmac_precomputed_t *pre_init, uint8_t *out, ...)
-{
- hmac_precomputed_t pre;
- va_list va;
- unsigned len;
-
- va_start(va, out);
- pre = *pre_init; /* struct copy */
- len = hmac_sha_precomputed_v(&pre, out, va);
- va_end(va);
- return len;
-}
-
-#if !ENABLE_FEATURE_TLS_SHA1
-#define hmac(tls,out,key,key_size,...) \
- hmac(out,key,key_size, __VA_ARGS__)
-#endif
-static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
-{
- hmac_precomputed_t pre;
- va_list va;
- unsigned len;
-
- va_start(va, key_size);
-
- hmac_begin(&pre, key, key_size,
- (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE)
- ? sha1_begin
- : sha256_begin
- );
- len = hmac_sha_precomputed_v(&pre, out, va);
-
- va_end(va);
- return len;
-}
-
// RFC 5246:
// 5. HMAC and the Pseudorandom Function
//...
@@ -559,7 +435,7 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/
const char *label,
uint8_t *seed, unsigned seed_size)
{
- hmac_precomputed_t pre;
+ hmac_ctx_t ctx;
uint8_t a[TLS_MAX_MAC_SIZE];
uint8_t *out_p = outbuf;
unsigned label_size = strlen(label);
@@ -569,26 +445,26 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/
#define SEED label, label_size, seed, seed_size
#define A a, MAC_size
- hmac_begin(&pre, secret, secret_size, sha256_begin);
+ hmac_begin(&ctx, secret, secret_size, sha256_begin);
/* A(1) = HMAC_hash(secret, seed) */
- hmac_sha_precomputed(&pre, a, SEED, NULL);
+ hmac_peek_hash(&ctx, a, SEED, NULL);
for (;;) {
/* HMAC_hash(secret, A(1) + seed) */
if (outbuf_size <= MAC_size) {
/* Last, possibly incomplete, block */
/* (use a[] as temp buffer) */
- hmac_sha_precomputed(&pre, a, A, SEED, NULL);
+ hmac_peek_hash(&ctx, a, A, SEED, NULL);
memcpy(out_p, a, outbuf_size);
return;
}
/* Not last block. Store directly to result buffer */
- hmac_sha_precomputed(&pre, out_p, A, SEED, NULL);
+ hmac_peek_hash(&ctx, out_p, A, SEED, NULL);
out_p += MAC_size;
outbuf_size -= MAC_size;
/* A(2) = HMAC_hash(secret, A(1)) */
- hmac_sha_precomputed(&pre, a, A, NULL);
+ hmac_peek_hash(&ctx, a, A, NULL);
}
#undef A
#undef SECRET
@@ -655,6 +531,29 @@ static void *tls_get_zeroed_outbuf(tls_state_t *tls, int len)
return record;
}
+/* Calculate the HMAC over the list of blocks */
+#if !ENABLE_FEATURE_TLS_SHA1
+#define hmac_block(tls,out,key,key_size,...) \
+ hmac_block(out,key,key_size, __VA_ARGS__)
+#endif
+static unsigned hmac_block(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...)
+{
+ hmac_ctx_t ctx;
+ va_list va;
+
+ hmac_begin(&ctx, key, key_size,
+ (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE)
+ ? sha1_begin
+ : sha256_begin
+ );
+
+ va_start(va, key_size);
+ hmac_hash_v(&ctx, va);
+ va_end(va);
+
+ return hmac_end(&ctx, out);
+}
+
static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type)
{
uint8_t *buf = tls->outbuf + OUTBUF_PFX;
@@ -676,7 +575,7 @@ static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, un
xhdr->len16_lo = size & 0xff;
/* Calculate MAC signature */
- hmac(tls, buf + size, /* result */
+ hmac_block(tls, buf + size, /* result */
tls->client_write_MAC_key, TLS_MAC_SIZE(tls),
&tls->write_seq64_be, sizeof(tls->write_seq64_be),
xhdr, RECHDR_LEN,
More information about the busybox-cvs
mailing list