HKDF

From Crypto++ Wiki
Jump to navigation Jump to search
HKDF
Documentation
#include <cryptopp/hkdf.h>

HKDF is HMAC-based Extract-and-Expand key derivation function by Krawczyk and Eronen. HKDF is state of the art and used in protocols like IPsec. The Crypto++ implementation is from Cryptographic Extraction and Key Derivation: The HKDF Scheme and RFC 5869, HMAC-based Extract-and-Expand Key Derivation Function (HKDF).

HKDF provides the KeyDerivationFunction interface rather than the MessageAuthenticationCode interface. The KDF interface consists of a default constructor and a method DeriveKey which derives a user key form the parameters.

All Crypto++ hashes derive from HashTransformation. The base class provides functions like Update, Final and Verify. You can swap-in any hash for any other hash in your program. You can also use ChannelSwitch to send data to multiple hashes at the same time.

Constructor

HKDF provides a default constructor.

DeriveKey

unsigned int DeriveKey (byte *derived, size_t derivedLen,
                        const byte *secret, size_t secretLen,
                        const byte *salt, size_t saltLen,
                        const byte *info, size_t infoLen) const

derived is the buffer to receive the derived key. derivedLen is the size of the buffer, in bytes.

secret is private information to use during derivation. secretLen is the size of the buffer, in bytes.

salt is possibly public information to use during derivation. saltLen is the size of the buffer, in bytes.

info is additional, possibly public information to use during derivation. infoLen is the size of the buffer, in bytes.

DeriveKey returns the number of bytes returned in the derived buffer.

salt and info are used to help distinguish one instance or run of the algorithm from another. The parameters can be NULL.

Sample Program

The sample program below demonstrates a HKDF with SHA1.

$ cat test.cxx
#include <iostream>
#include <string>

#include "cryptlib.h"
#include "hkdf.h"
#include "sha.h"
#include "filters.h"
#include "hex.h"

int main(int argc, char* argv[])
{
    using namespace CryptoPP;

    byte password[] ="password";
    size_t plen = strlen((const char*)password);

    byte salt[] = "salt";
    size_t slen = strlen((const char*)salt);

    byte info[] = "HKDF key derivation";
    size_t ilen = strlen((const char*)info);

    byte derived[SHA1::DIGESTSIZE];

    HKDF<SHA1> hkdf;
    hkdf.DeriveKey(derived, sizeof(derived), password, plen, salt, slen, info, ilen);

    std::string result;
    HexEncoder encoder(new StringSink(result));

    encoder.Put(derived, sizeof(derived));
    encoder.MessageEnd();

    std::cout << "Derived: " << result << std::endl;

    return 0;
}

Running the program results in the following.

$ ./test.exe
Derived: 9912F20853DFF1AFA944E9B88CA63C410CBB1938

You can swap-in any hash class that provides a blocksize. The code below uses BLAKE2b as the message digest. The BLAKE2b sample below requires Commit 758939ab2e1b.

$ cat test.cxx
#include <iostream>
#include <string>

#include "cryptlib.h"
#include "hkdf.h"
#include "blake2.h"
#include "filters.h"
#include "hex.h"

int main(int argc, char* argv[])
{
    using namespace CryptoPP;

    byte password[] ="password";
    size_t plen = strlen((const char*)password);

    byte salt[] = "salt";
    size_t slen = strlen((const char*)salt);

    byte info[] = "HKDF key derivation";
    size_t ilen = strlen((const char*)info);

    byte derived[BLAKE2b::DIGESTSIZE];

    HKDF<BLAKE2b> hkdf;
    hkdf.DeriveKey(derived, sizeof(derived), password, plen, salt, slen, info, ilen);

    std::string result;
    HexEncoder encoder(new StringSink(result));

    encoder.Put(derived, sizeof(derived));
    encoder.MessageEnd();

    std::cout << "Derived: " << result << std::endl;

    return 0;
}

Running the program results in the following.

$ ./test.exe
Derived: 4FF891CB129EC9F2B4CF715897B3FC5A0F58C61EC2CD59D71C635C75810EC04763E8C36
BAC7462D58820734B8B09A4FCB956743CDD6FA6976B26B1A1C00E0786

Independent Keys

Deriving several security parameters from a common secret is a frequent operation. The property is called Key Independence and you should ensure your security parameters are independent. Key independence would be useful for deriving a cipher and mac key, a cipher key and iv, or multiple keys for Multiple Encryption.

The sample program below demonstrates a HKDF with SHA256. Notice two different labels are used. First, the label HKDF key derivation to derive the key; and second, the label HKDF iv derivation to derive the initialization vector. The version number for the algorithm is also used during derivation.

Using the version number during derivation is a good idea. It ensures different versions of your algorithm have distinct keys. Distinct keys for different versions of an algorithm would have avoided WinZip's vulnerability in Attacking and Repairing the WinZip Encryption Scheme.

$ cat test.cxx
#include <iostream>
#include <string>

#include "cryptlib.h"
#include "hkdf.h"
#include "sha.h"
#include "filters.h"
#include "files.h"
#include "aes.h"
#include "modes.h"
#include "hex.h"

int main(int argc, char* argv[])
{
    using namespace CryptoPP;

    byte password[] ="password";
    size_t plen = strlen((const char*)password);

    byte salt[] = "Version 1";
    size_t slen = strlen((const char*)salt);

    byte info1[] = "HKDF key derivation";
    size_t ilen1 = strlen((const char*)info1);

    byte info2[] = "HKDF iv derivation";
    size_t ilen2 = strlen((const char*)info2);

    byte key[AES::DEFAULT_KEYLENGTH];
    byte iv[AES::BLOCKSIZE];

    HKDF<SHA256> hkdf;

    hkdf.DeriveKey(key, sizeof(key), password, plen, salt, slen, info1, ilen1);
    hkdf.DeriveKey(iv, sizeof(iv), password, plen, salt, slen, info2, ilen2);

    std::cout << "Key: ";
    StringSource(key, sizeof(key), true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    std::cout << "IV: ";
    StringSource(iv, sizeof(iv), true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    CBC_Mode<AES>::Encryption enc;
    enc.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));

    // Use AES/CBC encryptor

    return 0;
}

Running the program results in the following.

$ ./test.exe
Key: 059C0BAB3BBC352D14570606438AF08E
IV: EE744BF9F30D05046FBE25BF6C39E653

And when it comes time to release version 2 of your software, it will derive a different set of secrets:

$ ./test.exe
Key: 964F3C02C9B891615EEB55F4A21F90DB
IV: 22F722170B0CE4D49BB398CB9C249C1C

Downloads

No downloads.