Skip to main content
Block Ciphers Intermediate

The Twofish Algorithm

Twofish was Bruce Schneier's answer to Blowfish's biggest weakness, and one of the five finalists in the competition that ultimately chose AES. Learn how its Feistel network, PHT mixing, and key-dependent S-boxes work.

PL
Pashalis Laoutaris
August 4, 2026
8 min read

Interactive Twofish Visualizer

🔐 Twofish Encryption

6
Simplified key schedule for demo purposes. See the post for why real Twofish's MDS-based S-boxes aren't reproduced here.
Enter text and click a button to start!
Round: / 16
R0
R1
R2
R3
Click Encrypt to run all 16 rounds.

The Twofish Algorithm

Introduction

Twofish is Bruce Schneier’s direct successor to Blowfish, designed in 1998 by a team including Schneier, John Kelsey, Doug Whiting, David Wagner, Chris Hall, and Niels Ferguson. It was submitted to NIST’s AES competition and made it all the way to the final round of five candidates. It lost out only to Rijndael (AES), but never suffered any practical cryptanalytic break of its own. Twofish remains free, unpatented, and available for anyone to use.

Table of Contents

Fixing Blowfish’s Weakness

Blowfish’s biggest structural flaw was its 64-bit block size, which leaves it exposed to birthday-bound attacks (like Sweet32) once enough data has been encrypted under one key. Twofish’s most immediate design goal was simple: keep Blowfish’s spirit of fast, software-friendly, key-dependent design, but move to a full 128-bit block, matching what NIST required for any AES candidate.

How Twofish Works

Twofish operates on 128-bit blocks, split into four 32-bit words rather than the two 32-bit halves a classic Feistel cipher like DES or Blowfish uses. It runs 16 rounds, and each round:

  1. Passes two of the four words through a key-dependent function called g (itself built from four key-dependent 8-bit S-boxes combined through a fixed MDS (Maximum Distance Separable) matrix multiplication in a finite field, similar in spirit to AES’s MixColumns).
  2. Combines the two g outputs using a Pseudo-Hadamard Transform (PHT) (described below) along with two of the round’s key material words.
  3. XORs and rotates the results into the other two words.
  4. Swaps word pairs for the next round, much like the swap in a classical two-branch Feistel network, just generalized to four words instead of two.

Input and output whitening, XORing extra key material both before the first round and after the last, adds further protection against certain classes of attack that specifically target the outer rounds of a cipher.

Interactive Visualizer

The visualizer above demonstrates Twofish’s genuine structural shape: the four-word network, the PHT mixing step, and the rotate/XOR/swap pattern across all 16 rounds. As with the Blowfish demo, the underlying S-boxes here are derived from your key using a simplified method rather than Twofish’s real Reed-Solomon and MDS-matrix-based key schedule. That schedule is too involved to faithfully reproduce in an in-browser teaching tool.

The Pseudo-Hadamard Transform

One of Twofish’s more distinctive design elements is the PHT, a fast, simple mixing operation applied to the two outputs (call them T0 and T1) of the g function each round:

F0 = (T0 + T1) mod 2³²
F1 = (T0 + 2·T1) mod 2³²

PHT provides strong diffusion, spreading the influence of each input bit widely, using only addition. That’s extremely cheap on ordinary CPUs compared to more complex diffusion methods, continuing Twofish’s (and Blowfish’s) design philosophy of fast software performance over exotic mathematical machinery.

Python Implementation

This mirrors the visualizer’s own simplified approach described above: the genuine four-word network, g-function-plus-PHT mixing, and rotate/XOR/swap pattern, but with an S-box and key words derived from a fast xorshift PRNG seeded by the key, not Twofish’s real Reed-Solomon and MDS-matrix-based key schedule:

MASK32 = 0xFFFFFFFF

def tf_make_tables(key_bytes):
    seed = 0x9E3779B9
    for b in key_bytes:
        seed = ((seed ^ b) * 0x85EBCA6B) & MASK32
    def next_word():
        nonlocal seed
        seed = (seed ^ ((seed << 13) & MASK32)) & MASK32
        seed ^= seed >> 17
        seed = (seed ^ ((seed << 5) & MASK32)) & MASK32
        return seed
    sbox = [next_word() & 0xFF for _ in range(256)]
    k = [next_word() for _ in range(40)]
    return sbox, k

def rotl32(x, n):
    return ((x << n) | (x >> (32 - n))) & MASK32

def rotr32(x, n):
    return ((x >> n) | (x << (32 - n))) & MASK32

def tf_g(x, sbox):
    b0, b1, b2, b3 = x & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF
    y0, y1, y2, y3 = sbox[b0], sbox[(b1 + 1) & 0xFF], sbox[(b2 + 2) & 0xFF], sbox[(b3 + 3) & 0xFF]
    return y0 | (y1 << 8) | (y2 << 16) | (y3 << 24)

def twofish_toy_encrypt(key_bytes, plaintext_bytes):
    """Same four-word, PHT-mixing shape as real Twofish; a toy key schedule, not the real one."""
    sbox, k = tf_make_tables(key_bytes)
    words = [int.from_bytes(plaintext_bytes[i:i + 4], 'big') for i in range(0, 16, 4)]
    R0, R1, R2, R3 = (w ^ k[i] for i, w in enumerate(words))

    for round_ in range(16):
        T0 = tf_g(R0, sbox)
        T1 = tf_g(rotl32(R1, 8), sbox)
        F0 = (T0 + T1 + k[8 + 2 * round_]) & MASK32
        F1 = (T0 + 2 * T1 + k[9 + 2 * round_]) & MASK32
        new_R2 = rotr32(R2 ^ F0, 1)
        new_R3 = rotl32(R3, 1) ^ F1
        R0, R1, R2, R3 = new_R2, new_R3, R0, R1

    R0, R1, R2, R3 = R2, R3, R0, R1  # undo the final swap
    R0 ^= k[4]; R1 ^= k[5]; R2 ^= k[6]; R3 ^= k[7]

    return b''.join(w.to_bytes(4, 'big') for w in (R0, R1, R2, R3))

if __name__ == "__main__":
    key = bytes.fromhex("0123456789ABCDEFF0E1D2C3B4A59687")
    plaintext = bytes.fromhex("00112233445566778899AABBCCDDEEFF")

    ciphertext = twofish_toy_encrypt(key, plaintext)

    print(f"Key:        {key.hex().upper()}")
    print(f"Plaintext:  {plaintext.hex().upper()}")
    print(f"Ciphertext: {ciphertext.hex().upper()}")

This reproduces exactly what the visualizer above computes for the same key and plaintext, since it’s a direct port of the visualizer’s own JavaScript, down to the same PRNG mixing constants used in the Blowfish guide’s toy implementation.

Limitations

This code is explicitly a toy, in the same way, and for the same reason, the visualizer above is:

  • Not real Twofish. As the Interactive Visualizer note above explains, reproducing Twofish’s genuine Reed-Solomon-code-based, MDS-matrix key schedule is too involved for this teaching context. This code demonstrates the four-word Feistel-style network and PHT mixing shape Twofish uses, not byte-for-byte-compatible Twofish ciphertext.
  • The PRNG is not cryptographically secure. tf_make_tables uses the same simple xorshift generator as the Blowfish toy implementation, for speed and code brevity, not because it has any of the statistical or security properties a real key schedule needs.
  • Same scope limits as every toy block cipher on this site. Single 16-byte block only, no mode of operation, no padding, no timing-attack hardening.
  • Never use this, or reproduce real Twofish by hand, in production. Genuine, fully-specified Twofish has never been practically broken (see Security Status below), but that security guarantee only applies to the real algorithm with its real key schedule, not this simplified stand-in.

Security Status

No practical attack has ever broken full 16-round Twofish. Cryptanalysts found reduced-round attacks (breaking artificially weakened versions with fewer rounds) during the AES competition’s public analysis period. That’s exactly the kind of scrutiny the process was designed to surface, and Twofish held up well enough to remain a finalist throughout.

Twofish vs. AES: Why Rijndael Won

NIST’s final decision between the five AES finalists (MARS, RC6, Rijndael, Serpent, and Twofish) came down to a combination of security margin, performance across many different hardware platforms, and implementation simplicity. Rijndael (AES) was chosen primarily for its excellent performance on both high-end and constrained hardware and its comparatively simple, elegant algebraic structure. Twofish was considered highly secure (with some analysts, including Schneier’s own team, arguing it had a larger security margin than Rijndael). But its more intricate key schedule made it somewhat slower to set up new keys and more complex to implement correctly across diverse platforms.

FAQ

Is Twofish still considered secure?

Yes. No practical attack against full-round Twofish exists. It remains a solid choice, though AES’s ubiquity, standardization, and hardware acceleration (AES-NI) make AES the default choice for nearly all new systems.

Why didn’t Twofish win the AES competition?

It wasn’t for lack of security. NIST’s decision weighed performance across many platforms and implementation simplicity alongside pure cryptographic strength, and Rijndael’s simpler structure and better all-around performance profile won out.

What’s the Pseudo-Hadamard Transform used for?

It’s Twofish’s core diffusion step: mixing the outputs of the g-function each round using only fast addition operations, spreading each input bit’s influence widely across the block with minimal computational cost.

How does Twofish compare to Blowfish?

Twofish uses a 128-bit block (fixing Blowfish’s Sweet32-style vulnerability from its 64-bit block), a four-word network instead of a classic two-half Feistel structure, and a more sophisticated (though still fast) key-dependent S-box generation process.

Is Twofish free to use?

Yes, like Blowfish, Twofish was placed in the public domain by its designers and remains completely free and unpatented for any use.

References

  1. Schneier, B. et al. “Twofish: A 128-Bit Block Cipher.” AES submission, 1998. Available at: https://www.schneier.com/academic/twofish/

  2. Wikipedia. “Twofish.” Available at: https://en.wikipedia.org/wiki/Twofish

  3. NIST. “Report on the Development of the Advanced Encryption Standard (AES).” 2001. The official comparison of all five AES finalists.