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 = sys.stdin.buffer.read(64) # Read random IV from stdin
while True: # This is the main loop that processes each block until EOF
block = sys.stdin.buffer.read(64); 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