© rassco/Shutterstock.com
Rubinrote Sicherheit - Teil 2

Nichtdeterministische Verschlüsselung


Im ersten Teil dieser Artikelserie haben wir an Beispielen gesehen, dass eine deterministische Verschlüsselung zu Problemen führen kann. Die Beispiele haben unsere Definition von Sicherheit, nämlich nicht zuzulassen, dass Information gewonnen werden, verletzt.

Deterministische Verschlüsselungsverfahren, d. h. immer gleiche Ciphertexte bei immer gleichen Plaintexten unter denselben Schlüssel, können grundsätzlich nicht zu einer sicheren Mehrfachverschlüsselung mit dem gleichen Schlüssel führen [1]. Natürlich ist es für die Praxis wichtig, dass wir mehr als einmal mit dem gleichen Schlüssel verschlüsseln können, gerade in Hinblick darauf, welch großes Problem der sichere Schlüsselaustausch darstellt. Allzu häufiger Schlüsseltausch zwischen zwei Parteien ist unerwünscht. Die Lösung ist nichtdeterministische Verschlüsselung („Randomized Encryption“). Wenn man mit diesem Verfahren den gleichen Plaintext zigmal verschlüsselt, erhält man mit sehr großer Wahrscheinlichkeit zig unterschiedliche Ergebnisse. Die Stream-Cipher-Konstruktion, wie wir sie bisher kennengelernt haben, ist im Kern ein zutiefst deterministischer Prozess. Zwar setzen wir für die Sicherheit auf einen sicheren Pseudozufallsgenerator, doch dieser wirkt nur auf den Uneingeweihten wie zufällig. Für alle im Besitz des Schlüssels ist der Pseudozufallsgenerator auch nur ein deterministischer Prozess, der, wenn er mit dem Schlüssel gefüttert wird, immer das exakt gleiche Ergebnis ausspucken wird.

Um unsere Verschlüsselung also schlussendlich nichtdeterministisch zu machen, müssen wir auf einem anderen Weg Zufälligkeit in die Berechnung einstreuen, da der Verschlüsselungsalgorithmus als solcher streng deterministisch arbeitet. Wer mit Passwörtern und Passwortsicherheit hantiert hat, findet sich jetzt vielleicht an die „Salt“-Idee erinnert – genau diese Idee liefert auch hier die Lösung. Wir erzeugen ein einmaliges, zufälliges Wegwerfelement, das wir in die Berechnung mit einfließen lassen. Damit der Empfänger dieses Element seinerseits in der Entschlüsselung berücksichtigen kann, teilt der Sender dieses Element dem Empfänger einfach mit. Das Geniale daran: In diesem Wegwerfelement steckt keine Sicherheit, wir können es getrost öffentlich publizieren, ohne dass wir die Sicherheit unserer Verschlüsselung gefährden.

Ausführen der Codebeispiele

Sollten Sie Ruby noch nicht installiert haben, können Sie dies über www.ruby-lang.org/de vornehmen oder mit einem Tool wie www.rvm.io oder www.github.com/rbenv/rbenv.

Damit Sie auch die neueren Algorithmen wie ChaCha20/Poly1305 nutzen können, sollten Sie auf Ihrem System OpenSSL in einer Version >= 1.1.0 installiert haben.

Danach können Sie die Codebeispiele einfach ausführen, indem Sie das Git Repository unter www.github.com/emboss/stream-ciphers klonen, in das Verzeichnis code wechseln und dort die Beispiele ausführen mit ruby <beispiel.rb>.

In der Verschlüsselung bezeichnet man dieses Zufallselement häufig als „Nonce“ (Kunstwort für „number used once“) oder, meist im Block-Cipher-Umfeld, als IV (Initialization Vector). Wie können wir einen solchen IV nutzen, um unsere Stream-Cipher-Konstruktion nichtdeterministisch arbeiten zu lassen? Listing 1 zeigt ein Beispiel für unseren hausgemachten Stream Cipher [2].

Listing 1

require 'securerandom' require_relative 'rng' require_relative 'string_refinements' using StringRefinements class StreamCipher attr_reader :key, :iv, :rng def initialize(key:, iv:) randomized_key = key.xor(iv) @rng = RNG.new(randomized_key) end def encrypt(data) pad = rng.bytes(data.size) data.xor(pad) end alias_method :decrypt, :encrypt end key = "\x00" * 16 iv = SecureRandom.bytes(16) data = "Attack at dawn" puts "Data length: #{data.size}" encrypter = StreamCipher.new(key: key, iv: iv) encrypted = encrypter.encrypt(data) p encrypted # => Nichtdeterministisch decrypter = StreamCipher.new(key: key, iv: iv) decrypted = decrypter.decrypt(encrypted) puts decrypted # => Attack at dawn puts data == decrypted # => true

Wir verknüpfen den IV einfach mit dem Schlüssel via XOR. Da der IV mit einem kryptografisch sicheren Zufallsgenerator erzeugt wurde, reicht das aus, um das Verschlüsselungsergebnis willkürlich erscheinen zu lassen. Und auch die Entschlüsselung klappt einwandfrei, wie man schnell sieht, da sich die beiden k und iv am Ende rauskürzen.

c ⊕ (k ⊕ iv) = (m ⊕ k ⊕ iv) ⊕ (k ⊕ iv) = m ⊕ (k ⊕ k) ⊕ (iv ⊕ iv) = m

Ununterscheidbarkeit

Moderne Stream Cipher unterstützen diese Mehrfachverschlüsselung mittels IV von Haus aus, RC4 wie gesehen hingegen nicht. Das Sicherheitsmodell, das unsere bisherigen Betrachtungen um diesen Aspekt erweitert, wird als IND-CPA [3] bezeichnet. Der Begriff Ununterscheidbarkeit (Indistinguishability), der sich darin wiederfindet, spielt eine fundamentale Rolle in der modernen Verschlüsselung. Die Tatsache, dass eine sichere Verschlüsselung nichtdeterministisch sein muss, impliziert außerdem, dass eine sichere Verschlüsselung nicht unterscheidbar von willkürlich gewählten zufälligen Daten ist. Das ist ein mächtiges Kriterium bei der Bewertung eines Verschlüsselungsverfahrens: Sobald Sie im Ciphertext Muster erkennen können oder irgendeine andere Form des Determinismus, können Sie das Verfahren sofort als ein schlechtes aussortieren. Wohlgemerkt, Sie wissen an der Stelle zwar noch nicht, wie Sie es brechen würden, Sie wissen aber sehr wohl, dass es gebrochen werden kann, da es nicht den Anforderungen entspricht.

In den letzten Jahren konnte man den Eindruck gewinnen, dass Stream Cipher aus der Mode gekommen sind. Neben den oben besprochenen Tücken von RC4, die zwar allesamt lösbar sind, gab es immer mehr Forschungsergebnisse, die an der Sicherheit von RC4 selbst rütteln konnten. Da RC4 wegen seiner Performanz und vergleichsweise simplen Implementierung sehr beliebt war, bestand lange Zeit einfach keine Notwendigkeit, einen Nachfolger oder Konkurrenten im Stream-Cipher-Bereich zu schaffen. Als immer mehr abzusehen war, dass RC4 wackelt, wurde er aussortiert und meist der Block Cipher AES als Ersatz gewählt, da kein adäquater, weit verbreiteter Stream Cipher verfügbar war.

Das heißt aber nicht, dass Block Cipher den Stream Ciphern überlegen wären. Seit der Einführung von TLS in der Version 1.3 findet mittlerweile auch ein moderner Stream Cipher, der den Anforderungen von IND-CPA genügt, immer mehr Verbreitung. Es handelt sich um ChaCha20 [4], den Sie auch in Ruby OpenSSL nutzen können, vorausgesetzt, die zugrunde liegende native OpenSSL-Library ist aktuell genug [5] (Listing 2).

Listing 2

require 'openssl' require 'securerandom' data = "Attack at dawn" puts "Data length: #{data.size}" # => 14 encryptor = OpenSSL::Cipher.new('chacha20') key = encryptor.key = "\x00" * encryptor.key_len iv = encryptor.iv = SecureRandom...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang