There's not much point using strong encryption for this algorithm because your program needs to access the decryption key, so if a determined user looks at your program's code they can figure out where your program stores the key and what it does with it. True, reading & understanding the code in a .pyc is somewhat more difficult than reading a .py, but it's still a lot easier than breaking good encryption.
Anyway, here's some code that does simple encryption / decryption of an arbitrary string. It works best if the string isn't very random, and it works better on long strings than short ones.
To encrypt, it first compresses the data using gzip, which not only can make the data smaller, it reduces the amount of pattern in the data, which improves the quality of the encryption. It then XORs the integer value of each byte (in the zipped data) with a series of random integers in the range(256) to produce the encoded version.
The XOR operation is symmetrical in this sense: if a = b ^ c, then b = a ^ c (and also c = a ^ b). So to undo XORing, we just have to apply it again.
So to decrypt, we simply reverse the encryption process. First we XOR the encoded version with the same series of random integers we used to encode it and then we unzip it.
If the amount of data you want to hide is fairly small or highly random you can skip the zipping / unzipping steps, since XORing with a series of random bytes from a good pseudo-random number generator (like the Mersenne Twister that Python uses by default) is actually quite a good form of encryption, assuming that the attacker doesn't know both the algorithm that generates your random numbers and the seed for the random sequence. (Note that Python's random.seed() accepts any hashable object as the seed: eg an int, a string, or even a tuple).
The code below uses a calendar of the year as test data to make it easy to see that the process works.
gzipcrypt.py
#! /usr/bin/env python
''' Simple encryption
Encode by gziping then XORing bytes with a pseudorandom stream
Decode by XORing and then unziping with the same pseudorandom stream
Written by PM 2Ring 2014.09.28
'''
import random
import sys
from calendar import calendar
def randoms(seed):
random.seed(seed)
while True:
yield random.randint(0, 255)
def xorcrypt(data, key):
return str(bytearray(d ^ k for d, k in
zip(bytearray(data), randoms(key))))
def zipcrypt(data, key):
return xorcrypt(data.encode('zlib_codec'), key)
def decryptzip(data, key):
return xorcrypt(data, key).decode('zlib_codec')
def main():
#Test encryption & decryption
key = sys.argv[1] if len(sys.argv) > 1 else 42
data = calendar(2014)
print data
print 'Length:', len(data), '\n'
#code = xorcrypt(data, key)
code = zipcrypt(data, key)
print `code`
print 'Length:', len(code), '\n'
#newd = xorcrypt(code, key)
newd = decryptzip(code, key)
print newd
print newd == data
if __name__ == '__main__':
main()
Edit
Here's a simpler version of xorcrypt() that doesn't use a generator function, bytearray()
, or list comprehensions that's hopefully more comprehensible.
def xorcrypt1(data, key):
random.seed(key)
code = []
for ch in data:
n = ord(ch)
n = n ^ random.randint(0, 255)
code.append(chr(n))
return ''.join(code)