KDF Key Derivation Function Guide: PBKDF2, bcrypt, scrypt and Argon2 Explained

Key Derivation Functions (KDF) are indispensable building blocks of modern cryptography, used to derive keys of the right length and strength from low-entropy inputs such as user passwords or master passphrases. KDFs not only determine the security of password storage but also play a key role in disk encryption, protocol key agreement and API authentication. This article systematically covers the core properties of KDFs, the principles and parameter selection of the four mainstream algorithms (PBKDF2, bcrypt, scrypt, Argon2), and provides runnable JavaScript code examples and parameter selection guidance to help developers build more secure key derivation schemes.

I. Overview of KDF

A Key Derivation Function (KDF) is a cryptographic algorithm that derives one or more keys from input key material. Its input is typically a user password, passphrase or master key, and its output is a fixed-length, pseudorandom key suitable for encryption algorithms. Compared to using a password directly as a key, KDFs significantly improve brute-force resistance through salting, iteration and parameterized cost. The main features of KDF include:

  • Deterministic: The same input (password and salt) always derives the same key, which makes password verification possible
  • Tunable cost: The derivation overhead is controlled by parameters such as iteration count and memory usage, defending against hardware-accelerated attacks
  • Salted design: Random salts prevent rainbow tables and stop identical passwords from deriving identical keys
  • Pseudorandom output: The output is statistically close to random, making it suitable as a key for symmetric encryption algorithms

Typical application scenarios of KDF include secure storage and verification of user passwords, master key derivation for disk encryption (such as LUKS and FileVault), key agreement in TLS and protocol handshakes, deriving encryption keys from user passphrases, and API key derivation with version management. Wherever a strong key must be generated from weak key material, a KDF is needed.

II. Core Properties of KDF

To judge whether a KDF is secure, you need to understand the core properties it must possess. Together these properties determine how well a KDF resists various attack vectors:

  • Determinism: The same input must always produce the same output, otherwise it cannot be used for password verification
  • Pseudorandomness: The output is statistically indistinguishable from true random numbers, preventing keys from being predicted
  • Brute-force resistance: Tunable parameters raise the cost of each derivation, making the attacker's enumeration cost prohibitive
  • Precomputation resistance: Random salts stop attackers from building rainbow tables in advance
  • Memory hardness: The derivation process requires large amounts of memory, making it hard for parallel hardware such as GPUs and ASICs to accelerate

💡 Tip:Salt is just as important as parameters: even with a strong algorithm, if salts are reused or too short, attackers can still batch-compute derivation results for the same password and launch dictionary attacks.

III. PBKDF2 Explained

PBKDF2 (Password-Based Key Derivation Function 2) was defined in RFC 2898 by RSA Laboratories and is one of the earliest widely standardized KDFs. It applies a pseudorandom function (usually HMAC) to the password and salt for multiple iterations and outputs a derived key of the specified length. Because it is simple to implement and FIPS-approved, PBKDF2 remains the first choice for compliance scenarios such as banking and government systems.

3.1 How It Works

PBKDF2 concatenates the password and salt, then iteratively applies a pseudorandom function such as HMAC-SHA256. Each iteration's result is XOR-accumulated with the previous one, eventually producing the derived key. The higher the iteration count, the slower each derivation and the higher the attacker's enumeration cost.

3.2 HMAC Foundations

HMAC (Hash-based Message Authentication Code) is the core building block of PBKDF2. It combines a key with a hash function so that only parties holding the key can produce a valid MAC. In PBKDF2 the password acts as the HMAC key, and the salt is part of the input message.

3.3 Choosing the Iteration Count

The iteration count is the most important parameter of PBKDF2. OWASP currently recommends at least 310000 iterations for HMAC-SHA256 and 120000 for HMAC-SHA512. The principle is to choose the highest count acceptable for user experience, and adjust it regularly as hardware improves.

// Implement PBKDF2 with the Web Crypto API
async function deriveKeyPBKDF2(password, salt, iterations = 310000) {
  const encoder = new TextEncoder();
  const keyMaterial = await crypto.subtle.importKey(
    'raw',
    encoder.encode(password),
    { name: 'PBKDF2' },
    false,
    ['deriveKey']
  );

  const derivedKey = await crypto.subtle.deriveKey(
    {
      name: 'PBKDF2',
      salt: encoder.encode(salt),
      iterations: iterations,
      hash: 'SHA-256'
    },
    keyMaterial,
    { name: 'AES-GCM', length: 256 },
    true,
    ['encrypt', 'decrypt']
  );

  return derivedKey;
}

// Generate a random salt (hex string)
function generateSalt(length = 16) {
  const array = new Uint8Array(length);
  crypto.getRandomValues(array);
  return Array.from(array, b => b.toString(16).padStart(2, '0')).join('');
}

// Usage example
const password = 'user-password-123';
const salt = generateSalt(16);
const key = await deriveKeyPBKDF2(password, salt, 310000);
console.log('Salt:', salt);

💡 Tip:The Web Crypto API natively supports PBKDF2, so you can derive keys securely in the browser without any third-party library. Note, however, that PBKDF2 is not memory-hard and is weaker than Argon2 against ASIC/GPU attacks.

IV. bcrypt Explained

bcrypt was designed by Niels Provos and David Mazieres in 1999 specifically for password hashing. It is based on Eksblowfish (Expensive Key Schedule Blowfish), a variant of the Blowfish cipher, and controls computational cost through an adaptive cost parameter. It is a classic choice for password storage in web applications.

4.1 The Eksblowfish Algorithm

Eksblowfish introduces an expensive initialization phase into the Blowfish key schedule, making key setup impossible to precompute. During encryption, bcrypt repeatedly involves the salt and password in computation, so attackers cannot use precomputed tables to speed up cracking.

4.2 Adaptive cost Parameter

bcrypt's cost parameter (also called the work factor) controls the number of rounds as a power of two, so cost=10 means 2^10=1024 rounds. Each increment of 1 doubles the computation time. A cost between 10 and 14 is currently recommended, and should be re-evaluated every 1 to 2 years as hardware improves.

4.3 Salt and Output Format

bcrypt automatically generates a 16-byte random salt and encodes it together with the cost and hash result into a string like $2b$10$..., known as the PHC string. During verification you only need to pass the password and this string; there is no need to store the salt separately, which makes storage and migration convenient.

// Hash passwords with bcrypt in Node.js
const bcrypt = require('bcrypt');

// Hash a password (salt is generated automatically)
async function hashPassword(password) {
  const saltRounds = 12; // cost factor, recommended 10-14
  const hash = await bcrypt.hash(password, saltRounds);
  return hash;
}

// Verify a password
async function checkPassword(password, hash) {
  return await bcrypt.compare(password, hash);
}

// Usage example
const hash = await hashPassword('user-password-123');
console.log('Hash:', hash);
// Output looks like: $2b$12$abcdef.... (includes cost, salt and hash)

const isValid = await checkPassword('user-password-123', hash);
console.log('Verification result:', isValid);

⚠️ Important:bcrypt has a 72-byte password length limit, and anything longer is truncated. If you use long passwords or passphrases, hash them with SHA-256 first before passing them to bcrypt, to avoid the security risk caused by truncation.

V. scrypt Explained

scrypt was proposed by Colin Percival in 2009 and is one of the earliest memory-hard KDFs. By forcing the derivation process to consume large amounts of memory, it prevents parallel hardware such as ASICs and GPUs from efficiently accelerating cracking. It is widely used in cryptocurrencies (such as Litecoin) and high-security scenarios.

5.1 Memory-Hard Functions

scrypt first fills a large pseudorandom array during derivation, then randomly accesses that array for mixing. If an attacker wants to speed things up, they must either cache the entire array (consuming a lot of memory) or repeatedly recompute it (consuming a lot of time), creating a time-space tradeoff dilemma.

5.2 The N/r/p Parameters Explained

scrypt has three core parameters: N is the CPU/memory cost parameter (must be a power of two), r is the block size parameter, and p is the parallelism parameter. A common recommended combination is N=16384, r=8, p=1, with memory usage of about N*r*128 bytes. Increasing N raises memory pressure, while increasing p raises parallelism; both should be tuned to your server hardware.

// Derive keys in browser/Node with noble-hashes
const { scrypt } = require('@noble/hashes/scrypt');
const { randomBytes } = require('@noble/hashes/utils');

function deriveKeyScrypt(password, salt, options = {}) {
  const {
    N = 16384,    // CPU/memory cost parameter (must be a power of two)
    r = 8,        // block size parameter
    p = 1,        // parallelism parameter
    dklen = 32    // derived key length (bytes)
  } = options;

  return scrypt(
    Buffer.from(password, 'utf8'),
    salt,
    { N, r, p, dklen, maxmem: 512 * 1024 * 1024 }
  );
}

// Generate a random salt
const salt = randomBytes(16);

// Derive a 32-byte key
const derivedKey = deriveKeyScrypt('user-password-123', salt, {
  N: 16384,
  r: 8,
  p: 1,
  dklen: 32
});

console.log('Derived key:', Buffer.from(derivedKey).toString('hex'));

VI. Argon2 Explained

Argon2 is the winner of the 2015 Password Hashing Competition (PHC) and is currently regarded as the most advanced password hashing scheme. Its design balances memory hardness, parallelism resistance and side-channel protection, and it is recommended by both the IETF (RFC 9106) and OWASP as the preferred algorithm for password storage.

6.1 Argon2d / 2i / 2id Variants

Argon2d accesses memory in a data-dependent way, offering strong GPU resistance but potential side-channel exposure; Argon2i accesses memory in a data-independent way, providing side-channel resistance suitable for precomputation protection in password hashing; Argon2id is a hybrid of both, balancing security and performance, and is the OWASP-recommended default.

6.2 Memory and Parallelism Parameters

Argon2's core parameters include time (iteration count), memory (memory usage in KB), parallelism (parallel threads) and hashLen (output length). The OWASP-recommended combination is time=3, memory=64MB (65536 KB), parallelism=4, which can be gradually increased as server performance improves.

6.3 PHC String and Verification

Argon2 output includes a PHC-encoded string that records all parameters, the salt and the derived hash. During verification the library automatically parses the parameters and re-derives for comparison, so you do not need to manage the salt manually, which simplifies version migration and parameter upgrades.

// Derive and verify passwords with the argon2-browser library
const argon2 = require('argon2-browser');

async function deriveKeyArgon2(password, salt) {
  const result = await argon2.hash({
    pass: password,
    salt: salt,                       // at least 8 bytes
    time: 3,                          // iteration count
    mem: 65536,                       // memory (KB), i.e. 64 MB
    hashLen: 32,                      // output length
    parallelism: 4,                   // parallelism
    type: argon2.ArgonType.Argon2id   // recommended Argon2id
  });

  return {
    hash: result.hash,                // derived key (Uint8Array)
    hashHex: result.hashHex,          // hex string
    encoded: result.encoded           // PHC-encoded string
  };
}

// Verify a password (parameters and salt are parsed from the PHC string)
async function verifyPassword(password, encoded) {
  try {
    await argon2.verify({ pass: password, encoded: encoded });
    return true;
  } catch {
    return false;
  }
}

// Usage example
const salt = crypto.getRandomValues(new Uint8Array(16));
const result = await deriveKeyArgon2('user-password-123', salt);
console.log('PHC encoded:', result.encoded);

const isValid = await verifyPassword('user-password-123', result.encoded);
console.log('Verification result:', isValid);

VII. KDF Algorithm Comparison

The four mainstream KDFs differ in memory hardness, parallelism resistance and parameter flexibility. When choosing, consider the security level, runtime environment and compliance requirements together:

Algorithm Memory-Hard Parallelism Resistance Recommended Scenario
PBKDF2 No Weak Legacy compatibility, FIPS compliance
bcrypt No Weak Traditional web app password storage
scrypt Yes Medium High-security scenarios, cryptocurrencies
Argon2 Yes Strong Modern password storage, key derivation

Recommended Argon2 Parameters

Parameter Recommended Value Description
time 3 Iteration count; higher is slower and more secure
memory 65536 KB (64 MB) Memory usage, defends against ASIC parallelism
parallelism 4 Parallel threads, matched to CPU cores
hashLen 32 Output length (bytes), commonly 32 or 64
type Argon2id Balances side-channel protection and parallelism resistance

VIII. Application Scenarios

KDFs play a key role in multiple security scenarios, and choosing the right algorithm and parameters is critical to the overall security solution:

  • Password Storage: User passwords are derived through a KDF and the hash is stored; on login the password is re-derived and compared, avoiding plaintext password leakage
  • Disk Encryption: Full-disk encryption schemes such as LUKS and FileVault use a KDF to derive the master key from the user passphrase, protecting disk data
  • Key Derivation: Multiple sub-keys are derived from a master key for different services or sessions, enabling key layering and isolation
  • API Keys: Derive API authentication keys with version management to support key rotation and permission isolation

IX. Best Practices and Parameter Selection

In engineering practice, choosing a KDF algorithm and parameters requires balancing security, performance and maintainability. The following are proven general recommendations:

  • Prefer Argon2id: New projects should default to Argon2id, which balances memory hardness and side-channel protection and is the current OWASP preferred choice
  • Use Random Salts: Use at least 16 bytes of random salt for each derivation to avoid rainbow tables and identical passwords deriving identical keys
  • Tunable and Upgradeable Parameters: Store parameters together with the salt and hash (the PHC string) so cost can be smoothly raised later as hardware improves
  • Regularly Re-evaluate Cost: Re-evaluate parameters every 1 to 2 years, keeping a single derivation between 100 and 500 milliseconds
  • Constant-Time Comparison: Use a constant-time comparison function when verifying passwords to prevent timing side-channel attacks

Parameter Selection Cheat Sheet

Security Level Recommended Algorithm Recommended Parameters
Low / Compatibility PBKDF2 iterations >= 310000 (SHA-256)
Medium bcrypt cost = 12
High Security scrypt N=16384, r=8, p=1
Maximum Security Argon2id time=3, mem=64MB, p=4

🔐 Recommendation:Never store passwords directly with plain hashes such as MD5 or SHA-1/256; they have no tunable cost and are virtually defenseless against modern GPU brute force. New projects should adopt Argon2id directly.

X. Tudousi KDF Tool

To help developers quickly verify and compare KDFs, Tudousi Tools provides an online KDF tool that supports the following features:

🔑

KDF Key Derivation Tool

PBKDF2 / bcrypt / scrypt / Argon2 multi-algorithm support

The Tudousi Tools KDF tool supports mainstream key derivation algorithms including PBKDF2, bcrypt, scrypt and Argon2. It lets you tune iterations, memory cost and parallelism online, auto-generates random salts and outputs PHC-encoded strings, and supports password hash verification for debugging and migration. All computation runs locally in the browser and no password is ever uploaded, keeping your data safe.

Multi-algorithm Visual params PHC output Local compute
Use Now ->

XI. Summary

KDFs are the bridge between weak password material and strong cryptographic keys, and their security directly determines the overall security of password storage, disk encryption and protocol key derivation. Understanding core properties such as determinism, pseudorandomness, brute-force resistance and memory hardness is the prerequisite for correctly evaluating and choosing a KDF.

In practice, new projects should prefer Argon2id, combined with random salts and tunable parameters, and re-evaluate upgrades regularly as hardware improves. PBKDF2, bcrypt and scrypt each have their own applicable scenarios, and the choice should be weighed against compliance requirements and the runtime environment. Pairing with a professional KDF tool for parameter tuning makes the security solution more robust and easier to maintain.

← Back to Blog