# 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)
```