Welcome, guest | Sign In | My Account | Store | Cart

A very simple keyword encryption algorithm. Produces an encrypted string the same size as the orginal. Not very strong encryption but good enough for lightweight stuff. Works on bytes so could be used for binary streams if converted to and from strings - maybe!

Python, 85 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#PEcrypt - use a string key to encrypt/decrypt another string
#        - Simon Peverett - January 2004

class PEcrypt:
    """
    PEcrypt - very, very simple word key encryption system
              uses cyclic XOR between the keyword character
              bytes and the string to be encrypted/decrypted.
              Therefore, the same function and keyword will
              encrypt the string the first time and decrypt
              it if called on the encrypted string.
    """

    def __init__(self, aKey):
        """
        Initialise the class with the key that
        is used to encrypt/decrypt strings
        """
        self.key = aKey

        # CRC can be used to validate a key (very roughly)
        # if you store the CRC from a previous keyword
        # and then compare with a newly generated one and
        # they are the same then chances are the keyword
        # is correct - only a single byte so not that reliable
        self.crc = 0    
        for x in self.key:
            intX = ord(x)
            self.crc = self.crc ^ intX


    def Crypt(self, aString):
        """
        Encrypt/Decrypt the passed string object and return
        the encrypted string
        """
        kIdx = 0
        cryptStr = ""   # empty 'crypted string to be returned

        # loop through the string and XOR each byte with the keyword
        # to get the 'crypted byte. Add the 'crypted byte to the
        # 'crypted string
        for x in range(len(aString)):
            cryptStr = cryptStr + \
                       chr( ord(aString[x]) ^ ord(self.key[kIdx]))
            # use the mod operator - % - to cyclically loop through
            # the keyword
            kIdx = (kIdx + 1) % len(self.key)

        return cryptStr

if __name__ == "__main__":

    def strToHex(aString):
        hexStr = ""
        for x in aString:
            hexStr = hexStr + "%02X " % ord(x)

        return hexStr
    
    # self test routine

    print "\nTesting PEcrypt!"
    print "----------------\n"

    keyStr = "This is a key"
    testStr = "The quick brown fox jumps over the lazy dog!"

    print "\nString : ", testStr
    print "in hex : ", strToHex(testStr)
    print "key    : ", keyStr

    pe = PEcrypt(keyStr)  # generate the PEcrypt instance
    
    print "\nPEcrypt CRC = %02X" % pe.crc

    testStr = pe.Crypt(testStr)
    print "\nEncrypted string"
    print "Ascii  : ", testStr
    print "Hex    : ", strToHex(testStr)

    testStr = pe.Crypt(testStr)
    print "\nDecrypted string"
    print "Ascii  : ", testStr
    print "Hex    : ", strToHex(testStr)
            
    

You might want to use this for simple file or comms encryptions.

I had a look at the available encryption modules for Python (see section 15. Cryptographic services of the 2.3.2 on-line documentation) and they were too "heavy". I also looked here in the ASPN cookbook and none of the recipes were quite what I wanted.

Took 30-mins to knock up.

4 comments

Matt R 20 years, 1 month ago  # | flag

XOR-encryption is very weak. > You might want to use this for simple file or comms encryptions.

You probably don't want to use this -- encryption with XOR is notoriously weak and methods for ciphertext-only cryptanalysis have been around for centuries: do a Google for "Vigenere", a similar scheme based on the 26-letter alphabet.

Lots of good algorithms are at

http://www.amk.ca/python/code/crypto.html

. If you really need a simple cipher, consider implementing something like RC5 or TEA/XTEA which can be described in just a few lines of code.

(Incidentally, it's faster to build up a string by sticking the pieces in a list and doing a "".join(list), rather than by repeated concatenation.)

Justin Shaw 20 years, 1 month ago  # | flag

XOR and the one-time-pad. XOR encryption is what is used in the "perfect" encryption scheme of the one-time-pad. Use this method with a random key the same length as the message and you have the perfect code -- Just remember to never re-use the key!

Raymond Hettinger 20 years, 1 month ago  # | flag

Improvements. As a recipe for others to learn from, the original code leaves much to be desired. Foremost, don't build strings with "+" because every addition builds a whole new string (for longer inputs, this O(n**2) behavior is a disaster).

Also, try to not bury the code in comments -- python code is capable of "saying what it does". IOW, don't make something hard out of something simple.

Encryption-wise, it is not wise to transmit the key's CRC because it shrinks the key search space by an order of magnitude.

Instead of Vignere, using the random module is equally pithy but is much harder to crack. If you want the make this revised version even stronger without much effort, then compress the input string with: aString = zlib.compress(aString).

Here is an improved version that is still in the spirit of the original (kept short and sweet instead of complex and secure).

import random

class TinyCode:
    def __init__(self, aKey):
        self.rng = random.Random(aKey)

    def Crypt(self, aString):
        rand = self.rng.randrange
        crypted = [chr(ord(elem)^rand(256)) for elem in aString]
        return ''.join(crypted)

if __name__ == "__main__":

    def strToHex(aString):
        hexlist = ["%02X " % ord(x) for x in aString]
        return ''.join(hexlist)

    keyStr = "This is a key"
    testStr = "The quick brown fox jumps over the lazy dog!"

    print "String:", testStr
    testStr = TinyCode(keyStr).Crypt(testStr)
    print "Encrypted string:", testStr
    print "Hex    : ", strToHex(testStr)
    testStr = TinyCode(keyStr).Crypt(testStr)
    print "Decrypted string:", testStr
Raymond Hettinger 20 years, 1 month ago  # | flag

One other thought. Since the conversion is done is a single pass, there in no need to make this a class. Here is a short function that does the trick and adds compression:

import random
import zlib

def tinycode(key, text, reverse=False):
    rand = random.Random(key).randrange
    if not reverse:
        text = zlib.compress(text)
    text = ''.join([chr(ord(elem)^rand(256)) for elem in text])
    if reverse:
        text = zlib.decompress(text)
    return text

if __name__ == "__main__":

    def strToHex(aString):
        hexlist = ["%02X " % ord(x) for x in aString]
        return ''.join(hexlist)

    keyStr = "This is a key"
    testStr = "which witch had which witches wrist watch"

    print "String:", testStr
    testStr = tinycode(keyStr, testStr)
    print "Encrypted string:", testStr
    print "Hex    : ", strToHex(testStr)
    testStr = tinycode(keyStr, testStr, reverse=True)
    print "Decrypted string:", testStr
Created by Simon Peverett on Tue, 3 Feb 2004 (PSF)
Python recipes (4591)
Simon Peverett's recipes (3)

Required Modules

  • (none specified)

Other Information and Tasks