diff options
Diffstat (limited to '')
-rw-r--r-- | libbb/hash_hmac.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/libbb/hash_hmac.c b/libbb/hash_hmac.c new file mode 100644 index 000000000..b3138029f --- /dev/null +++ b/libbb/hash_hmac.c | |||
@@ -0,0 +1,154 @@ | |||
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. | ||
21 | #if !ENABLE_FEATURE_USE_CNG_API | ||
22 | void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, const uint8_t *key, unsigned key_size, md5sha_begin_func *begin) | ||
23 | { | ||
24 | #if HMAC_ONLY_SHA256 | ||
25 | #define begin sha256_begin | ||
26 | #endif | ||
27 | uint8_t key_xor_ipad[SHA2_INSIZE]; | ||
28 | uint8_t key_xor_opad[SHA2_INSIZE]; | ||
29 | unsigned i; | ||
30 | |||
31 | // "The authentication key can be of any length up to INSIZE, the | ||
32 | // block length of the hash function. Applications that use keys longer | ||
33 | // than INSIZE bytes will first hash the key using H and then use the | ||
34 | // resultant OUTSIZE byte string as the actual key to HMAC." | ||
35 | if (key_size > SHA2_INSIZE) { | ||
36 | uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE]; | ||
37 | /* use ctx->hashed_key_xor_ipad as scratch ctx */ | ||
38 | begin(&ctx->hashed_key_xor_ipad); | ||
39 | md5sha_hash(&ctx->hashed_key_xor_ipad, key, key_size); | ||
40 | key_size = sha_end(&ctx->hashed_key_xor_ipad, tempkey); | ||
41 | key = tempkey; | ||
42 | } | ||
43 | |||
44 | for (i = 0; i < key_size; i++) { | ||
45 | key_xor_ipad[i] = key[i] ^ 0x36; | ||
46 | key_xor_opad[i] = key[i] ^ 0x5c; | ||
47 | } | ||
48 | for (; i < SHA2_INSIZE; i++) { | ||
49 | key_xor_ipad[i] = 0x36; | ||
50 | key_xor_opad[i] = 0x5c; | ||
51 | } | ||
52 | |||
53 | begin(&ctx->hashed_key_xor_ipad); | ||
54 | begin(&ctx->hashed_key_xor_opad); | ||
55 | md5sha_hash(&ctx->hashed_key_xor_ipad, key_xor_ipad, SHA2_INSIZE); | ||
56 | md5sha_hash(&ctx->hashed_key_xor_opad, key_xor_opad, SHA2_INSIZE); | ||
57 | } | ||
58 | #undef begin | ||
59 | |||
60 | unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out) | ||
61 | { | ||
62 | unsigned len = sha_end(&ctx->hashed_key_xor_ipad, out); | ||
63 | /* out = H((key XOR opad) + out) */ | ||
64 | md5sha_hash(&ctx->hashed_key_xor_opad, out, len); | ||
65 | return sha_end(&ctx->hashed_key_xor_opad, out); | ||
66 | } | ||
67 | |||
68 | unsigned FAST_FUNC hmac_block(const uint8_t *key, unsigned key_size, md5sha_begin_func *begin, const void *in, unsigned sz, uint8_t *out) | ||
69 | { | ||
70 | hmac_ctx_t ctx; | ||
71 | hmac_begin(&ctx, key, key_size, begin); | ||
72 | hmac_hash(&ctx, in, sz); | ||
73 | return hmac_end(&ctx, out); | ||
74 | } | ||
75 | |||
76 | /* TLS helpers */ | ||
77 | |||
78 | void FAST_FUNC hmac_hash_v( | ||
79 | hmac_ctx_t *ctx, | ||
80 | va_list va) | ||
81 | { | ||
82 | uint8_t *in; | ||
83 | |||
84 | /* ctx->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */ | ||
85 | /* ctx->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */ | ||
86 | |||
87 | /* calculate out = H((key XOR ipad) + text) */ | ||
88 | while ((in = va_arg(va, uint8_t*)) != NULL) { | ||
89 | unsigned size = va_arg(va, unsigned); | ||
90 | md5sha_hash(&ctx->hashed_key_xor_ipad, in, size); | ||
91 | } | ||
92 | } | ||
93 | #else | ||
94 | void _hmac_begin(hmac_ctx_t *ctx, uint8_t *key, unsigned key_size, | ||
95 | BCRYPT_ALG_HANDLE alg_handle) { | ||
96 | DWORD hash_object_length = 0; | ||
97 | ULONG _unused; | ||
98 | NTSTATUS status; | ||
99 | |||
100 | status = BCryptGetProperty(alg_handle, BCRYPT_OBJECT_LENGTH, | ||
101 | (PUCHAR)&hash_object_length, sizeof(DWORD), &_unused, 0); | ||
102 | mingw_die_if_error(status, "BCryptGetProperty"); | ||
103 | status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH, | ||
104 | (PUCHAR)&ctx->output_size, sizeof(DWORD), &_unused, 0); | ||
105 | mingw_die_if_error(status, "BCryptGetProperty"); | ||
106 | |||
107 | ctx->hash_obj = xmalloc(hash_object_length); | ||
108 | |||
109 | status = BCryptCreateHash(alg_handle, &ctx->handle, ctx->hash_obj, | ||
110 | hash_object_length, key, key_size, BCRYPT_HASH_REUSABLE_FLAG); | ||
111 | mingw_die_if_error(status, "BCryptCreateHash"); | ||
112 | } | ||
113 | |||
114 | unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out) | ||
115 | { | ||
116 | NTSTATUS status; | ||
117 | |||
118 | status = BCryptFinishHash(ctx->handle, out, ctx->output_size, 0); | ||
119 | mingw_die_if_error(status, "BCryptFinishHash"); | ||
120 | |||
121 | return ctx->output_size; | ||
122 | } | ||
123 | |||
124 | void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va) | ||
125 | { | ||
126 | uint8_t *in; | ||
127 | |||
128 | while ((in = va_arg(va, uint8_t*)) != NULL) { | ||
129 | unsigned size = va_arg(va, unsigned); | ||
130 | BCryptHashData(ctx->handle, in, size, 0); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | void hmac_uninit(hmac_ctx_t *ctx) { | ||
135 | BCryptDestroyHash(ctx->handle); | ||
136 | free(ctx->hash_obj); | ||
137 | } | ||
138 | #endif | ||
139 | |||
140 | /* Using HMAC state, make a copy of it (IOW: without affecting this state!) | ||
141 | * hash in the list of (ptr,size) blocks, and finish the HMAC to out[] buffer. | ||
142 | * This function is useful for TLS PRF. | ||
143 | */ | ||
144 | unsigned hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...) | ||
145 | { | ||
146 | hmac_ctx_t tmpctx = *ctx; /* struct copy */ | ||
147 | va_list va; | ||
148 | |||
149 | va_start(va, out); | ||
150 | hmac_hash_v(&tmpctx, va); | ||
151 | va_end(va); | ||
152 | |||
153 | return hmac_end(&tmpctx, out); | ||
154 | } | ||