9
\$\begingroup\$

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.

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

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
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.