Fun crypto problem: Exploiting a vulnerable encryption scheme with Python

I came across a question asked here and here on the Crypto Stack Exchange site, and decided to analyze it as a kind of case study. To demonstrate the concept, I wrote a short python script depicting the proposed encryption scheme. It's mostly list comprehensions or generator expressions and bitwise operations.

$$C=E_k(m)$$ $$C_\alpha = C\oplus m$$ $$C_\beta = \overline{C}$$ $$C_\Omega = C_\alpha\ ^\frown\ C_\beta$$

#!/usr/bin/env python3

from Crypto.Cipher import DES

m = b'secret!!'
k = b'qwerty!!'
E = DES.new(k, DES.MODE_ECB)
C = E.encrypt(m)

C_alpha = int('0b'+''.join([f"{x:08b}" for x in C]), 2) ^ \
int('0b'+''.join([f"{x:08b}" for x in m]), 2)

C_beta  = int('0b'+''.join([f"{x:08b}" for x in C]), 2) ^ \
int('0b' + '1' * len(''.join([f"{x:08b}" for x in C])), 2)

C_omega = f"{C_alpha:064b}" + f"{C_beta:064b}"

if __name__ == '__main__':
print(C_omega)


Then I ended up with this alternative version. If you want to try it out, save it as bad_scheme.py so it can work properly with the next script:

#!/usr/bin/env python3

from Crypto.Cipher import DES

m = b'secret!!'
k = b'qwerty!!'
E = DES.new(k, DES.MODE_ECB)
C = E.encrypt(m)

def bitwise_xor(bits_a, bits_b):
bits_out = ''.join([str(int(x)^int(y)) for x, y in zip(bits_a, bits_b)])
return(bits_out)

def bitwise_complement(bits_in):
bits_out = ''.join([str(~int(x)+2) for x in bits_in])
return(bits_out)

def C_alpha():
bits_out = bitwise_xor(''.join([f"{x:08b}" for x in C]),
''.join([f"{x:08b}" for x in m]))
return(bits_out)

def C_beta():
bits_out = bitwise_complement(''.join([f"{x:08b}" for x in C]))
return(bits_out)

def C_omega():
bits_out = C_alpha() + C_beta()
return(bits_out)

if __name__ == '__main__':
print(C_omega())


And here's what is essentially a working exploit of the (hypothetical) proposed encryption scheme's vulnerability; demonstrating the plaintext can be revealed without the key. It imports the final ciphertext and the bitwise functions from the first script, and works backwards, and complements $$\C_\beta\$$ to get $$\C\$$ (because $$\C_\beta\$$ is essentially just $$\\overline{C}\$$), then $$\C \oplus C_\alpha\$$ to reveal $$\m\$$ without requiring $$\k\$$. So:

$$\overline{C_\beta}=C$$ $$C_\alpha \oplus C=m$$

#!/usr/bin/env python3

from bad_scheme import C_omega, bitwise_xor, bitwise_complement

def C_alpha():
bits_out = C_omega()[:int(len(C_omega())/2)]
return(bits_out)

def C_beta:
bits_out = C_omega()[int(len(C_omega())/2):]
return(bits_out)

def C():
bits_out = bitwise_complement(C_beta())
return(bits_out)

def m():
bits_out = bitwise_xor(C_alpha(), C_beta())
return(bits_out)

if __name__ == '__main__':
print(''.join([chr(int(m()[i:i+8],2)) for i in range(0,len(m()),8)]))


So, there it is. Just thought I'd put it out there and see what everyone thinks, which style is better, what's good about it, what's bad about it, and what I can do to improve it. It's not a real encryption scheme or anything, I'm mainly interested in feedback regarding design and execution, the handling of bitwise operations, and the general layout and structure of the code. I tried implementing a class and a few things to make it more robust and useful, but I couldn't pull it off. Also, is it better organized as functions? Or doing the same thing in fewer lines, just defining and operating on variables, and declaring no functions at all.

• To get LaTeX, use \\$ (notice a backslash). – vnp May 27 '19 at 19:14

The conversions to bit-strings, then ints and back to strings are unnecessary. m and k are bytes, so the elements (e.g., m[0] or k[i]) are already integers in the range 0-255; bitwise operators can be applied to the elements directly.

I think routines in the Crypto library return strings, so the encrypt() return value might need to be encoded before using it.

from Crypto.Cipher import DES

m = b'secret!!'
k = b'qwerty!!'
E = DES.new(k, DES.MODE_ECB)
C = E.encrypt(m)

C = C.encode()  # <- needed if encrypt() returns a string (i.e., not bytes)

C_alpha = bytes(x^y for x,y in zip(C, m))

C_beta  = bytes(x^0xff for x in C)

C_omega = C_alpha + C_beta