SHA-224 Algorithm Explained

Comprehensive breakdown of the 224-bit Secure Hash Algorithm

Introduction to the SHA-224 Algorithm

SHA-224 is a member of the SHA-2 family of cryptographic hash functions, standardized by the National Institute of Standards and Technology (NIST) in FIPS PUB 180-4. The "224" in its name refers to the output size: a 224-bit (28-byte) hash value, typically represented as a 56-character hexadecimal string.

SHA-224 is essentially a truncated version of SHA-256, using different initial hash values but the same internal structure and operations. It was designed to provide a security level appropriate for 112-bit symmetric encryption schemes while offering better performance than longer hashes in constrained environments.

SHA-224 Algorithm Key Properties

  • Output Size: 224 bits (28 bytes)
  • Internal State Size: 256 bits (same as SHA-256)
  • Block Size: 512 bits (64 bytes)
  • Word Size: 32 bits
  • Number of Rounds: 64
  • Operations: Bitwise operations (AND, OR, XOR, NOT), modular addition, rotations
  • Endianness: Big-endian
  • Security Level: 112 bits (against collision attacks)

Relationship to SHA-256

SHA-224 and SHA-256 are closely related algorithms with the following key differences:

Feature SHA-224 SHA-256
Output Size 224 bits (28 bytes) 256 bits (32 bytes)
Initial Hash Values Different set of constants Standard SHA-256 constants
Output Processing Uses only first 7 words of state Uses all 8 words of state
Internal Structure Identical to SHA-256 Identical to SHA-224
Compression Function Same as SHA-256 Same as SHA-224
Constants (K values) Same as SHA-256 Same as SHA-224
Message Schedule Same as SHA-256 Same as SHA-224

In essence, SHA-224 is SHA-256 with different initialization values and with the final hash value truncated to 224 bits (the first 7 words of the 8-word state). The internal processing is identical, which allows implementations to easily support both hash functions with minimal additional code.

SHA-224 Algorithm Components

Initial Hash Values (H0)

SHA-224 uses the following 8 initial hash values (32-bit words), which are different from those used in SHA-256:

Initial Hash Values
const H0 = [
  0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
  0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4
];

Round Constants (K)

SHA-224 uses the same 64 constant 32-bit words as SHA-256. These constants represent the first 32 bits of the fractional parts of the cube roots of the first 64 prime numbers:

Round Constants
const K = [
  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
  0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
  0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
  0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
  0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
  0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
  0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
  0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];

Core Functions

SHA-224 relies on six logical functions, each operating on 32-bit words:

Core Functions
// Bitwise rotation right
function ROTR(x, n) {
  return (x >>> n) | (x << (32 - n));
}

// Ch function: choose bits from y or z based on x
function Ch(x, y, z) {
  return (x & y) ^ (~x & z);
}

// Maj function: majority function (most common bit wins)
function Maj(x, y, z) {
  return (x & y) ^ (x & z) ^ (y & z);
}

// Sigma 0 function
function Σ0(x) {
  return ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22);
}

// Sigma 1 function
function Σ1(x) {
  return ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25);
}

// Lowercase sigma 0 function (for message schedule)
function σ0(x) {
  return ROTR(x, 7) ^ ROTR(x, 18) ^ (x >>> 3);
}

// Lowercase sigma 1 function (for message schedule)
function σ1(x) {
  return ROTR(x, 17) ^ ROTR(x, 19) ^ (x >>> 10);
}

SHA-224 Algorithm Step-by-Step

1. Preprocessing

  1. Padding the message: Append a single "1" bit, followed by zeros, and then the message length as a 64-bit value to make the total length a multiple of 512 bits (64 bytes).
  2. Initialize hash values: Set the initial hash state H(0) to the eight 32-bit values specified for SHA-224.

2. Message Processing (for each 512-bit block)

  1. Create message schedule (W): Break the block into 16 32-bit words (W0 to W15), then extend to create 64 words using the formula:
    Wt = σ1(Wt-2) + Wt-7 + σ0(Wt-15) + Wt-16
    for t = 16 to 63.
  2. Initialize working variables: Set a, b, c, d, e, f, g, h to the current hash values H(i-1).
  3. Compression function: For t = 0 to 63:
    T1 = h + Σ1(e) + Ch(e, f, g) + Kt + Wt
    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
  4. Update hash values: After processing each block, update the hash values:
    H(i)0 = a + H(i-1)0
    H(i)1 = b + H(i-1)1
    H(i)2 = c + H(i-1)2
    H(i)3 = d + H(i-1)3
    H(i)4 = e + H(i-1)4
    H(i)5 = f + H(i-1)5
    H(i)6 = g + H(i-1)6
    H(i)7 = h + H(i-1)7

3. Output

The final hash value is the concatenation of H0 through H6 (the first 7 words of the final state), producing a 224-bit output. Unlike SHA-256, which uses all 8 words, SHA-224 discards the final word to produce a 224-bit output.

SHA-224 Algorithm Processing Flow

Figure 1: SHA-224 algorithm processing flow, showing the compression function and state transformation.

Complete SHA-224 Implementation

Below is a complete implementation of the SHA-224 algorithm in JavaScript. This implementation is designed for clarity and educational purposes rather than optimized performance:

JavaScript
/**
 * SHA-224 implementation in JavaScript
 * Based on the Secure Hash Standard (SHS) - FIPS PUB 180-4
 */
function sha224(input) {
  // Initial hash values (SHA-224 specific)
  const H = [
    0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
    0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4
  ];
  
  // SHA-256 round constants
  const K = [
    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
  ];
  
  // Convert string to bytes
  function stringToBytes(str) {
    const encoder = new TextEncoder();
    return encoder.encode(str);
  }
  
  // Right rotate a 32-bit number
  function ROTR(x, n) {
    return ((x >>> n) | (x << (32 - n))) >>> 0;
  }
  
  // Logical functions
  function Ch(x, y, z) {
    return ((x & y) ^ ((~x) & z)) >>> 0;
  }
  
  function Maj(x, y, z) {
    return ((x & y) ^ (x & z) ^ (y & z)) >>> 0;
  }
  
  function Sigma0(x) {
    return (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) >>> 0;
  }
  
  function Sigma1(x) {
    return (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) >>> 0;
  }
  
  function sigma0(x) {
    return (ROTR(x, 7) ^ ROTR(x, 18) ^ (x >>> 3)) >>> 0;
  }
  
  function sigma1(x) {
    return (ROTR(x, 17) ^ ROTR(x, 19) ^ (x >>> 10)) >>> 0;
  }
  
  // Process the message
  function process() {
    // Convert input to bytes and add padding
    let bytes = (typeof input === 'string') ? stringToBytes(input) : input;
    const originalLength = bytes.length * 8;
    
    // Step 1: Padding the message
    // Append the bit '1' to the message
    bytes = [...bytes, 0x80];
    
    // Append padding zeros
    while ((bytes.length % 64) !== 56) {
      bytes.push(0);
    }
    
    // Append the length as a 64-bit big-endian integer
    const lengthBytes = new Array(8).fill(0);
    for (let i = 0; i < 8; i++) {
      lengthBytes[7 - i] = (originalLength >>> (i * 8)) & 0xff;
    }
    bytes = bytes.concat(lengthBytes);
    
    // Step 2: Process each 64-byte chunk
    for (let i = 0; i < bytes.length; i += 64) {
      // Create the message schedule array (W)
      const W = new Array(64);
      
      // Copy chunk into first 16 words of W
      for (let j = 0; j < 16; j++) {
        W[j] = (bytes[i + j * 4] << 24) | 
               (bytes[i + j * 4 + 1] << 16) | 
               (bytes[i + j * 4 + 2] << 8) | 
               (bytes[i + j * 4 + 3]);
      }
      
      // Extend the first 16 words into remaining 48 words of W
      for (let j = 16; j < 64; j++) {
        W[j] = (sigma1(W[j - 2]) + W[j - 7] + sigma0(W[j - 15]) + W[j - 16]) >>> 0;
      }
      
      // Initialize working variables with current hash value
      let [a, b, c, d, e, f, g, h] = H;
      
      // Compression function main loop
      for (let j = 0; j < 64; j++) {
        const T1 = (h + Sigma1(e) + Ch(e, f, g) + K[j] + W[j]) >>> 0;
        const T2 = (Sigma0(a) + Maj(a, b, c)) >>> 0;
        
        h = g;
        g = f;
        f = e;
        e = (d + T1) >>> 0;
        d = c;
        c = b;
        b = a;
        a = (T1 + T2) >>> 0;
      }
      
      // Add compressed chunk to current hash value
      H[0] = (H[0] + a) >>> 0;
      H[1] = (H[1] + b) >>> 0;
      H[2] = (H[2] + c) >>> 0;
      H[3] = (H[3] + d) >>> 0;
      H[4] = (H[4] + e) >>> 0;
      H[5] = (H[5] + f) >>> 0;
      H[6] = (H[6] + g) >>> 0;
      H[7] = (H[7] + h) >>> 0;
    }
    
    // Step 3: Produce the final hash (for SHA-224, only use first 7 words)
    let result = '';
    for (let i = 0; i < 7; i++) {
      result += H[i].toString(16).padStart(8, '0');
    }
    
    return result;
  }
  
  return process();
}

// Example usage
const text = "Hello, world!";
const hash = sha224(text);
console.log(`SHA-224("${text}") = ${hash}`);

// Ensure the standalone SHA224 function is available for testing
if (typeof window !== 'undefined') {
  window.sha224 = sha224;
}

This implementation follows the algorithm as specified in FIPS PUB 180-4 and produces a 224-bit (28-byte) hash output represented as a 56-character hexadecimal string.

Standard Test Vectors

To verify an implementation of SHA-224, you can use these standard test vectors from NIST:

Input SHA-224 Hash Output (224 bits / 28 bytes)
"" (empty string) d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f
abc 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7
abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq 75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525
One million repetitions of a 20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67

Verification Tool

You can verify your SHA-224 implementation against these test vectors using our SHA-224 Calculator.

Performance Considerations

When implementing SHA-224, several optimizations can improve performance:

Word-Level Parallelism

The algorithm operates on 32-bit words and can benefit from processors with 32-bit or wider data paths.

Unrolled Loops

Unrolling the main loop (partially or fully) can reduce branch prediction penalties and improve performance.

Message Schedule Optimization

Calculating the message schedule incrementally can reduce memory usage and improve cache locality.

SIMD Instructions

Modern CPUs with SIMD (Single Instruction, Multiple Data) can process multiple words in parallel.

Hardware Acceleration

Many modern processors include dedicated instructions or cryptographic acceleration for SHA-2 family functions.

Endianness Handling

Optimizing byte swapping operations for the target architecture can improve performance.

For more detailed performance optimizations, see our SHA-224 Performance Guide.

Algorithm Security

SHA-224 offers 112 bits of security against collision attacks, which means finding two different inputs that produce the same hash would require approximately 2112 operations. This security level is specifically designed to match symmetric encryption schemes with 112-bit keys.

Security Properties

Known Attacks

As of 2025, there are no known practical attacks against SHA-224 that significantly reduce its security below the theoretical levels described above. The best-known attacks on the SHA-2 family involve reduced-round versions or theoretical constructions that don't threaten real-world security.

Security Recommendations

While SHA-224 remains secure for most applications, consider the following:

  • For long-term security (beyond 2030), consider stronger alternatives like SHA-256 or SHA-3 family functions
  • For password hashing, always use specialized algorithms like Argon2, bcrypt, or PBKDF2 rather than plain SHA-224
  • For digital signatures, ensure you're using appropriate key sizes that match SHA-224's security level (112 bits)

For a more detailed security analysis, visit our SHA-224 Security Analysis page.

Advanced Algorithm Topics

Length Extension Attacks

Like other Merkle–Damgård construction hash functions, SHA-224 is vulnerable to length extension attacks. This means if you know H(message1), you can calculate H(message1 || padding || message2) without knowing the original message1. This property has security implications for certain uses like simple keyed hashing schemes.

To prevent length extension attacks, use constructions like HMAC-SHA224 rather than simple concatenation schemes like H(key || message).

Entropy and Output Distribution

SHA-224 exhibits excellent avalanche effect properties, where a small change in the input (even a single bit) results in a completely different hash output with approximately half the bits changed. The output distribution is uniform across the 224-bit space for any reasonable input distribution.

State Recovery

Unlike SHA-256, where the full internal state is exposed in the output, SHA-224 truncates the final state by omitting the last 32-bit word. This provides a small additional security margin against certain types of analytical attacks that attempt to recover the internal state.

Implementation in Constrained Environments

SHA-224 is particularly well-suited for memory-constrained environments like embedded systems and IoT devices. For specific optimization techniques tailored to these environments, see our SHA-224 for Embedded Systems guide.