Key Independence

From Crypto++ Wiki
Jump to navigation Jump to search

Key Independence refers to the property that an adversary who knows one of an algorithm's security parameters cannot determine whether another parameter is part of the algorithm's set of security parameters. An adversary could learn of additional security parameters if derivation lacks independence.

For example, an adversary could determine if [math]\displaystyle{ k_2 }[/math] is part of the security parameters given [math]\displaystyle{ k_1 = Hash(seed) }[/math] and [math]\displaystyle{ k_2 = Hash(k_1) }[/math]. The adversary would know the set because [math]\displaystyle{ k_2 }[/math] is dependent on [math]\displaystyle{ k_1 }[/math]. And if an iv was derived in a similar fashion, then the iv would be dependent, too.

A practical example of problems created by loss of key independence is given in Attacking and Repairing the WinZip Encryption Scheme. WinZip had encryption problems in version 1 of its encryption scheme. WinZip version 2 tried to fix the problems, but the same [dependent] derivation function was used, so WinZip version 2 still had problems because a downgrade attack could occur.

Generally speaking, you should avoid dependence when you are deriving security parameters. You can side step the problem by using either (1) random parameters or (2) independent key derivation with a KDF and unique labels. This article will demonstrate (2) using Krawczyk and Eronen's HKDF.

Sample Program

The following sample program uses HKDF with SHA256 to derive two security parameters from a common secret. The program then keys an AES cipher. HKDF is used because it state of the art with provable security properties.

In the code below, password is a secret common parameter. salt is a public parameter and used to input the version number of your scheme. Finally, info1 and info1 are labels used to ensure the derived material is independent.

If WinZip would have used independent keys then they would not have suffered the downgrade attacks in version 2 of their 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 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, you change salt to Version 2, and it will derive a different set of secrets:

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