Recently, I have been exploring the XOR cipher in Python. I have two 'working' methods:
def XORcipher(plaintext, key):
output = ""
for character in plaintext:
for letter in key:
character = chr(ord(character) ^ ord(letter))
output += character
return output
and
def XORcipher2(plaintext, key):
from itertools import izip, cycle
return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in izip(plaintext, cycle(key)))
Both of these are able to encrypt a given string and decrypt back. What I can't seem to understand is why they give different results.
Using 23
as my key:
XORcipher = Usx!un!dobsxqu!uihr!ldrr`fd!trhof!YNS!dobsxquhno/
and
XORcipher2 = fAKF\\V\\P@JBGGZZA_VA@STWG@[]Uj|`W]QAKCFZ]]
If anyone could help me better understand these results, I would appreciate it!
For the first function, the last character in the key is ALWAYS used. This is because it assigns to character
in the for-loop every time you iterate, and throws away the last character until you iterate to the end.
For the second one, take plaintext "abc"
and key "42"
. "a"
is encrypted with "4"
. "b"
with "2"
and "c"
with "4"
again as you used cycle()
so it goes back to the beginning.
With the first function, it is "a"
with "2"
, "b"
with "2"
and "c"
with "2"
.
To expand the list comprehension, it would be this:
from itertools import izip, cycle
def list_comp(plaintext, key):
return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in izip(plaintext, cycle(key)))
def not_list_comp(plaintext, key):
temp = []
for x, y in izip(plaintext, cycle(key)):
temp.append(chr(ord(x) ^ ord(y)))
return ''.join(temp)
In the first case, for each letter of the plaintext, you are performing a xor with each character of the key. If you do XORcipher2('foo', 'bar')
the sequence is:
chr(ord('f') ^ ord('b')) # '\x04'
chr(ord('\x04') ^ ord('a')) # ´e´
chr(ord('e') ^ ord('r')) # '\x17'
output += '\x17'
chr(ord('o') ^ ord('b')) # '\r'
chr(ord('\r') ^ ord('a')) # 'e'
chr(ord('l') ^ ord('r')) # '\x1e'
output += '\x1e'
chr(ord('o') ^ ord('b'))
chr(ord('\r') ^ ord('a'))
chr(ord('l') ^ ord('r'))
output += '\x1e'
Probably what you really want is (assuming you want an alternative to the algorithm using itertools
):
def XORcipher(plaintext, key):
output = ""
for i, character in enumerate(plaintext):
output += chr(ord(character) ^ ord(key[i % len(key)]))
return output
Proof:
>>> XORcipher('foo', 'bar')
'\x04\x0e\x1d'
>>> XORcipher2('foo', 'bar')
'\x04\x0e\x1d'
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.