5
$\begingroup$

Is the H0 block in Argon2 64-bits, or 64-bytes? The RFC says bits:

Establish H0 as the 64-bit value as shown below.

But then it attempts to generate 64-bytes using Blake2

Background

I'm trying to implement Argon2, and the original specs paper, and RFC are not a very friendly to implementors.

I'm stuck on the first step of the Argon2 pseudo-code:

Establish H0 as the 64-bit value as shown below.

H_0 = H^(64)(LE32(p), LE32(T), LE32(m), LE32(t), LE32(v), LE32(y), LE32(length(P)), P, LE32(length(S)), S, LE32(length(K)), K, LE32(length(X)), X)

There's a lot going on in there, but we can clean it up:

Establish H0 as the 64-bit value as shown below.

H0 = H^(64)( [stuff to hash] )

It would seem to indicate that the H^(64)(...) is going to return 64-bits (because of the phrase "...as the 64-bit value...".

But now we go hunting what H^ is, which brings us to the preamble of Section 3.2 in the RFC:

Argon2 uses an internal hash function H^x(). Here H^x() applied to string a is the BLAKE2b [RFC7693](a,|a|,0,x) hash function

Now we know that: H^(x)(a) = Blake2b(a, |a|, 0, x).

That brings us to RFC 7693 - The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC), in order to figure out what the four paramters are (especially the final paramter x):

The following procedure processes the padded data blocks into an
"nn"-byte final hash value.  See Section 2 for a description of
various variables and constants used.

   FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn )
   |
   ...snip...
   |
   END FUNCTION.

That's a little hard for implementors to read; we can make it a bit more understandable:

Function Blake2(DataToHash, OriginalMessageLength, KeyLength, DigestLength)  
   Inputs:
      DataToHash:            Bytes    //array of bytes to be hashed   
      OriginalMessageLength: Integer  //length of message in bytes   
      KeyLength:             Integer  //length of secret key in bytes (0..64)  
      DigestLength:          Integer  //length of desired digest in bytes (1..64)  
   Output:   
      Digest:                Bytes    //digest that is DigestLength long   

   ...snip...   

The final parameter to Blake2 (nn in RFC's parlance) is bytes (not bits):

|             | BLAKE2b          |
|-------------|------------------|
|Bits in word | w = 64           |
|Rounds in F  | r = 12           |
|Block bytes  | bb = 128         |
|Hash bytes   | 1 <= nn <= 64    |
|Key bytes    | 0 <= kk <= 64    |
|Input bytes  | 0 <= ll < 2**128 |

Back to Argon

We're looking at Argon2, which says:

H^x() = BLAKE2b(a,|a|,0,x)

So in our case:

H0 = H^(64)( [stuff to hash] )
H0 = Blake2b([stuff to hash], |[stuff to hash]|, 0, 64)

Which means that 64 is a number of bytes (not bits), which means that the very first line of the algorithm description should be:

Establish H0 as the 64-byte value as shown below.

Where am I going wrong? How big is H0? H1?

Bonus

I assume it's a typo when the RFC says:

Here H^x() applied to string a is the BLAKE2b(a,|a|,0,x) hash function

and that it should read:

Here H^(x)(a) applied to string a is the BLAKE2b(a,|a|,0,x) hash function

Because later it uses the second syntax:

H^(64)( [stuff to hash] )

$\endgroup$
  • $\begingroup$ This is a question whose answer you can confirm from the reference implementation, and which you should send to the authors of the spec to make it clearer. This often happens the first time someone else tries to implement a new spec! $\endgroup$ – Squeamish Ossifrage Nov 5 '17 at 23:45
  • $\begingroup$ I certainly tried looking at the reference code, both in the RFC, GitHub, and by stepping through the C# implementation in a debugger. $\endgroup$ – Ian Boyd Nov 6 '17 at 14:21
3
$\begingroup$

In core.h of the Argon2 reference implementation, we can see that ARGON2_PREHASH_DIGEST_LENGTH = 64. In core.c, we can see that ARGON2_PREHASH_DIGEST_LENGTH and ARGON2_PREHASH_SEED_LENGTH are used as numbers of bytes (to allocate a buffer in bytes and to specify the size in bytes of a BLAKE2b hash).

We can also reason that it makes no sense for the password, salt, optional secret key, and associated data to figure into the hash only via a 64-bit intermediate quantity—that would open it to preimage attacks on the intermediate quantity.

Please send email to the authors of the internet-draft identifying the incorrect ‘64-bit’ units!

Update: The November 22, 2018 revision of the Argon2 internet-draft, draft-irtf-cfrg-argon2-04, corrects the typo:

Establish H_0 as the 64-byte value as shown below.

draft-irtf-cfrg-argon2-04, §3.2 ‘Argon2 Operation’

$\endgroup$
  • $\begingroup$ TL;DR it should read "bytes". But I think that - while correct - the second section is pretty hard to read. Maybe revisit it? (+1 btw) $\endgroup$ – Maarten Bodewes Nov 6 '17 at 15:35
  • $\begingroup$ Apparently my left and right brain halves disagreed on which sentence structure to use for the left and right halves of the sentence. Better now? $\endgroup$ – Squeamish Ossifrage Nov 6 '17 at 15:37
  • $\begingroup$ What i was terrified of was that they actually allocate a 64-byte buffer, but then only fill the first 64 bits of it! But who am i to question an RFC - after all: if this was a bug surely someone would have found it by now! $\endgroup$ – Ian Boyd Nov 6 '17 at 23:44
  • $\begingroup$ Nope, it is often the case that the text of an RFC is insufficient to make a compatible implementation until someone actually tries it and weeds out all the mistakes in the text. $\endgroup$ – Squeamish Ossifrage Nov 7 '17 at 14:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.