Xit is a Python program that applies XOR to files. It's not designed to be used for cryptographic stuff, unless you know what you're doing or you want to hide from your little sister. https://sbgodin.fr/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

116 lines
3.6 KiB

#!/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)