lightweight encryption of text file

Steven D'Aprano steve at
Sun Jan 10 16:30:12 CET 2010

On Sun, 10 Jan 2010 09:59:31 +0100, Daniel Fetchinson wrote:

> Thanks, this looks very simple too, but where is the decryption code?
> Wikipedia seems to suggest that encryption and decryption are both the
> same but running crypt on the output of crypt doesn't give back the
> original string. So probably I'm misunderstanding something.

Yes, the nature of a stream cipher :)

What you're probably doing is what I did, before I had my Aha!!! moment:

>>> arc = arcfour("password")
>>> plaintext = "attack at dawn"
>>> ciphertext = arc.crypt(plaintext)
>>> print plaintext; print ciphertext.encode("hex").upper()
attack at dawn
>>> x = arc.crypt(ciphertext)
>>> assert x == plaintext
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
>>> x
'\x16\xf7\xf1\xcc\xda\xb5\xe0\xbf\x0b\x13 bF\x8f'

So what's going on? Consider:

Because Arcfour uses xor for the encryption step, decryption is exactly 
the same. So you only need one method to do both.

But because Arcfour stores state, calling it twice in a row doesn't give 
the same result:

>>> arc.crypt("hello").encode("hex").upper()
>>> arc.crypt("hello").encode("hex").upper()

Arcfour is a stream cipher. When you call it twice on two different 
strings, that is logically equivalent to calling it once on a single long 
string made up of concatenating the two strings together. Each time you 
encrypt a single character, the internal state ("self.s") changes. To 
undo the change, you need the same state. The easiest way to do this is 
by creating a new instance:

>>> encrypter = arcfour("password")
>>> decrypter = arcfour("password")
>>> plaintext = "attack at dawn"
>>> ciphertext = encrypter.crypt(plaintext)
>>> assert decrypter.crypt(ciphertext) == plaintext

So long as the two instances stay in lock-step (every time you use up a 
byte from the keystream in one, you do the same in the other) you can use 
one to decrypt the output of the other. It doesn't even really matter 
which one you use:

>>> x = decrypter.crypt("Nobody expects the Spanish Inquisition!!!")
>>> encrypter.crypt(x)
'Nobody expects the Spanish Inquisition!!!'

In summary: use the arcfour class to create a stream. If you are just 
encrypting, or just decrypting, you can use one stream, or as many 
streams as you like, using different keys. But to do both, you need two 
streams, initiated with the same key, and kept in lockstep.

The advantage of a stream cipher is that you can encrypt a text without 
needing all the text at once, and then decrypt it the same way:

>>> output = []
>>> output.append(encrypt.crypt("abcdefghi"))
>>> output.append(encrypt.crypt("jklmno"))
>>> output.append(encrypt.crypt("p"))
>>> output.append(encrypt.crypt("qrstuvwxyz"))
>>> output = ''.join(output)
>>> plain = []
>>> plain.append(decrypt.crypt(output[0:20]))
>>> plain.append(decrypt.crypt(output[20:24]))
>>> plain.append(decrypt.crypt(output[24:]))
>>> ''.join(plain)


More information about the Python-list mailing list