Question: MD 5 x 3 with = = = = = MD 5 x 3 is a block cipher that has a 3 - round Feistel

MD5x3with
=====
MD5x3 is a block cipher that has a 3-round Feistel structure and its round function is based on the MD5 hash function. The block size of MD5x3 is 32 bytes and its key length is 48 bits (6 bytes). The encryption operation is illustrated in the figure below, where | means concatenation of byte strings and (+) denotes the XOR operation:
X = L0|R0
L0 R0
||
|+-----+|
|||<--- K0|
(+)<---| MD5|<--------+
|||<--- K1|
|+-----+|
||
|+-----+|
| K2--->|||
+-------->| MD5|--->(+)
| K3--->|||
|+-----+|
||
|+-----+|
|||<--- K4|
(+)<---| MD5|<--------+
|||<--- K5|
|+-----+|
||
L3 R3
Y = L3|R3
The input block X is first divided into 2 halves, L0 and R0,16 bytes each. The right half R0 is hashed together with the first 2 bytes K0, K1 of the key (see figure), the result MD5(K0|R0|K1) is XORed with the left half L0, and then the two halves are swapped. This makes up one round. The same operation is repeated in the remaining 2 rounds, except that the swap of the two halves is omitted at the end of the last round, and each round uses the next 2 bytes from the key, i.e., K2, K3, and K4, K5. We obtain the output Y by concatenating the two halves at the end.
A Python implementation of MD5x3 is provided for your convenience in md5x3.py. It is written in Python 3 and it uses the MD5 implementation and string XORing from the PyCryptodome package as a dependence.
The following test plaintext block (without apostrophes)
'*THIS_IS_A_TEST_INPUT_FOR_MD5X3*'
was encrypted with an unknown key, and the resulting ciphertext block in hex format is
`f1eba0e1e77269c82f29b7a695dc7ae4899991ebcbeafe1c6a5fdb873621f750`
Break the cipher and figure out the 6-byte key!
### Note
As the round function of MD5x3 is based on MD5, you need to install PyCryptodome in order to use the `md5x3.py` script or the RF function from it. To install PyCryptodome, run
```bash
pip3 install pycryptodome
```
Hint:
Hints for challenge MD5x3
=========================
A key observation is that the rounds of MD5x3 use independent subsets of the key: the first round uses the first 2 bytes of the key, the second round uses the next 2 bytes, and finally, the third round uses the last 2 bytes of the key. Also, by looking at the internal structure of the cipher, we can see that
L0+ MD5(K0|R0|K1)= L3+ MD5(K4|R3|K5)
where L0, R0, L3, R3 are known (as L0|R0 is the test plaintex block and L3|R3 is the corresponding ciphertext block).
We can mount a meet-in-the-middle attack and determine K0, K1, K4, K5 in the following way: First, we compute and store L0+ MD5(K0|R0|K1) for all possible 2^16 values of (K0, K1). Then, we compute L3+ MD5(K4|R3|K5) for all possible values of (K4, K5), and for each computed value, we check if it is among the previously computed values of L0+ MD5(K0|R0|K1). When a match is found, we have both the right (K0, K1) and the right (K4, K5).
The remianing 2 bytes (K2, K3) can be found by brute force...
md5x3.py:
from Crypto.Hash import MD5
from Crypto.Util.strxor import strxor
class TypeError(Exception):
def __init__(self, message):
self.message = message
class LengthError(Exception):
def __init__(self, message):
self.message = message
def RF(L, R, RK):
md5= MD5.new()
md5.update(RK[0:1]+R+RK[1:2])
return strxor(L, md5.digest()), R
def ENC(X, K):
if type(K)!= bytes:
raise TypeError("Key must be of type bytes!")
return
if len(K)!=6:
raise LengthError("Key length must be of length 6 bytes!")
return
if type(X)!= bytes:
raise TypeError("Input block must be of type bytes!")
return
if len(X)!=32:
raise LengthError("Block length must be of length 32 bytes!")
return
K0= K[0:2]
K1= K[2:4]
K2= K[4:6]
L, R = X[0:16], X[16:32]
L, R = RF(L, R, K0)
L, R = R, L
L, R = RF(L, R, K1)
L, R = R, L
L, R = RF(L, R, K2)
Y = L + R
return Y
def DEC(Y, K):
if type(K)!= bytes:
raise TypeError("Key must be of type bytes!")
return
if len(K)!=6:
raise LengthError("Key length must be of length 6 bytes!")
return
if type(Y)!= bytes:
raise TypeError("Input block must be of type bytes!")
return
if len(Y)!=32:
raise LengthError("Block length must be of length 32 bytes!")
return
K0= K[0:2]
K1= K[2:4]
K2= K[4:6]

Step by Step Solution

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Programming Questions!