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.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 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.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.