Donc, nous envoyons des agents super secrets Alice et Bob dans un pays ennemi sous couverture. Au cours de la mission, ils devront contacter et travailler ensemble, échanger des informations, des affaires d'espionnage ordinaires. Bien sûr, tout cela doit être fait dans le respect de toutes les règles et procédures de sécurité possibles.
En effet, au dernier tour, nous voulons les révéler: tant la mission elle-même que les agents eux-mêmes et toute la sécurité nationale sont menacés. Par conséquent, il est dans notre intérêt de donner aux espions le minimum d'informations nécessaires. En particulier, moins ils se connaissent et connaissent les techniques de communication, mieux c'est.
Mais comment identifieront-ils alors leur camarade de quartier général?

TL; DR - inventer un mécanisme d'authentification des utilisateurs utilisant la stéganographie pour une agence imaginaire à trois caractères d'un pays inexistant.
À propos des peaux de loups et de moutons
Une couverture est une couverture, donc ni Alice ni Bob ne doivent éveiller les soupçons de leurs actions. Une bonne planification implique une paranoïa quant à leur suivi constant à tous les niveaux possibles. Cet article ne traitera pas de la tâche d'échange direct d'informations (il mérite sa propre série séparée), mais seulement d'un moyen de s'assurer qu'il est transmis par ceux qui en ont besoin à quiconque en a besoin.
Très probablement, les deux espions auront une histoire sous la forme de citoyens ordinaires, en outre, aucunement liés l'un à l'autre. Par conséquent, vous devez immédiatement opposer votre veto à l'utilisation d'outils cryptographiques classiques et de canaux sécurisés - chaque agent de contre-espionnage sait que les personnes honnêtes qui n'ont pas de connexion étroite n'ont rien à cacher.
Que faire
Bien sûr, une telle tâche n'est pas nouvelle, elle a heureusement existé et a été résolue bien avant l'avènement de celles de votre Internet. Et non seulement cela a été décidé, mais certaines décisions ont été renforcées dans la culture et se retrouvent toujours dans les livres, les films et les jeux.
Regardons une telle scène: deux personnes en longs manteaux convergent dans un lieu public et échangent des phrases très étranges. Si la phrase et la réponse initiales sont correctes, l'authentification a réussi et les gens échangent des dossiers marqués "Top Secret" et divergent dans des directions inconnues.
L'inconvénient d'un tel schéma est immédiatement évident - les phrases doivent être gardées secrètes et souvent modifiées , ce qui n'est pas très simple en territoire ennemi. Dans le même temps, afin de ne pas être parlés au hasard et de ne pas conduire au cas du KDPV, ils deviennent assez importants et aléatoires, ce qui signifie qu'ils peuvent donner des agents qui les prononcent.

À l'ère du numérique, nous n'aimons pas cette méthode. Surtout si vous vous souvenez que presque tous les canaux de communication sont contrôlés par quelqu'un et sont exploités à la fois pour de bons et de mauvais motifs. Et peu importe ce qu'ils nous assurent, la vie des gens ne doit pas faire confiance à la politique de confidentialité de Facebook.

Stéganographie (encore?)
Hérisson, il est clair que la capacité de se cacher dans une telle situation semble plus attrayante que jamais. Et en fait, même la méthode décrite est sa sous-espèce - les phrases de code peuvent être considérées comme des conteneurs avec seulement un bit d'information à l'intérieur.
Le même mammifère du détachement insectivore comprend qu'il ne s'agit pas simplement de se jeter des stégoconteneurs. Un tel échange provoquera presque plus de suspicion qu'un cryptage PGP commun, nous ne sommes donc pas intéressés.
Et alors?
Contrairement aux cryptogrammes, les stegocontainers ont un avantage évident - le contexte de l'application. Tout texte, image, fichier audio, etc., en plus du contenu évident, comporte également la possibilité de sa discussion naturelle et peut être envoyé non seulement depuis la baie, mais dans le processus d'un dialogue qui ne provoque pas de soupçons.
Armés de telles idées, nous pouvons déjà élaborer un protocole d'authentification stéganographique simple basé sur une clé commune:
- A -> B: un joli message demandant un conteneur stéganographique de certains paramètres;
- B: sélectionne le conteneur C qui correspond au contexte et aux paramètres demandés;
- B: crée de manière similaire un message M ;
- B -> A: C '= Embed (C, M, K) ;
- A: vérifie la conformité de C ' avec les paramètres définis;
- A -> B: M '= extrait (C', K) ;
- B: vérifie si M et M ' correspondent.
Un tel protocole présente des inconvénients évidents - Alice et Bob doivent avoir à la fois une clé commune et des fonctions d'intégration et d'extraction. Leur compromis peut conduire à la possibilité d'une analyse détaillée de la méthode d'authentification de l'ennemi et mettre en danger les autres utilisateurs et le quartier général. Quelque chose doit être réparé.
L'artiste n'est pas un bug
Si le lecteur est allé à l'école après l'avènement des cours d'informatique, il devrait se souvenir d'avoir appris les bases de l'algorithmisation en utilisant l'interprète d'une tortue, d'une fourmi et autres. Leur idée était de démontrer les possibilités d'optimiser un grand nombre d'actions individuelles manuelles en créant des programmes simples. Pour résoudre notre problème, nous devons aller dans la direction opposée.

Puisque nous pouvons simplifier l'écriture de l'algorithme final de la séquence des étapes à leur description procédurale en fonction des paramètres donnés, nous pouvons effectuer le processus inverse. Si vous imaginez un conteneur comme un tableau de certains de ses composants, l'incorporation d'un message par clé peut être écrite comme une séquence ordonnée d'opérations sur des éléments de conteneur à des indices spécifiques avec divers paramètres constants.
C'est là que les mathématiques ne commencent pas, alors je demande aux timides de parcourir simplement les paragraphes difficiles à regarder jusqu'à la section des opérations ou même un peu plus loin. Il ne se passera rien de terrible, je le promets.
Pour intégrer les données, nous avons besoin d'une séquence de la forme: (f1, S1, i, D1), (f2, S2, j, D2) ... , où:
- Di - une partie des données intégrées;
- i, j sont les indices des éléments du conteneur;
- fi: (State, Element, D) -> (State, Element) - fonction d'intégration;
- Si est un certain état, le contexte de l'opération, (El ', S [i + 1]) = fi (Si, El, Di) .
Pour l'extraire, vous n'avez pas besoin de stocker des parties des données (K.O.), donc il y a suffisamment de triplets: (g1, S1, I1), (g2, S2, I2) ... avec les mêmes valeurs, seulement gi: (State, Element) -> (État, D) .
Tout cela peut être représenté par le diagramme symétrique ci-dessous. Si pour une raison quelconque je n'ai pas réussi à atteindre la clarté, alors ce n'est pas effrayant, lisez la suite.

On peut voir que la fonction d'intégration a un plus grand nombre de degrés de liberté. Contrairement à sa sœur, elle modifie le conteneur, tout en le faisant sur la base de deux éléments indépendants - les données intégrées et l'élément. Merci, ou plus précisément à cause de cela, deux approches globales de la mise en œuvre de l'algorithme de stéganographie par un tel système sont possibles:
- Choisissez les indices les plus appropriés des éléments à modifier en fonction de la fonction d'intégration (moins perceptible ou ne l'exigeant pas du tout) et transférez la séquence formée liée à un conteneur spécifique. Avec cette approche, ils doivent être isolés les uns des autres avant qu'il ne soit nécessaire de les intégrer à l'aide de méthodes classiques telles que le chiffrement et d'autres supports sécurisés;
- Trouvez une telle méthode de division du conteneur en éléments et la fonction d'intégration que tout changement attendu par celui-ci sera également invisible. Dans ce cas, la séquence est indépendante du conteneur et peut être créée même par un générateur complètement aléatoire. Moins de flexibilité et un manque de contrôle du pire des cas. D'un autre côté, cette approche est plus simple et plus pratique lorsqu'elle est appliquée sur le terrain, je vais donc l'utiliser ci-dessous.
Si l'état n'est pas requis pour l'algorithme, alors tout ce qui précède reste valide, simplement sans une seule lettre et un bloc sur le diagramme. Sans cela, c'est encore plus facile, en fait.
Et pourquoi en avons-nous besoin?
Maintenant, si vous savez à l'avance quels conteneurs avec quels messages et clés seront utilisés, au lieu de révéler complètement les parties de l'algorithme, vous pouvez générer et donner aux agents à utiliser uniquement ces séquences et un interprète pour elles. Eh bien, ok, pas seulement donner, bien sûr, mais plus sur cela plus tard.
Ajouter une asymétrie
Même un artiste tortue peut dessiner un carré de centaines de façons différentes, en changeant simplement l'ordre des opérations et en en ajoutant de nouvelles. Cela signifie que personne ne nous dérange et ne fait de même avec les séquences décrites pour les données d'entrée fixes.
Autrement dit, nous pouvons prendre la séquence d'intégration, ajouter de nouvelles opérations, tout mélanger, et pour que le résultat reste le même. Sauf en présence d'un état, il sera nécessaire de le suivre et d'ajouter séparément les modifications nécessaires à la séquence. C'est pourquoi sans cela, c'est plus facile, oui.
D'une manière ou d'une autre, après un tel pétrissage et un tel bruit, même le plongeur lui-même ne pourra plus comprendre ce qu'il intègre réellement: toute séquence de N opérations représentera N! messages potentiellement intégrés - un pour chaque permutation des parties intégrées. En même temps, N lui-même est une grande question. Par conséquent, on peut appeler de telles séquences ouvertes - elles ne fournissent aucune information ni sur le message incorporé, ni sur l'algorithme et la clé utilisés.
Lors de l'extraction d'informations, il est très important pour nous à la fois l'ordre (pour restaurer le message correct de tous les possibles) et le nombre de pièces à extraire, de sorte que les séquences d'extraction restent inchangées depuis le moment de la naissance. Comme ils contiennent implicitement des informations sur la clé, le générateur et l'algorithme utilisés, ils doivent, comme les animaux du livre rouge, être stockés et protégés. Et gardez le secret.
Qu'est-ce que l'asymétrie a à voir avec cela? Le fait est que désormais chaque séquence d'extraction est associée à un nombre infini de plongeurs. Et restaurer l'un à partir de l'autre est, dans le cas général, une tâche insoluble.
Nous opérons
Nous oublions toutes les quasi-mathématiques et revenons à la tâche d'origine - comment pouvons-nous envoyer Alice et Bob en territoire ennemi afin de:
- ils ne se connaissaient pas
- n'avait aucun algorithme secret sous la main
- mais pourriez-vous vous vérifier tout en communiquant sur un canal ouvert?
Eh bien, avec le premier paragraphe, tout est clair, nous ne leur donnons simplement aucune information explicite les uns sur les autres, pas de clés partagées. Pour la seconde, vous devez vous souvenir de la description du protocole ci-dessus. Maintenant, nous pouvons exclure directement les algorithmes d' intégration et d' extraction qui représentent des secrets d'état potentiels et tout cela. Et, en tenant compte de cela, pour le troisième, il est possible d'élaborer le protocole en deux étapes suivant.
Génération d'informations d'authentification avant le début de la mission avec le siège en tant que partie de confiance de Trent:
- T: sélectionne l'algorithme secret et la clé secrète K, crée avec leur aide:
- Extraire la séquence Ex ;
- adapté à l'authentification (ci-dessous) Contexte Ctx ;
- T -> A: Ctx, Ex ;
- T: en utilisant Ex et le contexte créé, génère:
- message M non utilisé précédemment pour les agents sélectionnés parmi les pièces | Ex | ;
- une séquence unique de Em , la rendant ouverte comme décrit ci-dessus;
- T -> B: Em, h (M) , si vous le souhaitez, crée des ensembles supplémentaires.
Ainsi, Alice n'a qu'une seule séquence pour toutes les occasions et le contexte des contacts futurs, et Bob devient l'heureux propriétaire d'un ensemble de séquences uniques et de hachages de messages qu'ils intègrent.
Le protocole d'authentification déjà pendant la mission ressemble à ceci:
- A -> B: un message IM initiateur basé sur le contexte Ctx avec une description du conteneur;
- B: sélectionne le C ~ IM approprié;
- B -> A: C '= Em (C) ;
- R: vérifie la conformité avec C '~ IM (puisque les modifications sont invisibles, elles doivent être enregistrées);
- A -> B: M '= Ex (C') , marque M 'comme utilisé;
- B: vérifie, h (M ') == h (M) , détruit Em, h (M) .
Un lecteur attentif remarquera qu'avant le protocole, Alice et Bob ne disposent que d'un ensemble d'informations, ce qui en soi ne signifie rien pour eux, ni pour un adversaire potentiel, et uniquement pendant le "jeu avec les couleurs".
Chaque ensemble ouvert de Bob n'est utilisé qu'une seule fois, ce qui est contrôlé par l'avant-dernière étape d'Alice. Lorsqu'elle rencontre un M précédemment utilisé (et, par conséquent, un Em invisible par elle) par une autre personne, elle se rend compte que l'un de ses «associés» est un faux.
L'utilisation répétée par la même personne lui dit qu'elle n'est pas au courant des subtilités du protocole et n'est certainement pas celle avec qui elle a dû entrer en contact. Eh bien, mieux vaut tard que jamais.

D'accord, voilà à quoi tout cela semble trop compliqué et incompréhensible. Quelqu'un est arrivé ici?
Mieux vaut le démontrer dans la pratique, car même les espions eux-mêmes n'ont pas besoin de connaître les détails du protocole à utiliser, encore moins les pauvres lecteurs. Tout d'abord un peu sur la façon dont tout cela a été mis en œuvre.
Haute technologie
Il ne reste donc qu'à écrire tout ce qui est nécessaire au protocole. Eh bien, vous ne faites pas tout avec vos mains (même si vous le pouvez). Et aujourd'hui, la victime de mon code va ... faire tourner la roue de la fortune ... Java? Et bien, en même temps, tout en STL sera, vous n'aurez rien à chercher.
Commençons par l'API requise. Pour fonctionner, il vous suffit de déterminer la classe du tableau d'éléments de conteneur avec la possibilité de recevoir et de modifier des éléments par index:
class MyContainer implements StegoContainer<MyElement> { public MyElement get(int i) {
Une utilisation ultérieure est réduite à la création d'une enveloppe d'un automate stéganographique sur le conteneur nécessaire et à la fourniture des fonctions d'incorporation et d'extraction à son entrée:
StegoMachine<MyState, MyElement> myMachine = new StegoMachine( initialState, new MyContainer<MyElement>() ); final StegoEmbed myEmbed = (st, el, dp) -> {
Les classes avec le suffixe Stateless sont utilisées de la même manière, si la mise en œuvre de l'algorithme ne nécessite pas de maintenir l'état interne.
Les générateurs de séquences peuvent fonctionner comme vous le souhaitez et n'ont pas d'API commune. Dans le cas général, tout peut faire partie des données en général, des bits isolés à l'art rupestre dans un codage séparé.
Exemple d'implémentation
À propos de la méthode
À titre d'exemple d'implémentation, en utilisant les interfaces créées, j'ai implémenté un algorithme simple de la famille LSB pour les images bitmap avec compression sans perte. Leurs éléments sont des pixels qui n'ont pas de voisins dans le bit le moins significatif de tous les composants RVB. La fonction d'intégration fonctionne avec des bits uniques des données source et modifie simplement le bit de poids faible de la valeur de l'un des composants (vers lequel l'index pointera).
C'est assez simple, mais c'est génial pour implémenter le protocole, car changer un élément est également imperceptible selon son choix, donc les indices des éléments à changer sont générés à l'aide d'un générateur aléatoire. Dans le cas de Java, en utilisant SecureRandom , mais si vous le souhaitez, il passe facilement à sa source d'entropie.
Néanmoins, c'est une méthode très simple, je ne recommande pas de l'utiliser pour de vrais espions.
À propos des hachages
Étant donné que le texte a tendance à être déformé en fonction de l'identité simulée de l'agent (certains ne mettent pas de majuscules, d'autres aiment mettre des émoticônes, etc., d'autres sont généralement analphabètes), je suggère d'utiliser sha256 pour calculer le hachage, mais uniquement à partir de mots imprimés en minuscules:
h("Hello world?...") == h("hello, world!11")
À propos de l'interface
Le progiciel se compose de deux parties - l'une pour générer des séquences et d'autres hachages pour Trent, l'autre pour intégrer et vérifier la conformité des messages reçus.
Le travail avec les deux se produit à partir de la ligne de commande via ses arguments et ses flux d'entrée-sortie; aucune autre interface n'a été fournie (peur et horreur). Pourtant, être cet employé du quartier général, que l'espion - signifie avoir une sorte de qualification. Eh bien, sinon, je vais toujours montrer un exemple.
Que font-ils tous?
Pour commencer, Trent au siège doit élaborer des informations d'authentification. En particulier, réfléchir à l'avance à une situation dans laquelle les agents travailleront.
Par exemple, laissez Bob être un graphiste indépendant et Alice son client. L'authentification aura lieu sous le couvert d'une commande pour créer des graphiques / design / autre chose.
Nous rapportons ces informations utiles aux deux et revenons au protocole lui-même. Nous préparerons à l' avance un message intégré M.txt approprié, en minimisant le nombre de caractères qu'il contient : "cela me convient bien où transférer de l'argent." Générez Em et Ex à l'aide de l'utilitaire pour Trent:
Trent@HQWorkstation:~$ java -jar HQUtil.jar -ex $(stat -c%s "M.txt") 4096 > Ex.txt Trent@HQWorkstation:~$ cat Ex.txt | java -jar HQUtil.jar -em "$(cat M.txt)" 0.25 4096 > Em.txt Trent@HQWorkstation:~$ cat M.txt | java -jar HQUtil.jar -h > hash.bin
Ici $(stat -c%s "M.txt")
renvoie la taille du message en octets, et 4096 - la restriction sur la plage d'index générés (pour permettre l'utilisation de conteneurs plus petits). De même, $(cat M.txt)
utilisé pour passer le message lui-même au paramètre de ligne de commande. En principe, vous pouvez vous passer de la bash, en utilisant votre propre travail manuel, mais pour qui c'est plus pratique.
Ex.txt est transmis à Alice, Em.txt et hash.bin à Bob. Imaginez maintenant que les agents se sont déployés avec succès et souhaitent communiquer entre eux - nous procédons à l'exécution du protocole. Bob place son curriculum vitae ou offre d'emploi sur un échange, et Alice commence la communication:
: , %_% : , . ? : ,
Bob cherche une image d'un parapluie, peut-être même le dessine lui-même si l'âme est créative, compresse / impose un filigrane un peu (ou ce que les pigistes y font maintenant) et le fait:
Bob@PC:~$ cat Em.txt | java -jar SpyUtil.jar -e umbrella.png
Après avoir attendu un moment, faisant semblant de travailler, si en réalité il ne l'a pas fait, il envoie naturellement à Alice le conteneur reçu, en gardant à l'esprit le contexte:
: , ,
Transmet un parapluie avec un message, 670kb Cela, à son tour, récupère le message stocké en interne:
Alice@PC:~$ cat Ex.txt | java -jar SpyUtil.jar -e umbrella.png
Transforme un ensemble de mots en une phrase normale et l'envoie à Bob:
: , , ?
Il vérifie l'exactitude du message:
Bob@PC:~$ java -jar SpyUtil.jar -c hash.bin ", , ?" , , ? - Correct
Et continue une communication facile, si tout va bien. L'ensemble du dialogue de la part de l'observateur ressemble à ceci:
Il est clair que le contre-espionnage ne trouvera rien de suspect dans tout cela intercepté. En fait, même les méthodes d'analyse stéganique dans ce cas ne seront pas toujours appliquées - eh bien, quelqu'un a commandé une photo d'un parapluie pour 5 dollars, il a trouvé quelque chose pour surprendre Internet. Les ressources informatiques et les personnes ne sont pas interminables pour vérifier chacune de ces situations. L'authentification a réussi, le rideau.
-> github