---
# System prepended metadata

title: Involucrypt- II

---



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

```

