argon2-for-delphi icon indicating copy to clipboard operation
argon2-for-delphi copied to clipboard

TArgon2.DeriveBytes always returns an array of zeros no matter what passphrase is supplied

Open mbtaylor1982 opened this issue 6 years ago • 8 comments

I tested using this code and I always get the same digest in the hash string.

  Hash := TArgon2.HashPassword(Pwd);
  Matched := TArgon2.CheckPassword(Pwd, Hash, Rehash);

the check password function is returning true with the hash strings.

Maybe I'm doing something wrong, i'm not sure

password:

$Argon2id $v=19 $m=131072,t=10000,p=1 $l9yGr4S9jB7D1mZhNLHYWQ== $AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

password1:

$Argon2id $v=19 $m=131072,t=10000,p=1 $l2UP21fItrhdXTlnkI2+tQ== $AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

password2:

$Argon2id $v=19 $m=131072,t=10000,p=1 $cVH50MP3v87ngjRAKUpqHA== $AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

mbtaylor1982 avatar Mar 08 '18 10:03 mbtaylor1982

I think this is the problem

TArgon2.GetBytes Aprox line 750

	SetLength(Result, DesiredNumberOfBytes);
	FillChar(Result[0], DesiredNumberOfBytes, 0);

looks like it will always return an array of zeros!

mbtaylor1982 avatar Mar 08 '18 14:03 mbtaylor1982

The real problem is that this implementation is nowhere near working, because neither the RFC, nor the white-papers, nor the reference implementations, explain how the algorithm works. Without being able to decipher the algorithm, i have no hope of implementing it.

Hopefully others can decipher the god-awful mess that the different RFC's are, and complete the implementation.

Because they are so badly written, i want to choke his tongue out.

JackTrapper avatar Mar 09 '18 18:03 JackTrapper

I'm willing to give it a shot if you can let me know where to start looking

mbtaylor1982 avatar Mar 10 '18 11:03 mbtaylor1982

Also please add a comment to the home page to say the implementation is not finished or working

mbtaylor1982 avatar Mar 10 '18 11:03 mbtaylor1982

I've committed everything i was behind on.

There's five main pieces that make up Argon2; 1 is complete and tested, 2 more are mostly complete, but untestable, final 2 are completely unwritten.

  1. Blake2: a hash algorithm that produces 64-byte digests. I have this implemented and test vectors match the separate Blake2 RFC. The algorithm was extraordinary poorly documented, and took months of reverse engineering to make a Delphi port work. I created an understandable version of the Blake2 algorithm on its wikipedia page

  2. DeriveBytes: the main loop that performs the operations the specified number of times, on the parallelization number of lanes, on the specified amount of memory. This is mostly complete; but also untestable until the following two functions are written. This algorithm is horribly documented, but i believe i managed to decipher it correctly, and i created an understandable version on the Wikipedia page for Argon2

  3. Hash: a variable length hash algorithm based on Blake2, that is capable of producing digests up to the needed 1024 bytes (while Blake2 digest is fixed at 64 bytes). I have implemented this function, but it is untestable, as Argon2 has no test vectors to verify. This algorithm is horribly documented, but i believe i managed to decipher it correctly, and i created an understandable version on the Wikipedia page for Argon2

  4. GetBlockIndexes: the main DeriveBytes algorithm operates on memory in 1KB blocks arranged in a 2-d array. The i,j indexes that are operated on follow a separate algorithm. The algorithm to use depends on which variant of Argon2 you are using:

    • Argon2id
    • Argon2d
    • Argon2i

The algorithm that explains the 3 different algorithms of selecting indexes is horribly explained in the RFC and in various whitepapers. I have no idea how it works, and the implemention remains completely empty:

 procedure TArgon2.GetBlockIndexes(i, j: Integer; out iRef, jRef: Integer);
 begin
  	{
  		We are Argon2id.
  	}
 	//Since i cannot make heads nor tails of either their PDF, their RFC, their GitHub, nor their sample       code, this is a todo
 	iRef := 0;
 	jRef := 0;
  
 	raise ENotImplemented.Create('Someone needs to figure out the 2id block schedule alorithm');
 end;
  1. G: Both the RFC and the whitepapapers (and my translation of the main algorithm on Wikipedia) refer to a function known only as G. The function is not documented anywhere. On the Wikipedia page i leave it completely unexplained:

    https://i.imgur.com/nT1mAYT.png

the same in true in the Delphi code:

//TODO: They don't document what the function G is
//B[i][j] = G(B[i][j-1], B[iref][jref])

I've committed the RFC and two variations of the whitepapers that "document" Argon2. Someone needs to decipher them, and create the GetBlockIndexes and G function.

JackTrapper avatar Mar 10 '18 18:03 JackTrapper

Maybe i'm a fool or maybe Ii found the definition of G

2.1.4 Compression function G

Compression function G is built upon the Blake2b round function P (fully defined in Section 2.7.1). P operates on the 128-byte input, which can be viewed as 8 16-byte registers (see details below): P(A0, A1, . . . , A7) = (B0, B1, . . . , B7).

Compression function G(X, Y ) operates on two 1024-byte blocks X and Y . It first computes R = X ⊕ Y . Then R is viewed as a 8 × 8-matrix of 16-byte registers R0, R1, . . . , R63

. Then P is first applied rowwise, and then columnwise to get Z: Finally, G outputs Z ⊕ R:

Document attached below :)

Argon-v3.pdf

mbtaylor1982 avatar Apr 23 '18 16:04 mbtaylor1982

now works?

JeanPaulo-Eletron avatar Sep 30 '21 11:09 JeanPaulo-Eletron

This claims to support Argon2: https://github.com/Xor-el/HashLib4Pascal Perhaps you may be able to learn from it? I'd much rather use something standalone like your library

DelphiWorlds avatar Apr 18 '22 00:04 DelphiWorlds