[LINE CTF 2022] Forward-or

Key entropy is small. # of key[0:10] = # of key[0:10] = $4^10$, so meet in the middle approach is working. Beware of CTR mode.

 

solver.py

from present import Present
from itertools import product
from Crypto.Util.strxor import strxor
from tqdm import tqdm

def xor(a:bytes, b:bytes) -> bytes:
    return bytes(i^j for i, j in zip(a, b))

class CTRMode():
    def __init__(self, key, nonce=None):
        self.key = key # 20bytes
        self.cipher = DoubleRoundReducedPresent(key)
        if None==nonce:
            nonce = os.urandom(self.cipher.block_size//2)
        self.nonce = nonce # 4bytes
    
    def XorStream(self, data):
        output = b""
        counter = 0
        for i in range(0, len(data), self.cipher.block_size):
            keystream = self.cipher.encrypt(self.nonce+counter.to_bytes(self.cipher.block_size//2, 'big'))
            if b""==keystream:
                exit(1)

            if len(data)<i+self.cipher.block_size:
                block = data[i:len(data)]
            block = data[i:i+self.cipher.block_size]
            block = strxor(keystream[:len(block)], block)
            
            output+=block
            counter+=1
        return output

    def encrypt(self, plaintext):
        return self.XorStream(plaintext)

    def decrypt(self, ciphertext):
        return self.XorStream(ciphertext)

class DoubleRoundReducedPresent():

    def __init__(self, key):
        self.block_size = 8
        self.key_length = 160 # bits
        self.round = 16
        self.cipher0 = Present(key[0:10], self.round)
        self.cipher1 = Present(key[10:20], self.round)
    
    def encrypt(self, plaintext):
        if len(plaintext)>self.block_size:
            print("Error: Plaintext must be less than %d bytes per block" % self.block_size)
            return b""
        return self.cipher1.encrypt(self.cipher0.encrypt(plaintext))
    
    def decrypt(self, ciphertext):
        if len(ciphertext)>self.block_size:
            print("Error: Ciphertext must be less than %d bytes per block" % self.block_size)
            return b""
        return self.cipher0.decrypt(self.cipher1.decrypt(ciphertext))





cand_key = [''.join(key) for key in product('0123', repeat=10)]

mitm1 = {}

nonce = bytes.fromhex('32e10325')

C = xor(bytes.fromhex('3201339d0fcffbd1'), b'LINECTF{')
P = nonce + bytes(4)

C_all = bytes.fromhex("3201339d0fcffbd152f169ddcb8349647d8bc36a73abc4d981d3206f4b1d98468995b9b1c15dc0f0")

'''
cipher = CTRMode('32013230202123003302'.encode(), nonce)
print(cipher.decrypt(C_all))

exit()
'''

# key[0:10]
for key in tqdm(cand_key):
    cipher = Present(''.join(key).encode(), 16)
    mid = cipher.encrypt(P)
    mitm1[mid] = key

# key[10:20]
for key in tqdm(cand_key):
    cipher = Present(''.join(key).encode(), 16)
    mid = cipher.decrypt(C)
    if mid in mitm1:
        key1 = mitm1[mid]
        key2 = key
        print(key1+key2) # 32013230202123003302
        z = key1+key2
        cipher = CTRMode(z.encode(), nonce)
        print(cipher.decrypt(C_all))
        break

 

 

'CTF > Crypto' 카테고리의 다른 글

[SECCON CTF 2022] janken vs kurenaif  (0) 2022.11.13
[SECCON CTF 2022] this_is_not_lsb  (0) 2022.11.13
[LINE CTF 2022] lazy_stek  (0) 2022.03.27
[LINE CTF 2022] X Factor  (0) 2022.03.27
[LINE CTF 2022] ss-puzzle  (0) 2022.03.27
[zer0pts CTF 2022] ok  (0) 2022.03.22
  Comments