aboutsummaryrefslogtreecommitdiff
path: root/libbb/hash_hmac.c
blob: 9e48e0f511c7bec00094b69d0f5df9e76a4c6007 (plain)
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);
}