Skip to main content

SHA256

SHA256 is a cryptographic one-way hash function from the SHA-2 family, producing a fixed 32-byte digest standardized by NIST FIPS 180-4.

Ethereum Context

Mainnet algorithm - Available as EVM precompile at address 0x02 for legacy compatibility and cryptographic proofs. Not used in core protocol (Ethereum prefers Keccak256).

Overview

SHA256 (Secure Hash Algorithm 256-bit) is one of the most widely used cryptographic hash functions, part of the SHA-2 family designed by the NSA and published by NIST in 2001. It produces a 32-byte (256-bit) digest from arbitrary-length input data. SHA256 is fundamental to blockchain technology and cryptography, used for:
  • Bitcoin: Block hashing, transaction IDs, address derivation (combined with RIPEMD160)
  • Ethereum: EVM precompile at 0x02, rarely used in practice (Keccak256 dominates)
  • TLS/SSL: Certificate signatures and secure communications
  • Digital signatures: Message digest for signing algorithms
  • Merkle trees: Constructing efficient authenticated data structures
  • File integrity: Checksums, content addressing, digital forensics

Implementations

  • Pure Zig: Optimized software implementation following FIPS 180-4
  • Hardware accelerated: SHA-NI instructions on x86-64 (10x faster), AVX2 vectorization, ARM SHA2 extensions
  • WASM: Available via sha256.wasm.ts for browser environments (ReleaseSmall: 34KB)
  • TypeScript: Uses @noble/hashes pure implementation for JavaScript environments

Quick Start

import { Sha256 } from '@tevm/voltaire/crypto/sha256';
import * as Hex from '@tevm/voltaire/primitives/Hex';

// Hash bytes - constructor returns branded Sha256 type
const data = new Uint8Array([1, 2, 3, 4, 5]);
const hash = Sha256(data);
// BrandedSha256 (extends Uint8Array(32))

// Hash string (UTF-8 encoded)
const stringHash = Sha256('hello world');
// BrandedSha256

// Hash hex string
const hexData = Hex('0xdeadbeef');
const hexHash = Sha256(hexData);
// BrandedSha256

// Convert to hex
const hexString = Hex.fromBytes(hash);
// "0x..." (hex representation)

API Reference

Sha256(data: Uint8Array | string): BrandedSha256

Compute SHA256 hash of input data using constructor pattern. Parameters:
  • data: Input data to hash (Uint8Array or string)
Returns: BrandedSha256 - 32-byte hash with branded type Example:
import * as Hex from '@tevm/voltaire/primitives/Hex';

// Hash bytes
const hash = Sha256(new Uint8Array([1, 2, 3]));
console.log(hash.length); // 32

// Hash string (UTF-8 encoded automatically)
const stringHash = Sha256('hello world');

// Hash hex data
const hexHash = Sha256(Hex('0xdeadbeef'));

Sha256.hash(data: Uint8Array | string): BrandedSha256

Alternative namespace API for computing SHA256 hash. Parameters:
  • data: Input data to hash (Uint8Array or string)
Returns: BrandedSha256 - 32-byte branded hash Example:
// Equivalent to Sha256(data) constructor
const hash = Sha256.hash(new Uint8Array([1, 2, 3]));

Sha256.create(): Hasher

Create incremental hasher for streaming data. Useful when data arrives in chunks or is too large to hold in memory at once. Returns a hasher instance with update() and digest() methods. Returns: Hasher instance with update and digest methods Example:
const hasher = Sha256.create();
hasher.update(new Uint8Array([1, 2, 3]));
hasher.update(new Uint8Array([4, 5, 6]));
const hash = hasher.digest(); // BrandedSha256
Hasher Interface:
interface Hasher {
  update(data: Uint8Array): void;
  digest(): BrandedSha256;
}

Type Definition

// Branded 32-byte SHA256 hash for type safety
export type BrandedSha256 = Uint8Array & { readonly __tag: "Sha256" };

Constants

Sha256.OUTPUT_SIZE  // 32 - Output size in bytes (256 bits)
Sha256.BLOCK_SIZE   // 64 - Internal block size in bytes (512 bits)

Test Vectors

NIST SHA256 test vectors for validation:
// Empty string
Sha256("")
// Uint8Array(32) [
//   0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
//   0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
//   0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
//   0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55
// ]

// "abc"
Sha256("abc")
// Uint8Array(32) [
//   0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
//   0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
//   0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
//   0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
// ]

// "hello world"
Sha256("hello world")
// Uint8Array(32) [
//   0xb9, 0x4d, 0x27, 0xb9, 0x93, 0x4d, 0x3e, 0x08,
//   0xa5, 0x2e, 0x52, 0xd7, 0xda, 0x7d, 0xab, 0xfa,
//   0xc4, 0x84, 0xef, 0xe3, 0x7a, 0x53, 0x80, 0xee,
//   0x90, 0x88, 0xf7, 0xac, 0xe2, 0xef, 0xcd, 0xe9
// ]

// 448-bit message
Sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
// Uint8Array(32) [
//   0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
//   0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
//   0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
//   0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
// ]

// 896-bit message
Sha256("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
// Uint8Array(32) [
//   0xcf, 0x5b, 0x16, 0xa7, 0x78, 0xaf, 0x83, 0x80,
//   0x03, 0x6c, 0xe5, 0x9e, 0x7b, 0x04, 0x92, 0x37,
//   0x0b, 0x24, 0x9b, 0x11, 0xe8, 0xf0, 0x7a, 0x51,
//   0xaf, 0xac, 0x45, 0x03, 0x7a, 0xfe, 0xe9, 0xd1
// ]

Security Considerations

Collision Resistance

SHA256 provides strong collision resistance with 128-bit security. Finding two inputs that produce the same hash is computationally infeasible with current technology.

Preimage Resistance

Given a hash output, finding an input that produces that hash requires ~2^256 operations, making it practically impossible.

Second Preimage Resistance

Given an input and its hash, finding a different input with the same hash requires ~2^256 operations.

NIST Standardization

SHA256 is a NIST Federal Information Processing Standard (FIPS 180-4), providing regulatory compliance and widespread trust.

Known Attacks

No practical collision or preimage attacks exist against SHA256 as of 2025. The algorithm remains secure for all standard cryptographic uses.
SHA256 alone is NOT suitable for password hashing. Use proper password hashing functions like Argon2, bcrypt, or scrypt which include salt and computational cost factors to resist brute-force attacks.

Performance

Hardware Acceleration

  • TypeScript: Uses @noble/hashes (pure JS, constant-time)
  • Zig/Native: Automatic hardware acceleration using:
    • x86-64 SHA-NI: Intel SHA extensions (10x faster than software)
    • AVX2: Vectorized parallel hashing for multiple blocks
    • ARM SHA2: ARM Cryptography Extensions
    • Software fallback: Optimized implementation when hardware unavailable
  • WASM: Available via sha256.wasm.ts for browser environments

Benchmarks

Typical performance (varies by platform):
  • Native with SHA-NI: ~2000-3000 MB/s (10x faster than software)
  • Native with AVX2: ~800-1200 MB/s (2x faster than software)
  • Native software: ~400-600 MB/s
  • WASM: ~200-400 MB/s
  • Pure JS: ~100-200 MB/s

Performance vs Keccak256

Algorithm          Software    with Hardware Accel
---------          --------    -------------------
SHA256             ~500 MB/s   ~2500 MB/s (SHA-NI)
Keccak256          ~350 MB/s   ~350 MB/s (no accel)
Blake2b            ~700 MB/s   ~700 MB/s (no accel)
Key insight: SHA256 with hardware acceleration outperforms Keccak256, but in software-only environments (WASM, some servers), Blake2b is faster. Ethereum chose Keccak256 before SHA-NI became widespread.

CPU Feature Detection

Zig implementation automatically detects and uses available CPU features:
if (features.has_sha and builtin.target.cpu.arch == .x86_64) {
    // Use SHA-NI extensions
} else if (features.has_avx2) {
    // Use AVX2 SIMD
} else {
    // Fallback to optimized software
}

Implementation Details

TypeScript Implementation

Uses @noble/hashes pure TypeScript SHA256 implementation:
import { sha256 } from "@noble/hashes/sha2.js";

export function hash(data: Uint8Array): Uint8Array {
  return sha256(data);
}

WASM

Available via sha256.wasm.ts for browser environments. Compiled from Zig with wasm32-wasi target.
import { Sha256Wasm } from '@tevm/voltaire/crypto/sha256.wasm';
const hash = Sha256Wasm.hash(data);

Use Cases

Bitcoin Address Derivation

Bitcoin addresses combine SHA256 and RIPEMD160:
import { Sha256 } from '@tevm/voltaire/crypto/sha256';
import { Ripemd160 } from '@tevm/voltaire/crypto/ripemd160';
import * as Hex from '@tevm/voltaire/primitives/Hex';

// Simplified Bitcoin P2PKH address derivation
const publicKey = Hex('0x04' + '1234...'); // 65-byte uncompressed secp256k1 public key
const sha256Hash = Sha256(publicKey);      // BrandedSha256
const ripemd160Hash = Ripemd160(sha256Hash); // BrandedRipemd160
// Then Base58Check encode with version byte

Double SHA256

Bitcoin uses double SHA256 for block and transaction hashing:
import { Sha256 } from '@tevm/voltaire/crypto/sha256';

function doubleSha256(data: Uint8Array): BrandedSha256 {
  return Sha256(Sha256(data));
}

Merkle Trees

Build authenticated data structures:
import { Sha256 } from '@tevm/voltaire/crypto/sha256';
import * as Hex from '@tevm/voltaire/primitives/Hex';

function merkleRoot(leaves: Uint8Array[]): BrandedSha256 {
  if (leaves.length === 0) throw new Error("No leaves");
  if (leaves.length === 1) return Sha256(leaves[0]);

  const hashes = leaves.map(leaf => Sha256(leaf));
  while (hashes.length > 1) {
    const nextLevel: BrandedSha256[] = [];
    for (let i = 0; i < hashes.length; i += 2) {
      const left = hashes[i];
      const right = hashes[i + 1] || left; // Duplicate if odd
      const combined = new Uint8Array([...left, ...right]);
      nextLevel.push(Sha256(combined));
    }
    hashes.length = 0;
    hashes.push(...nextLevel);
  }
  return hashes[0];
}

Streaming Large Files

Process data in chunks:
import { Sha256 } from '@tevm/voltaire/crypto/sha256';

async function hashFile(file: File): Promise<BrandedSha256> {
  const hasher = Sha256.create();
  const chunkSize = 1024 * 1024; // 1MB chunks

  for (let offset = 0; offset < file.size; offset += chunkSize) {
    const chunk = await file.slice(offset, offset + chunkSize).arrayBuffer();
    hasher.update(new Uint8Array(chunk));
  }

  return hasher.digest();
}

Further Reading

Explore comprehensive SHA-256 documentation:
  • API Reference - Complete function reference with examples
  • Test Vectors - NIST FIPS 180-4 official test vectors
  • Security - Cryptographic properties and attack resistance
  • Performance - Benchmarks and optimization techniques
  • Usage Patterns - Common use cases and implementation patterns
  • Comparison - Compare with Keccak256, Blake2, and other hash functions