"""XOF Stream Cipher: encrypt and decrypt strings with shake256 - base64 version""" from base64 import b64encode, b64decode from hashlib import sha256, shake_256 from secrets import token_bytes from sys import argv def xor(b1: bytes, b2: bytes) -> bytes: """XOR two equal-length byte strings together.""" b3 = bytearray() for i in range(len(b1)): b3.append(b1[i] ^ b2[i]) return bytes(b3) def derive_key(*parts: list[bytes]) -> bytes: """Derives a one-time key from parts.""" pad = b''.join([sha256(p).digest() for p in parts]) return sha256(pad).digest() def encrypt(key: bytes, iv: bytes, plaintext: bytes) -> bytes: """Stream cipher encrypt.""" key_bytes = shake_256(derive_key(key, iv, b'enc')).digest(len(plaintext)) return xor(plaintext, key_bytes) def decrypt(key: bytes, iv: bytes, ciphertext: bytes) -> bytes: """Stream cipher decrypt.""" key_bytes = shake_256(derive_key(key, iv, b'enc')).digest(len(ciphertext)) return xor(ciphertext, key_bytes) def hmac(key: bytes, iv: bytes, message: bytes) -> bytes: """Create an hmac according to rfc 2104 specifications.""" # set up variables B = 136 ipad_byte = 0x36 opad_byte = 0x5c null_byte = 0x00 ipad = bytes([ipad_byte] * B) opad = bytes([opad_byte] * B) key = derive_key(key, iv, b'mac') # pad key with null bytes key = key + bytes([null_byte] * (B - len(key))) # compute and return the hmac partial = sha256(xor(key, ipad) + message).digest() return sha256(xor(key, opad) + partial).digest() def check_hmac(key: bytes, iv: bytes, message: bytes, mac: bytes) -> bool: """Check an hmac.""" # first compute the proper hmac computed = hmac(key, iv, message) # if it is the wrong length, reject if len(mac) != len(computed): return False # compute difference without revealing anything through timing attack diff = 0 for i in range(len(mac)): diff += mac[i] ^ computed[i] return diff == 0 def seal(key: bytes, plaintext: bytes, iv_size: int = 16) -> str: """Generate an iv, encrypt a message, and create an hmac all in one.""" iv = token_bytes(iv_size) ct = encrypt(key, iv, plaintext) return ( b64encode(iv) + b'.' + b64encode(ct) + b'.' + b64encode(hmac(key, iv, ct)) ).decode() def unseal(key: bytes, ciphertext: str) -> bytes: """Checks hmac, then decrypts the message.""" ciphertext = ciphertext.split('.') iv = b64decode(ciphertext[0]) ct = b64decode(ciphertext[1]) ac = b64decode(ciphertext[2]) if not check_hmac(key, iv, ct, ac): raise Exception('HMAC authentication failed') return decrypt(key, iv, ct) def main(args): """Main function for invoking as cli tool.""" # parse arguments mode = args[0] key = bytes(args[1], 'utf-8') text = args[2] iv = bytes(args[3], 'utf-8') if len(args) > 3 else None if mode == 'encrypt' or mode == 'e': if iv is None: return print('missing iv') print(b64encode(encrypt(key, iv, bytes(text, 'utf-8'))).decode()) elif mode == 'decrypt' or mode == 'd': if iv is None: return print('missing iv') print(decrypt(key, iv, b64decode(text)).decode()) elif mode == 'seal' or mode == 's': print(seal(key, bytes(text, 'utf-8'))) elif mode == 'open' or mode == 'o': print(unseal(key, text).decode()) else: print('unknown mode: ' + mode) if __name__ == '__main__': if len(argv) < 4: print( 'usage: ' + argv[0] + ' [encrypt|e|decrypt|d|seal|s|open|o] [key] [plaintext|ciphertext] [iv]' ) print('\tiv - necessary for encrypt and decrypt, but not seal') exit(1) main(argv[1:]) def license(): """Copyleft (c) 2026 k98kurz Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyleft notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ return license.__doc__