# -*- coding: utf-8 -*-
# Utilisation du programme a3a8.c
# pour créer une biliothèque partagée
# compiler avec
#   gcc -Wall -fPIC -c a3a8.c
#   gcc -shared -Wl,-soname,liba3a8.so.1 -o liba3a8.so.1.0   *.o
#
# Voir par exemple
# http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html
# puis
# http://docs.python.org/library/ctypes.html#module-ctypes
#
# La fonction A3A8 de a3a8.c prend en entrée deux tableaux de 16 octets
# qu'on passe sous forme de deux chaînes de longueur 12
# et écrit son résultat dans la variable  `Byte simoutput[12]`
# représentée par `o` ci-dessous


from ctypes import *

cdll.LoadLibrary("./liba3a8.so.1.0")
liba3a8=CDLL("./liba3a8.so.1.0")


x='\x00'*16
o = create_string_buffer('\x00'*12)

a = liba3a8.A3A8(x,x,o)

print "%02X"*12 % tuple(map(ord,tuple(o))[:12])


##
def a38str(rand,key):
    o = create_string_buffer('\x00'*12)
    r = ''.join(map(chr,rand))
    k = ''.join(map(chr,key))
    a = liba3a8.A3A8(r,k,o)
    return "%02X"*12 % tuple(map(ord,tuple(o))[:12])


def hex2(n): # hexadecimal sur 2 caracteres pour n < 256
    return "%02X"% n


# Attaque par recherche de collisions

def find_collision(x,Ki):# octets x et x+8
    a = [0]*16; found = False
    dd = {}
    for i in range(0,256):
        a[x] = i
        for j in range(0,256):
            a[x+8]=j                   # Conversion en chaines
            s = a38str(a,Ki)           # pour premiers tests seulement
            if dd.has_key(s):          # La version finale utilisera
                found = True           # des clefs entieres et n'imprimera pas
                print "Collision found:"
                print dd[s]
                b = ''.join(map(hex2,a))
                print b
                print "Hash: ", s
                return (dd[s],b)
                break
            else:
                dd[s] = ''.join(map(hex2,a))


def find_partial_key(x,c1,c2): # octets x et x+8, c1, c2 collision
#  par exemple
#    c1= '00000000002000000000000000ee0000'
#    c2= '00000000006800000000000000f80000'
    chal1 = map(lambda s: int(s,16),[c1[2*i:2*i+2] for i in range(0,16)])
    chal2 = map(lambda s: int(s,16),[c2[2*i:2*i+2] for i in range(0,16)])
    Ki= [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    for i in range(0,256):
        for j in range(0,256):
            Ki[x]=i; Ki[x+8]=j
            if a38str(chal1,Ki) == a38str(chal2,Ki):
                print "Partial key:",Ki
                return(''.join(map(hex2,Ki)))





if __name__ == "__main__":

#  Reconstruction de la clef Ki
#  On teste avec une clef aleatoire
#  construite ainsi:
#    from random import randint
#    b = [randint(0,255) for i in range(0,16)]
# b est la clef secrete Ki
    b = [4, 139, 193, 234, 147, 176, 248, 39, 51, 214, 124, 25, 38, 124, 145, 214]



    for i in range(0,16):
        print a38str([i]+[0]*15, b)

    print
    print "Example: Ki = " +"%02X"*16 % tuple(b)

    print
    K = []
    for i in range(8):
        c = find_collision(i,b)
        k = find_partial_key(i, *c)
        K.append(k)

    print
    print "Key found:"
    print

    print ''.join([K[i][2*i:2*i+2] for i in range(8)]+[K[i][2*i+16:2*i+18] for i in range(8)])







