SHAKE-256 Security-Focused API Documentation

Algorithm: SHAKE-256 (Secure Hash Algorithm Keccak 256)
Type: Extensible Output Function (XOF)
Security Level: 256-bit (min(d/2, 256) for d-bit output)
Specification: FIPS 202

Security Contract

shake256(data: Uint8Array, outputLength: number): Uint8Array

Preconditions:

Postconditions:

Side Effects:

Attack Resistance Matrix

Attack Type Resistance Notes
Collision ✅ 2^128 For ≥256-bit output
Preimage ✅ 2^256 Full security
Second Preimage ✅ 2^256 Strong resistance
Length Extension ✅ Immune Sponge construction
State Recovery ✅ 2^256 From output
Timing Attacks ✅ Resistant Constant-time
Quantum ⚠️ 2^128 Grover’s algorithm

Secure Usage Examples

✅ CORRECT: Variable-Length Key Derivation

import { shake256 } from 'metamui-shake256';

// Derive multiple keys from master secret
function deriveKeys(
  masterSecret: Uint8Array,
  context: string
): {
  encryptionKey: Uint8Array;
  authenticationKey: Uint8Array;
  ivKey: Uint8Array;
} {
  // Derive 96 bytes total (3 × 32-byte keys)
  const derived = shake256.xof(
    concat(masterSecret, stringToBytes(context)),
    96
  );
  
  return {
    encryptionKey: derived.slice(0, 32),
    authenticationKey: derived.slice(32, 64),
    ivKey: derived.slice(64, 96)
  };
}

✅ CORRECT: Domain-Separated Hashing

import { shake256 } from 'metamui-shake256';

// NIST SP 800-185 compliant domain separation
function cshake256(
  data: Uint8Array,
  outputLength: number,
  functionName: string,
  customization: string
): Uint8Array {
  // Encode strings as per NIST standard
  const fnEncoded = encodeString(functionName);
  const custEncoded = encodeString(customization);
  
  // Prepend domain separation
  const input = concat(
    fnEncoded,
    custEncoded,
    data
  );
  
  return shake256.xof(input, outputLength);
}

// Usage
const hash = cshake256(
  data,
  32,
  "EmailSignature",
  "MyApp v1.0"
);

✅ CORRECT: Randomness Expansion

import { shake256 } from 'metamui-shake256';

// Expand seed to arbitrary length
function expandSeed(
  seed: Uint8Array,
  outputBytes: number
): Uint8Array {
  // SHAKE-256 as deterministic random generator
  return shake256.xof(seed, outputBytes);
}

// Generate deterministic random values
const expanded = expandSeed(seed, 1024); // 1KB of randomness

✅ CORRECT: Post-Quantum KDF

import { shake256 } from 'metamui-shake256';

// Kyber/ML-KEM key derivation
function kyberKdf(
  sharedSecret: Uint8Array,
  publicKey: Uint8Array
): Uint8Array {
  // Standard post-quantum KDF construction
  const input = concat(sharedSecret, publicKey);
  return shake256.xof(input, 32);
}

Common Mistakes to Avoid

❌ WRONG: Output Too Short for Security

// NEVER DO THIS - Output too short
const key = shake256.xof(secret, 8); // Only 64 bits!

// ✅ CORRECT: Use adequate output length
const key = shake256.xof(secret, 32); // 256 bits

❌ WRONG: Using as Regular Hash

// NEVER DO THIS - Inefficient for fixed output
function hashFile(data: Uint8Array): Uint8Array {
  return shake256.xof(data, 32); // Works but SHA3-256 is better
}

// ✅ CORRECT: Use SHA3-256 for fixed-length hashing
import { sha3_256 } from 'metamui-sha3';
function hashFile(data: Uint8Array): Uint8Array {
  return sha3_256.hash(data);
}

❌ WRONG: Reusing Output Stream

// NEVER DO THIS - Security boundary violation
const stream = shake256.xof(secret, 1000);
const key1 = stream.slice(0, 32);
const key2 = stream.slice(32, 64); // Related to key1!

// ✅ CORRECT: Use domain separation
const key1 = shake256.xof(concat(secret, [0x01]), 32);
const key2 = shake256.xof(concat(secret, [0x02]), 32);

Implementation Security

Sponge Construction Security

class Shake256 {
  private state: Uint8Array;
  private rate: number = 136; // 1088 bits
  private capacity: number = 64; // 512 bits
  
  constructor() {
    // 1600-bit state (Keccak-f[1600])
    this.state = new Uint8Array(200);
  }
  
  absorb(data: Uint8Array): void {
    // Process in rate-sized blocks
    for (let i = 0; i < data.length; i += this.rate) {
      const block = data.slice(i, i + this.rate);
      this.xorIntoState(block);
      this.keccakF1600();
    }
  }
  
  squeeze(outputLength: number): Uint8Array {
    const output = new Uint8Array(outputLength);
    let outputOffset = 0;
    
    while (outputOffset < outputLength) {
      // Extract rate bytes
      const block = this.state.slice(0, this.rate);
      const copyLength = Math.min(
        this.rate,
        outputLength - outputOffset
      );
      
      output.set(
        block.slice(0, copyLength),
        outputOffset
      );
      
      outputOffset += copyLength;
      
      if (outputOffset < outputLength) {
        this.keccakF1600(); // Permute for more output
      }
    }
    
    return output;
  }
}

Streaming API for Large Outputs

import { shake256 } from 'metamui-shake256';

// Memory-efficient streaming
class Shake256Stream {
  private hasher: Shake256Instance;
  private buffer: Uint8Array;
  private position: number = 0;
  
  constructor(data: Uint8Array) {
    this.hasher = shake256.create();
    this.hasher.update(data);
    this.hasher.finalize(); // Switch to squeeze mode
    this.buffer = new Uint8Array(136); // Rate size
  }
  
  read(length: number): Uint8Array {
    const output = new Uint8Array(length);
    let filled = 0;
    
    while (filled < length) {
      if (this.position === 0) {
        this.hasher.squeeze(this.buffer);
      }
      
      const available = this.buffer.length - this.position;
      const needed = length - filled;
      const toCopy = Math.min(available, needed);
      
      output.set(
        this.buffer.slice(this.position, this.position + toCopy),
        filled
      );
      
      filled += toCopy;
      this.position = (this.position + toCopy) % this.buffer.length;
    }
    
    return output;
  }
}

Platform-Specific Notes

JavaScript/TypeScript

Python

Rust

Comparison with Other XOFs

Feature SHAKE-256 BLAKE3 XOF ChaCha20
Security 256-bit 128-bit 256-bit
Speed Moderate Fastest Fast
Standardization NIST Industry IETF
Quantum Resistance 128-bit 64-bit 128-bit
Parallelizable No Yes No

When to Use SHAKE-256

✅ Appropriate Uses:

❌ Inappropriate Uses:

Advanced Features

TupleHash (NIST SP 800-185)

function tupleHash256(
  tuples: Uint8Array[],
  outputLength: number,
  customization: string = ""
): Uint8Array {
  // Encode each tuple with length prefix
  const encoded = tuples.map(tuple => 
    concat(encodeLength(tuple.length), tuple)
  );
  
  return cshake256(
    concat(...encoded),
    outputLength,
    "TupleHash",
    customization
  );
}

ParallelHash (NIST SP 800-185)

function parallelHash256(
  data: Uint8Array,
  blockSize: number,
  outputLength: number,
  customization: string = ""
): Uint8Array {
  // Split into blocks for parallel processing
  const blocks: Uint8Array[] = [];
  for (let i = 0; i < data.length; i += blockSize) {
    blocks.push(data.slice(i, i + blockSize));
  }
  
  // Hash blocks in parallel
  const blockHashes = blocks.map(block =>
    shake256.xof(block, 32)
  );
  
  // Combine with domain separation
  return cshake256(
    concat(...blockHashes),
    outputLength,
    "ParallelHash",
    customization
  );
}

Security Checklist

Security Analysis

Threat Model: SHAKE-256 Threat Model

The comprehensive threat analysis covers:

For complete security analysis and risk assessment, see the dedicated threat model documentation.