# -*- coding: utf-8 -*-
# Code de Swenson pour la cryptanalyse linéaire de "easy1"

from easy1 import *
import random


def grab(x, pos):
    return (x >> pos) & 1

def xor(x, aa):
    return reduce(lambda i,j: i.__xor__(j), [grab(x,a) for a in aa])


def linex(p,u): # comprendre l'origine de cette expression
    return xor(p, [6,7,8,9,10]) ^ xor(u, [8,9])

#random.seed(12345)

numplaintexts = 1800
key = mux([0x12]*6)

e = easy1(key,3)

plaintext = []
ciphertext = []

for i in range(numplaintexts):
    r = long(random.randint(0,2**36))
    plaintext.append(r)
    c = e.encrypt_block(r)
    ciphertext.append(c)

maxdev = -1
maxk = -1
koffset = 6
ssize = 2**6 # ???
count = [0]*ssize

for k1 in range(ssize):
    k = k1 << koffset

    for j in range(len(plaintext)):
        pt = plaintext[j]
        ct = ciphertext[j]

        v = apbox(ct)
        v = demux(v)
        v = mix(v,k)

        u = mux([asbox(v[i]) for i in range(6)])
        if linex(pt,u) != 0: count[k1] += 1

    if abs(count[k1] - len(plaintext)/2) >= maxdev:
        maxdev = abs(count[k1] - len(plaintext)/2)
        maxk = k

        #print k1, count[k1]

def binary(x):
    if x:
        y = binary(x>>1)+str(x&1)
        if not y: return '0'
        else: return y
    else: return ''

print "guess: ",
print (binary(maxk >> koffset)).rjust(6, '0'),
print maxk >> koffset,
print "  deviation: ",
print maxdev/float(len(plaintext))

print "real: "
print (binary((apbox(key) >> koffset) & 0x3f)).rjust(6, '0'),
print (apbox(key) >> koffset) & 0x3f