Also schicken wir die Super-Geheimagenten Alice und Bob in ein verdecktes feindliches Land. Während der Mission müssen sie Kontakt aufnehmen und zusammenarbeiten, Informationen austauschen, gewöhnliche Spionageangelegenheiten. All dies muss natürlich unter Einhaltung aller möglichen Regeln und Sicherheitsverfahren erfolgen.
In der letzten Runde wollen wir sie aufdecken: Sowohl die Mission selbst als auch die Agenten selbst und die gesamte nationale Sicherheit sind gefährdet. Daher liegt es in unserem Interesse, Spionen die minimal notwendigen Informationen zu geben. Insbesondere, je weniger sie voneinander und von den Kommunikationstechniken wissen, desto besser.
Aber wie werden sie dann ihren Kameraden im Hauptquartier identifizieren?

TL; DR - Erfindung eines Benutzerauthentifizierungsmechanismus unter Verwendung der Steganographie für eine imaginäre dreistellige Agentur eines nicht existierenden Landes.
Über Wölfe und Schafsfelle
Ein Cover ist ein Cover, daher sollten weder Alice noch Bob durch eine ihrer Handlungen Verdacht erregen. Eine ordnungsgemäße Planung impliziert Paranoia hinsichtlich der ständigen Überwachung auf allen möglichen Ebenen. Dieser Beitrag befasst sich nicht mit der Aufgabe des direkten Informationsaustauschs (er verdient eine eigene Reihe), sondern nur mit einer Möglichkeit, sicherzustellen, dass er von denjenigen, die ihn benötigen, an jeden weitergegeben wird, der ihn benötigt.
Höchstwahrscheinlich werden beide Spione eine Geschichte im Format gewöhnlicher Bürger haben, die in keiner Weise miteinander verbunden ist. Daher müssen Sie sofort ein Veto gegen die Verwendung klassischer kryptografischer Tools und sicherer Kanäle einlegen - jeder Spionageabwehragent weiß, dass ehrliche Menschen, die keine enge Beziehung haben, nichts zu verbergen haben.
Was zu tun ist?
Natürlich ist eine solche Aufgabe nicht neu, sie existierte glücklich und wurde lange vor dem Aufkommen dieser in Ihrem Internet gelöst. Und es wurde nicht nur entschieden, so dass einige Entscheidungen in der Kultur gestärkt wurden und immer noch in Büchern, Filmen und Spielen zu finden sind.
Schauen wir uns eine solche Szene an: Zwei Menschen in langen Mänteln kommen an einem öffentlichen Ort zusammen und tauschen sehr seltsame Sätze aus. Wenn der einleitende Satz und die Antwort korrekt sind, war die Authentifizierung erfolgreich, und die Benutzer tauschen Ordner mit der Bezeichnung "Top Secret" aus und weichen in unbekannte Richtungen ab.
Der Nachteil eines solchen Schemas liegt auf der Hand - Phrasen müssen geheim gehalten und oft geändert werden , was im feindlichen Gebiet nicht sehr einfach ist. Gleichzeitig werden sie, um nicht zufällig ausgesprochen zu werden und nicht zum Fall von KDPV zu führen, ziemlich prominent und zufällig, was bedeutet, dass sie Agenten ausgeben können, die sie aussprechen.

Im Zeitalter der digitalen Technologie mögen wir diese Methode nicht. Vor allem, wenn Sie sich daran erinnern, dass fast alle Kommunikationskanäle von jemandem gesteuert werden und sowohl für gute als auch für schlechte Motive genutzt werden. Und egal, was sie uns versichern, das Leben der Menschen sollte in der Datenschutzrichtlinie von Facebook nicht als vertrauenswürdig eingestuft werden.

Steganographie (schon wieder?)
Igel es ist klar, dass die Fähigkeit, sich in einer solchen Situation zu verstecken, attraktiver als je zuvor aussieht. Tatsächlich handelt es sich bei der beschriebenen Methode sogar um die Unterart - Codephrasen können als Container mit nur einer Information betrachtet werden.
Das gleiche Säugetier der Insektenfresser-Abteilung versteht, dass es nicht nur darum geht, Stegocontainer gegeneinander zu werfen. Ein solcher Austausch wird fast mehr Verdacht erregen als eine übliche PGP-Verschlüsselung, daher sind wir nicht interessiert.
Was ist dann?
Im Gegensatz zu Kryptogrammen haben Stegocontainer einen offensichtlichen Vorteil - den Anwendungskontext. Jeder Text, jedes Bild, jede Audiodatei usw. bietet neben dem offensichtlichen Inhalt auch die Möglichkeit einer natürlichen Diskussion und kann nicht nur aus der Bucht gesendet werden, sondern auch während eines Dialogs, der keinen Verdacht erregt.
Mit genau diesen Ideen können wir bereits ein einfaches steganografisches Authentifizierungsprotokoll erstellen, das auf einem gemeinsamen Schlüssel basiert:
- A -> B: eine hübsche Nachricht, die einen steganografischen Container mit bestimmten Parametern anfordert;
- B: wählt Container C aus , der dem angeforderten Kontext und den angeforderten Parametern entspricht;
- B: erzeugt auf ähnliche Weise eine Nachricht M ;
- B -> A: C '= Einbetten (C, M, K) ;
- A: Überprüft C ' auf Übereinstimmung mit den eingestellten Parametern;
- A -> B: M '= Extrakt (C', K) ;
- B: Überprüft, ob M und M ' übereinstimmen.
Ein solches Protokoll hat offensichtliche Nachteile - Alice und Bob müssen sowohl einen gemeinsamen Schlüssel als auch Einbettungs- und Extraktionsfunktionen haben. Ihr Kompromiss kann zu einer detaillierten Analyse der Authentifizierungsmethode des Feindes führen und sowohl andere Benutzer als auch das Hauptquartier gefährden. Es muss etwas repariert werden.
Der Künstler ist kein Käfer
Wenn der Leser nach dem Aufkommen des Computerunterrichts zur Schule ging, sollte er sich daran erinnern, die Grundlagen der Algorithmusisierung mit dem Darsteller einer Schildkröte, einer Ameise und dergleichen gelernt zu haben. Ihre Idee war es, die Möglichkeiten der Optimierung einer großen Anzahl manueller Einzelaktionen durch die Erstellung einfacher Programme aufzuzeigen. Um unser Problem zu lösen, müssen wir in die entgegengesetzte Richtung gehen.

Da wir das Schreiben des endgültigen Algorithmus von der Abfolge der Schritte bis zu ihrer prozeduralen Beschreibung gemäß den angegebenen Parametern vereinfachen können, können wir den inversen Prozess ausführen. Wenn Sie sich einen Container als Array einiger seiner Komponenten vorstellen, kann das Einbetten einer Nachricht per Schlüssel als geordnete Folge von Operationen an Containerelementen an bestimmten Indizes mit verschiedenen konstanten Parametern geschrieben werden.
Hier beginnt die Nichtmathematik, daher bitte ich die Schüchternen, einfach durch die schwierig aussehenden Absätze zum Operationsabschnitt oder sogar ein wenig weiter zu blättern. Ich verspreche, dass nichts Schreckliches passieren wird.
Um die Daten einzubetten, benötigen wir eine Folge der Form: (f1, S1, i, D1), (f2, S2, j, D2) ... , wobei:
- Di - ein Teil der eingebetteten Daten;
- i, j sind die Indizes der Elemente des Containers;
- fi: (Zustand, Element, D) -> (Zustand, Element) - Einbettungsfunktion;
- Si ist ein bestimmter Zustand, der Kontext der Operation (El ', S [i + 1]) = fi (Si, El, Di) .
Um es zu extrahieren, müssen Sie keine Teile der Daten (K.O.) speichern, daher gibt es genügend Tripel: (g1, S1, I1), (g2, S2, I2) ... mit denselben Werten, nur gi: (State, Element) -> (Staat, D) .
All dies kann durch das folgende symmetrische Diagramm dargestellt werden. Wenn ich es aus irgendeinem Grund nicht geschafft habe, Klarheit zu erreichen, ist es nicht beängstigend, lesen Sie einfach weiter.

Es ist ersichtlich, dass die Einbettungsfunktion eine größere Anzahl von Freiheitsgraden aufweist. Im Gegensatz zu ihrer Schwester ändert sie den Container auf der Grundlage von zwei unabhängigen Elementen - eingebetteten Daten und dem Element. Dank oder genauer gesagt aus diesem Grund sind zwei globale Ansätze zur Implementierung des Steganographiealgorithmus durch ein solches System möglich:
- Wählen Sie die am besten geeigneten Indizes der Elemente aus, die entsprechend der Einbettungsfunktion geändert werden sollen (am wenigsten wahrnehmbar oder überhaupt nicht erforderlich), und übertragen Sie die gebildete Sequenz, die an einen bestimmten Container gebunden ist. Bei diesem Ansatz müssen sie voneinander isoliert werden, bevor die Einbettung mit klassischen Methoden wie Verschlüsselung und anderen sicheren Medien erforderlich wird.
- Finden Sie eine solche Methode zum Teilen des Containers in Elemente und die Einbettungsfunktion, dass jede erwartete Änderung durch ihn gleichermaßen unsichtbar ist. In diesem Fall ist die Sequenz unabhängig vom Container und kann sogar von einem vollständig zufälligen Generator erstellt werden. Weniger Flexibilität und mangelnde Kontrolle über den schlimmsten Fall gehen. Auf der anderen Seite ist dieser Ansatz einfacher und bequemer, wenn er vor Ort angewendet wird. Im Folgenden werde ich ihn daher verwenden.
Wenn der Status für den Algorithmus nicht erforderlich ist, bleibt alles oben Genannte gültig, einfach ohne einen einzelnen Buchstaben und Block im Diagramm. Ohne das ist es sogar noch einfacher.
Und warum brauchen wir das?
Wenn Sie jetzt im Voraus wissen, welche Container mit welchen Nachrichten und Schlüsseln verwendet werden, können Sie Agenten zur Verwendung nur solcher Sequenzen und einen Interpreter für diese generieren und angeben, anstatt die Teile des Algorithmus vollständig zu enthüllen. Na gut, natürlich nicht nur geben, sondern später mehr dazu.
Asymmetrie hinzufügen
Sogar ein Schildkrötenkünstler kann ein Quadrat auf Hunderte von verschiedenen Arten zeichnen, indem er einfach die Reihenfolge der Operationen ändert und neue hinzufügt. Dies bedeutet, dass uns niemand stört und dasselbe mit den beschriebenen Sequenzen für feste Eingabedaten tut.
Das heißt, wir können die Einbettungssequenz übernehmen, neue Operationen hinzufügen, alles mischen und so, dass das Ergebnis gleich bleibt. Es sei denn, bei Vorhandensein eines Zustands muss dieser verfolgt und die erforderlichen Änderungen an der Sequenz separat hinzugefügt werden. Deshalb ist es ohne einfacher einfacher, ja.
Auf die eine oder andere Weise kann selbst der Einbettungskünstler nach einem solchen Kneten und Rauschen nicht mehr verstehen, was er tatsächlich einbettet: Jede Folge von N Operationen repräsentiert N! potenziell eingebettete Nachrichten - eine für jede Permutation der eingebetteten Teile. Gleichzeitig ist N selbst eine große Frage. Daher kann man solche Sequenzen als offen bezeichnen - sie liefern weder Informationen über die eingebettete Nachricht noch über den verwendeten Algorithmus und Schlüssel.
Beim Extrahieren von Informationen ist es für uns sowohl die Reihenfolge (um die gleiche korrekte Nachricht von allen möglichen wiederherzustellen) als auch die Anzahl der zu extrahierenden Teile sehr wichtig, damit die Extraktionssequenzen vom Moment der Geburt an unverändert bleiben. Da sie implizit Informationen über den verwendeten Schlüssel, Generator und Algorithmus enthalten, müssen sie wie Tiere aus dem Roten Buch gespeichert und geschützt werden. Und halte es geheim.
Was hat Asymmetrie damit zu tun? Tatsache ist, dass jetzt jede Extraktionssequenz einer unendlichen Anzahl von Einbettern zugeordnet ist. Und das Wiederherstellen voneinander ist im Allgemeinen eine unlösbare Aufgabe.
Wir operieren
Wir vergessen jede mathematische Nähe und kehren zur ursprünglichen Aufgabe zurück - wie können wir Alice und Bob in feindliches Gebiet schicken, um:
- Sie kannten sich nicht
- hatte keine geheimen Algorithmen zur Hand
- Aber könnten Sie sich gegenseitig verifizieren, während Sie auf einem offenen Kanal kommunizieren?
Nun, mit dem ersten Absatz ist alles klar, wir geben ihnen nur keine expliziten Informationen über einander, keine gemeinsamen Schlüssel. Für den zweiten müssen Sie sich die Beschreibung des obigen Protokolls merken. Jetzt können wir die Einbettungs- und Extraktionsalgorithmen , die potenzielle Zustandsgeheimnisse darstellen, und all das direkt ausschließen. Und unter Berücksichtigung dessen ist es für das dritte möglich, das folgende zweistufige Protokoll zu erstellen.
Generierung von Authentifizierungsinformationen vor Beginn der Mission mit Hauptsitz als vertrauenswürdige Partei von Trient:
- T: wählt den geheimen Algorithmus und den geheimen Schlüssel K aus, erstellt mit deren Hilfe:
- Sequenz extrahieren Bsp .;
- geeignet zur Authentifizierung (unten) Ctx- Kontext;
- T -> A: Ctx, Ex ;
- T: Mit Ex und dem erstellten Kontext wird Folgendes generiert:
- Nachricht M , die zuvor für die ausgewählten Agenten aus den Teilen | Ex | nicht verwendet wurde ;;
- eine einmalige Folge von Em , die es wie oben beschrieben öffnet;
- T -> B: Em, h (M) erzeugt , falls gewünscht, zusätzliche Mengen.
Somit hat Alice nur eine Sequenz für alle Gelegenheiten und den Kontext des zukünftigen Kontakts, und Bob wird der glückliche Besitzer einer Reihe von einmaligen Sequenzen und Nachrichten-Hashes, die sie einbetten.
Das Authentifizierungsprotokoll bereits während der Mission sieht folgendermaßen aus:
- A -> B: eine initiierende IM- Nachricht basierend auf dem Ctx- Kontext mit einer Beschreibung des Containers;
- B: wählt das geeignete C ~ IM aus ;
- B -> A: C '= Em (C) ;
- A: prüft die Einhaltung von C '~ IM (da die Änderungen unsichtbar sind, sollten sie gespeichert werden);
- A -> B: M '= Ex (C') , markiert M 'als verwendet;
- B: prüft, h (M ') == h (M) , zerstört Em, h (M) .
Ein aufmerksamer Leser wird feststellen, dass Alice und Bob vor dem Protokoll nur eine Reihe von Informationen haben, die an sich weder für sie noch für einen potenziellen Gegner etwas bedeuten, und zwar nur während des "Spiels mit Farben".
Jeder offene Satz von Bob wird nur einmal verwendet, was durch den vorletzten Schritt von Alice gesteuert wird. Wenn sie ein zuvor verwendetes M (und daher von ihr unsichtbares Em ) einer anderen Person trifft, stellt sie fest, dass einer ihrer "Mitarbeiter" eine Fälschung ist.
Die wiederholte Verwendung durch dieselbe Person sagt ihr, dass sie sich der Feinheiten des Protokolls nicht bewusst ist und sicherlich nicht diejenige ist, mit der sie Kontakt aufnehmen musste. Besser spät als nie.

Okay, so sieht alles zu kompliziert und unverständlich aus. Hat jemand hier?
Lassen Sie es uns in der Praxis besser demonstrieren, denn selbst die Spione selbst müssen die Details des Protokolls nicht kennen, geschweige denn arme Leser. Zunächst nur ein wenig darüber, wie alles umgesetzt wurde.
Hightech
Es bleibt also nur alles zu schreiben, was für das Protokoll notwendig ist. Du machst nicht alles mit deinen Händen (obwohl du kannst). Und heute wird das Opfer meines Codes ... das Glücksrad drehen ... Java? Okay, zur gleichen Zeit wird alles in STL sein, Sie müssen nichts suchen.
Beginnen wir mit der erforderlichen API. Um zu arbeiten, müssen Sie nur die Klasse des Arrays von Containerelementen bestimmen, die Elemente nach Index empfangen und ändern können:
class MyContainer implements StegoContainer<MyElement> { public MyElement get(int i) {
Die weitere Verwendung beschränkt sich darauf, einen Wrapper eines steganografischen Automaten über dem erforderlichen Container zu erstellen und die Funktionen zum Einbetten und Extrahieren seiner Eingabe bereitzustellen:
StegoMachine<MyState, MyElement> myMachine = new StegoMachine( initialState, new MyContainer<MyElement>() ); final StegoEmbed myEmbed = (st, el, dp) -> {
Klassen mit dem Suffix Stateless werden auf die gleiche Weise verwendet, wenn für die Implementierung des Algorithmus der interne Status nicht beibehalten werden muss.
Sequenzgeneratoren können nach Belieben arbeiten und haben keine gemeinsame API. Im allgemeinen Fall kann alles Teil der Daten im Allgemeinen sein, von Einzelbits bis hin zu Felszeichnungen in einer separaten Codierung.
Implementierungsbeispiel
Über Methode
Als Beispiel für die Implementierung habe ich unter Verwendung der erstellten Schnittstellen einen einfachen Algorithmus aus der LSB-Familie für Bitmap-Bilder mit verlustfreier Komprimierung implementiert. Ihre Elemente sind Pixel, die keine Nachbarn im niedrigstwertigen Bit aller RGB-Komponenten haben. Die Einbettungsfunktion arbeitet mit einzelnen Bits der Quelldaten und ändert einfach das niederwertige Bit des Werts einer der Komponenten (auf den der Index verweist).
Es ist recht einfach, aber es eignet sich hervorragend für die Implementierung des Protokolls, da das Ändern eines Elements je nach Auswahl gleichermaßen nicht wahrnehmbar ist. Generieren Sie daher Indizes der zu ändernden Elemente mithilfe eines Zufallsgenerators. Im Fall von Java wird mithilfe von SecureRandom , falls gewünscht, leicht die Entropiequelle geändert.
Trotzdem ist dies eine sehr einfache Methode, die ich echten Spionen nicht empfehlen kann.
Über Hashes
Da der Text abhängig von der simulierten Identität des Agenten tendenziell verzerrt ist (einige setzen keine Großbuchstaben, andere setzen gerne Emoticons usw., andere sind im Allgemeinen Analphabeten), empfehle ich die Verwendung von sha256 zur Berechnung des Hashs, jedoch nur aus gedruckten Wörtern, die in Kleinbuchstaben geschrieben sind:
h("Hello world?...") == h("hello, world!11")
Über die Schnittstelle
Das Softwarepaket besteht aus zwei Teilen - einem zum Generieren von Sequenzen und anderen Hashes für Trent, dem anderen zum Einbetten und Überprüfen empfangener Nachrichten auf Konformität.
Die Arbeit mit beiden erfolgt über die Befehlszeile über ihre Argumente und Eingabe-Ausgabe-Streams, es wurden keine anderen Schnittstellen bereitgestellt (Angst und Entsetzen). Um dieser Angestellte des Hauptquartiers zu sein, bedeutet der Spion dennoch, eine Qualifikation zu haben. Wenn nicht, werde ich noch ein Beispiel zeigen.
Was machen sie alle?
Zunächst muss Trent im Hauptquartier Authentifizierungsinformationen ausarbeiten. Insbesondere, um im Voraus über eine Situation nachzudenken, in der Agenten arbeiten werden.
Lassen Sie Bob zum Beispiel Grafik-Freiberufler und Alice seine Kundin sein. Die Authentifizierung erfolgt unter dem Deckmantel eines Auftrags zum Erstellen von Grafiken / Design / etwas anderem.
Wir melden diese nützlichen Informationen an beide und kehren zum Protokoll selbst zurück. Wir werden im Voraus eine geeignete eingebettete M.txt- Nachricht vorbereiten, um die Anzahl der darin enthaltenen Zeichen zu minimieren: "Es passt mir gut, wohin ich Geld überweisen soll." Generieren Sie Em und Ex mit dem Dienstprogramm für 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
Hier gibt $(stat -c%s "M.txt")
die Größe der Nachricht in Bytes und 4096 die Einschränkung des Bereichs der generierten Indizes zurück (um die Verwendung kleinerer Container zu ermöglichen). In ähnlicher Weise wird $(cat M.txt)
verwendet, um die Nachricht selbst an den Befehlszeilenparameter zu übergeben. Im Prinzip können Sie auf die Bash verzichten, indem Sie Ihre eigene Handarbeit leisten, für wen es jedoch bequemer ist.
Ex.txt wird an Alice, Em.txt und hash.bin an Bob übergeben. Stellen Sie sich nun vor, dass die Agenten erfolgreich bereitgestellt wurden und miteinander kommunizieren möchten - wir fahren mit der Ausführung des Protokolls fort. Bob stellt seinen Lebenslauf oder sein Stellenangebot auf einen Austausch und Alice beginnt mit der Kommunikation:
: , %_% : , . ? : ,
Bob sucht nach einem Bild eines Regenschirms, zeichnet es vielleicht sogar selbst, wenn die Seele kreativ ist, ein Wasserzeichen ein wenig komprimiert / auferlegt (oder was Freiberufler dort gerade tun) und tut:
Bob@PC:~$ cat Em.txt | java -jar SpyUtil.jar -e umbrella.png
Nachdem er eine Weile gewartet hat und so getan hat, als würde er arbeiten, schickt er Alice natürlich den empfangenen Container, wobei er den Kontext berücksichtigt:
: , ,
Überträgt einen Regenschirm mit einer Nachricht, 670kb Dadurch wird die intern gespeicherte Nachricht abgerufen:
Alice@PC:~$ cat Ex.txt | java -jar SpyUtil.jar -e umbrella.png
Verwandelt eine Reihe von Wörtern in einen normalen Satz und sendet ihn an Bob:
: , , ?
Er überprüft die Richtigkeit der Nachricht:
Bob@PC:~$ java -jar SpyUtil.jar -c hash.bin ", , ?" , , ? - Correct
Und weiterhin einfache Kommunikation, wenn alles in Ordnung ist. Der gesamte Dialog des Beobachters sieht ungefähr so aus:
Es ist klar, dass die Spionageabwehr bei all dem Abfangen nichts Verdächtiges finden wird. Tatsächlich werden selbst die Methoden der Stegoanalyse in diesem Fall nicht immer angewendet - nun, jemand hat ein Bild eines Regenschirms für 5 Dollar bestellt und herausgefunden, wie man das Internet überrascht. Rechenressourcen und Mitarbeiter sind nicht endlos, um jede solche Situation zu überprüfen. Die Authentifizierung war erfolgreich, der Vorhang.
-> Github