PBKDF2 (Password-Based Key Derivation Function 2)
WPA2-PSK (Wi-Fi Protected Access 2 – Pre-Shared Key) employs CCMP-AES encryption with a pre-shared key (PSK). However, user-entered passphrases are not suitable for direct use as cryptographic keys.
To derive a strong key from a passphrase, the 802.11 specification relies on PBKDF2 (Password-Based Key Derivation Function 2), as described in RFC 2898. For this post, we will use the test vector from RFC 6070.
PBKDF2 strengthens the key by applying an iterative process using HMAC with a salt, making it more secure for encryption.
Let’s break down each step involved in converting a passphrase into a key using PBKDF2!
The PBKDF2 function takes the following inputs and produces the corresponding output:

Where:
- Password (P) – The input passphrase.
- Salt (S) – A unique value used to enhance security.
- Iteration count (c) – A positive integer specifying the number of iterations.
- Intended key length (dkLen) – The desired key length in octets, with a maximum value of (232-1) * hLen, where hLen is the output length of the chosen pseudorandom function.
- Derived key (DK) – The final output of the function.
Process
- Check if the dkLen is too long
- Check if the dkLen > (232-1) * hLen, if it is, stop the process and output “derived key too long”.
- Calculate how many blocks of the derived key will be needed (l).
- Example: Suppose we want a dkLen of 32 bytes, as specified in the 802.11 standard. Using HMAC-SHA1 as the core of the pseudorandom function, it produces 20 bytes per iteration. Dividing 32 by 20 gives 1.6, and rounding up to the next whole number results in 2. This means the process needs to run twice, making the value of l equal to 2.
- For each necessary block, apply the F function:
- Where:
- T_1 = F(P, S, c, 1)
- T_2 = F(P, S, c, 2)
- ….
- T_l = F(P, S, c, l)
- Where:
- The last step is to concatenate the T blocks: T_1 || T_2 || … || T_l, and then take the first r bytes from the final block (0…r-1).
- From the last block, we take only the necessary bytes (0…r-1). Mathematically, this can be written as:
- r = dklen – ( 1 – l ) * hLen
- From the last block, we take only the necessary bytes (0…r-1). Mathematically, this can be written as:
F Function
The core of the algorithm is the F function, which takes the password (P), salt (S), iteration count (c), and block number (i), where i ranges from 1 to l.
- F(P, S, c, i) = U_1 ⊕ U_2 ⊕ …. ⊕ U_c
Where:
U_1 = HMAC_SHA1(P, S || INT(i) )
U_2 = HMAC_SHA1(P, U_1 )
….
U_2 = HMAC_SHA1(P, U_{c-1})
This implementation passes the password (P) to HMAC as the key, while the message continues to be hashed after initialization with the salt concatenated with the block number (i), which is encoded in four octets.
802.11 Passphrase to PSK mapping
PSK = PBKDF2(passphrase, ssid, 4096, 32)
Where:
- passphrase is a sequence between 8 and 63 ASCII-encoded characters.
- ssid is the SSID of the network which is used as the Salt (S).
- 4096 is the iteration counter,
- 32 is the number of output bytes
Test Vector
Since the iteration count in the IEEE specification is quite high, it is impractical to show a complete step-by-step process with all the intermediate values. Instead, let’s use a test vector from RFC 6070:
Input:
P = "password" (8 octets)// 0x70617373776f7264
S = "salt" (4 octets) // 0x73616c74
c = 2
dkLen = 20
- Check if the dkLen is too long:
- dklen < (232-1) * 20
- Calculate how many blocks (l) are necessary:
- l = ceil ( 20 / 20 ) = 1
- Only one block is necessary
- Apply the F function to each block:
- T_1 = F(P, S, c, 1)
- F(P, S, c, 1) = U_1 ⊕ U_2 ⊕ …. ⊕ U_c
- Since c is 2 we have:
- U_1 = HMAC_SHA1(P, S || INT(i) )
- U_2 = HMAC_SHA1(P, U_1 )
- Calculating U_1:
- S || INT(i) = 0x73616c74 || 0x00000001
- HMAC_SHA1(0x70617373776f7264, 0x73616c7400000001)
- U_1 = 0x0c60c80f961f0e71f3a9b524af6012062fe037a6
- Calculating U_2:
- U_2 = HMAC_SHA1(P, U_1 )
- U_2 = HMAC_SHA1(0x70617373776f7264, 0x0x0c60c80f961f0e71f3a9b524af6012062fe037a6)
- U_2 = 0xe60cc942513261fd3eb76c0e617d53f6f73ebef1
- F(P, S, c, 1) = U_1 ⊕ U_2 ⊕ …. ⊕ U_c
- F(P, S, c, 1) = 0x0c60c80f961f0e71f3a9b524af6012062fe037a6 ⊕ 0xe60cc942513261fd3eb76c0e617d53f6f73ebef1
- F(P, S, c, 1) = 0xea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957
- T_1 = F(P, S, c, 1)
- The final step is to concatenate the T blocks and extract the most significant bytes (r).
- Since there is only one block and dkLen is the same length as hLen:
- r = 20 – (1 – 1) * 20
- r = 20
- Therefore, the first 20 bytes (the full block) are the final output: 0xea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957.
- Since there is only one block and dkLen is the same length as hLen:

In the next posts, we will dive deeper into the key hierarchy and begin to put everything together using the knowledge we’ve covered from the previous functions.
I hope this has been informative for you
Stay tuned,
Diego Capassi
References:
F. Liu, L. Doraswamy, and T. Polk, PKCS #5: Password-Based Key Derivation Function 2 (PBKDF2) Test Vectors, RFC 6070, Jan. 2011. [Online]. Available: [https://datatracker.ietf.org/doc/html/rfc6070](https://datatracker.iet)
B. Kaliski, PKCS #5: Password-Based Cryptography Specification Version 2.0, RFC 2898, Sept. 2000. [Online]. Available: https://www.ietf.org/rfc/rfc2898.txt.
IEEE Std 802.11™-2016, "IEEE Standard for Information technology—Telecommunications and information exchange between systems—Local and metropolitan area networks—Specific requirements—Part 11: Wireless LAN Medium Access Control (MAC) and Physical Layer (PHY) Specifications," IEEE, 2016.