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
|
/*
* 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
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;
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->output_size, sizeof(DWORD), &_unused, 0);
mingw_die_if_error(status, "BCryptGetProperty");
ctx->hash_obj = xmalloc(hash_object_length);
status = BCryptCreateHash(alg_handle, &ctx->handle, ctx->hash_obj,
hash_object_length, key, key_size, BCRYPT_HASH_REUSABLE_FLAG);
mingw_die_if_error(status, "BCryptCreateHash");
}
unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out)
{
NTSTATUS status;
status = BCryptFinishHash(ctx->handle, out, ctx->output_size, 0);
mingw_die_if_error(status, "BCryptFinishHash");
return 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->handle, in, size, 0);
}
}
void hmac_uninit(hmac_ctx_t *ctx) {
BCryptDestroyHash(ctx->handle);
free(ctx->hash_obj);
}
#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, ...)
{
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);
}
|