diff options
Diffstat (limited to 'libbb/hash_hmac.c')
| -rw-r--r-- | libbb/hash_hmac.c | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/libbb/hash_hmac.c b/libbb/hash_hmac.c new file mode 100644 index 000000000..8cf936949 --- /dev/null +++ b/libbb/hash_hmac.c @@ -0,0 +1,99 @@ +/* + * 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, 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); +} + +/* 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); +} |
