127 lines
3.1 KiB
Python
127 lines
3.1 KiB
Python
import base64
|
|
import hashlib
|
|
|
|
_version = 2
|
|
|
|
def execLayerSeq(layers, msg, key, decode=False):
|
|
# Make sure to reverse layers for decoding
|
|
if decode:
|
|
for layer in layers[::-1]:
|
|
msg, key = layer.decode(msg, key)
|
|
else:
|
|
for layer in layers:
|
|
msg, key = layer.encode(msg, key)
|
|
|
|
return msg
|
|
|
|
class BitshiftLayer(object):
|
|
|
|
def encode(self, file, key):
|
|
# Encode once to allow for binary data
|
|
file = base64.b85encode(file.encode())
|
|
output = ""
|
|
|
|
# Iterate and shift
|
|
for i, byte in enumerate(file):
|
|
# Mod the current bit
|
|
output += chr(byte ^ key[i%len(key) - 1])
|
|
|
|
# Mod the key
|
|
key.append(i%key[0])
|
|
|
|
# Final encoding
|
|
output = base64.b85encode(output.encode())
|
|
|
|
# Return as string
|
|
return (output.decode(), key)
|
|
|
|
def decode(self, file, key):
|
|
# Decode the file to shifted bytes
|
|
try:
|
|
file = base64.b85decode(file.strip().encode())
|
|
except:
|
|
return ("INVALID DATA", key)
|
|
output = ""
|
|
|
|
for i, byte in enumerate(file):
|
|
# Unmod the current byte
|
|
mod = byte ^ key[i%len(key) - 1]
|
|
if mod not in range(0x110000):
|
|
mod = 0
|
|
output += chr(mod)
|
|
|
|
# Mod the current key
|
|
key.append(i%key[0])
|
|
|
|
# Pull the resulting b64 back to binary if needed
|
|
# This may fail due to "incorrect padding" from a wrong key
|
|
# Just return some random text in this case for now
|
|
try:
|
|
output = base64.b85decode(output.encode()).decode()
|
|
except:
|
|
output = output
|
|
|
|
# Return as bytes
|
|
return (output, key)
|
|
|
|
class DataHeaderLayer(object):
|
|
|
|
def encode(self, file, key):
|
|
version = _version
|
|
fdl = len(file)
|
|
base = 85
|
|
|
|
# Construct header
|
|
header = []
|
|
header.append(version)
|
|
header += list((fdl & 0xFFFFFFFF).to_bytes(4, 'big'))
|
|
header += list((base & 0xFFFFFFFF).to_bytes(2, 'big'))
|
|
|
|
header = base64.b64encode(bytes(header)).decode()
|
|
|
|
return (header + file, key)
|
|
|
|
def decode(self, file, key):
|
|
# Header is the first 12 chars
|
|
header = file[:12]
|
|
file = file[12:]
|
|
try:
|
|
header = base64.b64decode(header.encode())
|
|
except:
|
|
return ("INVALID DATA", key)
|
|
header = bytes(header)
|
|
|
|
version = header[0]
|
|
|
|
# Conversion failed if the version is incorrect
|
|
if version != _version:
|
|
return ("INVALID DATA", key)
|
|
|
|
fdl = header[:5][1:]
|
|
fdl = int.from_bytes(fdl, byteorder='big')
|
|
|
|
# Chop off possible extra data
|
|
file = file[:fdl]
|
|
|
|
return (file, key)
|
|
|
|
def key2shifts(key: str) -> list:
|
|
output = []
|
|
|
|
for char in key:
|
|
output.append(ord(char))
|
|
|
|
return output
|
|
|
|
|
|
crypt_layers = [
|
|
DataHeaderLayer(),
|
|
BitshiftLayer()
|
|
|
|
]
|
|
|
|
def encode(file, key):
|
|
return execLayerSeq(crypt_layers, file, key)
|
|
|
|
def decode(file, key):
|
|
return execLayerSeq(crypt_layers, file, key, decode=True) |