The Magic of HKDF — At The Heart of Virtually Every Web Connection

Before I talk about HKDF (HMAC Key Derivation function), I would like to start with a disclaimer. In this article, we will use a…

The Magic of HKDF — At The Heart of Virtually Every Web Connection

Before I talk about HKDF (HMAC Key Derivation function), I would like to start with a disclaimer. In this article, we will use a passphrase to generate an encryption key. But, in real-life scenarios, we would not use HKDF to convert the passphrase into a key, as it operates too fast. This speed of operation would make it easier for Eve to try lots of passphrases and possibly match the passphrase to the key. For these applications, we would use something like PBKDF2, scrypt or Argon2id, as these generally slow down the translation of a passphrase to the key output. But, in areas where we want to produce a key from a Diffie-Hellman exchange of secrets, HKDF is well suited.

In this case, we will use HKDF to derive an encryption key from a passphrase. This could be defined as taking a low-entropy source of information and creating a high-entropy encryption key. Underneath we are using a message authentication key (MAC) to generate the required randomization of the input. This randomization is typically generated from a hash function, but in some cases, such as with GMAC (as used in AES GCM), we use a symmetric key method.

The roots of HKDF are from a paper published in 2010 [1]:

Generally, HKDF has since been used to integrate into TLS (Tunnel Layer Security). With this, we use a key exchange method, such as ECDH, to generate a shared secret between Bob and Alice (K). We pass a salt value from Bob to Alice (Salt), and both of them will use this to help generate the encryption key. Along with this, too, we can add extra information to the key generation process. This might take data from the session and bind it into the key generation (Info), such as for a Session ID. This will generally stop the key from being used in another session.

The symmetric key used for the tunnel is thus generated from HKDF, and where we can generate a symmetric key with a given length. Typically this is a 128-bit or a 256-bit symmetric key. As much as possible, this key should look as it is it randomly generated.

Coding

Initially, we can define the hashing method that we will use for HKDF with [here]:

var hkdf = new HkdfBytesGenerator(new Sha256Digest ());

if (hash=="SHA256") hkdf = new HkdfBytesGenerator(new Sha256Digest ());
else if (hash=="SHA1" ) hkdf = new HkdfBytesGenerator(new Sha1Digest ());
else if (hash=="MD5" ) hkdf = new HkdfBytesGenerator(new MD5Digest ());
else if (hash==" Blake2b" ) hkdf = new HkdfBytesGenerator(new Blake2bDigest ());
else if (hash=="SHA384") hkdf = new HkdfBytesGenerator(new Sha384Digest ());
else if (hash=="SHA512") hkdf = new HkdfBytesGenerator(new Sha512Digest ());
else if (hash=="Blake2s") hkdf = new HkdfBytesGenerator(new Blake2sDigest ());
else if (hash=="SHA3") hkdf = new HkdfBytesGenerator(new Sha3Digest ());
else if (hash=="Sha224") hkdf = new HkdfBytesGenerator(new Sha224Digest ());
else if (hash=="Gost341") hkdf = new HkdfBytesGenerator(new Gost3411Digest ());
else if (hash=="SM3") hkdf = new HkdfBytesGenerator(new SM3Digest ());

We can see we could use SHA256, Blake2 and SHA3. Next, we can use a salt (saltiv) and an info value (info) to generate the shared key [here]:

hkdf.Init(new HkdfParameters (Encoding.UTF8.GetBytes(msg), saltiv, info));
byte[] derivedKey = new byte[size / 8];
hkdf.GenerateBytes(derivedKey, 0, derivedKey.Length);

The final code is [here]:

namespace HKDF
{


using Org.BouncyCastle.Crypto.Parameters;


using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Digests;
using System.Text;


class Program
{

static void Main(string[] args)
{

try {


var hash="SHA512";
var msg="hello";
var salt="8e94ef805b93e683ff18";

var information="";


var size=128;

if (args.Length >0) msg=args[0];
if (args.Length >1) size=Convert.ToInt32(args[1]);
if (args.Length >2) salt=args[2];
if (args.Length >3) hash=args[3];
if (args.Length >4) information=args[4];





var saltiv=Convert.FromHexString(salt);
var info=Convert.FromHexString(information);


var hkdf = new HkdfBytesGenerator(new Sha256Digest ());

if (hash=="SHA256") hkdf = new HkdfBytesGenerator(new Sha256Digest ());
else if (hash=="SHA1" ) hkdf = new HkdfBytesGenerator(new Sha1Digest ());
else if (hash=="MD5" ) hkdf = new HkdfBytesGenerator(new MD5Digest ());
else if (hash==" Blake2b" ) hkdf = new HkdfBytesGenerator(new Blake2bDigest ());
else if (hash=="SHA384") hkdf = new HkdfBytesGenerator(new Sha384Digest ());
else if (hash=="SHA512") hkdf = new HkdfBytesGenerator(new Sha512Digest ());
else if (hash=="Blake2s") hkdf = new HkdfBytesGenerator(new Blake2sDigest ());

else if (hash=="SHA3") hkdf = new HkdfBytesGenerator(new Sha3Digest ());
else if (hash=="Sha224") hkdf = new HkdfBytesGenerator(new Sha224Digest ());
else if (hash=="Gost341") hkdf = new HkdfBytesGenerator(new Gost3411Digest ());
else if (hash=="SM3") hkdf = new HkdfBytesGenerator(new SM3Digest ());

hkdf.Init(new HkdfParameters (Encoding.UTF8.GetBytes(msg), saltiv, info));
byte[] derivedKey = new byte[size / 8];
hkdf.GenerateBytes(derivedKey, 0, derivedKey.Length);

Console.WriteLine("Password:\t{0}",msg);
Console.WriteLine("Salt:\t\t{0}",salt);
Console.WriteLine("Information:\t{0}",information);
Console.WriteLine("Hash:\t\tHMAC-{0}",hash);


Console.WriteLine("\n\nHKDF Key:\t{0}", Convert.ToBase64String(derivedKey));

Console.WriteLine("HKDF Key:\t{0}", Convert.ToHexString(derivedKey));


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

}
}
}



A sample run for a message of “hello” and a hash of SHA256 and with a 128-bit key:

Password: Hello World!
Salt: 000102030405060708
Information: 00010203
Hash: HMAC-SHA256
Length: 128 bits


HKDF Key: mG9Sl9EAb6phr8ZUoETCHg==
HKDF Key: 986F5297D1006FAA61AFC654A044C21E

Conclusion

While not perfect security, HKDF gives us a way to convert a secret into an encryption key. You can try it here:

https://asecuritysite.com/bouncy/bc_key

References

[1] Krawczyk, H. (2010, August). Cryptographic extraction and key derivation: The HKDF scheme. In Annual Cryptology Conference (pp. 631–648). Berlin, Heidelberg: Springer Berlin Heidelberg.