# AngstromCTF2019: Classy Cipher

## The Challenge

Classy Cipher Crypto 20

Every CTF starts off with a Caesar cipher, but we’re more classy.

Author: defund

The linked file is a simple python script.

``````from secret import flag, shift

def encrypt(d, s):
e = ''
for c in d:
e += chr((ord(c)+s) % 0xff)
return e

assert encrypt(flag, shift) == ':<M?TLH8<A:[email protected]'
``````

## The Solution

The python script rotates characters by an unknown amount. The character rotation must be less than 255 (the ASCII range) as enforced by `​% 0xff`​ in the `​encrypt()`​ function.

The rotation can be calculated because the first 4 values of the flag are known:

`​:<M?`must be `​actf`

The easiest way to calculate the shift is using the `encrypt()` algorithm to compare the known characters.

``````def shift_test(have, want):
for x in range(255):
tested = encrypt(want, x)
if tested == have:
return x
``````

After calculating the rotation, a lookup table can be created for all ascii values and used to decipher the ciphertext

``````def generate_table(shift):
table = {}
# generate all ascii chars
for x in range(255):
# get the key and its cipher equivalent
key = chr(x)
cipheredKey = encrypt(key,shift)
# add to table
table[cipheredKey] = key
return table

def decipher(ciphertext, shift):
lookup = generate_table(shift)
out = ''
for char in ciphertext:
out += lookup[char]
return out
``````

Combining the above scripts reveals the flag:

• The rotation was 216
• The flag is actf{so_charming}

Here is the script I used, commented for clarity

``````#from secret import flag, shift

def encrypt(d, s):
e = ''
for c in d:
e += chr((ord(c)+s) % 0xff)
return e

#assert encrypt(flag, shift) == ':<M?TLH8<A:[email protected]'

# test for the correct shift
def shift_test(have, want):
for x in range(255):
tested = encrypt(want, x)
if tested == have:
return x

def generate_table(shift):
table = {}
# generate all ascii chars
for x in range(255):
# get the key and its cipher equivalent
key = chr(x)
cipheredKey = encrypt(key,shift)
# add to table
table[cipheredKey] = key
return table

def decipher(ciphertext, shift):
lookup = generate_table(shift)
out = ''
for char in ciphertext:
out += lookup[char]
return out

# algorithm starts here

# we know the flag
ciphertext = ':<M?TLH8<A:[email protected]'
# test for the shift used to get the known part of the flag
shift = shift_test(have=':<M?', want='actf')
print(f'Shift is: {shift}')

# generate a lookup table and print the flag
flag = decipher(ciphertext, shift)
print(f"The flag says: {flag}")

``````