Tags: crypto
Rating: 5.0
# pseudo-key
Author: [roerohan](https://github.com/roerohan)
Basic reversing challenge.
# Requirements
- Python
# Source
- [pseudo-key.py](./pseudo-key.py)
- [pseudo-key-output.txt](./pseudo-key-output.txt)
```
Keys are not always as they seem...
Note: Make sure to wrap the plaintext with flag{} before you submit!
```
```python
#!/usr/bin/env python3
from string import ascii_lowercase
chr_to_num = {c: i for i, c in enumerate(ascii_lowercase)}
num_to_chr = {i: c for i, c in enumerate(ascii_lowercase)}
def encrypt(ptxt, key):
ptxt = ptxt.lower()
key = ''.join(key[i % len(key)] for i in range(len(ptxt))).lower()
ctxt = ''
for i in range(len(ptxt)):
if ptxt[i] == '_':
ctxt += '_'
continue
x = chr_to_num[ptxt[i]]
y = chr_to_num[key[i]]
ctxt += num_to_chr[(x + y) % 26]
return ctxt
with open('flag.txt') as f, open('key.txt') as k:
flag = f.read()
key = k.read()
ptxt = flag[5:-1]
ctxt = encrypt(ptxt,key)
pseudo_key = encrypt(key,key)
print('Ciphertext:',ctxt)
print('Pseudo-key:',pseudo_key)
```
# Exploitation
The most important function is of course, the `encrypt` function.
```
def encrypt(ptxt, key):
ptxt = ptxt.lower()
key = ''.join(key[i % len(key)] for i in range(len(ptxt))).lower()
ctxt = ''
for i in range(len(ptxt)):
if ptxt[i] == '_':
ctxt += '_'
continue
x = chr_to_num[ptxt[i]]
y = chr_to_num[key[i]]
ctxt += num_to_chr[(x + y) % 26]
```
This takes `ptxt` and `key`, makes `key` match the size of `ptxt` by repeating characters in a cyclic fashion, then adds the number equivalent (a->0, z->25) of each character of `key` to that of `ptxt` and mods it with 26. This value is converted to the corresponding character.
There is also a pseudo key along with this file. The pseudo key is made by encrypting the `key` using the `key` itself.
```python
pseudo_key = encrypt(key,key)
```
Which means, there might be many possible keys (because of the %26). However, we can get all possible characters in the key by adding 26 and by not adding 26 to the number equivalent of each character:
```python
def get_key(pkey):
x = ''
y = ''
for i in range(len(pkey)):
c = chr_to_num[pkey[i]]
x += num_to_chr[c/2]
y += num_to_chr[(c+26)/2]
print(x)
print(y)
```
This gives us 2 strings:
```
eedcjjjacgf
rrqpwwwnpts
```
If you see carefully, you can combine these 2 keys to get `redpwwwnctf` (first character from key2, second and third from key1, and so on...), which seems like it's going to be the correct key. (Yes, it's sort of a guess)
Now, we can simply use almost exactly the same function to decrypt the key, except, here you subtract instead of adding:
```python
from string import ascii_lowercase
chr_to_num = {c: i for i, c in enumerate(ascii_lowercase)}
num_to_chr = {i: c for i, c in enumerate(ascii_lowercase)}
ctxt = 'z_jjaoo_rljlhr_gauf_twv_shaqzb_ljtyut'
pseudo_key = 'iigesssaemk'
def get_key(pkey):
x = ''
y = ''
for i in range(len(pkey)):
c = chr_to_num[pkey[i]]
x += num_to_chr[c/2]
y += num_to_chr[(c+26)/2]
print(x)
print(y)
get_key(pseudo_key)
key = 'redpwwwnctf'
def decrypt(ct, key):
flag = ''
key = ''.join(key[i % len(key)] for i in range(len(ct))).lower()
for i in range(len(ct)):
if ct[i] == '_':
flag += '_'
continue
flag += num_to_chr[(chr_to_num[ct[i]] - chr_to_num[key[i]]) % 26]
print(flag)
decrypt(ctxt, key)
```
You can run this using python to get the flag.
```bash
$ python pseudo-key/crack.py
eedcjjjacgf
rrqpwwwnpts
i_guess_pseudo_keys_are_pseudo_secure
```
The flag is:
```
flag{i_guess_pseudo_keys_are_pseudo_secure}
```