> For the complete documentation index, see [llms.txt](https://cajac.gitbook.io/ctf-notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cajac.gitbook.io/ctf-notes/crypto/generic-crypto/xor-encryption.md).

# XOR-encryption

There are four main properties we should consider when we solve challenges using the XOR operator:

* Commutative: A ⊕ B = B ⊕ A&#x20;
* Associative: A ⊕ (B ⊕ C) = (A ⊕ B) ⊕ C&#x20;
* Identity: A ⊕ 0 = A&#x20;
* Self-Inverse: A ⊕ A = 0

Commutative means that the order of the XOR operations is not important. Associative means that a chain of operations can be carried out without order (we do not need to worry about brackets). The identity is 0, so XOR with 0 "does nothing", and lastly something XOR'd with itself always returns zero.

## In Python

### Hex-strings

If the length of the `key` is equal to the length of the `msg` you can use the [Zip-function](https://docs.python.org/3/library/functions.html#zip) in standard Python.

```python
#!/usr/bin/python
# -*- coding: latin-1 -*-

def xor(var, key):
    return bytes(a ^ b for a, b in zip(var, key))

msg = bytes.fromhex("44585d6b2368737c65252166234f20626d")
key = bytes.fromhex("1010101010101010101010101010101010")

print(xor(msg, key).decode())
```

Slightly the same but with single-byte key

```python
#!/usr/bin/python
# -*- coding: latin-1 -*-

def xor(var, key):
    return bytes(a ^ b for a, b in zip(var, key))

msg = bytes.fromhex("44585d6b2368737c65252166234f20626d")
key = bytes.fromhex("10") * len(msg)

print(xor(msg, key).decode())
```

And with a multi-byte ASCII-key

```python
#!/usr/bin/python
# -*- coding: latin-1 -*-

from itertools import cycle

def xor(var, key):
    return bytes(a ^ b for a, b in zip(var, cycle(key)))

msg = bytes.fromhex("1e 00 10 01 04 13 16 45 17 1d 45 11 1d 06 11 0b 15 00 53 12 0a 06 0d 54 2b 2a 31 53")
key = "secret".encode()

print(xor(msg, key).decode())
```

With pwntools and the [unhex](https://docs.pwntools.com/en/stable/util/fiddling.html#pwnlib.util.fiddling.unhex) and [xor](https://docs.pwntools.com/en/stable/util/fiddling.html#pwnlib.util.fiddling.xor) functions

```python
#!/usr/bin/env python3

from pwn import unhex, xor

msg = unhex("44585d6b2368737c65252166234f20626d")
key = unhex("1010101010101010101010101010101010")

print(xor(msg, key).decode())
```

Same but with single-byte key

```python
#!/usr/bin/env python3

from pwn import unhex, xor

msg = unhex("44585d6b2368737c65252166234f20626d")
key = b"\x10"

print(xor(msg, key).decode())
```

Note that pwntools [xor](https://docs.pwntools.com/en/stable/util/fiddling.html#pwnlib.util.fiddling.xor) function supports more than two arguments and you can do this

```python
#!/usr/bin/env python3

from pwn import xor, unhex

KEY1 = unhex("a6c8b6733c9b22de7bc0253266a3867df55acde8635e19c73313")
XOR_K2_K3 = unhex("c1545756687e7573db23aa1c3452a098b71a7fbf0fddddde5fc1")

flag = xor(unhex("04ee9855208a2cd59091d04767ae47963170d1660df7f56f5faf"), KEY1, XOR_K2_K3).decode()
print(flag)
```

## Online Services

You can also decode XOR-encryption with one of these online services:

CrypTool-Online: <https://www.cryptool.org/en/cto/xor/>

CyberChef: <https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')XOR(%7B'option':'Hex','string':''%7D,'Standard',false)>

dcode.fr: <https://www.dcode.fr/xor-cipher>

md5decrypt.net: <https://md5decrypt.net/en/Xor/>

## Resources

Exclusive or - Wikipedia: <https://en.wikipedia.org/wiki/Exclusive_or>

XOR cipher - Wikipedia: <https://en.wikipedia.org/wiki/XOR_cipher>
