Mercredi 14/09

taktoy.py

sbox = [3, 14, 1, 10, 4, 9, 5, 6, 8, 11, 15, 2, 13, 12, 0, 7]
# sbox = [12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2] # from PRESENT
xobs = [14, 2, 11, 0, 4, 6, 7, 15, 8, 5, 3, 9, 13, 12, 1, 10]


def round(msg, subkey):
    return sbox[msg ^ subkey]


def enc(msg, key):
    t0 = round(msg, key[0])
    t1 = round(t0, key[1])
    return t1


def simplified_enc(msg, key):
    t0 = round(msg, key[0])
    return t0 ^ key[1]

cryptanalyse differentielle

diff.py

from random import choice, randint
import taktoy

debug = True


def compute_differential_characteristics():
    sbox_dc = [[0 for _ in range(0, 16)] for _ in range(0, 16)]
    for a in range(0, 16):
        for b in range(0, 16):
            sbox_dc[a ^ b][taktoy.sbox[a] ^ taktoy.sbox[b]] += 1
    if debug:
        print("\n> XOR differential table:")
        print("     ", end="")
        for i in range(0, 16):
            print("%2x " % i, end="")
        print("\n   +-", end="")
        for i in range(0, 16):
            print("---", end="")
        for i in range(0, 16):
            print("\n%2x | " % i, end="")
            for j in range(0, 16):
                print("%2d " % sbox_dc[i][j], end="")
    return sbox_dc


def get_most_probable_diff_sbox_io(dc):
    mpd = 0
    for i in range(1, 16):
        for j in range(1, 16):
            mpd = max(mpd, dc[i][j])
    if debug:
        print(f"\n> Most probable diff occurs {mpd} times\n")
        print("> Most probable diff I/O:")
    io = []
    for i in range(1, 16):
        for j in range(1, 16):
            if dc[i][j] == mpd:
                io.append((i, j))
                if debug:
                    print(f"{hex(i)[2:]} --> {hex(j)[2:]}")
    return io


def get_possible_intermediate_values(diff_io):
    di, do = diff_io
    if debug:
        print(f"\n> Selected diff: di={di} --> do={do}")
    piv = []  # probable intermediate values
    for a in range(0, 16):
        b = a ^ di
        if taktoy.sbox[a] ^ taktoy.sbox[b] == do:
            piv.append(a)
            if debug:
                print(
                    f"> Possible a ^ b --> S[a] ^ S[b]: {a} ^ {b} --> {taktoy.sbox[a]} ^ {taktoy.sbox[b]}.")
    return piv


def generate_pairs(count, di, key):
    pairs_rand = []
    pairs_diff = []
    for _ in range(0, count):
        m = randint(0, 15)
        c0 = taktoy.simplified_enc(m, key)
        pairs_rand.append((m, c0))
        md = m ^ di
        cd = taktoy.simplified_enc(md, key)
        pairs_diff.append((md, cd))
    return pairs_rand, pairs_diff


def find_good_pair(pairs_r, pairs_d, do):
    print(f"> Looking for a pair with output diff = {do}...")
    for i in range(0, len(pairs_r)):
        if pairs_r[i][1] ^ pairs_d[i][1] == do:
            if debug:
                print(f"> Found {pairs_r[i]}.")
            return pairs_r[i]
    print("> Cannot find good pair...")


def test_key(key, pairs):
    for p in pairs:
        if p[1] != taktoy.simplified_enc(p[0], key):
            return False
    return True


def attack(piv, m, c, pairs_r, pairs_d):
    for i in range(0, len(piv)):
        k0 = piv[i] ^ m
        k1 = taktoy.sbox[piv[i]] ^ c
        if test_key((k0, k1), pairs_r) and test_key((k0, k1), pairs_d):
            return k0, k1
    print("> Raté")


if __name__ == "__main__":
    dc = compute_differential_characteristics()
    mpd_io = get_most_probable_diff_sbox_io(dc)
    io = choice(mpd_io)
    piv = get_possible_intermediate_values(io)

    key = (randint(0, 15), randint(0, 15))
    print(f"KEY: {key}")
    pairs_r, pairs_d = generate_pairs(8, io[0], key)
    m, c = find_good_pair(pairs_r, pairs_d, io[1])

    k = attack(piv, m, c, pairs_r, pairs_d)
    print(f"ATTACK: {k}")