2009-08-24 7 views
2

Ich versuche, eine Delphi-Version dieses Algorithmus zu erstellen:Was ist falsch an dieser "StretchKey" Implementierung?

void PWSfileV3::StretchKey(const unsigned char *salt, unsigned long saltLen, 
          const StringX &passkey, 
          unsigned int N, unsigned char *Ptag) 
{ 
    /* 
    * P' is the "stretched key" of the user's passphrase and the SALT, as defined 
    * by the hash-function-based key stretching algorithm in 
    * http://www.schneier.com/paper-low-entropy.pdf (Section 4.1), with SHA-256 
    * as the hash function, and N iterations. 
    */ 
    int passLen = 0; 
    unsigned char *pstr = NULL; 

    ConvertString(passkey, pstr, passLen); 
    unsigned char *X = Ptag; 
    SHA256 H0; 
    H0.Update(pstr, passLen); 
    H0.Update(salt, saltLen); 
    H0.Final(X); 

#ifdef UNICODE 
    trashMemory(pstr, passLen); 
    delete[] pstr; 
#endif 

    ASSERT(N >= MIN_HASH_ITERATIONS); // minimal value we're willing to use 
    for (unsigned int i = 0; i < N; i++) { 
    SHA256 H; 
    // The 2nd param in next line was sizeof(X) in Beta-1 
    // (bug #1451422). This change broke the ability to read beta-1 
    // generated databases. If this is really needed, we should 
    // hack the read functionality to try both variants (ugh). 
    H.Update(X, SHA256::HASHLEN); 
    H.Final(X); 
    } 
} 

Update: (Fehlende Funktion)

void ConvertString(const StringX &text, 
        unsigned char *&txt, 
        int &txtlen) 
{ 
    LPCTSTR txtstr = text.c_str(); 
    txtlen = text.length(); 

#ifndef UNICODE 
    txt = (unsigned char *)txtstr; // don't delete[] (ugh)!!! 
#else 
#ifdef _WIN32 
    txt = new unsigned char[3*txtlen]; // safe upper limit 
    int len = WideCharToMultiByte(CP_ACP, 0, txtstr, txtlen, 
    LPSTR(txt), 3*txtlen, NULL, NULL); 
    ASSERT(len != 0); 
#else 
    mbstate_t mbs; 
    memset(&mbs, '\0', sizeof(mbs)); 
    size_t len = wcsrtombs(NULL, &txtstr, 0, &mbs); 
    txt = new unsigned char[len+1]; 
    len = wcsrtombs((char *)txt, &txtstr, len, &mbs); 
    ASSERT(len != (size_t)-1); 
#endif 
    txtlen = len; 
    txt[len] = '\0'; 
#endif /* UNICODE */ 
} 

Hier ist, was ich habe (D2009-Version):

(Bitte beachten Sie: T256BitArray als Array definiert ist [0..31] of Byte)

procedure StretchKey(Const Salt:T256BitArray; Const Passkey:string; Const Iterations:LongWord; Var KeyResult:T256BitArray); 
var 
    pStr : RawByteString; 
    wHash : THash_sha256; 
    loop : integer; 
begin 
    pStr := AnsiString(PassKey); 

    wHash := THash_SHA256.Create; 
    try 
    wHash.Init; 
    wHash.Calc(pStr[1], Length(pStr)); 
    wHash.Calc(Salt, Length(Salt)); 
    wHash.Done; 
    PStr := wHash.DigestStr; 
    finally 
    FreeAndNil(wHash); 
    end; 

    for loop := 0 to Iterations-1 do 
    begin 
    wHash := THash_sha256.Create; 
    try 
     wHash.Init; 
     wHash.Calc(PStr[1], wHash.DigestSize); 
     wHash.Done; 
     PStr := wHash.DigestStr; 
    finally 
     FreeAndNil(wHash); 
    end; 
    end; 

    move(pStr[1], KeyResult, sizeof(KeyResult)); 
end; 

Das ursprüngliche Code-Snippet stammt aus der Opensource-Anwendung Password Safe.

Ich versuche, eine vorhandene Password Save (v3) -Datenbank zum Lesen zu öffnen.

Es scheint, dass es egal ist, was ich mache Ich kann nicht den Algorithmus den erforderlichen Hash generieren lassen.

Im obigen Delphi-Ausschnitt verwende ich den Komponentensatz DEC v5.2 2009. Ich habe auch die DCPcrypt-Bibliothek ausprobiert. Lustigerweise bekomme ich die gleichen Werte von beiden Bibliotheken, aber nichts ist kompatibel mit dem Hash aus der PWSv3-Datei.

Die SHA256-Komponenten, die ich verwendet habe, bestehen beide die SHA256-Testvektor-Hashes, also nehme ich an, dass ich etwas falsch gemacht habe, wenn ich die Methode umcodiere.

Fehle ich etwas?

Gelöst: Alles ist korrekt. Das Problem tritt bei der Umwandlung der Hauptschlüsselzeichenfolge auf. Ich habe herausgefunden, dass ich die WideCharToMultiByte Funktion verwenden muss, um die richtige Codepage-Konvertierung zu erhalten.

+0

Bevor Sie mit String-Typen und Gießen beginnen Hantieren, lesen Sie in dieser Antwort auf eine ähnliche Frage: http://stackoverflow.com/questions/392657/md5-hashing -in-delphi-2009/736232 # 736232 – mghie

+0

@mghie: Während ich die Tatsache nicht abzählen werde, dass das das Problem sein könnte, wandelt die fehlende Funktion (ich habe es hinzugefügt) die Zeichenkette aus dem ursprünglichen Code um Unicode zu dem, was ich glaube, ist das ANSI-Äquivalent. Trotzdem habe ich alle meine Testfälle mit dem Fiedeln durchgeführt, um zu bestätigen, dass ich das bekomme, was ich über bekannte Testhashes bekommen soll. Ich bin momentan ziemlich zuversichtlich, dass das, was ich so mache, richtig ist. Ich habe das in D2007 ausprobiert und immer noch keine Freude. –

+0

@Ryan: Wenn Sie Ihr Problem gelöst haben, sollten Sie es als Antwort einreichen und akzeptieren. Es könnte sehr hilfreich sein, wenn jemand in Zukunft ein ähnliches Problem hat, um zu sehen, wie Sie das gelöst haben. –

Antwort

0

Ich habe das Problem gelöst.

Alles mit meiner Implementierung ist korrekt. Das Problem tritt bei der Umwandlung der Hauptschlüsselzeichenfolge auf. Ich habe herausgefunden, dass ich unbedingt die WideCharToMultiByte-Funktion verwenden musste, um die korrekte Umwandlung der Codepage zu erhalten. Hier

ist der korrigierte Code:

procedure StretchKey(Const Salt:T256BitArray; Const Passkey:string; Const Iterations:LongWord; Var KeyResult:T256BitArray); 
var 
    pStr : RawByteString; 
    wHash : THash_sha256; 
    loop : integer; 
    wStr : AnsiString; 
    wLen : integer; 
begin 
    wLen := 3*length(PassKey); 
    SetLength(wStr, wLen); 

    wLen := WideCharToMultiByte(GetACP, 0, PChar(PassKey), length(PassKey), PAnsiChar(wStr), wLen, nil, nil); 
    SetLength(wStr, wLen); 

    pStr := wStr; 

    wHash := THash_SHA256.Create; 
    try 
    wHash.Init; 
    wHash.Calc(pStr[1], Length(pStr)); 
    wHash.Calc(Salt, Length(Salt)); 
    wHash.Done; 
    PStr := wHash.DigestStr; 
    finally 
    FreeAndNil(wHash); 
    end; 

    for loop := 0 to Iterations-1 do 
    begin 
    wHash := THash_sha256.Create; 
    try 
     wHash.Init; 
     wHash.Calc(PStr[1], wHash.DigestSize); 
     wHash.Done; 
     PStr := wHash.DigestStr; 
    finally 
     FreeAndNil(wHash); 
    end; 
    end; 

    move(pStr[1], KeyResult, sizeof(KeyResult)); 
end; 
0

Die Schleife im unteren Teil sollte kein wHash.Init haben; drin.

+0

Warum? Müssen Sie es nicht vor der Verwendung initialisieren? Es ist ein brandneues Objekt jedes Mal durch die Schleife, genau wie im ursprünglichen Code. –

+0

@Rob: Sie haben Recht. Der ursprüngliche Algorithmus verwendet, was mir scheint, jedes Mal eine neue Komponenteninstanz. BTW, ich habe versucht, die Init außerhalb der Schleife zu bewegen, und es funktioniert auch nicht. –