简体   繁体   中英

XOR Cipher in Python: Two Methods With Two Results?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM