1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
/*
* 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, const 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);
}
unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size, md5sha_begin_func *begin, const void *in, unsigned sz, uint8_t *out)
{
hmac_ctx_t ctx;
hmac_begin(&ctx, key, key_size, begin);
hmac_hash(&ctx, in, sz);
return hmac_end(&ctx, 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);
}
|