aboutsummaryrefslogtreecommitdiff
path: root/libbb/hash_hmac.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libbb/hash_hmac.c107
1 files changed, 107 insertions, 0 deletions
diff --git a/libbb/hash_hmac.c b/libbb/hash_hmac.c
new file mode 100644
index 000000000..9e48e0f51
--- /dev/null
+++ b/libbb/hash_hmac.c
@@ -0,0 +1,107 @@
1/*
2 * Copyright (C) 2025 Denys Vlasenko
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
5 */
6//kbuild:lib-$(CONFIG_TLS) += hash_hmac.o
7//kbuild:lib-$(CONFIG_USE_BB_CRYPT_YES) += hash_hmac.o
8
9#include "libbb.h"
10
11// RFC 2104:
12// HMAC(key, text) based on a hash H (say, sha256) is:
13// ipad = [0x36 x INSIZE]
14// opad = [0x5c x INSIZE]
15// HMAC(key, text) = H((key XOR opad) + H((key XOR ipad) + text))
16//
17// H(key XOR opad) and H(key XOR ipad) can be precomputed
18// if we often need HMAC hmac with the same key.
19//
20// text is often given in disjoint pieces.
21void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin)
22{
23#if HMAC_ONLY_SHA256
24#define begin sha256_begin
25#endif
26 uint8_t key_xor_ipad[SHA2_INSIZE];
27 uint8_t key_xor_opad[SHA2_INSIZE];
28 unsigned i;
29
30 // "The authentication key can be of any length up to INSIZE, the
31 // block length of the hash function. Applications that use keys longer
32 // than INSIZE bytes will first hash the key using H and then use the
33 // resultant OUTSIZE byte string as the actual key to HMAC."
34 if (key_size > SHA2_INSIZE) {
35 uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE];
36 /* use ctx->hashed_key_xor_ipad as scratch ctx */
37 begin(&ctx->hashed_key_xor_ipad);
38 md5sha_hash(&ctx->hashed_key_xor_ipad, key, key_size);
39 key_size = sha_end(&ctx->hashed_key_xor_ipad, tempkey);
40 key = tempkey;
41 }
42
43 for (i = 0; i < key_size; i++) {
44 key_xor_ipad[i] = key[i] ^ 0x36;
45 key_xor_opad[i] = key[i] ^ 0x5c;
46 }
47 for (; i < SHA2_INSIZE; i++) {
48 key_xor_ipad[i] = 0x36;
49 key_xor_opad[i] = 0x5c;
50 }
51
52 begin(&ctx->hashed_key_xor_ipad);
53 begin(&ctx->hashed_key_xor_opad);
54 md5sha_hash(&ctx->hashed_key_xor_ipad, key_xor_ipad, SHA2_INSIZE);
55 md5sha_hash(&ctx->hashed_key_xor_opad, key_xor_opad, SHA2_INSIZE);
56}
57#undef begin
58
59unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out)
60{
61 unsigned len = sha_end(&ctx->hashed_key_xor_ipad, out);
62 /* out = H((key XOR opad) + out) */
63 md5sha_hash(&ctx->hashed_key_xor_opad, out, len);
64 return sha_end(&ctx->hashed_key_xor_opad, out);
65}
66
67unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size, md5sha_begin_func *begin, const void *in, unsigned sz, uint8_t *out)
68{
69 hmac_ctx_t ctx;
70 hmac_begin(&ctx, key, key_size, begin);
71 hmac_hash(&ctx, in, sz);
72 return hmac_end(&ctx, out);
73}
74
75/* TLS helpers */
76
77void FAST_FUNC hmac_hash_v(
78 hmac_ctx_t *ctx,
79 va_list va)
80{
81 uint8_t *in;
82
83 /* ctx->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */
84 /* ctx->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */
85
86 /* calculate out = H((key XOR ipad) + text) */
87 while ((in = va_arg(va, uint8_t*)) != NULL) {
88 unsigned size = va_arg(va, unsigned);
89 md5sha_hash(&ctx->hashed_key_xor_ipad, in, size);
90 }
91}
92
93/* Using HMAC state, make a copy of it (IOW: without affecting this state!)
94 * hash in the list of (ptr,size) blocks, and finish the HMAC to out[] buffer.
95 * This function is useful for TLS PRF.
96 */
97unsigned FAST_FUNC hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...)
98{
99 hmac_ctx_t tmpctx = *ctx; /* struct copy */
100 va_list va;
101
102 va_start(va, out);
103 hmac_hash_v(&tmpctx, va);
104 va_end(va);
105
106 return hmac_end(&tmpctx, out);
107}