DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ssh_hmac_sha256.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ssh_hmac_sha256.cpp
6 * @brief HMAC-SHA2-256 implementation (RFC 2104).
7 *
8 * Implemented in terms of ssh_sha256 streaming functions so it compiles
9 * identically on Arduino and native. The inner SHA-256 hardware acceleration
10 * (where present) is transparent through the ssh_sha256_* calls.
11 *
12 * RFC 2104 construction:
13 * HMAC(K, m) = H((K XOR opad) || H((K XOR ipad) || m))
14 *
15 * where ipad = 0x36 repeated, opad = 0x5c repeated, H = SHA-256.
16 *
17 * Since SSH-derived MAC keys are exactly 32 bytes (< SHA-256 block size of
18 * 64 bytes), the key is NOT pre-hashed - it is padded with zeros to 64 bytes
19 * internally and used directly as the HMAC key.
20 */
21
23#include <string.h>
24
25// ---------------------------------------------------------------------------
26// Helpers
27// ---------------------------------------------------------------------------
28
29// Build one 64-byte HMAC key block from a variable-length key.
30// Keys > 64 bytes are pre-hashed per RFC 2104 §2; keys ≤ 64 are zero-padded.
31static void build_key_block(const uint8_t *key, size_t key_len, uint8_t block[64], uint8_t pad_byte)
32{
33 uint8_t k[64] = {0};
34 if (key_len > 64)
35 {
36 // Keys longer than the block size are replaced by their SHA-256 hash.
37 ssh_sha256(key, key_len, k);
38 // SHA-256 output is 32 bytes; remaining 32 bytes stay zero.
39 }
40 else
41 {
42 for (size_t i = 0; i < key_len; i++)
43 k[i] = key[i];
44 }
45 for (int i = 0; i < 64; i++)
46 block[i] = k[i] ^ pad_byte;
47}
48
49// ---------------------------------------------------------------------------
50// Streaming API
51// ---------------------------------------------------------------------------
52
53void ssh_hmac_sha256_init(SshHmacCtx *ctx, const uint8_t *key, size_t key_len)
54{
55 uint8_t ikey[64];
56 build_key_block(key, key_len, ikey, 0x36u); // ipad
57 build_key_block(key, key_len, ctx->okey, 0x5cu); // opad (stored for final step)
58
60 ssh_sha256_update(&ctx->inner, ikey, 64);
61}
62
63void ssh_hmac_sha256_update(SshHmacCtx *ctx, const uint8_t *data, size_t len)
64{
65 ssh_sha256_update(&ctx->inner, data, len);
66}
67
69{
70 uint8_t inner_digest[SSH_SHA256_DIGEST_LEN];
71 ssh_sha256_final(&ctx->inner, inner_digest);
72
73 // Outer hash: H(okey || inner_digest)
74 SshSha256Ctx outer;
75 ssh_sha256_init(&outer);
76 ssh_sha256_update(&outer, ctx->okey, 64);
77 ssh_sha256_update(&outer, inner_digest, SSH_SHA256_DIGEST_LEN);
78 ssh_sha256_final(&outer, mac);
79}
80
81// ---------------------------------------------------------------------------
82// One-shot convenience
83// ---------------------------------------------------------------------------
84
85void ssh_hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *data, size_t len,
86 uint8_t mac[SSH_HMAC_SHA256_LEN])
87{
88 SshHmacCtx ctx;
89 ssh_hmac_sha256_init(&ctx, key, key_len);
90 ssh_hmac_sha256_update(&ctx, data, len);
91 ssh_hmac_sha256_final(&ctx, mac);
92}
void ssh_hmac_sha256_final(SshHmacCtx *ctx, uint8_t mac[SSH_HMAC_SHA256_LEN])
Finalize and write the 32-byte MAC.
void ssh_hmac_sha256_init(SshHmacCtx *ctx, const uint8_t *key, size_t key_len)
Initialize a streaming HMAC-SHA2-256 context.
void ssh_hmac_sha256_update(SshHmacCtx *ctx, const uint8_t *data, size_t len)
Feed len bytes into the running HMAC.
void ssh_hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *data, size_t len, uint8_t mac[SSH_HMAC_SHA256_LEN])
Compute HMAC-SHA2-256 over a single contiguous buffer.
HMAC-SHA2-256 (RFC 2104 + FIPS 198-1).
#define SSH_HMAC_SHA256_LEN
HMAC-SHA2-256 output length in bytes.
void ssh_sha256(const uint8_t *data, size_t len, uint8_t digest[SSH_SHA256_DIGEST_LEN])
One-shot SHA-256: hash len bytes and write the digest.
void ssh_sha256_init(SshSha256Ctx *ctx)
Initialize a streaming SHA-256 context.
void ssh_sha256_update(SshSha256Ctx *ctx, const uint8_t *data, size_t len)
Feed len bytes of data into the running hash.
void ssh_sha256_final(SshSha256Ctx *ctx, uint8_t digest[SSH_SHA256_DIGEST_LEN])
Finalize the hash and write the 32-byte digest.
#define SSH_SHA256_DIGEST_LEN
SHA-256 digest length in bytes.
Definition ssh_sha256.h:29
Streaming HMAC-SHA2-256 context.
SshSha256Ctx inner
Inner hash context (key XOR ipad prepended).
uint8_t okey[64]
Outer key block (key XOR opad), stored for final step.
Streaming SHA-256 context.
Definition ssh_sha256.h:44