{"id":1422,"date":"2013-05-09T16:00:32","date_gmt":"2013-05-09T14:00:32","guid":{"rendered":"http:\/\/blog.benny-baumann.de\/?p=1422"},"modified":"2013-05-14T09:51:31","modified_gmt":"2013-05-14T07:51:31","slug":"fuzzy-passwords","status":"publish","type":"post","link":"https:\/\/blog.benny-baumann.de\/?p=1422","title":{"rendered":"Fuzzy Passwords"},"content":{"rendered":"<p>Jeder kennt das: Man m\u00f6chte sich bei einem Dienst seiner Wahl mit seinem ultra-sicheren 42-stelligen Passwort mit 1337 Byte Entropie anmelden und stellt 0.23 Sekunden nach dem Absenden fest, dass man an der 5. Stelle statt dem Kanji* f\u00fcr 10^28 nur 10^4 geschrieben hat. Und da der Server wie so h\u00e4ufig \u00fcberlastet, und das Mobilfunknetz am Handy schon vor 3 Minuten wegen obligatorischer \u00dcberlastung von jeder toten Schnecke \u00fcberholt wird, denkt man sich, wie froh man doch sein kann, dass man nach weiteren 5 Minuten Bildschirm-Starren endlich erneut seine kostbare Zeit darauf verwenden darf, sein aus Buchstaben, Zahlen, Sonderzeichen, Keilschrift, Rhunen, Kanji und Klingonisch zusammengesetztes 42-stelliges Passwort auf der 3 Millimeter gro\u00dfen Touch-Tastatur einzutippen.<\/p>\n<p>Doch dies muss nicht sein!<!--more--><\/p>\n<h2>Ein Ansatz<\/h2>\n<p>Warum lassen wir nicht einfach die Blechkiste auf der anderen Seite der Verbindung entscheiden, dass die prinzipielle Kenntnis des Passwortes f\u00fcr diesen Account vorhanden ist und man sich einfach nur ungl\u00fccklich verschrieben hat? M\u00f6glich w\u00e4re das doch immerhin, da der Vergleich des Passwortes auf Server-Seite nahezu beliebig gestaltet werden kann.<\/p>\n<p>Eine L\u00f6sung zur Erh\u00f6hung des Komforts bei der Kennwort-Eingabe hat nun der <a href=\"http:\/\/heise.de\/-1832965\">Heise-Verlag vorgestellt<\/a>, nachdem bei der Analyse der Logfiles aufgefallen war, das insbesondere Nutzer von Handy-Tastaturen \u00fcberdurchschnittlich h\u00e4ufig an der Eingabe des Passwortes scheitern und dadurch unn\u00f6tige Last durch die erneuten Versuche beim wiederholten Eingeben des Passwortes auf dem Server erzeugt wird.<\/p>\n<h2>Die L\u00f6sung<\/h2>\n<p>Die L\u00f6sung ist so einfach, wie genial: Fuzzy Passwords!<\/p>\n<p>Statt also einen exakten Vergleich zwischen gespeichertem und eingegebenen Passwort zu erzwingen, wird die Pr\u00fcfroutine auf dem Server derart angepasst, dass auch Abvarianten des Passwortes, die nah genug am Original sind, akzeptiert werden; neben &#8222;test1234&#8220; werden also auch &#8222;text1234&#8220; und &#8222;test123&#8220; akzeptiert.<\/p>\n<p>Ein Einwand, der hierbei spontan einf\u00e4llt, wenn man das Vorgehen zur Speicherung von Passworten kennt, h\u00e4ngt mit dem Hashen des Passwortes zusammen: Hashes sind darauf ausgelegt, dass die gehashten Daten nicht wieder in lesbare Informationen \u00fcberf\u00fchrt werden k\u00f6nnen. M\u00f6chte man aber eine Autokorrektur haben, so m\u00fcsste das korrekte Passwort aber bekannt sein oder zumindest rekonstruierbar. Naiv gesehen stimmt das, rein praktisch gesehen kann man aber sagen, dass der Login-Server das Klartext-Passwort eh kennen muss, danach aber in eine Form \u00fcberf\u00fchrt, in der keine Rekonstruktion mehr m\u00f6glich ist und erst dann vergleicht.<\/p>\n<p>Herausforderung ist es also, das gehashte Passwort so zu hinterlegen, dass ein Angreifer aus den gespeicherten Informationen nur dann rekonstruieren kann, wenn er das Passwort eh bereits so gut kennt, dass das System dieses Passwort eh akzeptieren w\u00fcrde. Der hierzu notwendige Weg ist jedoch nicht auf den ersten Blick ersichtlich, da er mit klassischem Hashen, Crypten oder Pr\u00fcfsummieren, wie es f\u00fcr Passworte standardm\u00e4\u00dfig verwendet wird, nichts zu tun hat. Ein reiner Hash hilft nur f\u00fcr den exakten vergleich, ein verschl\u00fcsseltes Passwort verr\u00e4t bei Kenntnis des Schl\u00fcssls das Passwort und eine Pr\u00fcfsumme sagt einem nur, dass man falsch geraten hat. Die Performance von MACs und HMACs ist vergleichbar der von Hashes, steigert aber die Sicherheitsschranke auf eine Art vergleichbar mit Salted Hashes.<\/p>\n<h2>Die Fuzzyness<\/h2>\n<p>Der Ansatz ist einfach: Fehlerkorrektur!<\/p>\n<p>Bereits in den 1960er Jahren wurde an Verfahren gearbeitet, die es erlauben, dass selbst bei fehlerhafter \u00dcbertragung von Daten diese nicht vollst\u00e4ndig neu \u00fcbertragen werden mussten, solange die Fehlerrate unterhalb einer gewissen Schranke blieb. Dies wurde erreicht, indem in den zu \u00fcbertragenden Datenstrom zus\u00e4tzliche Korrektur-Informationen eingebettet wurden, die Redundanz im Datenstrom erzeugt haben und somit das Lokalisieren von Fehlerstellen im empfangenen Datenstrom erm\u00f6glichen. Einer der bekanntesten Fehlerkorrekturcodes ist der auf CDs verwendete Reed-Solomon-Code &#8211; Auf CDs kommt zus\u00e4tzlich noch eine Redundanz-Kodierung zum Einsatz, die f\u00fcr unseren Fall aber uninteressant ist.<\/p>\n<p>Aber wie hilft uns das?<\/p>\n<p>Bei Reed-Solomon-Codes handelt es sich um einen separierbaren Fehlerkorrektur-Code, bei dem die Ausgangsinformation und die Fehlerkorrektur-Information getrennt codiert werden &#8211; zu der kodierten Information ist bekannt, welche Stellen Daten und welche Stellen Korrektur-Information besitzen. Betrachtet man jetzt unser obiges Szenario unter diesem Aspekt, ergibt sich, dass ein Passwort genau dann \u00e4hnlich ist, wenn bei Eingabe eines Passwortes dieses unter Zuhilfenahme der Fehlerkorrektur-Informationen so korrigiert werden kann, so das aus dem korrigierten Passwort der korrekte Passwort-Hash gebildet werden kann. Hat man jedoch nur die Fehlerkorrektur-Information reicht diese f\u00fcr die Rekonstruktion des Passwortes nicht aus, solange das Passwort l\u00e4nger als die Fehlerkorrektur-Information ist**.<\/p>\n<p>Soweit die Theorie, aber es wird noch besser: \u00dcber die Menge an Fehlerkorrektur kann man sogar die Fuzzyness dieser Korrektur steuern. Die Stufe Komfort-Toleranz auf der <a href=\"http:\/\/www.heise.de\/extras\/ct\/fuzzy\/\">Heise-Testseite<\/a> h\u00e4tte beispielsweise deutlich mehr Korrektur-Information als mittlere oder wenig Toleranz. Nachteilig ist jedoch, dass Buchstabendreher als jeweils zwei Fehler erkannt werden und fehlende Zeichen als so viele Fehle interpretiert werden, wie noch nachfolgende Zeichen auftreten. Mit etwas mehr Aufwand lassen sich aber auch diese Probleme erkennen und korrigieren, ohne Klartextinformation zu ben\u00f6tigen.<\/p>\n<p>Aber der Reihe nach.<\/p>\n<h2>Die Umsetzung<\/h2>\n<p>Im ersten Schritt k\u00fcmmern wir uns um das Speichern des Passwortes. Wie bereits angedeutet reicht hierf\u00fcr ein einfacher Hash nicht aus, da mit diesem keine Fehler korrigiert werden k\u00f6nnen. Daher muss neben dem Hash noch ein Fehlerkorrekturwert gespeichert werden. Um das Bruteforcen zus\u00e4tzlich zu erschweren, salzen wir den Hash zudem und sorgen mit stark erh\u00f6hten Iterationszahlen f\u00fcr zus\u00e4tzlichen Aufwand beim Pr\u00fcfen der Korrektheit. Um zus\u00e4tzlich das Ausnutzen der Codierung des Passwortes innerhalb des Fehlerkorrekturcodes zu erschweren, randomisieren wir die Reihenfolge der Buchstaben im Fehlerkorrekturcode; ein Angreifer hat nun zus\u00e4tzliche Arbeit, um die Fehlerkorrektur durchzuf\u00fchren, die er in jedem Schritt erledigen muss.<\/p>\n<p>Neben diesen &#8211; im Wesentlichen die Pr\u00fcfung betreffenden &#8211; Optionen haben wir aber auch beim Kodieren der Fehlerkorrektur-Informationen reichlich Auswahl: Neben der Bitbreite der kodierten Zeichen (wir k\u00f6nnen hier w\u00e4hlen von 2 bis unendlich), k\u00f6nnen wir auch das Generator-Polynom des Feldes beeinflussen. Codiert man nun noch eine zuf\u00e4llige Basis-Information in die nicht genutzten Positionen, vermeidet man Probleme mit als Konstant bekannten Positionen.<\/p>\n<p>F\u00fcr unseren Fuzzy-Hash nutzen wir demnach folgendes Format:<\/p>\n<pre>$fuzzy$[salt]$[iterations]$gf$[gfsize]$[gfpoly]$[correctables]$[permutation]$[noise]$[hashtype]$[hash]$[fecbytes]<\/pre>\n<p>Der Aufbau folgt hierbei einem Standard-Crypt-Hash, f\u00fcgt jedoch eine Reihe weiterer Felder hinzu:<\/p>\n<ul>\n<li>fuzzy: Literaler String, um auf einen Fuzzy-Hash hinzuweisen<\/li>\n<li>salt: zuf\u00e4llige Bytes f\u00fcr den Salt<\/li>\n<li>iterations: Anzahl an Iterationen, die gehasht werden m\u00fcssen<\/li>\n<li>gf: Hinweis darauf, dass ein Galois-Feld zur Fehlerkorrektur verwendet wird. Weitere Fehlerkorrektur-Codes k\u00f6nnten alternativ eingesetzt oder kombiniert werden<\/li>\n<li>gfsize: Die Bitbreite des verwendeten Galois-Feldes<\/li>\n<li>gfpoly: Das Generator-Polynom f\u00fcr das eingesetzte Galois-Feld<\/li>\n<li>correctables: Anzahl der korrigierbaren Zeichen<\/li>\n<li>permutation: Ein Seed-Wert f\u00fcr einen PRNG, um eine zuf\u00e4llige Permutation der Zeichen des Passwortes innerhalb des Fehlerkorrekturcodes zu erhalten<\/li>\n<li>noise: Ein Seed-Wert zum Erzeugen von Grundrauschen beim zu korrigierenden Passwort<\/li>\n<li>hashtype: Das verwendete Hash-Verfahren<\/li>\n<li>hash: Der gesalzene Hash des Passwortes<\/li>\n<li>fecbytes: Die Fehlerkorrektur-Information<\/li>\n<\/ul>\n<h2>Die Grundlagen<\/h2>\n<p>Um zu verstehen, wie die Kodierung funktioniert, ist ein Blick auf etwas Mathe n\u00f6tig, die <a href=\"https:\/\/en.wikiversity.org\/wiki\/Reed\u2013Solomon_codes_for_coders\">am Beispiel von QR-Codes<\/a> gut veranschaulicht werden kann.<\/p>\n<h3>Galois-Felder<\/h3>\n<p>Bei Galois-Feldern handelt es sich um Zahlenr\u00e4ume endlicher Gr\u00f6\u00dfe, in denen definiert ist, wie eine Addition und wie eine Multiplikation je zweier Elemente funktioniert. Dies \u00e4hnelt stark der Art und Weise bei den nat\u00fcrlichen oder ganzen Zahlen, mit dem Unterschied, dass Elemente au\u00dferhalb des Zahlenraums durch Restbildung wieder in diesen abgebildet werden. Im GF(2^4), der die Elemente von 0 bis 15 enth\u00e4lt, wird also bei Addition von 7 + 11 = 18 \u2261 2. Eine Multiplikation im gleichen Zahlenraum von 7 * 11 = 77 \u2261 13.<\/p>\n<p>Eine einfache Umsetzung in PHP sieht dabei wie folgt aus:<\/p>\n<pre lang=\"php\" escaped=\"true\">class GF {\r\n\u00a0\u00a0\u00a0 public $value;\r\n\u00a0\u00a0\u00a0 public $len;\r\n\u00a0\u00a0\u00a0 public $high;\r\n\u00a0\u00a0\u00a0 public $mask;\r\n\r\n\u00a0\u00a0\u00a0 public $g_exp;\r\n\u00a0\u00a0\u00a0 public $g_log;\r\n\r\n\u00a0\u00a0\u00a0 function __construct($value, $length) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;len = $length;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;high = 1 &lt;&lt; $this-&gt;len;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;mask = $this-&gt;high - 1;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;value = $value &amp; $this-&gt;mask | $this-&gt;high;\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;generate();\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 function generate() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;g_exp = array_fill(0, $this-&gt;mask, 1);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;g_log = array_fill(0, $this-&gt;high, 0);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $x = 1;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for($i = 1; $i &lt; $this-&gt;high; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $x += $x;\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if($x &amp; $this-&gt;high) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $x ^= $this-&gt;value;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;g_exp[$i] = $x;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;g_log[$x] = $i;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n};\r\n\r\nfunction gf_add(GF $gf, $a, $b) {\r\n\u00a0\u00a0\u00a0 return ($a ^ $b) &amp; $gf-&gt;mask;\r\n}\r\n\r\nfunction gf_mul_explicit(GF $gf, $a, $b) {\r\n\u00a0\u00a0\u00a0 $a &amp;= $gf-&gt;mask;\r\n\u00a0\u00a0\u00a0 $a += $a;\r\n\r\n\u00a0\u00a0\u00a0 $b &amp;= $gf-&gt;mask;\r\n\r\n\u00a0\u00a0\u00a0 $result = 0;\r\n\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; $gf-&gt;len; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result += $result;\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if($result &amp; $gf-&gt;high) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result ^= $gf-&gt;value;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(($a &lt;&lt; $i) &amp; $gf-&gt;high) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result ^= $b;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction gf_mul(GF $gf, $a, $b) {\r\n\u00a0\u00a0\u00a0 $a &amp;= $gf-&gt;mask;\r\n\u00a0\u00a0\u00a0 $b &amp;= $gf-&gt;mask;\r\n\r\n\u00a0\u00a0\u00a0 $la = $gf-&gt;g_log[$a];\r\n\u00a0\u00a0\u00a0 $lb = $gf-&gt;g_log[$b];\r\n\r\n\u00a0\u00a0\u00a0 return ($a &amp;&amp; $b) ? $gf-&gt;g_exp[($la + $lb) % $gf-&gt;mask] : 0;\r\n}\r\n\r\nfunction gf_div(GF $gf, $a, $b) {\r\n\u00a0\u00a0\u00a0 $a &amp;= $gf-&gt;mask;\r\n\u00a0\u00a0\u00a0 $b &amp;= $gf-&gt;mask;\r\n\r\n\u00a0\u00a0\u00a0 if(!$b) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 throw new Exception('Division by Zero');\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 if(!$a) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return 0;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $gf-&gt;g_exp[($gf-&gt;g_log[$a] + $gf-&gt;mask - $gf-&gt;g_log[$b]) % $gf-&gt;mask];\r\n}\r\n\r\ndefine('GF_DEFAULT_POLY', 0x1d);\r\ndefine('GF_DEFAULT_LEN', 8);\r\n$GF_DEFAULT = new GF(GF_DEFAULT_POLY, GF_DEFAULT_LEN);<\/pre>\n<h3>Polynome mit Koeffizienten aus Galois-Feldern<\/h3>\n<p>\u00c4hnlich wie man mit den gewohnten Zahlenbereichen auch Funktionen und Polynome bauen kann, geht dies auch bei einem Galois-Feld: Dabei bildet man ein Polynom und verwendet als Faktoren vor der Unbekannten jeweils ein Element aus dem Galois-Feld. Die unbekannte ist h\u00e4ufig auch ein Element aus dem Galois-Feld, kann aber theoretisch auch aus jedem anderen Zahlenbereich stammen, wenn eine sinnvolle Verkn\u00fcpfung definiert ist. Aber dies ist f\u00fcr uns erst einmal zweitrangig, da wir ohne diese Verallgemeinerung auskommen.<\/p>\n<p>Nehmen wir also wieder das obige Feld, so kann man wiederum je eine Addition und eine Multiplikation definieren: Aus 3x^2 + 7x + 1 und 0x^2 + 8x + 15 wird durch Addition 3x^2 + 15x + 0 und durch Multiplikation (3*0)x^4 + (3*8)x^3 + (3*15)x^2 + (7*0)x^3 + (7*8)x^2 + (7*15)x + (1*0)x^2 + (1*8)x + (1*15) oder vereinfacht: 31x^3 + 101x^2 + 113x + 15 \u2261 15x^3 + 5x^2 + 1x + 15.<\/p>\n<p>Auch dieser Teil ist mit wenigen Zeilen in PHP umsetzbar:<\/p>\n<pre lang=\"php\" escaped=\"true\">require_once \"gf.php\";\r\n\r\nfunction gfpoly_scale(GF $gf, array $a, $scale) {\r\n\u00a0\u00a0\u00a0 return array_map(\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 function ($v) use ($gf, $scale) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return gf_mul($gf, $v, $scale);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }, $a);\r\n}\r\n\r\nfunction gfpoly_add(GF $gf, array $a, array $b) {\r\n\u00a0\u00a0\u00a0 $result = array_fill(0, max(count($a), count($b)), 0);\r\n\r\n\u00a0\u00a0\u00a0 $cb = function ($v, $k) use ($gf, &amp;$result) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result[$k] = gf_add($gf, $result[$k], $v);\r\n\u00a0\u00a0\u00a0 };\r\n\r\n\u00a0\u00a0\u00a0 array_walk($a, $cb);\r\n\u00a0\u00a0\u00a0 array_walk($b, $cb);\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction gfpoly_mul(GF $gf, array $a, array $b) {\r\n\u00a0\u00a0\u00a0 $result = array_fill(0, count($a) + count($b) - 1, 0);\r\n\r\n\u00a0\u00a0\u00a0 foreach($a as $ak =&gt; $av) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 foreach($b as $bk =&gt; $bv) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $k = $ak + $bk;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result[$k] = gf_add($gf, $result[$k], gf_mul($gf, $av, $bv));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction gfpoly_eval(GF $gf, array $p, $x) {\r\n\u00a0\u00a0\u00a0 $x &amp;= $gf-&gt;mask;\r\n\r\n\u00a0\u00a0\u00a0 $result = 0;\r\n\r\n\u00a0\u00a0\u00a0 for($i = count($p); $i &gt; 0; $i--) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result = gf_add($gf, $p[$i-1], gf_mul($gf, $result, $x));\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}<\/pre>\n<h3>Reed-Solomon-Codes<\/h3>\n<p>Reed-Solomon-Codes bilden eine Unterklasse von sogenannten separierbaren BCH-Blockcodes und dienen der Fehlerkorrektur bei der Daten\u00fcbertragung. Im Falle von RS-Codes wird die Fehlerkorektur-Information unabh\u00e4ngig von den zu \u00fcbertragenden Daten \u00fcbermittelt, d.h. in einem Codewort ist ersichtlich, welche Elemente die urspr\u00fcngliche Nachricht bilden und welche der Fehlerkorrektur dienen. Diese Eigenschaft l\u00e4sst sich f\u00fcr unseren Anwendungsfall sehr gut nutzen, nur dass die Nutzinformation nie \u00fcbertragen wird und daher zur Pr\u00fcfung geliefert werden muss.<\/p>\n<p>Die Kodierung dieser Information basiert nun darauf, dass man jedes Polynom n-ten Grades durch n+1 St\u00fctzstellen eindeutig bestimmen kann. Kennt man mehr als n+1 St\u00fctzstellen, so ist das Polynom \u00fcberbestimmt und man kann bei Abweichungen an einer Stelle das urspr\u00fcngliche Polynom bestimmen und somit auch den urspr\u00fcnglichen Wert an der Fehlerstelle wiederherstellen.<\/p>\n<p>Die eigentlichen Algorithmen sind auf der oben verlinkten Seite genauer erkl\u00e4rt, eine passende Implementierung in PHP l\u00e4sst sich wie folgt realisieren:<\/p>\n<pre lang=\"php\" escaped=\"true\">require_once 'gf.php';\r\nrequire_once 'gfpoly.php';\r\n\r\nfunction rs_poly_gen(GF $gf, $sym) {\r\n\u00a0\u00a0\u00a0 $p = array(1);\r\n\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; $sym; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $p = gfpoly_mul($gf, $p, array($gf-&gt;g_exp[$i], 1));\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $p;\r\n}\r\n\r\nfunction rs_encode_data(GF $gf, array $poly, array $value) {\r\n\u00a0\u00a0\u00a0 $cpoly = count($poly);\r\n\u00a0\u00a0\u00a0 $cvalue = count($value);\r\n\r\n\u00a0\u00a0\u00a0 $result = array_fill(0, $cpoly + $cvalue - 2, 0);\r\n\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; $cvalue; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result[$cpoly+$i-1] = $value[$i];\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 for($i = $cvalue; $i &gt; 0; $i--) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $icoeff = $cpoly + $i - 2;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $coeff = $result[$icoeff];\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(!$coeff) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 continue;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for($j = 0; $j &lt; $cpoly; $j++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $icoeff2 = $i + $j - 1;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result[$icoeff2] = gf_add($gf, $result[$icoeff2], gf_mul($gf, $poly[$j], $coeff));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; $cvalue; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result[$cpoly + $i - 1] = $value[$i];\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction rs_encode_syndrome(GF $gf, array $poly, array $value) {\r\n\u00a0\u00a0\u00a0 return array_slice(rs_encode_data($gf, $poly, $value), 0, count($poly) - 1);\r\n}\r\n\r\nfunction rs_calc_syndrome(GF $gf, array $poly, array $msg) {\r\n\u00a0\u00a0\u00a0 $result = array();\r\n\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; count($poly) - 1; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result[$i] = gfpoly_eval($gf, $msg, $gf-&gt;g_exp[$i]);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction rs_calc_errlocpoly(GF $gf, array $pos) {\r\n\u00a0\u00a0\u00a0 $result = array(1);\r\n\r\n\u00a0\u00a0\u00a0 foreach($pos as $p) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $x = $gf-&gt;g_exp[$p % $gf-&gt;mask];\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result = gfpoly_mul($gf, $result, array(1, $x));\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction rs_correct_errata(GF $gf, array $msg, array $syndrome, array $pos) {\r\n\u00a0\u00a0\u00a0 \/\/Error Locator Polynomial\r\n\u00a0\u00a0\u00a0 $errpoly = rs_calc_errlocpoly($gf, $pos);\r\n\r\n\u00a0\u00a0\u00a0 $p = array_slice($syndrome, 0, count($pos));\r\n\u00a0\u00a0\u00a0 $p = gfpoly_mul($gf, $p, $errpoly);\r\n\u00a0\u00a0\u00a0 $p = array_slice($p, 0, count($pos));\r\n\r\n\u00a0\u00a0\u00a0 for($i = 1, $qq = array(); $i &lt; count($errpoly); $i += 2) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $qq[] = $errpoly[$i];\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 $errpoly = $qq;\r\n\r\n\u00a0\u00a0\u00a0 foreach($pos as $i) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $x = $gf-&gt;g_exp[($gf-&gt;mask - $i) % $gf-&gt;mask];\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $y = gfpoly_eval($gf, $p, $x);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $z = gfpoly_eval($gf, $errpoly, gf_mul($gf, $x, $x));\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $msg[$i] = gf_add($gf, $msg[$i], gf_div($gf, $y, gf_mul($gf, $x, $z)));\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $msg;\r\n}\r\n\r\nfunction rs_find_errors(GF $gf, array $msg, array $syndrome) {\r\n\u00a0\u00a0\u00a0 $oldpoly = array(1);\r\n\u00a0\u00a0\u00a0 $errpoly = array(1);\r\n\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; count($syndrome); $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $oldpoly = gfpoly_mul($gf, $oldpoly, array(0,1));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $delta = $syndrome[$i];\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for($j = 1; $j &lt; count($errpoly); $j++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $delta = gf_add($gf, $delta, gf_mul($gf, $errpoly[$j], $syndrome[$i - $j]));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if($delta) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(count($errpoly) &lt; count($oldpoly)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $newpoly = gfpoly_scale($gf, $oldpoly, $delta);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $oldpoly = gfpoly_scale($gf, $errpoly, gf_div($gf, 1, $delta));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $errpoly = $newpoly;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $errpoly = gfpoly_add($gf, $errpoly, gfpoly_scale($gf, $oldpoly, $delta));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $errors = count($errpoly) - 1;\r\n\u00a0\u00a0\u00a0 if( 2 * $errors &gt; count($syndrome) ) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return false;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $errpos = array();\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; count($msg); $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $v = gfpoly_eval($gf, $errpoly, $gf-&gt;g_exp[$gf-&gt;mask - 1 - $i]);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(0 == gfpoly_eval($gf, $errpoly, $gf-&gt;g_exp[$gf-&gt;mask - 1 - $i])) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $errpos[] = count($msg) - $i - 2;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 if(count($errpos) != $errors) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return false;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $errpos;\r\n}\r\n\r\nfunction rs_calc_forney_syndromes(GF $gf, array $syndrome, array $pos) {\r\n\u00a0\u00a0\u00a0 $result = $syndrome;\r\n\r\n\u00a0\u00a0\u00a0 foreach($pos as $i) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $x = $gf-&gt;g_exp[$i];\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for($j = 0; $j &lt; count($result) - 1; $j++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result[$j] = gf_add($gf, gf_mul($gf, $result[$j], $x), $result[$j+1]);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 array_pop($result);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction rs_correct_message(GF $gf, array $poly, array $message) {\r\n\u00a0\u00a0\u00a0 $result = array_reverse($message);\r\n\r\n\u00a0\u00a0\u00a0 $erasepos = array();\r\n\u00a0\u00a0\u00a0 foreach($result as $i =&gt; $v) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if($v &lt; 0) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result[$i] = 0;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $erasepos[] = $i;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 if(count($erasepos) &gt; count($poly)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return false;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $syndrome = rs_calc_syndrome($gf, $poly, $message);\r\n\r\n\u00a0\u00a0\u00a0 if(!count(array_filter($syndrome))) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return array_reverse($result);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $fsyndromes = rs_calc_forney_syndromes($gf, $syndrome, $erasepos);\r\n\r\n\u00a0\u00a0\u00a0 $errorpos = rs_find_errors($gf, $result, $fsyndromes);\r\n\u00a0\u00a0\u00a0 if(!is_array($errorpos)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return false;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $errloc = array_merge($errorpos, $erasepos);\r\n\u00a0\u00a0\u00a0 $errloc = array_map(function ($l) use ($result) { return count($result) - 1 - $l; }, $errloc);\r\n\u00a0\u00a0\u00a0 $result = rs_correct_errata($gf, array_reverse($result), $syndrome, $errloc);\r\n\r\n\u00a0\u00a0\u00a0 $syndrome = rs_calc_syndrome($gf, $syndrome, $result);\r\n\u00a0\u00a0\u00a0 if(!count(array_filter($syndrome))) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return $result;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return false;\r\n}<\/pre>\n<h2>Die Implementierung<\/h2>\n<p>Um mit diesen Bausteinen nun die fertige Crypt-Funktion zu bauen, sind noch ein paar Kleinigkeiten vorzubereiten, aber wir sind bereits fast da!<\/p>\n<h3>Der reine Passwort-Hash<\/h3>\n<p>Um die Komplexit\u00e4t bei der Pr\u00fcfung variieren zu k\u00f6nnen und auf Fortschritte bei der Rechentechnik reagieren zu k\u00f6nnen, ist es ratsam, einige Gegenma\u00dfnahmen vorzusehen. In der hier verwendeten Implementierung sind dies zum einen wiederholtes Hashing als auch eine f\u00fcr jeden Durchgang zuf\u00e4llig berechnete Permutation der Eingabe basierend auf Fisher-Yates. Die hierf\u00fcr ben\u00f6tigten &#8222;Zufallsdaten&#8220; werden mit hilfe eines PRNG aus den im Hash enthaltenen Seed-Werten generiert.<\/p>\n<p>Im ersten Durchgang erfolgt die Permutation auf Salt + Passwort, in jedem folgenden Durchgang wird zus\u00e4tzlich der aus der vorherigen Runde erhaltene Hash in die Permutation einbezogen, wodurch diese auf Salt + Hash&#8216; + Salt angewendet wird.<\/p>\n<p>Hierbei Iterations ist die Anzahl der Hash-Schritte, bei denen ein Hash Teil des Ausgangsmaterials ist. Iterations==0 entspricht somit einem permutiert gehashten Salted Password. Der Parameter im Hash gibt hierbei eine Verschiebung an: die tats\u00e4chliche Anzahl an durchl\u00e4ufen berechnet sich aus 64 * 2^Wert und ist somit bereits f\u00fcr normale Computer mit einem Wert um 8 ausreichend komplex.<\/p>\n<h3>Format des fehlerzukorrigierenden Blocks<\/h3>\n<p>Um den Fehlerkorrekturblock zu erhalten, muss das Password um ein Nullbyte am Ende erweitert werden. Danach wird ein PRNG auf noise geseedet und das Passwort auf ein Vielfaches der Gr\u00f6\u00dfe eines Datenblocks mit Zufallsdaten aufgef\u00fcllt. Diese Information wird nun mit einem auf permutation geseedeten PRNG permutiert. In der ersten, unten gezeigten Implementierung ist dieser Schritt leicht vereinfacht, was am Grundprinzip der Korrektur jedoch nichts \u00e4ndert, solange bei der Pr\u00fcfung der \u00e4quivalente Datenblock erzeugt werden kann.<\/p>\n<p><!-- Der entstandene Datenstrom wird in Bl\u00f6cke zerlegt, die der Gr\u00f6\u00dfe eines Datenblocks des Fehlerkorrekturcodes entsprechen. --><\/p>\n<h3>Zum Abschluss<\/h3>\n<p>Nimmt man die oben dargestellten Einzelteile, so ergibt sich f\u00fcr die Realisierung des Fuzzy-Password-Hashes folgende Beispielimplementierung:<\/p>\n<pre lang=\"php\" escaped=\"true\">include 'rs.php';\r\n\r\nclass PRNG {\r\n\u00a0\u00a0\u00a0 public $state = 4; \/\/Choosen by fair dice roll, guaranteed to be random!\r\n};\r\n\r\nfunction fc_permutate($string, PRNG &amp;$prng) {\r\n\u00a0\u00a0\u00a0 mt_srand($prng-&gt;state);\r\n\u00a0\u00a0\u00a0 $result = '';\r\n\r\n\u00a0\u00a0\u00a0 while(strlen($string)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $r = mt_rand(0, strlen($string));\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $r %= strlen($string);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result .= $string[$r];\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $string = substr($string, 0, $r) . substr($string, $r + 1);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return strrev($result);\r\n}\r\n\r\nfunction fc_dohash($password, $salt, $iterations, $permutation, $hashspec = \"sha1\") {\r\n\u00a0\u00a0\u00a0 $prng = new PRNG($permutation);\r\n\r\n\u00a0\u00a0\u00a0 $iterations = 64 &lt;&lt; $iterations;\r\n\r\n\u00a0\u00a0\u00a0 $result = $salt . $password;\r\n\u00a0\u00a0\u00a0 $result = fc_permutate($result, $prng);\r\n\u00a0\u00a0\u00a0 $result = $hashspec($result, true);\r\n\r\n\u00a0\u00a0\u00a0 do {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result = $salt . $result . $password . $salt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result = fc_permutate($result, $prng);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result = $hashspec($result, true);\r\n\u00a0\u00a0\u00a0 } while(--$iterations);\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction fc_gensalt($len) {\r\n\u00a0\u00a0\u00a0 $result = '';\r\n\u00a0\u00a0\u00a0 while(strlen($result) &lt; $len) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result .= chr(mt_rand(0, 256));\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction fc_fecdata_encode(GF $gf, array $data) {\r\n\u00a0\u00a0\u00a0 $byte_per_item = floor(($gf-&gt;len + 7) \/ 8);\r\n\r\n\u00a0\u00a0\u00a0 $result = '';\r\n\r\n\u00a0\u00a0\u00a0 foreach($data as $x) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for($i = 0; $i &lt; $byte_per_item; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result .= chr($x &amp; 0xFF);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $x = $x &gt;&gt; 8;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction fc_fecdata_decode(GF $gf, $data) {\r\n\u00a0\u00a0\u00a0 $byte_per_item = floor(($gf-&gt;len + 7) \/ 8);\r\n\r\n\u00a0\u00a0\u00a0 $result = array_fill(0, floor((strlen($data) + $byte_per_item - 1) \/ $byte_per_item), 0);\r\n\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; strlen($data); $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result[floor($i\/$byte_per_item)] += ord($data[$i]) &lt;&lt; (($i % $byte_per_item) &lt;&lt; 3);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n\r\nfunction fc_check_rs(GF $gf, array $gfpoly, $password, $salt, $noise, array $fecdata) {\r\n\u00a0\u00a0\u00a0 $prng = new PRNG($noise);\r\n\u00a0\u00a0\u00a0 mt_srand($prng-&gt;state);\r\n\r\n\u00a0\u00a0\u00a0 $msg = array_slice(array_map(\"ord\", preg_split('\/\/', $password)), 1, -1);\r\n\r\n\u00a0\u00a0\u00a0 $msg_data = array();\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; $gf-&gt;mask; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $msg_data[$i] = mt_rand(0, $gf-&gt;high);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $msg_data = array_reverse($msg_data);\r\n\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; count($gfpoly)-1; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $msg_data[$i] = $fecdata[$i];\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; strlen($password); $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $msg_data[$i+count($gfpoly)-1] = ord($password[$i]);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $msg = rs_correct_message($gf, $gfpoly, $msg_data);\r\n\r\n\u00a0\u00a0\u00a0 if(!is_array($msg)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return $password;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $pw_len = strlen($password);\r\n\u00a0\u00a0\u00a0 $password = '';\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; $pw_len; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $password .= chr($msg[$i+count($gfpoly)-1]);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $password;\r\n}\r\n\r\nfunction fc_calc_rs(GF $gf, array $gfpoly, $password, $salt, $noise) {\r\n\u00a0\u00a0\u00a0 $prng = new PRNG($noise);\r\n\u00a0\u00a0\u00a0 mt_srand($prng-&gt;state);\r\n\r\n\u00a0\u00a0\u00a0 $msg = array_slice(array_map(\"ord\", preg_split('\/\/', $password)), 1, -1);\r\n\r\n\u00a0\u00a0\u00a0 $msg_data = array();\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; $gf-&gt;mask; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $msg_data[$i] = mt_rand(0, $gf-&gt;high);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $msg_data = array_reverse($msg_data);\r\n\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; count($gfpoly) - 1; $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $msg_data[$i] = 0;\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 for($i = 0; $i &lt; strlen($password); $i++) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $msg_data[$i+count($gfpoly)-1] = $msg[$i];\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $msg_data = array_slice($msg_data, count($gfpoly) - 1);\r\n\r\n\u00a0\u00a0\u00a0 $msg = rs_encode_syndrome($gf, $gfpoly, $msg_data);\r\n\r\n\u00a0\u00a0\u00a0 return $msg;\r\n}\r\n\r\nfunction fuzzycrypt($password, $hash = \"\") {\r\n\u00a0\u00a0\u00a0 global $GF_DEFAULT;\r\n\u00a0\u00a0\u00a0 global $GF_POLY_DEFAULT;\r\n\r\n\u00a0\u00a0\u00a0 \/\/$hash = $fuzzy$salt$iterations$gf$gfbits$gfpoly$gfdegree$permutation$noise$hashtype$hash$fecdata\r\n\r\n\u00a0\u00a0\u00a0 $salt = fc_gensalt(12);\r\n\u00a0\u00a0\u00a0 $iterations = 8;\r\n\r\n\u00a0\u00a0\u00a0 $gf = GF_DEFAULT_LEN;\r\n\u00a0\u00a0\u00a0 $gf_poly = GF_DEFAULT_POLY;\r\n\u00a0\u00a0\u00a0 $gf_degree = 5;\r\n\r\n\u00a0\u00a0\u00a0 $permutate = mt_rand(0, 1 &lt;&lt;24);\r\n\u00a0\u00a0\u00a0 $noise = mt_rand(0, 1 &lt;&lt;24);\r\n\r\n\u00a0\u00a0\u00a0 $hashtype = \"sha1\";\r\n\r\n\u00a0\u00a0\u00a0 $verify_fail = false;\r\n\r\n\u00a0\u00a0\u00a0 $fecdata = '';\r\n\r\n\u00a0\u00a0\u00a0 if('$fuzzy$' == substr($hash, 0, 7)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/Read arguments from $hash parameter\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $params = explode('$', $hash);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(13 != count($params)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $verify_fail = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 list($ignore_empty, $ignore_fuzzy, $salt, $iterations,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $ignore_gf, $gf_bits, $gf_poly, $gf_degree,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $permutate, $noise, $hashtype, $hash, $fecdata) = $params;\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(!$verify_fail &amp;&amp; '' != $ignore_empty) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $verify_fail = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(!$verify_fail &amp;&amp; 'fuzzy' != $ignore_fuzzy) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $verify_fail = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(!$verify_fail &amp;&amp; 'gf' != $ignore_gf)\u00a0\u00a0 \u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $verify_fail = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(!$verify_fail &amp;&amp; 'sha1' != $hashtype) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $verify_fail = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(!$verify_fail &amp;&amp; 8 != $gf_bits) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $verify_fail = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(!$verify_fail &amp;&amp; '' == $gf_poly) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $verify_fail = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(!$verify_fail &amp;&amp; 2 &gt; $gf_degree) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $verify_fail = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $gf = $gf_bits;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $gf_poly = hexdec($gf_poly);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $permutate = hexdec($permutate);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $noise = hexdec($noise);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $salt = base64_decode($salt);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $fecdata = base64_decode($fecdata);\r\n\r\n\u00a0\u00a0\u00a0 } else {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(\"\" != $hash) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return crypt($password, $hash);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/Generate some default parameters for the hash\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $verify_fail = true;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 if($verify_fail) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $salt = fc_gensalt(12);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $iterations = 8;\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $gf = GF_DEFAULT_LEN;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $gf_poly = GF_DEFAULT_POLY;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $gf_degree = 5;\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $permutate = mt_rand(0, 1 &lt;&lt;24);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $noise = mt_rand(0, 1 &lt;&lt;24);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $hashtype = \"sha1\";\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $fecdata = 'Fubar';\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $o_gf = new GF($gf_poly, $gf);\r\n\u00a0\u00a0\u00a0 $o_gf_poly = rs_poly_gen($o_gf, $gf_degree);\r\n\r\n\u00a0\u00a0\u00a0 $fecdata = fc_fecdata_decode($o_gf, $fecdata);\r\n\r\n\u00a0\u00a0\u00a0 if(!$verify_fail) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $password = fc_check_rs($o_gf, $o_gf_poly, $password, $salt, $noise, $fecdata);\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 $fecdata = fc_calc_rs($o_gf, $o_gf_poly, $password, $salt, $noise);\r\n\r\n\u00a0\u00a0\u00a0 $h = fc_dohash($password, $salt, $iterations, $permutate);\r\n\u00a0\u00a0\u00a0 $h = trim(base64_encode($h), '=');\r\n\r\n\u00a0\u00a0\u00a0 $s = fc_fecdata_encode($o_gf, $fecdata);\r\n\u00a0\u00a0\u00a0 $s = trim(base64_encode($s), '=');\r\n\r\n\u00a0\u00a0\u00a0 \/\/$hash = $fuzzy$salt$iterations$gf$gfbits$gfpoly$gfdegree$permutation$noise$hashtype$hash$fecdata\r\n\u00a0\u00a0\u00a0 $result = sprintf('$fuzzy$%s$%d$%s$%d$%x$%d$%x$%x$%s$%s$%s',\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 trim(base64_encode($salt),'='),\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%s:\u00a0\u00a0 salt\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $iterations,\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%d:\u00a0\u00a0 iterations\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 'gf',\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%s:\u00a0\u00a0 ecc type 'gf'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $o_gf-&gt;len,\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%d:\u00a0\u00a0 gf bit length\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $o_gf-&gt;value,\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%x:\u00a0\u00a0 gf polynomial\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $gf_degree,\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%d:\u00a0\u00a0 degree of ecc polynomial\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $permutate,\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%x:\u00a0\u00a0 permutation IV\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $noise,\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%x:\u00a0\u00a0 noise IV\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 'sha1',\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%s:\u00a0\u00a0 type of used hash function\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $h,\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%s:\u00a0\u00a0 strict hash value\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $s\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/%s:\u00a0\u00a0 fuzzy correction information\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 );\r\n\r\n\u00a0\u00a0\u00a0 return $result;\r\n}\r\n<\/pre>\n<p>Zum Erzeugen nutzt man nun<\/p>\n<pre lang=\"php\">$a = fuzzycrypt(\"Hello World!\");<\/pre>\n<p>w\u00e4hrend die Pr\u00fcfung mittels<\/p>\n<pre lang=\"php\">$b = fuzzycrypt(\"Hello World!\", $a);<\/pre>\n<p>erfolgt. Sind beide identisch, war das vom Nutzer eingegebene Passwort hinreichend \u00e4hnlich:<\/p>\n<pre lang=\"text\">\r\nHello World!\t$fuzzy$dcmGLl5RmD1ZYGai$8$gf$8$11d$5$612f1d$c78f42$sha1$UwLJxeScqVDyNvdejjoOiMGNmwo$rMGsJ4Q\r\n\r\nHello World!\t$fuzzy$dcmGLl5RmD1ZYGai$8$gf$8$11d$5$612f1d$c78f42$sha1$UwLJxeScqVDyNvdejjoOiMGNmwo$rMGsJ4Q\r\nHallo World!\t$fuzzy$dcmGLl5RmD1ZYGai$8$gf$8$11d$5$612f1d$c78f42$sha1$UwLJxeScqVDyNvdejjoOiMGNmwo$rMGsJ4Q\r\nHell0 W0rld!\t$fuzzy$dcmGLl5RmD1ZYGai$8$gf$8$11d$5$612f1d$c78f42$sha1$UwLJxeScqVDyNvdejjoOiMGNmwo$rMGsJ4Q\r\nHallo Wellt!\t$fuzzy$dcmGLl5RmD1ZYGai$8$gf$8$11d$5$612f1d$c78f42$sha1$5PPJP4YR0lMdsUPCTmBxZXRvTZk$zDxMUY8\r\n<\/pre>\n<p>Auf weniger fehlgeschlagene Logins!<\/p>\n<p>* Die erw\u00e4hnten Kanji gibt es \ud83d\ude09<br \/>\n** Da f\u00fcr die Kodierung bekannt ist, wo das Passwort im Code steht ist keine Fehlerkorrektur an zu suchenden Stellen, sondern lediglich eine St\u00f6rungskorrektur an bekannten Stellen durchzuf\u00fchren, f\u00fcr die pro Fehlerstelle nur ein Korrekturwert ben\u00f6tigt wird.<\/p>\n<p class=\"wp-flattr-button\"><a href=\"https:\/\/blog.benny-baumann.de\/?flattrss_redirect&amp;id=1422&amp;md5=ec310df3ab62a3bf61690100b1dfac0e\" title=\"Flattr\" target=\"_blank\"><img src=\"http:\/\/blog.benny-baumann.de\/wp-content\/plugins\/flattr\/img\/flattr-badge-large.png\" srcset=\"http:\/\/blog.benny-baumann.de\/wp-content\/plugins\/flattr\/img\/flattr-badge-large.png\" alt=\"Flattr this!\"\/><\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>Jeder kennt das: Man m\u00f6chte sich bei einem Dienst seiner Wahl mit seinem ultra-sicheren 42-stelligen Passwort mit 1337 Byte Entropie anmelden und stellt 0.23 Sekunden nach dem Absenden fest, dass man an der 5. Stelle statt dem Kanji* f\u00fcr 10^28 nur 10^4 geschrieben hat. Und da der Server wie so h\u00e4ufig \u00fcberlastet, und das Mobilfunknetz [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[29],"tags":[98,347,36,69,7,94,21,346],"class_list":["post-1422","post","type-post","status-publish","format-standard","hentry","category-software","tag-developement","tag-fun","tag-heise","tag-internet","tag-links","tag-medien","tag-php","tag-server"],"_links":{"self":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts\/1422","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1422"}],"version-history":[{"count":9,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts\/1422\/revisions"}],"predecessor-version":[{"id":1436,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=\/wp\/v2\/posts\/1422\/revisions\/1436"}],"wp:attachment":[{"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1422"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1422"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.benny-baumann.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1422"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}