The Great WiFi and Password Protector: PBKDF2

It’s a humble little hashing method that was defined in RFC 2898 [here]:

The Great WiFi and Password Protector: PBKDF2

It’s a humble little hashing method that was defined in RFC 2898 [here]:

but, it protects your online security like few other methods. Why? Because every time you connect to a Wifi network, it is PBKDF2 (Password-Based Key Derivation Function 2) that protects your Wifi password. Overall, it uses a number of hash iterations to determine a final hash value, and where these iterations slow the process of hashing down. And, as we add a salt to the hash, an adversary cannot use rainbow tables to crack the password. Commonly, an adversary will only be able to try less than 1,000 pass phrases every second, while a fast hashing method such as SHA-256, where it can allow 100s of thousands to be tried.

TrueCrypt

Often PBKDF2 is also used to create an encryption key from a defined password, and where it is not possible to reverse the password from the hashed value. Otherwise, we can use it to protect sensitive assets, such as where it is used in TrueCrypt to generate the encryption key that is required to read the header information of the encrypted drive and which stores the encryption keys. In TrueCrypt, we use PBKDF2 to generate the key (with salt) and which will decrypt the header and reveal the keys which have been used to encrypt the disk (using AES, 3DES or Twofish):

We this we use:

byte[] result = passwordDerive.GenerateDerivedKey(16, 
ASCIIEncoding.UTF8.GetBytes(message), salt, 1000);

which has a key length of 16 bytes (128 bits — dklen), uses a salt byte array, and 1000 iterations of the hash (Miterations). You should find that the resulting hash value will have 32 hexadecimal characters (16 bytes).

WPA2

The generalised format for PBKDF2 is:

DK = PBKDF2(Password, Salt, Miterations, dkLen)

Where Password is the passphrase, Salt is the salt, Miterations is the number of iterations, and dklen is the length of the derived hash. Overall, we can create DK of a size that matches our application.

But, its major use is in WPA-2 and where we created a hashed version of the password. With this, WPA-2 uses 4,096 interations. Its main focus is to produced a hashed version of a password and includes a salt to reduce the opportunity for a rainbow table attack. It generally uses over 1,000 iterations in order to slow down the creation of the hash, so that it can overcome brute force attacks.

The IEEE 802.11i standard which define the WPA security used in Wifi, outlines that the pre-shared key is defined by:

PSK = PBKDF2(PassPhrase, ssid, ssidLength, 4096, 256)

And, so, we see that we use 4,096 iterations.

Coding

The .NET framework has supported PBKDF2 through the Rfc2898DeriveBytes class since .NET 2.0. Unfortunately, it has a weak Rfc2898DeriveBytes constructor and should be deprecated. Now, Microsoft have released .NET 8, and which includes the Pbkdf2 constructor, and which has improved defaults.

In this case we will using the System.Security.Cryptography.Rfc2898DeriveBytes class, and the Pbkdf2 constructor to create an output of variable length. Using .NET

First we create a folder named “pbkdf2”, and then go into that folder. We can create a Dotnet console project for .NET 7.0 with:

dotnet new console --framework net8.0

This produces a Csproject file of:

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

Notice that the target framework is 8.0. The following is the coding:

namespace PBKDF2
{
class Program
{
static void Main(string[] args)
{
var password="qwerty";
var salt="test";
var iterations=500;
var hash="SHA256";
var length=32;

try {
if (args.Length >0) password=args[0];
if (args.Length >1) salt=args[1];
if (args.Length >2) iterations=System.Convert.ToInt32(args[2]);
if (args.Length >3) hash=args[3];
if (args.Length >4) length=System.Convert.ToInt32(args[4]);


var p =System.Text.Encoding.UTF8.GetBytes(password);
var saltBytes =System.Text.Encoding.UTF8.GetBytes(salt);
var keyder=System.Security.Cryptography.Rfc2898DeriveBytes.Pbkdf2(p,saltBytes,iterations,new System.Security.Cryptography.HashAlgorithmName(hash),length);

Console.WriteLine("Password: {0}",password);
Console.WriteLine("Salt: {0}",salt);
Console.WriteLine("Iterations: {0}",iterations);
Console.WriteLine("Hash: {0}",hash);
Console.WriteLine("Length: {0}",length);
Console.WriteLine("\nKey Deriation: {0} [{1}]",Convert.ToBase64String(keyder),Convert.ToHexString(keyder));

} catch (Exception e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
}
}

In this case, we add a passphrase (p), some salt bytes (saltBytes), a length for the output, and a number of iterations:

var keyder=System.Security.Cryptography.Rfc2898DeriveBytes.Pbkdf2(p,saltBytes,
iterations,new System.Security.Cryptography.HashAlgorithmName("SHA256"),length);

We see we can pick the hashing method that we can use. In this case it is SHA-256. Overall, we should pick the insecure ones such as MD5 and SHA1, and should focus on SHA-256, SHA-384 and SHA-512.

And, so, here is the final solution:

We then enter the command of:

dotnet run

A sample run shows:

Password: qwerty
Salt: test
Iterations: 500
Hash: SHA256
Length: 32
Key Deriation: IbEO0rAG0fCCa0ouOhaEHWFKzOFV93/6KxegweSPktg= [21B10ED2B006D1F0826B4A2E3A16841D614ACCE155F77FFA2B17A0C1E48F92D8]

Conclusions

It’s a great method, and while there are other better methods, such as Argon 2, it is still one of the best methods around for protecting secrets.