CSE-107 / hw4 / hw4_p2.py
hw4_p2.py
Raw
import sys, os, itertools, json

from playcrypt.tools import *
from playcrypt.ideal.block_cipher import *
from playcrypt.ideal.message_authentication_code import *
from playcrypt.games.game_ufcma import GameUFCMA
from playcrypt.simulator.ufcma_sim import UFCMASim
from playcrypt.games.game_lr import GameLR
from playcrypt.simulator.lr_sim import LRSim
from playcrypt.games.game_int_ctxt import GameINTCTXT
from playcrypt.simulator.ctxt_sim import CTXTSim
from playcrypt.ideal.function_family import *


"""
Problem 2

Let E : {0,1}^k x {0,1}^{2n} -> {0,1}^{2n} be a block cipher with k, n >= 128.
Let K be 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. Note that 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


"""
[10 points] Show that SE is not INT-CTXT secure by presenting an O(n)-time
adversary A2 making two queries with advantage at least 1 - 2^{-n}.
"""

def A2(enc):
    """You must fill in this method. We will define variables k, n, k_bytes,
    n_bytes, Enc and Dec for you.

    :param enc: This is the oracle supplied by the game.
    return: a forged ciphertext
    """
    M0 = int_to_string(0, n_bytes) # can be a random string
    R1 = random_string(n_bytes)
    c_t0 = enc(M0)
    c_t1 = enc(R1)
# NOTE  
    # Since 0^n and 1^n are repeating patterns for the encryption scheme and is validated from the decryption scheme
    # we can switch the encryption of the first ciphertext with the 2nd ciphertext, and it would still be valid
# NOTE
    c_t0 = split(c_t0, 2*n_bytes) 
    c_t1 = split(c_t1, 2*n_bytes)
    c_t0[1] = c_t1[1]

    return join(c_t0)



"""
==============================================================================================
The following lines are used to test your code, and should not be modified.
==============================================================================================
"""

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()))