Suppose we have a block cipher
$$E:\{0,1\}^k \text{ x } \{0,1\}^{2k} \rightarrow \{0,1\}^{2n} \quad \text{ with } \quad k,n\geq128$$
K is the key generation algorithm that returns a random k-bit key. Let SE = (K,Enc,Dec) be the symmetric encryption scheme with encryption and decryption algorithms as described below in the code.
The message input to Enc is an n-bit string, and the ciphertext input to Dec is a 4n-bit string.
def Enc(K,M):
if len(M) != n_bytes :
return None
A1 = random_string(n_bytes)
A2 = xor_strings(M,A1)
C = []
C.append(E(K,( A1 + "\x00" * n_bytes )))
C.append(E(K,( A2 + "\xFF" * n_bytes )))
return join(C)
def Dec(K,C):
if len(C) != 4 * n_bytes :
return None
C = split(C,2 * n_bytes)
X1 = E_I(K,C[0]) #X1 = A1 || P1 in the pseudocode
X2 = E_I(K,C[1]) #X2 = A2 || P2 in the pseudocode
X1 = split(X1,n_bytes) #A1 is X1[0] ; P1 is X1[1]
X2 = split(X2,n_bytes) #A2 is X2[0] ; P2 is X2[1]
if (X1[1] != "\x00" * n_bytes) or (X2[1] != "\xFF" * n_bytes) :
return None
M = xor_strings(X1[0],X2[0])
return M
#THIS IS WHERE ADVERSARY CODE GOES ----- NEED TO FILL THIS UP
def A(enc):
"""
:param enc: This is the oracle supplied by the game.
return: a forged ciphertext
"""
pass
#below is to test the code to see if correct adversary is given
if __name__ == '__main__':
k = 128
n = 128
k_bytes = k//8
n_bytes = n//8
EE = BlockCipher(k_bytes, 2*n_bytes)
E = EE.encrypt
E_I = EE.decrypt
g = GameINTCTXT(2, Enc, Dec, k_bytes)
s = CTXTSim(g, A2)
print ("When k=128, n=128:")
print ("The advantage of your adversary A2 is ~" + str(s.compute_advantage()))
k = 256
n = 128
k_bytes = k//8
n_bytes = n//8
EE = BlockCipher(k_bytes, 2*n_bytes)
E = EE.encrypt
E_I = EE.decrypt
g = GameINTCTXT(2, Enc, Dec, k_bytes)
s = CTXTSim(g, A2)
print ("When k=256, n=128:")
print ("The advantage of your adversary A2 is ~" + str(s.compute_advantage()))
What O(n)-time Adversary can show that SE is not INT-CTXT secure, making at most 2 queries with Advantage = 1 - 2^(-n)
Here is a Latex version of the Encryption and Decryption scheme for ease of reading:
Encryption:
$\underline{ Alg E_K(M)}$
$\text{if } |M| \neq n \text{ then return } \perp$
$A[1] \leftarrow{$} \{0,1\}^n; A[2] \leftarrow M \oplus A[1]$
$C[1] \leftarrow E_K (A[1] || 0^n)$
$C[2] \leftarrow E_K (A[2] || 0^n)$
$\text{return } C$
Decryption:
$\underline{ Alg D_K(M)}$
$\text{if } |C| \neq 4n \text{ then return } \perp$
$C[1]C[2] \leftarrow C$
$A[1] || P[1] \leftarrow E^{-1}_K(C[1]) ; A[2] || P[2] \leftarrow E^{-1}_K(C[2])$
$\text{if }(P[1] \neq 0^n \text{ or } P[2] \neq 1^n) \text{ then return } \perp$
$M \leftarrow A[1] \oplus A[2]$
$\text{return } M$
Here's what I've tried so far:
def A(enc):
half = n_bytes // 2
C1 = enc(random_string(n_bytes))
C2 = enc("\x00" * n_bytes)
C_split1 = C1[:half]
C_split2 = C2[half:]
return C_split1 + C_split2;
but get output:
When k=128, n=128:
The advantage of your adversary A2 is ~0.0
When k=256, n=128:
The advantage of your adversary A2 is ~0.0
```