Extra Systems

CYPHERNET

module de brassage de l'alphabet


Le module de brassage alphabétique est conçu pour une protection supplémentaire des informations secrètes transmises via un canal de communication. Bien que le cryptage de flux RC4 que nous utilisons dans Extra Systems Cypher Net ne présente aucune vulnérabilité dans notre implémentation, la possibilité théorique d'énumérer des clés avec une analyse lexicale ultérieure des tentatives de décryptage des informations transmises demeure. Nous ne parlons bien entendu pas des opportunités dont dispose aujourd’hui l’humanité. Pour les systèmes modernes, le temps nécessaire pour rechercher les clés RC4 dans notre cas est plusieurs fois plus long que la durée de vie de l’Univers. Nous parlons de ces possibilités hypothétiques qui pourraient apparaître dans le futur. Dans le but de nous assurer contre toutes les options imaginables, nous avons ajouté à Extra Systems Cypher Net ce module de brassage alphabétique, qui rend en principe impossible l'analyse lexicale des tentatives de décryptage, et prive ainsi un attaquant de toute possibilité de cracker ce système de communication crypté.

L'essence de cette méthode est que les codes de 256 caractères (le nombre d'options pour un octet de huit bits) sont déplacés de manière complètement aléatoire dans le tableau, de sorte que le texte dans n'importe quelle langue se transforme en charabia complet, composé de caractères imprimés et non imprimables mélangés de manière totalement imprévisible. En conséquence, la méthode de piratage par énumération par force brute des mots de passe de cryptage, dont l'essence est de rechercher des options lorsque le texte chiffré se transforme en un ensemble de certains caractères imprimés, perd complètement sa validité, puisque le mot de passe correct, avec notre approche, qui est décrite ici, se transforme, comme déjà indiqué, en abracadabra, qui ne peut en aucun cas être confondu avec du texte normal.

Ainsi, le cracker Extra Systems Cypher Net n'a pas la capacité de deviner le mot de passe, se concentrant sur l'obtention d'un texte significatif.

Initialement, le système crée une table qui correspond à la transformation d'identité (c'est-à-dire qu'il ne fait rien – il ne modifie pas les informations de codage des caractères). Le module shuffle_init fait ceci:

int main(void)
{
	int i,file_handle;
	unsigned char data[256];
	for (i=0; i<256; i++) data[i] = i;
	file_handle = creat("shuffle.encrypt", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
	write(file_handle, data, sizeof(data));
	close(file_handle);
	return 0;
}

Ensuite, les informations de ce tableau sont mélangées de manière aléatoire (cette procédure peut, et même doit, être effectuée plusieurs fois). Ceci est fait par le module shuffle_randomize:

int main(void)
{
	unsigned char src, dst;
	unsigned char shuffle_encrypt_data[256];
	unsigned char random_src[256], random_dst[256];
	int i, file_handle;
	file_handle = open("shuffle.encrypt", O_RDONLY);
	read(file_handle, shuffle_encrypt_data, sizeof(shuffle_encrypt_data));
	close(file_handle);
	fill_random_buffer(random_src, sizeof(random_src));
	fill_random_buffer(random_dst, sizeof(random_dst));
	for(i = 0; i < 256; i++)
	{
		src = shuffle_encrypt_data[random_src[i]];
		dst = shuffle_encrypt_data[random_dst[i]];
		shuffle_encrypt_data[random_src[i]] = dst;
		shuffle_encrypt_data[random_dst[i]] = src;
	}
	file_handle = creat("shuffle.encrypt", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
	write(file_handle, shuffle_encrypt_data, sizeof(shuffle_encrypt_data));
	close(file_handle);
	return 0;
}

Il convient de souligner en particulier que la fonction fill_random_buffer (utilisée dans ce module et dans un certain nombre d'autres modules de ce système) renvoie toujours une séquence cryptographiquement forte d'octets aléatoires. Dans différents systèmes d'exploitation, il s'appuie sur des appels à divers services système spécialement conçus et officiellement documentés (spécialement conçus et officiellement déclarés comme tels, qui servent à générer des séquences cryptographiquement fortes d'octets aléatoires). Dans chacun des systèmes d'exploitation que nous prenons en charge, la fonction fill_random_buffer transmet simplement un appel à une telle fonction système (et renvoie simplement les résultats de son travail à son client).

L'application de cette clé dans le système Extra Systems Cypher Net est gérée par le module shuffle (notez que shuffle_decrypt_data est créé «à la volée» sur la base de shuffle_encrypt_data immédiatement après son chargement - ce fait garantit automatiquement une cohérence totale entre les algorithmes de codage et de décodage des informations transmises), dont le code en C est le suivant:

unsigned char shuffle_encrypt_data[256], shuffle_decrypt_data[256];

int load_shuffle_data(void)
{
	unsigned char src;
	int i, file_handle;
	if((file_handle = open("shuffle.encrypt", O_RDONLY)) == -1) return 0;
	read(file_handle, shuffle_encrypt_data, sizeof(shuffle_encrypt_data));
	close(file_handle);
	for(i = 0; i < 256; i++)
	{
		src = shuffle_encrypt_data[i];
		shuffle_decrypt_data[src] = i;
	}
	return 1;
}

void shuffle_rotate_l(unsigned char *buffer_ptr, int buffer_len)
{
	int i;
	unsigned char x;
	if (!buffer_len) return;
	x = buffer_ptr[0];
	for(i = 0; i < (buffer_len - 1); i++) buffer_ptr[i] = (buffer_ptr[i] << 4) | (buffer_ptr[i + 1] >> 4);
	buffer_ptr[buffer_len - 1] = (buffer_ptr[buffer_len - 1] << 4) | (x >> 4);
}

void shuffle_rotate_r(unsigned char *buffer_ptr, int buffer_len)
{
	int i;
	unsigned char x;
	if (!buffer_len) return;
	x = buffer_ptr[buffer_len - 1];
	for(i = (buffer_len - 1); i > 0; i--) buffer_ptr[i] = (buffer_ptr[i] >> 4) | (buffer_ptr[i - 1] << 4);
	buffer_ptr[0] = (buffer_ptr[0] >> 4) | (x << 4);
}

void shuffle_twist(unsigned char *buffer_ptr, int buffer_len)
{
	int i;
	unsigned char x, y;
	if (buffer_len < 2) return;
	for(i = 1; i < buffer_len; i++)
	{
		x = buffer_ptr[i - 1];
		y = buffer_ptr[i];
		buffer_ptr[i - 1] = (x & 0xF0) | (y >> 4);
		buffer_ptr[i] = (y & 0xF) | (x << 4);
	}
}

void shuffle_buffer(unsigned char *buffer_ptr, int buffer_len, unsigned char *shuffle_data)
{ 
	short counter;
	for(counter = 0; counter < buffer_len; counter++) buffer_ptr[counter] = shuffle_data[buffer_ptr[counter]];
}

void shuffle_swap_l(unsigned char *buffer_ptr, int buffer_len, unsigned char swap_old)
{
	int i;
	unsigned char x, y, swap_new;
	if (buffer_len < 2) return;
	swap_new = ~swap_old;
 	for(i = 1; i < buffer_len; i++)
	{
		x = buffer_ptr[i - 1];
		y = buffer_ptr[i];
		buffer_ptr[i - 1] = (x & swap_old) | (y & swap_new);
		buffer_ptr[i] = (y & swap_old) | (x & swap_new);
	}
}

void shuffle_swap_r(unsigned char *buffer_ptr, int buffer_len, unsigned char swap_old)
{
	int i;
	unsigned char x, y, swap_new;
	if (buffer_len < 2) return;
	swap_new = ~swap_old;
 	for(i = (buffer_len - 1); i > 0; i--)
	{
		x = buffer_ptr[i - 1];
		y = buffer_ptr[i];
		buffer_ptr[i - 1] = (x & swap_old) | (y & swap_new);
		buffer_ptr[i] = (y & swap_old) | (x & swap_new);
	}
}

unsigned char i_shuffle_rol(unsigned char x, unsigned char y)
{
	unsigned char a, b;
	a = ((x >> 1) & 0xF0) | ((y << 3) & 0x80);
	b = ((x << 1) & 0x0F) | ((y >> 3) & 0x01);
	return a | b;
}

void shuffle_rol(unsigned char *buffer_ptr, int buffer_len)
{
	int i;
	unsigned char x, y;
	if (buffer_len < 2) return;
	for(i = 1; i < buffer_len; i++)
	{
		x = buffer_ptr[i - 1];
		y = buffer_ptr[i];
		buffer_ptr[i - 1] = i_shuffle_rol(x, y);
		buffer_ptr[i]     = i_shuffle_rol(y, x);
	}
}

unsigned char i_shuffle_ror(unsigned char x, unsigned char y)
{
	unsigned char a, b;
	a = ((x & 0xF0) << 1) | ((y >> 3) & 0x10);
	b = ((x & 0x0F) >> 1) | ((y << 3) & 0x08);
	return a | b;
}

void shuffle_ror(unsigned char *buffer_ptr, int buffer_len)
{
	int i;
	unsigned char x, y;
	if (buffer_len < 2) return;
	for(i = (buffer_len - 1); i > 0; i--)
	{
		x = buffer_ptr[i - 1];
		y = buffer_ptr[i];
		buffer_ptr[i - 1] = i_shuffle_ror(x, y);
		buffer_ptr[i]     = i_shuffle_ror(y, x);
	}
}

void shuffle_encrypt_buffer(unsigned char *buffer_ptr, int buffer_len)
{
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data);
	shuffle_rol(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data);
	shuffle_swap_l(buffer_ptr, buffer_len, SHUFFLE_SWAP_STEP_1);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data);
	shuffle_rotate_l(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data);
	shuffle_twist(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data);
	shuffle_rotate_l(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data);
	shuffle_swap_l(buffer_ptr, buffer_len, SHUFFLE_SWAP_STEP_2);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data);
	shuffle_rol(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_encrypt_data);
}

void shuffle_decrypt_buffer(unsigned char *buffer_ptr, int buffer_len)
{
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data);
	shuffle_ror(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data);
	shuffle_swap_r(buffer_ptr, buffer_len, SHUFFLE_SWAP_STEP_2);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data);
	shuffle_rotate_r(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data);
	shuffle_twist(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data);
	shuffle_rotate_r(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data);
	shuffle_swap_r(buffer_ptr, buffer_len, SHUFFLE_SWAP_STEP_1);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data);
	shuffle_ror(buffer_ptr, buffer_len);
	shuffle_buffer(buffer_ptr, buffer_len, shuffle_decrypt_data);
}

Grâce à ce réarrangement chaotique des lettres, l'attaquant est complètement privé de la capacité d'analyser le texte qu'il obtient en essayant de déchiffrer par force brute. Le fichier «shuffle.encrypt» est créé différemment pour chaque client et, de plus, chaque client, s'il le souhaite, est équipé du programme shuffle_randomize, à l'aide duquel il peut effectuer un certain nombre de permutations supplémentaires dans ce fichier de clé.

Le but des procédures shuffle_rotate_l et shuffle_rotate_r est d'éliminer les répétitions régulières des octets clés du codage UTF8 (qui se produisent lors de l'utilisation de toutes les langues sauf l'anglais - et ceci est particulièrement typique de l'alphabet cyrillique) - car cette circonstance pourrait servir d'indice pour sélectionner la clé RC4 en utilisant la méthode de la «force brute». Notre utilisation de ces procédures dans ce module élimine complètement un tel danger dans notre produit.

La procédure shuffle_swap résout un problème similaire: son travail consiste à échanger les bits dans les octets adjacents selon un certain masque (SHUFFLE_SWAP_STEP). Nous sélectionnons le type spécifique de ce masque en fonction du critère d'atteinte du niveau maximum d'entropie de la séquence finale d'octets (précédemment chiffrée par ce module). Nous utilisons différents masques pour différentes langues. Leur efficacité dépasse généralement 7,9 bits d’entropie par octet. Nous ne publions pas les valeurs spécifiques de ces masques de bits, car cela n'affecte pas la force cryptographique de notre système. Cette décision de notre part ne viole pas le principe de Kerkhoffs, puisque les codes des algorithmes sont entièrement publiés ici et que les valeurs spécifiques de SHUFFLE_SWAP_STEP ne font pas référence à des codes, mais plutôt à des données clés, que le principe de Kerkhoffs n'interdit à personne de garder secrètes.

Quant à la procédure shuffle_rol (sa réversibilité est assurée par shuffle_ror), son ajout au chiffrement de Pavlenko a été provoqué par la nécessité de trouver des paramètres optimaux pour augmenter l'entropie d'une séquence d'octets abstraite dans n'importe quelle langue européenne. Nos recherches préliminaires ont révélé un certain nombre de valeurs optimales de SHUFFLE_SWAP_STEP_1 et SHUFFLE_SWAP_STEP_2 qui ont produit les valeurs d'entropie les plus élevées pour le texte résultant (soumis au chiffrement de Pavlenko) en fonction de la langue. Mais les paramètres qui étaient optimaux pour une langue ne l’étaient pas pour une autre. C'est pour résoudre ce problème que nous avons ajouté ces procédures (shuffle_rol et shuffle_ror) à notre algorithme. En conséquence, l'entropie finale de tout texte dans n'importe quelle langue (après traitement avec le chiffrement de Pavlenko) avec tous les paramètres optimaux pour une autre langue dépassait significativement le chiffre de 7,9 bits par octet (ce qui est pratiquement identique à une séquence purement aléatoire). Et, dans cette situation, après avoir ajouté ces procédures, il est devenu clair que la signification pratique du choix de valeurs spécifiques à la langue pour les paramètres SHUFFLE_SWAP_STEP_1 et SHUFFLE_SWAP_STEP_2 n'a plus aucune signification pratique. En particulier, les valeurs que nous avons sélectionnées précédemment pour la langue anglaise fonctionnent parfaitement pour toutes les autres langues européennes, y compris le cyrillique (russe et ukrainien).

Ceux qui souhaitent tester le fonctionnement de cet algorithme peuvent par exemple utiliser les valeurs suivantes de SHUFFLE_SWAP_STEP_1 et SHUFFLE_SWAP_STEP_2 : 0xC3 et 0x3C. Dans nos expériences, ces paramètres (purement aléatoires et en aucun cas les plus optimaux) ont fourni une entropie de 7,97 bits par octet pour les textes dans toutes les langues de ce site (russe, ukrainien, anglais, etc.) et un écart type inférieur à 7 pour un texte de 8192 octets de long (la fréquence moyenne d'occurrence des octets dans ce cas est de 8192:256=32). On peut véritablement parler de transformation d’un texte significatif dans n’importe quelle langue européenne en une séquence de symboles complètement aléatoires.

Naturellement, toute paire (ou entreprise) d'abonnés communiquant entre eux via Extra Systems Cypher Net doit avoir un fichier «shuffle.encrypt» identique. Initialement, les abonnés reçoivent ces fichiers de notre part, mais, comme indiqué ci-dessus, ils peuvent les créer eux-mêmes et les mélanger. Il est seulement important que dans ce cas, pour garantir l'identité de tous les ensembles, ils les transfèrent physiquement à leurs partenaires (lors d'une réunion personnelle ou par l'intermédiaire d'un coursier de confiance).

Le contenu de cette page est également disponible en anglais, allemand, portugais, espagnol, italien, ukrainien et en russe.


© Extra Systems, 2024 Extra Web Top