hmac.nx
buildroot/runtime/hmac.nx
about
############################################################################################
## DO NOT IMPORT THIS FILE. USE nx_hmac.nx INSTEAD. (2026-07-31, debt 1785524913) ##
## This is a CODE-IDENTICAL twin of nx_hmac.nx (49/49 statements) that hangs off the ##
## LEGACY syscalls.nx + sha256.nx family. Both files define hmac_sha256 AND main, and the ##
## import expander dedupes BY FILE PATH, NOT BY SYMBOL -- so importing this one anywhere ##
## the nx_ family is also reachable produces `duplicate definition of hmac_sha256` and ##
## breaks EVERY organ in that closure. That is not hypothetical: it happened the moment ##
## nx_https_fetch_follow began importing nx_tls12_req (which imported this file), and it ##
## broke the whole research-fetch family until all 9 importers were repointed. ##
## Kept (not deleted) so no work is destroyed; importers: 0. Keep it that way. ##
############################################################################################
hmac.nx -- HMAC-SHA-256 (RFC 2104, FIPS 198-1).
Keyed-hash MAC built on sha256.nx. Used throughout TLS 1.3 key
schedule (RFC 8446 ยง7.1) as the HKDF primitive, in older TLS as
Finished-message signer, and wherever we need a symmetric
authenticator without a fresh nonce (unlike Poly1305).
Construction (RFC 2104):
HMAC(K, M) = H( (K' xor opad) || H( (K' xor ipad) || M ) )
where H = SHA-256, block size B = 64 bytes, and K' =
- H(K) if len(K) > B -- pre-hash oversized keys
- K || zeros if len(K) < B -- zero-pad short keys
- K if len(K) == B
ipad = 0x36 repeated; opad = 0x5c repeated.
Why not Poly1305 everywhere:
- Poly1305 is one-time-key (P4 in poly1305.nx); reusing a key
across messages breaks it. HMAC is many-to-one: a long-lived
HMAC key authenticates many messages safely. TLS transcripts
span the whole handshake so HMAC is the right tool there.
Invariants:
HM1 Key processing depends only on key LENGTH, not key VALUE:
short keys zero-padded, long keys pre-hashed through SHA-
256 (which is constant-time by construction).
HM2 XOR loops read every byte of the block regardless of key
content; no early exit.
HM3 Output is exactly 32 bytes (SHA-256 digest width).
dependencies 2 imports · 0 importers
imports: syscalls.nxsha256.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 48 | const HMAC_BLOCK: i64 = 64 // SHA-256 block size |
| 49 | const HMAC_HASH: i64 = 32 // SHA-256 output size |
| 50 | const IPAD: i64 = 0x36 |
| 51 | const OPAD: i64 = 0x5C |
functions
| 54 | func hmac_sha256(key: *u8, key_len: i64, msg: *u8, msg_len: i64, called by 1: main |
| 102 | func main() -> i64 calls 1: hmac_sha256 |