# crypto.hmac-sha256 `hmacSha256(key, message)` is the 32-byte HMAC-SHA256 tag of `message` under `key`, as RFC 2104 defines HMAC and RFC 4231 pins it for SHA-256. It is what signs an HS256 JWT (`auth.jwt`), a webhook payload or a signed cookie. Bytes in and out are lists of integers 0 to 255, as everywhere in the registry (see `encoding.hex`). A text secret or message goes through `encoding.utf8` first. The construction is `H((K xor opad) || H((K xor ipad) || message))` with the key zero-padded to SHA-256's 64-byte block, or first hashed to 32 bytes when it is longer than 64. That last rule is the one a naive implementation misses, and RFC 4231 test cases 6 and 7 (a 131-byte key) are among the vectors. The empty key is allowed, as the RFC allows it, but a real key should be at least 32 random bytes (RFC 2104 section 3; RFC 7518 requires it for HS256). **Compare tags with `crypto.constant-time-equal`.** Comparing with `==` stops at the first differing byte, and the time that takes tells an attacker how many leading bytes of a forged tag were right. TypeScript and Rust build on `crypto.sha256` and so run anywhere, the browser included, with no `node:crypto` or `crypto.subtle`; Python uses the standard library's `hmac`. Test case 5 of RFC 4231 publishes only the first 16 bytes of its tag; its vector holds the full 32, computed with Python's `hmac` module as the reference, and begins with the RFC's 16. Source: RFC 2104, HMAC: Keyed-Hashing for Message Authentication (https://www.rfc-editor.org/rfc/rfc2104); RFC 4231, Identifiers and Test Vectors for HMAC-SHA-224, HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512, section 4 (https://www.rfc-editor.org/rfc/rfc4231).