Mini SHA-256 CBC feistel cipher

I was playing around with code screenshot tools that make those fancy screenshots for Twitter. I wanted to post a dense chunk of code that does something relatively useful, so I made this code to encrypt and decrypt data using SHA-256 as a Block cipher. It’s made by using SHA-256 as the round function of a Feistel network.

Probably not a good idea to use for anything, I haven’t even used this myself aside from some “Hello, world!” style testing.

#!/usr/bin/env python3
import sys, hashlib, os # Tiny Feistel cipher that uses SHA-256 in CBC mode
encrypt, key = sys.argv[1] == "encrypt", sys.argv[2].encode("utf-8") # Params
if encrypt: prev = os.urandom(64); sys.stdout.buffer.write(prev) # Random IV
if not encrypt: prev = # Read random IV from stdin
while True: # This is the main loop that processes each block until EOF
  block =; p=block # Read up to one block (64 bytes)
  if not block: break # Exit the program when there are no more blocks
  while len(block) != 64: block += b"\x00" # Pad block to 64 bytes with nulls
  if encrypt: block = bytes([x^y for x,y in zip(block, prev)]) # For CBC mode
  for r in range(32)[::(-1,1)[encrypt]]: # Decrypt is the reverse of encrypt
    rk = hashlib.sha256(key+r.to_bytes(2, "big")).digest() # Generate round key
    f = hashlib.sha256(rk+block[32:]).digest() # Transform right part of block
    block = block[32:] + bytes([x^y for x, y in zip(block[:32], f)]) # Feistel
  block = block[32:] + block[:32] # Swap the left/right parts of the block
  if encrypt: prev = block # Keep previous ciphertext block for CBC mode
  if not encrypt: block = bytes([x^y for x,y in zip(block,prev)]);prev=p # CBC
  sys.stdout.buffer.write(block) # Write encrypted/decrypted block to stdout