XOR
The XOR cipher is very useful in cryptography. The XOR (Exclusive OR) is a logical operator with two inputs:
| a | b | ^ |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
So, with XOR, we have:
ciphertext = plaintext ^ key
But, we can easily reverse it, following the rule above, we have:
plaintext = ciphertext ^ key
or
key = plaintext ^ ciphertext
To use the XOR in Python, we use the operator ^. The simple python below show to us the implementation of the XOR:
$ cat xor.py
def xor(secret, key):
new_key = key
index = 0
while len(new_key) < len(secret):
new_key = new_key + key[index]
index = (index + 1) % len(key)
return "".join([chr(ord(secret_c) ^ ord(new_key_c)) for (secret_c, new_key_c) in zip(secret, new_key)])
def to_bin(a):
return ' '.join(format(ord(x), '08b') for x in a)
key = "a"
message = "hello"
bkey = to_bin(key)
bmsg = to_bin(message)
enc = xor(message, key)
benc = to_bin(enc)
print(f"KEY : {key}")
print(f"MSG : {message}")
print(f"BIN KEY: {bkey}")
print(f"BIN MSG: {bmsg}")
print(f"BIN ENC: {benc}")
dec = xor(enc, key)
bdec = to_bin(dec)
print(f"BIN DEC: {bdec}")
print(f"DEC: {dec}")
$ python3 xor.py
KEY : a
MSG : hello
BIN KEY: 01100001
BIN MSG: 01101000 01100101 01101100 01101100 01101111
BIN ENC: 00001001 00000100 00001101 00001101 00001110
BIN DEC: 01101000 01100101 01101100 01101100 01101111
DEC: hello
XOR example
In the example below, we XORing the data with PyCryptodome. In this example, we can reverse the XOR without the key, because we know the first plaintext data (FLAG) and with that, we can reverse it:
$ cat xor.py
import os
from Crypto.Util.strxor import strxor
import codecs
data = "FLAG{+tteqPggPy3CVNZpBayoG5487U7QfNnK/+cGDPBMjekGHmt435Kz8kO/bayH7DVi}".encode()
## XOR the data
key = os.urandom(4) * 20
c = strxor(data, key[:len(data)])
print(f"Data XORed: {c.hex()}")
## Reverse the XOR
### Find the key
key = "FLAG".encode() * 60
output = codecs.decode(c.hex(), "hex") # Important, we need to convert from hex
# Otherwise: output = bytes.fromhex(c.hex())
print(f"Data decoded from hex: {output}")
nkey = strxor(output, key[:len(output)])
print(f"Key: {nkey[:4]}")
### Get the key
k = nkey[:4] * 80
### Decoding
data = strxor(output, k[:len(output)])
#print(data.decode("latin1"))
print(f"Data decoded: {data.decode('utf8')}")
$ python3 xor.py
Data XORed: b86390b78504a5849b5e8197997fa8c3bd799faa8e6db0899168e4c4c61884c7af499f9eb500fa93b96b81b2b345b49bb967bc84ca1ce4bb8417babfd14db089b61895a69752
Data decoded from hex: b'\xb8c\x90\xb7\x85\x04\xa5\x84\x9b^\x81\x97\x99\x7f\xa8\xc3\xbdy\x9f\xaa\x8em\xb0\x89\x91h\xe4\xc4\xc6\x18\x84\xc7\xafI\x9f\x9e\xb5\x00\xfa\x93\xb9k\x81\xb2\xb3E\xb4\x9b\xb9g\xbc\x84\xca\x1c\xe4\xbb\x84\x17\xba\xbf\xd1M\xb0\x89\xb6\x18\x95\xa6\x97R'
Key: b'\xfe/\xd1\xf0'
Data decoded: FLAG{+tteqPggPy3CVNZpBayoG5487U7QfNnK/+cGDPBMjekGHmt435Kz8kO/bayH7DVi}