Argon2 is a key derivation function that was selected as the winner of the 2015 Password Hashing Competition. It was designed by Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich from the University of Luxembourg. The reference implementation of Argon2 is released under a Creative Commons CC0 license (i.e. public domain) or the Apache License 2.0.
The Argon2 function uses a large, fixed-size memory region (often called the 'memory array' in documentation) to make brute-force attacks computationally expensive. The three variants differ in how they access this memory:
All three modes allow specification by three parameters that control:
While there is no public cryptanalysis applicable to Argon2d, there are two published attacks on the Argon2i function. The first attack is applicable only to the old version of Argon2i, while the second has been extended to the latest version (1.3).
The first attack shows that it is possible to compute a single-pass Argon2i function using between a quarter and a fifth of the desired space with no time penalty, and compute a multiple-pass Argon2i using only / (â /2.72) space with no time penalty. According to the Argon2 authors, this attack vector was fixed in version 1.3.
The second attack shows that Argon2i can be computed by an algorithm which has complexity O(<sup>7/4</sup> log()) for all choices of parameters (space cost), (time cost), and thread-count such that =âÂÂ. The Argon2 authors claim that this attack is not efficient if Argon2i is used with three or more passes. However, Joël Alwen and Jeremiah Blocki improved the attack and showed that in order for the attack to fail, Argon2i v1.3 needs more than 10 passes over memory.
To address these concerns, RFC9106 recommends using Argon2id to largely mitigate such attacks.
Source:
<span style="color:blue;">Function</span> Argon2 <span style="color:blue;">Inputs:</span> password (P): Bytes (0..2<sup>32</sup>-1) <span style="color:green;">Password (or message) to be hashed</span> salt (S): Bytes (8..2<sup>32</sup>-1) <span style="color:green;">Salt (16 bytes recommended for password hashing)</span> parallelism (p): Number (1..2<sup>24</sup>-1) <span style="color:green;">Degree of parallelism (i.e. number of threads)</span> tagLength (T): Number (4..2<sup>32</sup>-1) <span style="color:green;">Desired number of returned bytes</span> memorySizeKB (m): Number (8p..2<sup>32</sup>-1) <span style="color:green;">Amount of memory (in kibibytes) to use</span> iterations (t): Number (1..2<sup>32</sup>-1) <span style="color:green;">Number of iterations to perform</span> version (v): Number (0x13) <span style="color:green;">The current version is 0x13 (19 decimal)</span> key (K): Bytes (0..2<sup>32</sup>-1) <span style="color:green;">Optional key (Errata: PDF says 0..32 bytes, RFC says 0..2<sup>32</sup> bytes)</span> associatedData (X): Bytes (0..2<sup>32</sup>-1) <span style="color:green;">Optional arbitrary extra data</span> hashType (y): Number (0=Argon2d, 1=Argon2i, 2=Argon2id) <span style="color:blue;">Output:</span> tag: Bytes (tagLength) <span style="color:green;">The resulting generated bytes, tagLength bytes long</span>
<span style="color:green;">Generate initial 64-byte block H<sub>0</sub>. All the input parameters are concatenated and input as a source of additional entropy. Errata: RFC says H<sub>0</sub> is 64-bits; PDF says H<sub>0</sub> is 64-bytes. Errata: RFC says the Hash is H^, the PDF says it's â (but doesn't document what â is). It's actually Blake2b. Variable length items are prepended with their length as 32-bit little-endian integers.</span> buffer â parallelism âÂÂ¥ tagLength âÂÂ¥ memorySizeKB âÂÂ¥ iterations âÂÂ¥ version âÂÂ¥ hashType âÂÂ¥ Length(password) âÂÂ¥ Password âÂÂ¥ Length(salt) âÂÂ¥ salt âÂÂ¥ Length(key) âÂÂ¥ key âÂÂ¥ Length(associatedData) âÂÂ¥ associatedData H<sub>0</sub> â Blake2b(buffer, 64) <span style="color:green;">//default hash size of Blake2b is 64-bytes</span>
<span style="color:green;">Calculate number of 1 KB blocks by rounding down memorySizeKB to the nearest multiple of 4*parallelism kibibytes</span> blockCount â Floor(memorySizeKB, 4*parallelism)
<span style="color:green;">Allocate two-dimensional array of 1 KiB blocks (parallelism rows x columnCount columns)</span> columnCount â blockCount / parallelism; <span style="color:green;">//In the RFC, columnCount is referred to as q</span>
<span style="color:green;">Compute the first and second block (i.e. column zero and one) of each lane (i.e. row)</span> for i â 0 to parallelism-1 do <span style="color:green;">for each row</span> B<sub>i</sub>[0] â Hash(H<sub>0</sub> âÂÂ¥ 0 âÂÂ¥ i, 1024) <span style="color:green;">//Generate a 1024-byte digest</span> B<sub>i</sub>[1] â Hash(H<sub>0</sub> âÂÂ¥ 1 âÂÂ¥ i, 1024) <span style="color:green;">//Generate a 1024-byte digest</span>
<span style="color:green;">Compute remaining columns of each lane</span> for i â 0 to parallelism-1 do <span style="color:green;">//for each row</span> for j â 2 to columnCount-1 do <span style="color:green;">//for each subsequent column</span> <span style="color:green;">//i' and j' indexes depend if it's Argon2i, Argon2d, or Argon2id (See section 3.4)</span> iâ², jâ² â GetBlockIndexes(i, j) <span style="color:green;">//the GetBlockIndexes function is not defined</span> B<sub>i</sub>[j] = G(B<sub>i</sub>[j-1], B<sub>iâ²</sub>[jâ²]) <span style="color:green;">//the G hash function is not defined</span>
<span style="color:green;">Further passes when iterations > 1</span> for nIteration â 2 to iterations do for i â 0 to parallelism-1 do <span style="color:green;">for each row</span> for j â 0 to columnCount-1 do <span style="color:green;">//for each subsequent column</span> <span style="color:green;">//i' and j' indexes depend if it's Argon2i, Argon2d, or Argon2id (See section 3.4)</span> iâ², jâ² â GetBlockIndexes(i, j) if j == 0 then B<sub>i</sub>[0] = B<sub>i</sub>[0] xor G(B<sub>i</sub>[columnCount-1], B<sub>iâ²</sub>[jâ²]) else B<sub>i</sub>[j] = B<sub>i</sub>[j] xor G(B<sub>i</sub>[j-1], B<sub>iâ²</sub>[jâ²])
<span style="color:green;">Compute final block C as the XOR of the last column of each row</span> C â B<sub>0</sub>[columnCount-1] for i â 1 to parallelism-1 do C â C xor B<sub>i</sub>[columnCount-1]
<span style="color:green;">Compute output tag</span> return Hash(C, tagLength)
Argon2 makes use of a hash function capable of producing digests up to 2<sup>32</sup> bytes long. This hash function is internally built upon Blake2.
The Request for Comments document standardizing Argon2, which was published in September 2021, recommends: