Argon2
Argon2 is a family of memory-hard key derivation functions designed primarily for secure password hashing, proof-of-work applications, and key derivation, emphasizing resistance to GPU, ASIC, and side-channel attacks through high memory requirements and efficient use of multiple computing cores.[1] It was developed by Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich at the University of Luxembourg and selected as the final winner of the Password Hashing Competition (PHC) in July 2015 from among 24 submissions.[2] The PHC, launched in 2013, aimed to identify a modern standard for password hashing to replace older algorithms like PBKDF2 and bcrypt by prioritizing memory hardness to counter parallel hardware attacks.[2] Argon2 operates on inputs including a password, salt, and tunable parameters for time cost (iterations), memory cost (in kibibytes), and parallelism (number of threads), producing a fixed-length output hash.[3] It uses the BLAKE2b cryptographic hash function internally and is optimized for x86 architectures to achieve high memory-filling rates while minimizing time-space trade-offs, where attackers attempt to reduce memory usage at the expense of increased computation time.[1] The design provides collision resistance for up to 2256 inputs and preimage resistance for 2512 inputs, making it suitable for protecting low-entropy secrets like passwords without requiring additional secret keys.[3] The family includes three main variants to address different security needs: Argon2d, which uses data-dependent memory access for maximum resistance to GPU attacks and is ideal for proof-of-work in cryptocurrencies; Argon2i, which employs data-independent access to mitigate side-channel vulnerabilities like timing attacks, though it is slower and preferred for password hashing; and Argon2id, a hybrid introduced in version 1.3 that combines the strengths of the first two for balanced protection in general use cases.[4] Argon2id is now recommended as the default variant due to its effectiveness against both trade-off and side-channel threats.[1] The algorithm's specifications were formalized in an IETF informational RFC 9106 in September 2021 by the Crypto Forum Research Group, providing an implementer-focused description and reference code.[1]Introduction
Purpose and Overview
Argon2 is a memory-hard key derivation function (KDF) specifically designed for secure password hashing and proof-of-work applications. It aims to protect against brute-force attacks by demanding substantial computational resources, particularly memory bandwidth, which increases the cost of parallelization on specialized hardware like GPUs and ASICs.[5][3] Developed by Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich at the University of Luxembourg, Argon2 was released in 2015 and is dual-licensed under the CC0 public domain dedication and the Apache License 2.0, allowing broad adoption and implementation.[4][6] As a state-of-the-art successor to earlier memory-hard functions like bcrypt and scrypt, Argon2 improves upon their designs by offering higher memory efficiency and resistance to side-channel attacks while maintaining tunable parameters for performance. It emerged as the winner of the 2015 Password Hashing Competition (PHC) and includes variants—Argon2d for data-dependent applications, Argon2i for resistance to certain side-channels, and Argon2id as a hybrid—to address diverse security requirements.[5][3]Key Features
Argon2 is designed with extensive configurability to allow implementers to balance security, performance, and resource constraints. Key adjustable parameters include the number of iterations (t), which controls execution time and ranges from 1 to 2^{32}-1; memory usage (m) in kibibytes (KiB), set between 8p and 2^{32}-1 to ensure sufficient resource demands; degree of parallelism (p), specifying the number of lanes or threads from 1 to 2^{24}-1 for multi-core utilization; and output length (T), the tag size in bytes from 4 to 2^{32}-1.[3][7] The function employs BLAKE2b as its underlying permutation for both initialization and compression operations, leveraging this secure and efficient hash primitive to process data blocks.[3] Argon2 adopts a sponge-like construction, where inputs are absorbed into the internal state and outputs are squeezed from it, facilitating flexible handling of variable-length data while maintaining security properties.[7] It supports a range of inputs to enhance versatility: a mandatory salt of 8 to 2^{32}-1 bytes for domain separation and resistance to rainbow table attacks; an optional secret key of up to 2^{32}-1 bytes for key derivation scenarios; and optional associated data up to 2^{32}-1 bytes for additional context binding.[3][7] The core memory-hard design utilizes a fixed-size array of up to 2^{32}-1 KiB, intentionally allocating substantial memory to deter cost-effective parallel attacks on specialized hardware like GPUs or ASICs.[3][7]History and Development
Password Hashing Competition
The Password Hashing Competition (PHC) was announced in the first quarter of 2013 by a panel of cryptographers led by Jean-Philippe Aumasson, with the aim of identifying a modern standard for password hashing to address vulnerabilities in existing schemes like PBKDF2 and bcrypt.[8] The competition ran until 2015, receiving 24 submissions by the deadline of March 31, 2014, and shortlisting nine finalists in December 2014 based on criteria including security analysis and performance benchmarks.[8] Its primary goals were to develop memory-hard functions that resist brute-force attacks accelerated by GPUs or ASICs, side-channel attacks, and time-memory tradeoff (TMTO) attacks, while maintaining cryptographic properties such as preimage resistance.[9] Argon2 was submitted by a team from the University of Luxembourg—Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich—and was announced as the winner on July 20, 2015, after extensive community review and cryptanalysis.[8] The selection highlighted Argon2's balance of strong security against parallel hardware attacks, efficient performance across diverse platforms, and relative simplicity in implementation compared to other candidates.[9] For context, the panel also gave special recognition to finalists like Lyra2, noted for its multi-round structure, and yescrypt, praised for its hardware attack resistance, underscoring the competition's emphasis on diverse approaches to memory-hardness.[8] The initial specification for Argon2 was released in December 2015 on the PHC website, detailing its parameters and variants to facilitate immediate adoption and further scrutiny.[3]Standardization and Adoption
Following its selection as the winner of the Password Hashing Competition in 2015, Argon2 was initially released as version 1.0 that year by its designers, Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich. This version established the core memory-hard design for password hashing. In 2017, version 1.3 was issued to address early security issues, including mitigations against time-memory trade-off attacks in the Argon2i variant and refinements to side-channel resistance, enhancing overall robustness without altering the fundamental algorithm.[10] A key milestone in formalization came in September 2021, when the Internet Engineering Task Force (IETF) published RFC 9106, an informational document specifying Argon2 as a memory-hard function suitable for password hashing and proof-of-work applications. The RFC provides detailed recommendations on parameters and variants, such as preferring Argon2id for balanced security, but it does not represent an official IETF standard or endorsement. This publication has guided implementers in achieving interoperability and best practices.[1] Adoption accelerated post-2015, with integrations into major libraries and frameworks. Libsodium incorporated Argon2 in version 1.0.9, released April 2, 2016, making it available via its high-level password hashing API, with explicit support for the Argon2id variant added in version 1.0.15, released December 13, 2017.[11] PHP added native support for Argon2i in version 7.2, released November 30, 2017, through thepassword_hash() function, with Argon2id following in version 7.3. The Open Web Application Security Project (OWASP) has recommended Argon2, particularly the Argon2id variant, in its Password Storage Cheat Sheet since at least 2017 updates, positioning it as the preferred option over older algorithms like bcrypt.[12] By 2025, analyses of open-source repositories indicate widespread use in web applications and databases, though configuration practices vary, with approximately 46.6% of deployments using parameters below OWASP guidelines.[13]
The reference implementation's release under the Creative Commons CC0 1.0 dedication, equivalent to public domain, has enabled unrestricted adoption and modification across programming languages and platforms, contributing to its proliferation without licensing barriers. As of 2025, Argon2 serves as the de facto standard for password hashing in new systems, benefiting from ongoing security audits of implementations, such as those in libraries like libsodium, to address evolving threats like GPU acceleration and side-channel exploits.[14]
Design Principles
Memory-Hardness Mechanism
Argon2 is a memory-hard function, designed such that its optimal evaluation requires substantial random-access memory (RAM) and sequential memory access patterns, thereby imposing a high computational cost on parallelized attacks using specialized hardware like graphics processing units (GPUs) or application-specific integrated circuits (ASICs). This property is formally characterized by a time penalty D(\alpha) for using only \alpha M memory, where M is the full memory requirement, satisfying D(\alpha) > 1/\alpha as \alpha \to 0, ensuring that reducing memory usage leads to superlinear increases in computation time.[3] The core of Argon2's memory-hardness lies in its block-based memory structure, where the allocated memory is organized as a matrix B consisting of blocks, each 1024 bytes (1 KiB) in size. This matrix has p rows, corresponding to lanes that enable parallelism, and q = m'/p columns, where m' is the adjusted number of blocks derived from the user-specified memory parameter m in KiB to ensure compatibility with the parallelism degree p (specifically, m' = \lfloor m / (4p) \rfloor \cdot 4p). The first two blocks of each lane are initialized using a hash of the inputs, while subsequent blocks are filled through a process that depends on prior blocks across lanes and columns, enforcing widespread data dependencies throughout the memory array. The total memory footprint is thus M = m \times 1024 bytes.[3] To establish these dependencies and prevent efficient parallel evaluation or memory reuse, Argon2 employs a permutation-based mixing operation via the G-function, which is derived from the Blake2b cryptographic permutation. The G-function takes two 1024-byte blocks X and Y as inputs. It computes R = X ⊕ Y, views R as an 8×8 matrix of 16-byte registers, applies the Blake2b-based permutation P first row-wise and then column-wise to produce Z, and outputs the single 1024-byte block Z ⊕ R. This operation is used to compute each new block from the prior block in the same lane and a selected reference block, ensuring that each new block relies on pseudorandomly selected previous blocks from different lanes and positions, which disrupts locality and forces sequential traversal of the large memory space.[3] In contrast to CPU-hard functions that primarily burden general-purpose processors through intensive arithmetic operations, Argon2's design specifically targets limitations in memory bandwidth and cache hierarchy, which are particularly acute on GPUs and ASICs optimized for high-throughput computation but inefficient at handling large, non-local memory accesses. By requiring the loading and mixing of distant 1024-byte blocks repeatedly over multiple passes, Argon2 elevates the area-time product cost for hardware implementations, making brute-force attacks economically infeasible even with massive parallelism.[3]Resistance to Attacks
Argon2's memory-hard design imposes significant time and memory requirements on computations, which directly contributes to its resistance against brute-force attacks, particularly offline dictionary and rainbow table attacks on stolen password databases. By mandating large memory allocations—such as 1 GB or more for practical security levels—the function ensures that attackers face exponentially higher costs in terms of both computational time and hardware resources compared to lighter hashing algorithms. This approach raises the economic barrier for exhaustive searches, making brute-force cracking infeasible on commodity hardware within reasonable timeframes.[15] To counter the advantages of parallel processing hardware like GPUs and ASICs, Argon2 employs access patterns that are either data-dependent (in Argon2d) or data-independent (in Argon2i), limiting the efficiency gains from massive parallelism. The memory-hard nature requires each parallel unit to access substantial independent memory, reducing the throughput benefits of specialized hardware that excels in compute-intensive but memory-light tasks. Additionally, the inclusion of costly operations like multiplications in the core compression function increases the circuit depth for ASIC implementations, aiming to balance performance across CPU, GPU, and custom hardware.[15][7] Argon2 mitigates time-memory tradeoff (TMTO) attacks through its multi-pass architecture and inter-block dependencies, which prevent attackers from significantly reducing memory usage at the expense of increased computation time. In the Argon2i variant, multiple passes over the memory array ensure that shortcuts in memory access do not yield substantial tradeoffs, with the design limiting any potential reductions to minimal factors even under optimized attacks. This structure enforces a high time-area product, preserving the overall resource cost for adversaries attempting to trade space for time.[15][7] For protection against side-channel attacks, such as timing or cache-based leaks, Argon2 incorporates constant-time operations throughout its processing and offers mode-specific mitigations. The Argon2i variant uses data-independent memory addressing to avoid patterns that could reveal information through access timings, making it suitable for environments where side-channel vulnerabilities are a concern, like password hashing in interactive applications. In contrast, the hybrid Argon2id mode combines data-dependent and independent phases to balance security and performance while still providing resistance in the initial passes.[15][7] Argon2's tunable parameters— including memory size, iterations, and parallelism—make it well-suited for proof-of-work applications, such as cryptocurrency mining, where adjustable difficulty levels can enforce equitable resource consumption across participants. The memory-hard requirements discourage the dominance of ASIC miners by favoring general-purpose hardware, promoting a more decentralized ecosystem. For instance, configurations like 250 MB of memory with short computation times on standard CPUs allow for fair verification while scaling the challenge based on network needs.[15][7]Algorithm
Initialization and Parameters
Argon2 requires two primary inputs: a password P of arbitrary length up to $2^{32}-1 bytes and a salt S of length between 8 and $2^{32}-1 bytes, where the salt must be unique for each password to prevent rainbow table attacks.[3] Optionally, it accepts a secret key K of up to $2^{32}-1 bytes for keyed hashing scenarios, such as when additional server-side protection is needed, and associated data X of up to $2^{32}-1 bytes to bind the hash to contextual information for integrity verification.[1] The algorithm is configured via several core parameters that control its computational cost and output: the degree of parallelism p, an integer from 1 to $2^{24}-1 specifying the number of parallel lanes; the memory allocation m in kibibytes (KiB), ranging from $8p to $2^{32}-1 to ensure at least 8 KiB per lane; the number of iterations t, from 1 to $2^{32}-1, dictating the number of passes over the memory; the desired output length in bytes, between 4 and $2^{32}-1; the variant type y, where 0 denotes Argon2d, 1 denotes Argon2i, and 2 denotes Argon2id; and the version number v = 0x13 (decimal 19), indicating the specification revision.[3][1] Initialization begins by computing a 64-byte seed value H_0 using the BLAKE2b hash function applied to a concatenated input block consisting of the little-endian 32-bit encodings of p, the output length, m, t, v, and y, followed by the length-prefixed representations of P, S, K, and X.[3] This H_0 seeds the memory array B, which is structured as an array of 1024-byte blocks totaling m blocks nominally (since m is specified in KiB and each block is 1 KiB). To ensure alignment with the processing requirements across p lanes, the effective number of blocks is set to m' = 4 \times p \times \left\lfloor \frac{m}{4p} \right\rfloor.[1] For each lane i from 0 to p-1, the initial blocks B{{grok:render&&&type=render_inline_citation&&&citation_id=0&&&citation_type=wikipedia}} and B{{grok:render&&&type=render_inline_citation&&&citation_id=1&&&citation_type=wikipedia}} are derived by applying BLAKE2b (configured for 1024-byte output) to H_0 concatenated with the little-endian 32-bit values of j (0 or 1) and i.[3]Core Processing Steps
The core processing of Argon2 occurs in three main phases, which manipulate a memory array organized into p parallel lanes, each consisting of q sequential 1024-byte blocks, for a total of up to m bytes of memory. These phases are common across all variants, differing only in how reference blocks are selected during computation. The process relies on a compression function G to mix data, ensuring memory-hardness by requiring sequential access to pseudorandomly addressed blocks. Phase 1: Filling Memory Blocks. This initial phase populates the entire memory array starting from a seed derived from input parameters. For each lane i (0 ≤ i < p), the first two blocks B[i] and B[i][16] are initialized using a 1024-byte hash H' of the 64-byte seed H_0 concatenated with little-endian encodings of the block index j (0 or 1) and lane index i. The remaining blocks in each lane are then filled column-wise: for j from 2 to q-1, compute *B[i][j] = G(*B[i][j-1], B[l][z]) , where (l, z) is a reference block address determined by the variant-specific indexing function applied to B[i][j-1] and j. This sequential intra-lane dependency in the filling pass ensures that each block depends on the immediately preceding one in the same lane, while the inter-lane reference promotes diffusion across the memory. The filling is performed slice-wise—dividing the q columns into SL = 4 slices of size s ≈ q/4 blocks each (specifically, columns 2 to q-1 divided into 4 slices)—to enable lane-parallel execution without altering dependencies.[15] The following pseudocode illustrates the structure of Phase 1 (adapted for clarity, with variant-specific indexing in the Index function):This phase requires accessing nearly all memory blocks once, setting up the data-dependent structure for subsequent passes. Phase 2: Main Computation. If the number of passes t exceeds 1, this phase performs t-1 additional iterations over the memory to further mix the data and increase computational cost. Each pass processes the memory slice-wise, similar to Phase 1, but updates blocks in place by XORing the new computation with the existing value: for each j ≥ 1 in a slice, *B[i][j] ← G(*B[i][j-1], B[l][z]) ⊕ B[i][j] , where (l, z) is again a variant-dependent reference address. For the first block in each lane (j=0), the reference is typically from the end of the previous pass to close the loop. The slice-wise processing maintains intra-lane sequential dependencies (each block depends on the prior one in the lane) while allowing parallel computation across lanes, with inter-lane references providing randomization. This multi-pass structure amplifies resistance to time-memory trade-offs by forcing repeated full-memory traversals.[15] G-Function Details. The core mixing operation G takes two 1024-byte input blocks X and Y and produces a 1024-byte output block. It begins by computing R = X ⊕ Y, treating R as an 8×8 matrix of 128-byte subblocks (rows and columns). A permutation function P—based on BLAKE2b's quarter-round operations and consisting of 2 rounds of mixing—is applied row-wise to each of the 8 rows: for row index r, the 128 bytes (equivalent to 8 consecutive 16-byte words) are mixed using the GB function on its working registers, followed by a rotation to mix within the row. The same P is then applied column-wise to the resulting 8 columns, again treating each as 128 bytes across the rows. The output is Z ⊕ R, where Z is the doubly-permuted matrix. This design leverages BLAKE2b's proven security for diffusion while operating on fixed-size subblocks to avoid side-channel leaks from variable addressing. The G-function ensures that each block update propagates changes across 1024 bytes, contributing to the overall avalanche effect in the memory array.[15] Phase 3: Finalization. After all passes, a final 1024-byte block C is computed by XORing the last block from each lane: C = ⊕_{i=0}^{p-1} B[i][q-1] . This aggregates information from all lanes into a compact representation. The output tag of length T bytes is then derived by applying the T-byte hash function H' to C. This step ensures the final result depends on the entire memory state without requiring additional computation.[15]for i = 0 to p-1: B[i][0] ← H'(H_0 || LE32(0) || LE32(i)) B[i][1] ← H'(H_0 || LE32(1) || LE32(i)) for slice = 0 to SL-1: // SL = 4 slices for parallelism for i = 0 to p-1: for pos = 0 to s-1: // s blocks per slice j ← slice * s + pos // Column index if j < 2: continue (l, z) ← Index(B[i][j-1], j, variant parameters) B[i][j] ← G(B[i][j-1], B[l][z])for i = 0 to p-1: B[i][0] ← H'(H_0 || LE32(0) || LE32(i)) B[i][1] ← H'(H_0 || LE32(1) || LE32(i)) for slice = 0 to SL-1: // SL = 4 slices for parallelism for i = 0 to p-1: for pos = 0 to s-1: // s blocks per slice j ← slice * s + pos // Column index if j < 2: continue (l, z) ← Index(B[i][j-1], j, variant parameters) B[i][j] ← G(B[i][j-1], B[l][z])
Output Generation
After the core processing steps, the finalization phase extracts a representative state from the memory array to produce the hash output. The final block C is computed as the bitwise XOR of the last block from each of the p lanes:C = B{{grok:render&&&type=render_inline_citation&&&citation_id=0&&&citation_type=wikipedia}}[q-1] \oplus B{{grok:render&&&type=render_inline_citation&&&citation_id=1&&&citation_type=wikipedia}}[q-1] \oplus \cdots \oplus B[p-1][q-1],
where q = m'/p is the number of blocks per lane and each block is 1024 bytes, yielding a 1024-byte C. This aggregation ensures the output depends on the entire memory state while maintaining a fixed intermediate size for hashing.[1] The output tag is then generated by applying the variable-length hash function H' to C, controlled by the desired output length T (ranging from 4 to $2^{32}-1 bytes):
\text{Tag} = H'^{T}(C).
The function H' is built atop BLAKE2b, which supports arbitrary output sizes through its extensible design: for T \leq 64, it computes BLAKE2b on the concatenation of the little-endian 32-bit encoding of T and C, truncated to T bytes; for T > 64, it uses an iterative construction where r = \lceil T/32 \rceil - 2, computes intermediate 64-byte values V_i starting from V_1 = BLAKE2b^{64}(LE32( T ) || C ), then V_i = BLAKE2b^{64}(V_{i-1}) for i=2 to r, and V_{r+1} = BLAKE2b^{T - 32r}(V_r) ; the output is the concatenation of the first 32 bytes of each V_1 to V_r followed by V_{r+1}. This mechanism avoids security degradation for varying lengths, as BLAKE2b's internal structure permits secure extension and truncation.[1] The resulting binary tag is typically encoded in a human-readable string format for storage and verification, following the Password Hashing Competition (PHC) convention. This includes the variant (e.g., argon2id), version (e.g., v=19), parameters (e.g., m=65536 for memory in KiB, t=3 for iterations, p=1 for parallelism), a base64url-encoded salt, and the base64url-encoded tag, delimited by dollar signs:
$argon2id$v=19$m=65536,t=3,p=1$<base64salt>$<base64tag>. The base64url encoding uses the alphabet A-Z, a-z, 0-9, -, and _ (without padding), ensuring compatibility and compactness.[3]
Variants
Argon2d
Argon2d is the data-dependent variant of the Argon2 password-hashing function, designed to prioritize resistance against time-memory tradeoff attacks in offline scenarios.[17] In this mode, memory access patterns are input-specific, as block indices for reading previous blocks depend directly on the values derived from the password and other inputs.[1] This dependency makes Argon2d particularly suitable for applications like proof-of-work systems in cryptocurrencies, where side-channel threats are minimal and the focus is on computational hardness without real-time access to the system.[17] The addressing mode in Argon2d employs a permutation function \phi(j) that selects the reference block for the j-th iteration based on the content of the immediately preceding block. Specifically, the address is calculated as \phi(j) = \text{Truncate}(M_{j-1}) \mod j, where M_{j-1} is the previous block and Truncate extracts a 32-bit integer from it.[17] Unlike data-independent variants, this approach allows the seed—derived from the password, salt, and other parameters—to influence all subsequent memory accesses directly, creating a chain of dependencies that propagates through the computation.[1] During initialization, the first block M_1 is set to the hash of the input parameters using BLAKE2b, ensuring that variations in the input immediately affect the addressing sequence.[17] This data-dependency enables performance optimizations, such as preventing CPU prefetching of memory blocks, which results in Argon2d being approximately 50% faster than comparable functions like scrypt in multi-threaded environments.[17] For instance, on a 2 GHz processor with 8 threads, it achieves around 0.61 cycles per byte for certain parameter sets.[17] However, the input-specific access patterns increase vulnerability to timing attacks, making Argon2d less ideal for online password verification where adversaries might exploit execution time variations.[1] Recommended parameters for proof-of-work applications include 2 lanes, 250 MB of memory, and timings around 0.1 seconds on a single 2 GHz core to balance security and usability.[1]Argon2i
Argon2i is the data-independent variant of the Argon2 memory-hard function, specifically engineered to mitigate side-channel attacks in environments where memory access patterns could leak sensitive information, such as through cache timing or branch prediction. Unlike data-dependent variants, Argon2i employs fixed, password-independent patterns for referencing memory blocks, ensuring that the sequence of accesses remains uniform and unpredictable based on input data. This design promotes resistance to attacks that exploit variations in execution time or resource usage to infer password details. The core of Argon2i's addressing mode relies on block indices derived from predetermined, non-input-dependent mechanisms, such as a counter-based pseudo-random generator G2 to produce indices J1 and J2, based on fixed parameters like pass number, lane, and slice indices.[3] Such patterns are generated using a pseudo-random number generator seeded by fixed parameters like pass number, lane, and slice indices, further randomizing references without input influence.[7] Argon2i finds primary application in scenarios involving online password verification, where servers must compute hashes in real-time and are vulnerable to remote timing attacks that could probe cache states over the network. By maintaining consistent memory access regardless of the password, it reduces the feasibility of these attacks, making it suitable for frontend authentication systems or key derivation in resource-constrained settings.[3] A key trade-off in Argon2i is its performance penalty compared to the data-dependent Argon2d variant: the uniform access requires additional computational overhead, resulting in slower execution—approximately 2 cycles per byte on x86 processors (e.g., with 8 threads) versus 0.6 for Argon2d—due to the need for more passes to compensate for reduced parallelism opportunities.[3] However, this comes at the benefit of enhanced protection against cache-timing exploits, which could otherwise allow attackers to reconstruct memory contents with significantly less effort. To counter vulnerabilities like time-memory trade-off (TMTO) attacks in multi-target scenarios, version 1.3 of Argon2i incorporated fixes that necessitate more than 10 passes over the memory for adequate security, elevating the computational barrier for adversaries while preserving side-channel resilience.[18]Argon2id
Argon2id is a hybrid variant of the Argon2 password-hashing function that combines elements of Argon2i and Argon2d to provide a balanced approach to security and performance. It operates in data-independent mode, similar to Argon2i, for the first half of the first pass over the memory blocks, and then switches to data-dependent mode, akin to Argon2d, for the remainder of the computation.[7] This design ensures that the initial memory access pattern remains unpredictable to mitigate side-channel attacks, while the later data-dependent addressing enhances resistance to GPU-based parallelization and time-memory trade-off attacks.[7] The variant is specified with a type parameter value of 2, distinguishing it from Argon2d (type 0) and Argon2i (type 1).[7] It requires a minimum of one iteration (t=1) to exhibit its hybrid behavior, though additional passes can be configured for increased computational cost. Argon2id was introduced in version 1.3 of the Argon2 specification to address vulnerabilities identified in earlier iterations of Argon2i, such as time-memory trade-off exploits that allowed reduced memory usage during attacks.[7][19] RFC 9106 recommends Argon2id as the preferred option for general-purpose password hashing, positioning it as the first choice over the pure variants due to its ability to mitigate the individual weaknesses of Argon2i (vulnerability to side-channel and trade-off attacks) and Argon2d (susceptibility to side-channel timing).[7] By retaining early resistance to side-channel attacks through data-independent access and incorporating later data-dependency for robust GPU resistance, Argon2id achieves a comprehensive security profile suitable for most applications without requiring specialized tuning.[7]Security Analysis
Cryptanalysis Results
Argon2d has demonstrated strong resistance to practical cryptanalytic attacks, primarily due to its data-dependent memory access pattern, which complicates time-memory tradeoff (TMTO) attacks by making precomputation password-specific.[1] The best known TMTO on Argon2d achieves only a modest reduction factor of approximately 1.33 in the time-area product, far from enabling efficient parallelization on GPUs or ASICs.[1] For Argon2i, a significant vulnerability was identified in version 1.2 by Biryukov, Dinu, and Khovratovich in 2016, exploiting the fixed addressing scheme in the low-storage attack to compute the function with roughly 5 times less memory for single- and two-pass configurations without increasing computation time.[20] This attack relied on precomputing access patterns independently of the password and salt, allowing space savings of up to n/5 empirically; it was mitigated in version 1.3 by modifying the overwrite mechanism to XOR new blocks with the previous contents, disrupting the fixed-address exploitation.[20] Subsequent analysis by Alwen and Blocki extended TMTO attacks on the updated Argon2i, achieving a reduction factor of about 3 for three passes at 1 GiB memory, with the factor decreasing as passes or memory increase; asymptotically, the best attacks require O(m^{0.233}) time-space resources, approaching the optimal O(m^{0.25}) bound.[1][18] The hybrid Argon2id variant inherits the mitigations from both Argon2d and Argon2i, with no dedicated breaks reported beyond generic TMTOs; it experiences a reduction factor of about 2.1 for one pass (combining low-storage and ranking attacks) but drops to 1.33 for multiple passes, making it recommended for use with sufficient iterations (e.g., one with adequate memory) to minimize vulnerabilities.[1] In general, Argon2's collision resistance derives directly from that of the underlying BLAKE2b hash function, which requires at least 2^{256} operations for practical collisions under standard assumptions. Memory-hardness proofs establish that TMTO attacks on Argon2 incur a cost scaling with the square root of the memory size M, formalized approximately as attack time \sim t \sqrt{m/p}, where t is the number of iterations, m the memory in blocks, and p the passes, ensuring that reducing memory quadratically increases time.[1][21]Side-Channel Resistance
Argon2's design incorporates measures to mitigate side-channel attacks, such as timing and cache leaks, which exploit implementation details rather than algorithmic weaknesses. All variants require constant-time implementations to prevent timing leaks, where execution time varies based on input data; the reference implementation adheres to this by avoiding data-dependent branches and using uniform memory access patterns where possible.[3][4] The variants differ in their inherent resistance to side-channel attacks due to memory access patterns. Argon2i employs data-independent addressing, accessing memory in a password-independent order to resist cache-timing and other side-channel attacks, making it suitable for environments where such threats are prominent. Argon2id, a hybrid, provides similar protection by operating in data-independent mode for the first half of the first pass, transitioning to data-dependent mode thereafter, thus balancing side-channel resistance with other security goals. In contrast, Argon2d relies on data-dependent memory access for its core operations, which enhances resistance to GPU-based cracking but introduces vulnerabilities to side-channel attacks if not carefully implemented.[1][3] Early analyses identified specific issues with Argon2d, including susceptibility to cache-timing attacks stemming from its data-dependent indexing function, which can leak information through cache state variations. Mitigations for these vulnerabilities include selecting Argon2i or Argon2id over Argon2d in threat models involving side-channels, enabling memory-wiping options to clear sensitive data post-computation, and employing constant-time memory access techniques in custom implementations to avoid predictable patterns. Best practices also recommend using oblivious RAM constructions or randomized memory layouts where feasible, though these add computational overhead; the RFC emphasizes Argon2id with one iteration and sufficient memory (e.g., 2 GiB) as a uniformly safe configuration against side-channel threats.[1] Custom implementations remain risky, as deviations from constant-time practices can reintroduce vulnerabilities, underscoring the need for rigorous code reviews and adherence to reference designs.[1]Recommended Parameters
Guidelines from RFC 9106
RFC 9106, published in September 2021 by A. Biryukov, D. Dinu, D. Khovratovich, and S. Josefsson, standardizes Argon2 as a memory-hard function for password hashing and proof-of-work applications, providing specific parameter guidelines to ensure interoperability and security compliance.[22] These guidelines emphasize the use of Argon2id (type=2) as the mandatory variant for new implementations due to its resistance to side-channel attacks, while Argon2d and Argon2i are optional.[22] All compliant implementations must support version v=0x13 (19 in decimal).[22] The RFC outlines three parameter profiles tailored to different security and resource needs, with memory cost (m) expressed in KiB, time cost (t) in passes, and parallelism (p) in lanes. The default profile, recommended for general-purpose password hashing on typical backend servers, uses m=2^{21} KiB (2 GiB), t=1, and p=4, producing a 256-bit (32-byte) output and targeting approximately 0.5 seconds of computation on a 2 GHz CPU with 4 cores.[22] For high-security scenarios requiring maximized adversarial costs, the memory-hard profile retains m=2^{21} KiB (2 GiB), t=1, and p=4, also with a 256-bit output, to prioritize memory usage while maintaining side-channel protections.[22] In contrast, the low-memory profile for constrained devices sets m=2^{16} KiB (64 MiB), t=3, and p=4, again yielding 256 bits of output to balance security with limited resources.[22] Salt lengths should be at least 64 bits (8 bytes) but are recommended at 128 bits (16 bytes) to prevent attacks exploiting fixed salts, while output lengths of 128 bits are sufficient for most uses but 256 bits are preferred for stronger derived keys.[22] These parameters assume the standard definitions of m, t, and p as outlined in the initialization process.[22]| Profile | Type | m (KiB) | t | p | Output (bits) | Target Runtime (2 GHz, 4-core CPU) | Use Case |
|---|---|---|---|---|---|---|---|
| Default | Argon2id | 2^{21} (2 GiB) | 1 | 4 | 256 | ~0.5 seconds | General backend servers |
| Memory-Hard | Argon2id | 2^{21} (2 GiB) | 1 | 4 | 256 | ~0.5 seconds | High-security hashing |
| Low-Memory | Argon2id | 2^{16} (64 MiB) | 3 | 4 | 256 | Adjusted for constraints | Resource-limited devices |