Ha1 und HA2 sind deklariert -> PByteArray.
1.) beim nachfolgendem .Calc(HA1, SizeOf(HA1)) berechnets du also den Hash über einen Zeiger mit 4 Bytes Länge ?
2.) HA1/HA2 := .Digest; zeigen auf eine interne Datenstruktur = Buffer des Hash Objectes. Schaue dir mal den Inhalt von HA1^ und HA2^ an, du wirst feststellen das sie gleich sind. .Digest gibt dier also den Zugriff über einen Zeiger auf die internen Register der Hash Funktion und nicht die darin enthaltenen Daten als Kopie, das macht .DigestStr(). Nach dem .Init/.Done steht in diesem internen Buffer der Initialisierungswert der Hash Funktion, und rate mal was bei MD5 da drinnen steht nach dem .Done ? Es ist 0123456789abcdeffedcba9876543210
Du arbeitest also mit Referenzen, statt den gewünschten Daten.
3.) die beiden Hash1 und Hash2 sind in HEXL formatiert, du benutzt sie in binärer Repräsentation, eben Zeiger auf Digest des Hashs.
4.) die SIP wird nicht in Anführungsstriche gesetzt
5.) es geht im
DEC viel einfacher zu coden
Mache es so:
Delphi-Quellcode:
begin
Response := THash_MD5.CalcBinary(THash_MD5.CalcBinary('21702:asterisk:20712', TFormat_HEXL) + ':5e6feac9:' +
THash_MD5.CalcBinary('REGISTER:sip:192.168.1.14', TFormat_HEXL), TFormat_HEXL);
end;
so komme ich auf den Wert den du suchst. Davon mal absehen das man schon versucht hat es einigermaßen sicher zu machen, also kryptographisch betrachtet, ist es immer noch nicht ideal gelösst.
1.) man sollte einen Salt für die Berechnung von Hash1 vorsehen der mindestens 16 Bytes lang ist, so wie jetzt kann man Rainbow Tabellen anlegen
2.) man sollte den Nonce ebenfalls 16 Bytes vorsehen, und in binär statt HEX dem Hash berechnen
3.) diese Salts sollten in den Daten immer am Anfang stehen, nicht mittendreinnen noch hintendran, wegens Lawineneffekt der Hashfunktion
4.) wenn man den Hash berechnet so sollte man diese Berechnung mehrmals ausführen, immer vom Hash vom Hash vom Hash und das ca. 1000mal. Durch diese iterative Anwednung der "langsammen" Hashfunktionen vervielfacht sich der Aufwand einer Brute Force Attacke. Also wir machen 1x 1000 mal den Hash, der Angreifer aber ~2^64x 1000mal das gleiche um mit 50% Wahrscheinlichkeit zu knacken.
Die Länge der Salts/Nonce sollte immer 16 Bytes = 128 Bits nicht unterschreiten. Im Grunde sollten sie exakt so groß sein wie die interne Arbeitsbreite der Hashfunktion, das ist ideal, also nicht weniger und nicht mehr. Damit ist die Wahrscheinlichkeitsverteilung zwischen Salt-Zufallsbits gleich der Arbeitsbreite der Hashfunktion, also 50/50. Somit kann ein Angreifer eben keine Informationen mehr zuordnen zum Salt oder den Daten im resultierenden Hashdigest.
KDFs -> Key Derivation Functions arbeiten dann exakt so und sind bei weitem sicherer als obige Methode.
Also für Hash1 sollte man eine KDF benutzen, -> THash.KDFx(UserName + ':' + UserPassword, RandomSalt), mal anschauen im Source
, und für Hash2 kann man die bisherige Methode benutzen, und für den Response sollte man wieder eine KDF benutzen.
Das sähe so aus KDF(KDF(Username:Userpassword, PasswordSalt) || MGF(MethodName:SIP'), NonceSalt), wobei man im Datnprotokoll Passwordsalt und Noncesalt lesbar mit überträgt.
Ok, ich weis das du das nicht beeinflussen kannst
ist also nur mal so am Rande eine Analyse eines bestehenden Protokolles.
Um das nochmal genauer so erklären. Der 2. hash hat den Aufbau -> REGISTER:sip:
IP. Die Anzahl der wählbaren Methoden wird sehr beschränkt sein, zb. REGISTER, UPDATE und CHANGE == 3 Stück. Danach folgt immer SIP also nur eine Möglichkeit. Wir wissen also schonmal REGSITER:sip: + CHANGE:sip: + UPDATE:sip:. Danach folgt eine
IP Addresse. Davon dürfen viele mögliche
IP Adressen garnicht benutzt werden, zb. 192.xxx.xxx.xxx oder 127
xx
xx
xxx oder 255
xx
xx
xx und meistens wissen wir sogar das ein Benutzer bei T-Online seinen Account hat und somit können wir den
IP Bereich nochmals einschränken. Im besten Falle kennen wir als Angreifer sogar diese
IP komplett. Ergo: ohne Probleme könnten wir den 2. Hash errechnen als Angreifer, oder wir legen eine Rainbow Tabelle im Vorhinein an mit allen möglichen
IP Addressen, das ist dann eine Datenbank in der Größe einer DVD in der wir dann live und online sofort zu einem Hash die
IP Addresse auslesen können.
Ergo: für den 2. Hash wird es so schnelle Angriffsmöglichkeiten geben das es defakto sinnlos ist darüber in obiger Form einen Hash zu berechnen, man könnte diese Daten gleich unverschlüsselt übertragen. Ich schätz mal das dies auch der Fall ist und der 2. Hash nur als Bestätigung von Serverseite gilt.
Wenn nicht dann sollte man eine KDF benutzen. Also man erzeugt einen 16 Bytes langen Zufallswert zieht nun von Zufallwert:REGISTER:sip:
IP einen Hash und dann ca. 100mal von dem Zufallswert:Vorgänger-Hash nochmals den hash iterativ. Dann überträgt man den Zufallswert und den letzendlichen Hashwert zum Server. Nun gibts keine Angriffsmöglichkeit mehr denn der Angreifer benötigt nun zu jedem der Zufallswerte eine eigene Rainbow Tabelle, das macht in diesem Falle also 2^128 DVDs mit Rainbow Tabellen, 2^128 Stück rechne das mal aus, ich schätze mal das alle diese DVD übereinander gestapelt durch unsere komplette Galaxie reichen würde
Wie man sieht hat so ein popeliger 16 Bytes Zufallswert enorme Konsequenzen. Im Idealfall knackt man die alte Methode sofort weil man die
IP des benutzers kennt oder enorm schnell weil man den Provider des User kennt. Im sicheren Falle mit Salt und KDF knackt man es nie da der Aufwand gigantisch ist, eben ein rießen Stapel mit DVDs im Falle einer Rainbow Tabelle, die heutzutage bester Stand der Kryptoanalyse in diesem Falle darstellt.
Noch viel wichtiger wäre aber der 1. Hash über Password + Username zu verändern. Denn knackt dies ein Angreifer dann ist die Wahrscheinlichkeit enorm hoch das diese Daten vom Benutzer auch bei seinen anderen Accounts wie Online Banking usw. benutzt werden. Der Angreifer hat damit Zugriff auf noch viel mehr Daten des Benutzers. Also für den 1. Hash sollte man unbedingt eine KDF + Salt benutzen.
Gruß Hagen