This is an old revision of the document!


Begriffe:

Eine Hash-Funktion verarbeitet eine Bitfolge beliebiger Länge (Input) reproduzierbar zu einer Bitfolge konstanter Länge $\ell$ (Output). Gewünschte Eigenschaften einer kryptographisch nutzbaren Hashfunktion (im Regelfall nicht beweisbar):

  • Der Output ist “zufällig”, d.h. die Änderung eines Bits im Input sollte “zufällig” etwa die Hälfte der Bits im Output ändern.
  • Es ist nicht möglich, zu einem gegebenen Output einen Input zu konstruieren (mal abgesehen von “alles durchprobieren”, was einen Aufwand von ca. $2^{\ell-1}$ bedeutet).
  • Es ist nicht möglich, zu einem gegebenen Input einen zweiten Input zu kontruieren, der den gleichen Output hat.
  • Es ist nicht möglich, zwei (frei wählbare) Inputs mit gleichem Output zu konstruieren (mal abgesehen von “alles durchprobieren”, was einen Aufwand von ca. $2^{\frac{ell}{2}}$ bedeutet).

Je nach Anwendung kann auch gewünscht sein, dass die Erzeugung des Hashes rechenaufwendig sein muss.

Konkrete Hashfunktionen (mit Kommandozeilenargument)

  • MD5 (ist geknackt): md5sum. Mögliche Anwendung: Dateien auf gleichen Inhalt prüfen, “Szenarien ohne Gegner”.
  • SHA1 (im Moment noch sicher, wird aber davon abgeraten): sha1sum.
  • SHA-2 SHA256 sha256sum, SHA512 sha512sum. Zur Zeit als sicher angesehene Hash-Funktionen.
  • SHA-3 Neuste standardisierte Hashfunktionen. (Im package rhash: z.B. rhash –sha3-256

Aufgaben

  1. Erzeugen Sie verschiedene Hashwerte einer Text-Datei (z.B. eines Ruby-Codes).
  2. Kopieren Sie die Datei und ändern Sie genau 1 Bit (nicht 1 Byte!). Bestimmen Sie dann wieder die Hashwerte.
  3. Schreiben Sie ein Ruby-Programm, das für einen gegebenen Text den Hashwert berechnet und dann für alle Bit-Folgen, die sich um genau ein Bit vom gegebenen Text unterscheiden. Berechnen Sie dann die Anzahl Bits, um die sich die neuen Hashwerte vom Original-Hashwert unterscheiden und erzeugen Sie daraus ein Histogramm.

Hilfestellungen zu Punkt 3:

  • Ein einzelnes Byte in einem String kann mit .getbyte(index) und .setbyte(index,byte) gelesen und gesetzt werden. Mit der XOR-Operation kann ein Bit umgelegt werden (z.B. i^0b100 legt das Bit 2 (drittes Bit) in der Zahl i um).
  • Ein String kann mit a=File.read(“datei”) gelesen und mit File.write(“datei”,a) geschrieben werden.
  • Ein Hashwert kann entweder über die Kommandozeile mit resultat = `sha256sum datei` berechnet werden. Das ist zwar sehr flexibel, aber auch sehr langsam (Datei schreiben, lesen, Kommando starten).
  • Ein Hashwert kann auch mit Ruby-Libraries berechnet werden, siehe z.B. hier. Das ist sehr viel schneller.
  • Das Histogramm kann als csv-Datei exportiert werden, wobei jede Zeile so aussieht: '128;423' (Erster Eintrag: Anzahl Bits, zweiter Eintrag: Anzahl Vorkommnisse).

Lösungsansatz

Lösungsansatz

Hinweis: Code herunterladen, nicht kopieren.

hashtest.rb
require 'digest'
 
# s: String, bit: nummer
def flipBit(s, bit)
    # Kopie von s
    s = String.new(s)
    byte = bit/8;
    bit = bit%8;
    s.setbyte(byte, 
      s.getbyte(byte)^(1 << bit))
    return s
end
 
# s1,s2: Zwei gleich lange Strings
def count_bit_diff(s1,s2)
    diff = 0
    s1.size.times{|i|
      xor = s1.getbyte(i)^s2.getbyte(i)
      # Ganzzahl: 1 Byte
      8.times{|bit|
        diff+=1 if (xor & (1<<bit))!=0
      }
    }
    return diff
end
 
text = File.read("hashtest.rb")
modif = flipBit(text, 30);
puts "Veränderte Bits im text: #{count_bit_diff(text,modif)}"
hash_orig = Digest::SHA256.digest(text)
hash_modif = Digest::SHA256.digest(modif)
puts "Anzahl bits: #{hash_orig.size*8}"
puts "Veränderte Bits: #{count_bit_diff(hash_orig, hash_modif)}"
 
p hash_orig
p hash_modif

Anwendungen

  • Abspeichern von Passwörtern. Nicht das Passwort sondern der Hash davon wird gespeichert. Einfach überprüfbar, Passwort kann nicht ermittelt werden. Problem: Im Fall von Datenreichtum (lies Datenleak) kann eine Dixionär-Attacke gefahren werden. State of the Art: Verfahren von DropBox. Wie man es nicht tun sollte: Yahoo (nur einer von ganz vielen). Und zur Erheiterung: häufigste deutsche Passwörter und weltweit.
  • Integrität einer Datei überprüfen (nur sinnvoll im Szenario ohne Gegner). Damit können Übertragungsfehler festgestellt werden. Ein Gegner könnte wohl auch den Hash anpassen. Lösung: Hash muss digital unterschrieben werden.
  • Forensik: Vor der Analyse eines Datenträgers werden Hashwerte davon hinterlegt. (Damit nachträglich Manipulation festgestellt werden könnte).
  • Digitale Unterschrift: Anstatt das ganze Dokument zu signieren, wird der Hash davon signiert (ist erheblich schneller).
  • Blockchain, für z.B. Bitcoins.

Rainbow-Tables

Trade-Off: Speicherplatz/Rechenpower.

Zutaten: Bakannte Hash-Funktion $h(s)$, Spezielle Hash-ähnliche Funktion $p(h)$, die aus Hashes Passwörter generiert.

Generierung der Tabelle:

  1. Starte mit einem Passwort $x$.
  2. Speichere das Passwort $x$
  3. Wiederhole $n$ mal (so $10^6$ oder so): $x := p(h(x))$
  4. Speichere $h(x)$
  5. Wiederhole ab Schritt 2.

Entschlüsseln eines Passworts, gegeben Hash $y$.

  1. Wiederhole bis $y$ in der Tabelle vorkommt (maximal $n$ mal ($n$ wie oben))
    1. Setzte $y := h(p(y))$
  2. Wenn $y$ nicht in der Tabelle vorkommt, Passwort ist nicht in der Rainbow-Table.
  3. Wenn $y$ in der Rainbow-Table ist, vom Startpasswort starten, bis passendes Passwort gefunden.

Naja, das ist nur mal die Grundidee. So wie oben beschrieben funktioniert die Sache nicht wirklich, weil die Passwortketten ineinander übergehen. 1. Lösungsansatz war, verschiedenste Funtionen zur Passwortgenerierung zu haben. Der Witz an Rainbow-Tables ist, dass die passwortgenerierende Funktion für jeden Schritt ändert. Kollisionen sind weiterhin möglich. Ein zusammenlaufen der Ketten ist viel unwahrscheinlicher.

Siehe http://lasec.epfl.ch/pub/lasec/doc/Oech03.pdf (ist offenbar eine Erfindung aus Lausanne ;-))

Spielzeug Beispiel

Code zur Generierung der Rainbow-Table

Code zur Generierung der Rainbow-Table

rainbowtable.rb
require 'digest'
 
# Passwords are 7 lower case letters a-z
# Hash is SHA256
 
# Makes a password out of a hex hash
def makePW(hash, n=7)
  z = hash.to_i(16)
  pw = ""
  n.times{
    pw += (z % 26 + 'a'.ord).chr
    z/=26
  }
  return pw
end
 
#Random password (for starting a chain)
def randPass(n=7)
  Array.new(n){('a'.ord+rand(26)).chr}.join  
end
 
# Generate a row of the rainbow table
def makeRow(pw, n=1_000_000)
  h = ""
  len = pw.size
  n.times{
    h = Digest::SHA256.hexdigest(pw)
    pw = makePW(h,len)
  }
  return h
end
 
# Check, how many hashes per second/minute/hour can be computed
def speedTest(n=5)
  text = "a"*5
  n = 26**text.size
  start = Time.now
  n.times {
    Digest::SHA256.hexdigest(text)
    text.succ!
  }
  total = Time.now-start
  puts "#{n} Hashes in #{total} secs."
  puts "Makes #{(n/total).to_i} hashes/s"
  puts "Makes #{(n/total*60).to_i} hashes/min"
  puts "Makes #{(n/total*3600).to_i} hashes/h"
end
 
# Generate entries for the table
while true
  pw = randPass(7)
  h = makeRow(pw)
  puts "#{pw}:#{h}"
end
  • Rechnen in Restklassen
  • Potenzieren in $\mathbb{Z} / n\mathbb{Z}$
  • Diskreter Logarithmus ist schwierig

Grundlage: Zwei Schlüssel, einer privat, einer öffentlich. Was mit dem einen verschlüsselt wird, kann nur mit dem anderen Schlüssel entschlüsselt werden.

Verschlüsselung

Alice verschlüsselt mit dem öffentlichen Schlüssel von Bob. Nur Bob kann die Nachricht mit seinem privaten Schlüssel lesen.

Effektiv wird normalerweise ein Schlüssel für ein symmetrisches Verfahren (typischerweise AES) verschlüsselt und dann die Nachricht damit.

Digitale Unterschrift

Alice verschlüsselt mit ihrem privaten Schlüssel. Bob entschlüsselt mit dem öffentlichen Schlüssel von Alice. Bob weiss, dass nur Alice diesen Text/Dokument verschlüsselt haben kann.

Effektiv wird normalerweise nur ein Hash der Nachricht verschlüsselt.

Authetifizierung

Zuordnung Schlüssel-Person (oder Server) muss überprüft werden. Ansätze:

  • Austausch über weiteren Kanal (z.B. in Person, Austausch auf Papier).
  • Zertifizierung über vertrauenswürdige Drittstelle. Diese signiert, dass der Schlüssel zur Person (oder Server) gehört.
  • efinf/blc2016/krypto.1484510539.txt.gz
  • Last modified: 2017/01/15 21:02
  • by Ivo Blöchliger