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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
/*
* 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.
#if !ENABLE_FEATURE_USE_CNG_API
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);
}
}
#else
// Clones an HMAC context from src to dst
static void hmac_clone(hmac_ctx_t *src, hmac_ctx_t *dst) {
DWORD hash_object_length = 0;
ULONG _unused;
NTSTATUS status;
status = BCryptGetProperty(src->alg_handle, BCRYPT_OBJECT_LENGTH,
(PUCHAR)&hash_object_length, sizeof(DWORD), &_unused, 0);
mingw_die_if_error(status, "BCryptGetProperty");
dst->hash_ctx.hash_obj = xmalloc(hash_object_length);
status = BCryptDuplicateHash(src->hash_ctx.handle, &dst->hash_ctx.handle, dst->hash_ctx.hash_obj,
hash_object_length, 0);
mingw_die_if_error(status, "BCryptDuplicateHash");
dst->alg_handle = src->alg_handle;
dst->hash_ctx.output_size = src->hash_ctx.output_size;
}
void _hmac_begin(hmac_ctx_t *ctx, uint8_t *key, unsigned key_size,
BCRYPT_ALG_HANDLE alg_handle) {
DWORD hash_object_length = 0;
ULONG _unused;
NTSTATUS status;
ctx->alg_handle = alg_handle;
status = BCryptGetProperty(alg_handle, BCRYPT_OBJECT_LENGTH,
(PUCHAR)&hash_object_length, sizeof(DWORD), &_unused, 0);
mingw_die_if_error(status, "BCryptGetProperty");
status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH,
(PUCHAR)&ctx->hash_ctx.output_size, sizeof(DWORD), &_unused, 0);
mingw_die_if_error(status, "BCryptGetProperty");
ctx->hash_ctx.hash_obj = xmalloc(hash_object_length);
status = BCryptCreateHash(alg_handle, &ctx->hash_ctx.handle, ctx->hash_ctx.hash_obj,
hash_object_length, key, key_size, 0);
mingw_die_if_error(status, "BCryptCreateHash");
}
void hmac_uninit(hmac_ctx_t *ctx) {
BCryptDestroyHash(ctx->hash_ctx.handle);
free(ctx->hash_ctx.hash_obj);
}
unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out)
{
NTSTATUS status;
status = BCryptFinishHash(ctx->hash_ctx.handle, out, ctx->hash_ctx.output_size, 0);
mingw_die_if_error(status, "BCryptFinishHash");
hmac_uninit(ctx);
return ctx->hash_ctx.output_size;
}
void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va)
{
uint8_t *in;
while ((in = va_arg(va, uint8_t*)) != NULL) {
unsigned size = va_arg(va, unsigned);
BCryptHashData(ctx->hash_ctx.handle, in, size, 0);
}
}
#endif
/* 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 hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...)
{
va_list va;
#if !ENABLE_FEATURE_USE_CNG_API
hmac_ctx_t tmpctx = *ctx; /* struct copy */
#else
hmac_ctx_t tmpctx;
hmac_clone(ctx, &tmpctx);
#endif
va_start(va, out);
hmac_hash_v(&tmpctx, va);
va_end(va);
return hmac_end(&tmpctx, out);
}
|