libbb: factor out HMAC code from TLS
This commit was genuinely broken at the time (not related to our build system): libbb/yescrypt eklenirken (2025-07-06 - 2025-07-07 arasi) once unutulan kaynak dosyalari (pw_encrypt_yes.c, hash_sha256_block.c, pw_ascii64.c), ardindan 64-bit'te derlenmeyen bir 'buflen' degiskeni (alg-yescrypt-kdf.c) vardi - busybox'in kendi kodundaki gercek hatalar (e5d3a8763 ile duzeltildi)
No binary (build failed)
—
(log yok)
| @@ -2193,6 +2193,16 @@ int FAST_FUNC i2a64(int i); | |
| int FAST_FUNC a2i64(char c); | |
| char* FAST_FUNC num2str64_lsb_first(char *s, unsigned v, int n); | |
| + | enum { |
| + | /* how many bytes XYZ_end() fills */ |
| + | MD5_OUTSIZE = 16, |
| + | SHA1_OUTSIZE = 20, |
| + | SHA256_OUTSIZE = 32, |
| + | SHA512_OUTSIZE = 64, |
| + | SHA3_OUTSIZE = 28, |
| + | /* size of input block */ |
| + | SHA2_INSIZE = 64, |
| + | }; |
| typedef struct md5_ctx_t { | |
| uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ | |
| void (*process_block)(struct md5_ctx_t*) FAST_FUNC; | |
| @@ -2226,18 +2236,32 @@ unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; | |
| void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; | |
| void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; | |
| unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC; | |
| + | void FAST_FUNC sha256_block(const void *in, size_t len, uint8_t hash[32]); |
| /* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */ | |
| typedef struct md5_ctx_t md5sha_ctx_t; | |
| #define md5sha_hash md5_hash | |
| #define sha_end sha1_end | |
| - | enum { |
| - | MD5_OUTSIZE = 16, |
| - | SHA1_OUTSIZE = 20, |
| - | SHA256_OUTSIZE = 32, |
| - | SHA512_OUTSIZE = 64, |
| - | SHA3_OUTSIZE = 28, |
| - | }; |
| - | void FAST_FUNC sha256_block(const void *in, size_t len, uint8_t hash[32]); |
| + | |
| + | /* RFC 2104 HMAC (hash-based message authentication code) */ |
| + | typedef struct hmac_ctx { |
| + | md5sha_ctx_t hashed_key_xor_ipad; |
| + | md5sha_ctx_t hashed_key_xor_opad; |
| + | } hmac_ctx_t; |
| + | #define HMAC_ONLY_SHA256 (!ENABLE_FEATURE_TLS_SHA1) |
| + | typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC; |
| + | #if HMAC_ONLY_SHA256 |
| + | #define hmac_begin(ctx,key,key_size,begin) \ |
| + | hmac_begin(ctx,key,key_size) |
| + | #endif |
| + | void FAST_FUNC hmac_begin(hmac_ctx_t *ctx, uint8_t *key, unsigned key_size, md5sha_begin_func *begin); |
| + | static ALWAYS_INLINE void hmac_hash(hmac_ctx_t *ctx, const void *in, size_t len) |
| + | { |
| + | md5sha_hash(&ctx->hashed_key_xor_ipad, in, len); |
| + | } |
| + | unsigned FAST_FUNC hmac_end(hmac_ctx_t *ctx, uint8_t *out); |
| + | /* HMAC helpers for TLS: */ |
| + | void FAST_FUNC hmac_hash_v(hmac_ctx_t *ctx, va_list va); |
| + | unsigned FAST_FUNC hmac_peek_hash(hmac_ctx_t *ctx, uint8_t *out, ...); |
| extern uint32_t *global_crc32_table; | |
| uint32_t *crc32_filltable(uint32_t *tbl256, int endian) FAST_FUNC; | |
| @@ -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); |
| + | } |
| @@ -188,8 +188,6 @@ | |
| #define TLS_MAX_OUTBUF (1 << 14) | |
| enum { | |
| - | SHA_INSIZE = 64, |
| - | |
| AES128_KEYSIZE = 16, | |
| AES256_KEYSIZE = 32, | |
| @@ -393,128 +391,6 @@ static void hash_handshake(tls_state_t *tls, const char *fmt, const void *buffer | |
| # define TLS_MAC_SIZE(tls) (tls)->MAC_size | |
| #endif | |
| - | // 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. |
| - | typedef struct hmac_precomputed { |
| - | md5sha_ctx_t hashed_key_xor_ipad; |
| - | md5sha_ctx_t hashed_key_xor_opad; |
| - | } hmac_precomputed_t; |
| - | |
| - | typedef void md5sha_begin_func(md5sha_ctx_t *ctx) FAST_FUNC; |
| - | #if !ENABLE_FEATURE_TLS_SHA1 |
| - | #define hmac_begin(pre,key,key_size,begin) \ |
| - | hmac_begin(pre,key,key_size) |
| - | #define begin sha256_begin |
| - | #endif |
| - | static void hmac_begin(hmac_precomputed_t *pre, uint8_t *key, unsigned key_size, md5sha_begin_func *begin) |
| - | { |
| - | uint8_t key_xor_ipad[SHA_INSIZE]; |
| - | uint8_t key_xor_opad[SHA_INSIZE]; |
| - | // uint8_t tempkey[SHA1_OUTSIZE < SHA256_OUTSIZE ? SHA256_OUTSIZE : SHA1_OUTSIZE]; |
| - | 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 > SHA_INSIZE) { |
| - | bb_simple_error_msg_and_die("HMAC key>64"); //does not happen (yet?) |
| - | // md5sha_ctx_t ctx; |
| - | // begin(&ctx); |
| - | // md5sha_hash(&ctx, key, key_size); |
| - | // key_size = sha_end(&ctx, tempkey); |
| - | // //key = tempkey; - right? RIGHT? why does it work without this? |
| - | // // because SHA_INSIZE is 64, but hmac() is always called with |
| - | // // key_size = tls->MAC_size = SHA1/256_OUTSIZE (20 or 32), |
| - | // // and prf_hmac_sha256() -> hmac_sha256() key sizes are: |
| - | // // - RSA_PREMASTER_SIZE is 48 |
| - | // // - CURVE25519_KEYSIZE is 32 |
| - | // // - master_secret[] is 48 |
| - | } |
| - | |
| - | for (i = 0; i < key_size; i++) { |
| - | key_xor_ipad[i] = key[i] ^ 0x36; |
| - | key_xor_opad[i] = key[i] ^ 0x5c; |
| - | } |
| - | for (; i < SHA_INSIZE; i++) { |
| - | key_xor_ipad[i] = 0x36; |
| - | key_xor_opad[i] = 0x5c; |
| - | } |
| - | |
| - | begin(&pre->hashed_key_xor_ipad); |
| - | begin(&pre->hashed_key_xor_opad); |
| - | md5sha_hash(&pre->hashed_key_xor_ipad, key_xor_ipad, SHA_INSIZE); |
| - | md5sha_hash(&pre->hashed_key_xor_opad, key_xor_opad, SHA_INSIZE); |
| - | } |
| - | #undef begin |
| - | |
| - | static unsigned hmac_sha_precomputed_v( |
| - | hmac_precomputed_t *pre, |
| - | uint8_t *out, |
| - | va_list va) |
| - | { |
| - | uint8_t *text; |
| - | unsigned len; |
| - | |
| - | /* pre->hashed_key_xor_ipad contains unclosed "H((key XOR ipad) +" state */ |
| - | /* pre->hashed_key_xor_opad contains unclosed "H((key XOR opad) +" state */ |
| - | |
| - | /* calculate out = H((key XOR ipad) + text) */ |
| - | while ((text = va_arg(va, uint8_t*)) != NULL) { |
| - | unsigned text_size = va_arg(va, unsigned); |
| - | md5sha_hash(&pre->hashed_key_xor_ipad, text, text_size); |
| - | } |
| - | len = sha_end(&pre->hashed_key_xor_ipad, out); |
| - | |
| - | /* out = H((key XOR opad) + out) */ |
| - | md5sha_hash(&pre->hashed_key_xor_opad, out, len); |
| - | return sha_end(&pre->hashed_key_xor_opad, out); |
| - | } |
| - | |
| - | static unsigned hmac_sha_precomputed(hmac_precomputed_t *pre_init, uint8_t *out, ...) |
| - | { |
| - | hmac_precomputed_t pre; |
| - | va_list va; |
| - | unsigned len; |
| - | |
| - | va_start(va, out); |
| - | pre = *pre_init; /* struct copy */ |
| - | len = hmac_sha_precomputed_v(&pre, out, va); |
| - | va_end(va); |
| - | return len; |
| - | } |
| - | |
| - | #if !ENABLE_FEATURE_TLS_SHA1 |
| - | #define hmac(tls,out,key,key_size,...) \ |
| - | hmac(out,key,key_size, __VA_ARGS__) |
| - | #endif |
| - | static unsigned hmac(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...) |
| - | { |
| - | hmac_precomputed_t pre; |
| - | va_list va; |
| - | unsigned len; |
| - | |
| - | va_start(va, key_size); |
| - | |
| - | hmac_begin(&pre, key, key_size, |
| - | (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE) |
| - | ? sha1_begin |
| - | : sha256_begin |
| - | ); |
| - | len = hmac_sha_precomputed_v(&pre, out, va); |
| - | |
| - | va_end(va); |
| - | return len; |
| - | } |
| - | |
| // RFC 5246: | |
| // 5. HMAC and the Pseudorandom Function | |
| //... | |
| @@ -559,7 +435,7 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/ | |
| const char *label, | |
| uint8_t *seed, unsigned seed_size) | |
| { | |
| - | hmac_precomputed_t pre; |
| + | hmac_ctx_t ctx; |
| uint8_t a[TLS_MAX_MAC_SIZE]; | |
| uint8_t *out_p = outbuf; | |
| unsigned label_size = strlen(label); | |
| @@ -569,26 +445,26 @@ static void prf_hmac_sha256(/*tls_state_t *tls,*/ | |
| #define SEED label, label_size, seed, seed_size | |
| #define A a, MAC_size | |
| - | hmac_begin(&pre, secret, secret_size, sha256_begin); |
| + | hmac_begin(&ctx, secret, secret_size, sha256_begin); |
| /* A(1) = HMAC_hash(secret, seed) */ | |
| - | hmac_sha_precomputed(&pre, a, SEED, NULL); |
| + | hmac_peek_hash(&ctx, a, SEED, NULL); |
| for (;;) { | |
| /* HMAC_hash(secret, A(1) + seed) */ | |
| if (outbuf_size <= MAC_size) { | |
| /* Last, possibly incomplete, block */ | |
| /* (use a[] as temp buffer) */ | |
| - | hmac_sha_precomputed(&pre, a, A, SEED, NULL); |
| + | hmac_peek_hash(&ctx, a, A, SEED, NULL); |
| memcpy(out_p, a, outbuf_size); | |
| return; | |
| } | |
| /* Not last block. Store directly to result buffer */ | |
| - | hmac_sha_precomputed(&pre, out_p, A, SEED, NULL); |
| + | hmac_peek_hash(&ctx, out_p, A, SEED, NULL); |
| out_p += MAC_size; | |
| outbuf_size -= MAC_size; | |
| /* A(2) = HMAC_hash(secret, A(1)) */ | |
| - | hmac_sha_precomputed(&pre, a, A, NULL); |
| + | hmac_peek_hash(&ctx, a, A, NULL); |
| } | |
| #undef A | |
| #undef SECRET | |
| @@ -655,6 +531,29 @@ static void *tls_get_zeroed_outbuf(tls_state_t *tls, int len) | |
| return record; | |
| } | |
| + | /* Calculate the HMAC over the list of blocks */ |
| + | #if !ENABLE_FEATURE_TLS_SHA1 |
| + | #define hmac_block(tls,out,key,key_size,...) \ |
| + | hmac_block(out,key,key_size, __VA_ARGS__) |
| + | #endif |
| + | static unsigned hmac_block(tls_state_t *tls, uint8_t *out, uint8_t *key, unsigned key_size, ...) |
| + | { |
| + | hmac_ctx_t ctx; |
| + | va_list va; |
| + | |
| + | hmac_begin(&ctx, key, key_size, |
| + | (ENABLE_FEATURE_TLS_SHA1 && tls->MAC_size == SHA1_OUTSIZE) |
| + | ? sha1_begin |
| + | : sha256_begin |
| + | ); |
| + | |
| + | va_start(va, key_size); |
| + | hmac_hash_v(&ctx, va); |
| + | va_end(va); |
| + | |
| + | return hmac_end(&ctx, out); |
| + | } |
| + | |
| static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, unsigned type) | |
| { | |
| uint8_t *buf = tls->outbuf + OUTBUF_PFX; | |
| @@ -676,7 +575,7 @@ static void xwrite_encrypted_and_hmac_signed(tls_state_t *tls, unsigned size, un | |
| xhdr->len16_lo = size & 0xff; | |
| /* Calculate MAC signature */ | |
| - | hmac(tls, buf + size, /* result */ |
| + | hmac_block(tls, buf + size, /* result */ |
| tls->client_write_MAC_key, TLS_MAC_SIZE(tls), | |
| &tls->write_seq64_be, sizeof(tls->write_seq64_be), | |
| xhdr, RECHDR_LEN, | |