# Involucrypt- II --- La faille se situe dans la fonction ci-dessous: ``` def keystream(seeds, length, base=None): key = base if base else [] for seed in seeds: random = mersenne_rng(seed) for _ in range(BLOCK): if len(key) == length: break key.append(random.get_rand_int(0, 255)) random.shuffle(key) if len(key) == length: break return key ``` L'objet mersenne_rng est instancié pour chaque caractère du mot de passe, * On ajoute un entier a la clef, * On mélange la clef, * Et on répète cela au maximum 150 fois avec la même seed (ie caractere du mot de passe) Donc chaque caractère du mot de passe génère un maximum de 150 éléments de la clef, On remarque la variable "base" dans l'entete de la fonction, et donc: * On peut "padder" la clef avec des 0 en utilisant "base", * On peut déterminer l'impact du dernier caractère du mot de passe sur la clef (Ajout & Mélange) ie les éléments non nuls, * On a au maximum 256 valeurs à tester, * On ne regarde que le texte décrypté correspondant au éléments non nuls de la clef, * Si le texte est affichable, on a le bon caractère N du mot de passe, * Quand on a déterminé le caractère N, on s'attaque au caractère N-1, on met donc moins de padding et le bon caractère N, * On boucle. Et cela tourne en 5 minutes, --- ``` import itertools import operator import sys BLOCK = 150 class mersenne_rng(object): def __init__(self, seed=5489): self.state = [0] * 624 self.f = 1812433253 self.m = 397 self.u = 11 self.s = 7 self.b = 0x9D2C5680 self.t = 15 self.c = 0xEFC60000 self.l = 18 self.index = 624 self.lower_mask = (1 << 31) - 1 self.upper_mask = 1 << 31 # update state self.state[0] = seed for i in range(1, 624): self.state[i] = self.int_32(self.f * (self.state[i - 1] ^ (self.state[i - 1] >> 30)) + i) def twist(self): for i in range(624): temp = self.int_32((self.state[i] & self.upper_mask)+ (self.state[(i + 1) % 624] & self.lower_mask)) temp_shift = temp >> 1 if temp % 2 != 0: temp_shift = temp_shift ^ 0x9908B0DF self.state[i] = self.state[(i + self.m) % 624] ^ temp_shift self.index = 0 def get_random_number(self): if self.index >= 624: self.twist() y = self.state[self.index] y = y ^ (y >> self.u) y = y ^ ((y << self.s) & self.b) y = y ^ ((y << self.t) & self.c) y = y ^ (y >> self.l) self.index += 1 return self.int_32(y) def get_rand_int(self, min, max): return (self.get_random_number() % (max - min)) + min def int_32(self, number): return int(0xFFFFFFFF & number) def shuffle(self, my_list): for i in range(len(my_list) - 1, 0, -1): j = self.get_rand_int(0, i + 1) my_list[i], my_list[j] = my_list[j], my_list[i] def keystream(seeds, length, base=None): key = base if base else [] for seed in seeds: random = mersenne_rng(seed) for _ in range(BLOCK): if len(key) == length: break key.append(random.get_rand_int(0, 255)) random.shuffle(key) if len(key) == length: break return key def encrypt(string, key): key = keystream(map(ord, key), len(string)) stream = itertools.starmap(operator.xor, zip(key, map(ord, string))) return bytearray(stream) # decrypt with open("involucrypt2","r") as fp: data = fp.read() print(len(data)) nb_char=len(data)//BLOCK+1 print("Estimate : " + str(len(data)//BLOCK+1)) import string pwd=[0] # for each caractere of the password for n in range(nb_char-1,-1,-1): print("--------------------") for i in range(95,256,1): print(str(i)) pwd[-1]=i key = keystream(pwd[::-1], len(data), base=[0 for x in range((n)*BLOCK)]) solve=bytearray(itertools.starmap(operator.xor, zip(key, map(ord, data)))) msg="" tmp=[] for j,elt in enumerate(key): if elt >0: msg=msg+chr(solve[j]) tmp.append(solve[j]) else: msg=msg+"." if all(c in string.printable for c in msg): print(pwd[::-1]) print(str(min(tmp))+" - "+str(max(tmp))) print(msg[0:200]) print("--------------------") pwd.append(0) break print(msg) ```