Key Takeaways

  • SHA-256 (Secure Hash Algorithm 256-bit) was published by NIST in FIPS 180-2 in 2001 and revised in FIPS 180-4 (2015). It produces a 256-bit hash value (64 hexadecimal characters) from input data of any length. SHA-256 processes input in 512-bit blocks through 64 rounds of compression, using six logical functions (Ch, Maj, Σ0, Σ1, σ0, σ1) defined in FIPS 180-4 §4.1.2. The compression function operates on eight 32-bit working variables (a, b, c, d, e, f, g, h) initialized from fractional parts of square roots of the first eight primes.
  • SHA-256 is the dominant cryptographic hash function in 2024: it secures Bitcoin (double-SHA-256 for mining), TLS 1.3 (HMAC-based key derivation), SSH (HMAC-SHA-256 for integrity), JWT signatures (HS256 algorithm), TLS certificates (signatures in X.509 v3), and password hashing (PBKDF2-HMAC-SHA256 with 600,000+ iterations per OWASP 2023). SHA-256 is also the algorithm behind Git LFS content addressing and Docker layer integrity verification.
  • The Web Crypto API (W3C, 2017) provides browser-native SHA-256 computation via crypto.subtle.digest('SHA-256', data), returning an ArrayBuffer digest. This eliminates the need for JavaScript SHA-256 libraries and works in all modern browsers. For HMAC-SHA-256, crypto.subtle.sign('HMAC', key, message) implements the HMAC construction per RFC 2104. The Web Crypto API is the only cryptographically-secure browser API for hash computation.
  • SHA-256 is collision-resistant as of 2024 — no practical collision attack has been published. The best known collision attack against SHA-256 requires 2^128 operations (the birthday bound). By comparison, MD5's collision attack requires 2^39 operations (trivially feasible) and SHA-1's collision attack (published by Google in 2017 as SHAttered) requires 2^63 operations. SHA-256's security margin is sufficient for all current and foreseeable cryptographic applications.
  • HMAC-SHA-256 (Hash-based Message Authentication Code) uses SHA-256 as a building block for message authentication. The HMAC construction is defined in RFC 2104 (1997): HMAC(K, m) = SHA-256((K ⊕ opad) || SHA-256((K ⊕ ipad) || m)), where ipad and opad are fixed constants. HMAC-SHA-256 is widely used for API request signing, JWT tokens, and webhook verification — any scenario where both message integrity and authenticity are required, not just integrity.

SHA-256: The 256-Bit Hash That Powers Modern Cryptography

In August 2001, NIST published FIPS 180-2, introducing SHA-256 as the primary member of the SHA-2 family. By 2024, SHA-256 is the most widely deployed cryptographic hash function in history: it secures Bitcoin's proof-of-work, signs TLS certificates, authenticates SSH sessions, derives encryption keys for TLS 1.3, anchors Docker container integrity, and verifies software package signatures. Unlike its predecessor MD5 (broken since 2004) and SHA-1 (broken since 2017's SHAttered attack), SHA-256 has no known practical collision attack as of 2024 — its security margin remains sufficient for all foreseeable cryptographic needs.

Table of Contents

  1. The SHA-256 Algorithm Structure
  2. The 64-Round Compression Function
  3. SHA-256 vs MD5 vs SHA-1
  4. HMAC-SHA-256: Authenticated Hashing
  5. Web Crypto API: Browser-Native SHA-256
  6. Frequently Asked Questions

The SHA-256 Algorithm Structure

SHA-256 follows the Merkle-Damgård construction: input is padded to a multiple of 512 bits, then processed block-by-block through a compression function that maintains a running 256-bit state.

Padding stage:

The input message is padded with a single 1 bit followed by 0 bits until the length is congruent to 448 modulo 512 bits. The remaining 64 bits encode the original message length in bits as a 64-bit big-endian integer.

Input:  "hello" (40 bits / 5 bytes)
Padded: "hello" + 0x80 + (407 zero bits) + (40 as 64-bit BE) = 512 bits total

For inputs exceeding 2^64 bits (impractical but theoretically possible), the high 64 bits of length are encoded in an additional block.

Initial hash values (H[0] through H[7]):

SHA-256 starts with eight 32-bit constants derived from the fractional parts of the square roots of the first eight prime numbers:

H[0] = 0x6a09e667
H[1] = 0xbb67ae85
H[2] = 0x3c6ef372
H[3] = 0xa54ff53a
H[4] = 0x510e527f
H[5] = 0x9b05688c
H[6] = 0x1f83d9ab
H[7] = 0x5be0cd19

These constants are specified in FIPS 180-4 §5.3.3 and are identical across all SHA-256 implementations.

Round constants (K[0] through K[63]):

Each of the 64 rounds uses a different 32-bit constant derived from the fractional parts of the cube roots of the first 64 prime numbers. The constants are specified in FIPS 180-4 §4.2.2 and provide cryptographic diversity per round.

The 64-Round Compression Function

Each 512-bit block runs through 64 rounds of the compression function. The function uses eight working variables (a, b, c, d, e, f, g, h) initialized from the current hash state, then updates them in each round.

Per-round computation:

T1 = h + Σ1(e) + Ch(e, f, g) + K[i] + W[i]
T2 = Σ0(a) + Maj(a, b, c)
h = g
g = f
f = e
e = d + T1
d = c
c = b
b = a
a = T1 + T2

Logical functions:

Function Definition Purpose
Ch(x, y, z) (x ∧ y) ⊕ (¬x ∧ z) Choice — selects y if x is set, z otherwise
Maj(x, y, z) (x ∧ y) ⊕ (x ∧ z) ⊕ (y ∧ z) Majority — outputs the majority value
Σ0(x) ROTR^2(x) ⊕ ROTR^13(x) ⊕ ROTR^22(x) Big sigma 0 (uppercase)
Σ1(x) ROTR^6(x) ⊕ ROTR^11(x) ⊕ ROTR^25(x) Big sigma 1 (uppercase)
σ0(x) ROTR^7(x) ⊕ ROTR^18(x) ⊕ SHR^3(x) Small sigma 0 (lowercase)
σ1(x) ROTR^17(x) ⊕ ROTR^19(x) ⊕ SHR^10(x) Small sigma 1 (lowercase)

Message schedule expansion:

The 16 input words W[0]..W[15] (each 32 bits, derived from the 512-bit block) are extended to 64 words W[0]..W[63] via:

W[i] = σ1(W[i-2]) + W[i-7] + σ0(W[i-15]) + W[i-16]   for i = 16 to 63

This schedule propagation ensures that each input bit influences the entire output within a few rounds.

Output:

After all 64 rounds, the eight working variables are added to the initial hash state:

H'[i] = H[i] + (a, b, c, d, e, f, g, h)[i]   for i = 0 to 7

The final hash is the concatenation H'[0] || H'[1] || ... || H'[7], producing 256 bits (64 hex characters).

SHA-256 vs MD5 vs SHA-1

Property MD5 SHA-1 SHA-256
Digest size 128 bits 160 bits 256 bits
Block size 512 bits 512 bits 512 bits
Rounds 64 (4×16) 80 (1×80) 64 (1×64)
Collision resistance 2^18 (broken, Wang 2004) 2^63 (broken, SHAttered 2017) 2^128 (secure as of 2024)
Pre-image resistance 2^128 (theoretical) 2^160 2^256
Speed (1 GB, x86-64) ~350 MB/s ~250 MB/s ~150 MB/s (without SHA-NI)
Speed (1 GB, with SHA-NI) n/a n/a ~500 MB/s
Status Legacy, non-security only Deprecated by NIST 2011 Recommended (FIPS 140-3)
Hardware acceleration None None SHA-NI (Intel/AMD since 2013)

The performance gap has narrowed dramatically with hardware acceleration. Modern Intel and AMD CPUs include dedicated SHA-256 instructions (SHA256MSG1, SHA256MSG2, SHA256RNDS2) that make SHA-256 nearly as fast as MD5 for many workloads.

HMAC-SHA-256: Authenticated Hashing

HMAC (Hash-based Message Authentication Code) constructs a message authentication code using SHA-256 as a building block. Unlike plain SHA-256 (which only provides integrity), HMAC-SHA-256 provides both integrity and authenticity — only parties with the shared secret key can produce valid HMACs.

Construction (RFC 2104):

HMAC-SHA-256(K, m) = SHA-256((K ⊕ opad) || SHA-256((K ⊕ ipad) || m))

Where:

  • K is the secret key (padded to 512 bits by appending zeros if shorter than 64 bytes, or hashed if longer)
  • opad = 0x5c repeated 64 times
  • ipad = 0x36 repeated 64 times
  • || denotes byte concatenation
  • m is the message

Common use cases:

Use Case Algorithm Why HMAC
JWT (JSON Web Tokens) HS256 Stateless session tokens — receiver validates HMAC without DB lookup
AWS Signature V4 HMAC-SHA-256 over canonical request API request authentication
Stripe webhook signature HMAC-SHA-256 of payload + timestamp Verify webhook came from Stripe
Slack request signing HMAC-SHA-256 Verify Slack request authenticity
GitHub webhook HMAC-SHA-256 Verify GitHub webhook origin

Example: AWS Signature V4 signing key derivation

def sign(key, msg):
    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()

# kDate = HMAC(("AWS4" + secret_key).encode("utf-8"), datestamp)
# kRegion = HMAC(kDate, region)
# kService = HMAC(kRegion, service)
# kSigning = HMAC(kService, "aws4_request")
# signature = HMAC(kSigning, string_to_sign)

Web Crypto API: Browser-Native SHA-256

The Web Crypto API (W3C, 2017) provides SHA-256 computation in all modern browsers without third-party libraries.

Basic SHA-256 hashing (JavaScript):

async function sha256Hex(text) {
  const data = new TextEncoder().encode(text);
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
  return Array.from(new Uint8Array(hashBuffer))
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

const hash = await sha256Hex("hello");
// hash = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

HMAC-SHA-256 with Web Crypto:

async function hmacSha256Hex(secret, message) {
  const keyData = new TextEncoder().encode(secret);
  const key = await crypto.subtle.importKey(
    "raw", keyData,
    { name: "HMAC", hash: "SHA-256" },
    false, ["sign"]
  );
  const msgData = new TextEncoder().encode(message);
  const sig = await crypto.subtle.sign("HMAC", key, msgData);
  return Array.from(new Uint8Array(sig))
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

const mac = await hmacSha256Hex("my-secret", "payload");
// mac = "e8b9c8d8a4c1... (64 hex chars)"

Browser support: Chrome 37+, Firefox 34+, Safari 11+, Edge 12+. Available in secure contexts only (HTTPS or localhost).

People Also Ask

A brute-force pre-image attack against SHA-256 requires 2^256 operations on average — approximately 1.16 × 10^77 hash computations. At 10^18 hashes per second (a high-end GPU cluster), this would require 1.16 × 10^59 seconds — about 3.7 × 10^51 years. The age of the universe is approximately 1.38 × 10^10 years. SHA-256 brute force is computationally infeasible with current and foreseeable hardware.
Last updated: January 1, 2024