Fun with Simple Encryption

Michael Shepanski
November 20, 2011
Nov
20

Every programmer loves thinking they can come up with some crafty way to encrypt a string. Having no background in encryption, of course I think the same thing. The hard part is coming up with something that is also easily decryptable, but clever enough that the average joe can't figure it out. Here is my attempt.

#!/usr/bin/env python
"""
Fun with simple encryption. The previous encrypted letter + the current unencrypted letter
indexes determine the offset to encrypt the next letter. Each string has len(ALPHA)
encryption possibilities. The initial key to decrypt is appended to the end of the
encrypted string.

http://www.reddit.com/r/codes/comments/ml480/i_wrote_a_simple_encryptiondecryption_function/
"""
import random
from optparse import OptionParser
ALPHA = 'abcdefghijklmnopqrstuvwxyz '

class MyCrypt(object):

    def encrypt(self, str):
        """ Return all possible encryptions of the specified string. """
        results = []
        for key in ALPHA:
            result = ""
            offset = ALPHA.index(key)
            for char in str:
                result += ALPHA[(ALPHA.index(char) + offset) % len(ALPHA)]
                offset = ALPHA.index(char) + ALPHA.index(result[-1])
            result += key    # Append initial key to the end
            # Dont consider it a result if it begins or ends with a space
            if not result.startswith(" ") and not result.endswith(" "):
                results.append(result)
        return results

    def decrypt(self, str):
        """ Decrypt the specified string. """
        result = ""
        key = str[-1]
        offset = ALPHA.index(key)
        for char in str[:-1]:
            result += ALPHA[(ALPHA.index(char) - offset) % len(ALPHA)]
            offset = ALPHA.index(char) + ALPHA.index(result[-1])
        return result


if __name__ == '__main__':
    parser = OptionParser()
    parser.add_option("-e", "--encrypt", help="Encrypt the specified string.")
    parser.add_option("-a", "--all", action='store_true', default=False, help="Show all possible encryption strings.")
    parser.add_option("-d", "--decrypt", help="Decrypt the specified string.")
    options, args = parser.parse_args()
    mycrypt = MyCrypt()
    # Run the Encrypt command
    if options.encrypt:
        print ">>> %s" % options.encrypt
        results = mycrypt.encrypt(options.encrypt)
        if not options.all: print "<<< %s" % random.choice(results)
        else: print "\n".join("%2s: %s" % (i,results[i]) for i in xrange(len(results)))
    # Run the Decrypt command
    elif options.decrypt:
        print ">>> %s" % options.decrypt
        print "<<< %s" % mycrypt.decrypt(options.decrypt)

I simplified it a bit, and only allowed lower case letters and spaces for this example, but it seems to work pretty well. What's going on here, is a simple letter shift. The index of the current letter plus the index of the previous encrypted letter decide the amount to shift. So it sorta grows on itself. If you mess up one letter, in the decryption you'll screw up the rest of the cipher. Since the current letter encryption depends on the previous letter, I need some sorta key to decrypt the first letter. I randomly pick one during encryption and tack it on the end of the encrypted result. This also means that there are len(ALPHA) possible way to encrypt any string.

I believe if I were to add additional characters I wanted to use in ALPHA, and also jumble the letters a bit, it would be much harder to track what exactly is going on.


comments powered by Disqus