#!/usr/bin/python3 import os, re, sys, random, hashlib import sys from utils import Sha1File useUrandom = True def xorIterable(data): """ Xors the iterable data, each element againts all the others """ def xorList(data): """ Computes [var0^var1^var2^... for (var0,var1,var2,...) in zip(*data)] It's more effecient this way instead of xoredData = bytearray() for d in zip(*data): acc = 0 for byte in d: acc ^= byte xoredData.append(acc) or this way: xoredData = bytearray([functools.reduce(operator.xor,d) for d in zip(*data)]) """ assert(len(data)>1) variables = ["var"+str(i) for i in range(len(data))] expressionGauche = "^".join(variables) expressionDroite = "("+",".join(variables)+")" expression = "["+expressionGauche + " for " + expressionDroite #XNOR: expression = "[~("+expressionGauche + ")%256 for " + expressionDroite expression += " in zip(*data)]" return eval(expression) assert(len(data)) xoredData = data[0] if len(data)==1 else xorList(data) # len=1 => xor([data]) == data return bytearray(xoredData) def resizeData(dataIterable, size=None): """ Expands dataIterable elements so they reach the maximum length, truncate to size Size can be either smaller than the smallest data or bigger than the bigest data """ if type(size) is int and size > 0: for data in dataIterable: del data[size:] lenMax = max([len(data) for data in dataIterable]+[size if size else 0]) for data in dataIterable: lenData = len(data) for i in range(lenData, lenMax): data.append(data[i%lenData]) def xorData(inData, count, size): """ returns count random xored data from inData Example: len(inData) == 3 and count == 4 So: clear = inData[0] XOR inData[1] XOR inData[2] xored = rand1, rand2, rand3, rand1 XOR rand2 XOR rand3 XOR clear return shuffle(xored) """ if type(inData) is not list: inData = [inData] # resize data, all of them of the same size resizeData(inData, size if type(size) is int else None) # From this point: # if size is min, then fine, data will be truncated # if size is max, then the looping was just made # if size is even (default), then the size were successfuly checked # Xors the input data if len(inData): clearContent = xorIterable(inData) elif type(size) is int: # No input data, thus generating random file clearContent = bytearray(getRandomBytes(size)) else: raise Exception("No input file specified, use --size with an integer to build a random file.") # If more than one outputs, adds random data, so that # All of it is random, except the last one with the data outData = [bytearray(getRandomBytes(len(clearContent))) for _ in range(count-1)] \ + [clearContent] # Xor the random data and the clear data and replaces the last element # containing the clear data by the result outData[-1] = xorIterable(outData) # Until now, all outs are kind of "keys" and the last one is "encrypted" # With shuffle, one won't tell the difference random.shuffle(outData) # Test #assert(xorIterable(inData)==clearContent) #assert(xorIterable(outData)==clearContent) return outData def getRandomBytes(size): """ Returns random bytes The non urandom way is slower and uses Python getrandbits(). """ if useUrandom: return os.urandom(size) else: return random.getrandbits(size<<2).to_bytes(size, sys.byteorder)