5
$\begingroup$

I would like to encrypt some data using a password. I want to use a function like PBKDF2 to turn my password into a key. However, I would like to also require a keyfile, for added security. My data should only be decryptable if I have the password and the keyfile.

What is the "best" way to do this?

From what I understand, I should not be using the salt for this purpose, correct? I.e. I could use the salt as my "key file", and the password entered by the user as the password, but most literature I can find does not recommend using the salt this way.

$\endgroup$
2
$\begingroup$

So the advantage of the keyfile in addition to the password is that it is basically a form of two factor authentication: You have a password (something you know), and a token (something you have).

The problem is that your salt must be available in order to derive your password based secret, which would mean your token (the salt) is not really separable from your password - this means you don't really have two factor authentication anymore, as the salt can't be separate from the password. So using the salt as the keyfile would not really improve security at all.

One way to accomplish what you are looking for is to generate your keyfile using /dev/urandom or equivalent to generate an appropriately sized token (128-256 bits). Then keep this keyfile on a USB drive. This way, whenever you need to access your secret, you require both knowledge of your password, and possession of the physical token.

After validating the password and keyfile, you could generate a single master key via something like hash(keyfile || password_derived_secret).

$\endgroup$
  • 2
    $\begingroup$ If you'd use the salt as a "pepper" then I don't see how that will invalidate the two factor authentication. +1 anyway as this is a nice way to keep the two keys separate. In that sense the keyfile and password_derived_secret could also be used as input for a KBKDF such as HKDF (although even XOR'ing them together would be a secure scheme as far as I can see). $\endgroup$ – Maarten Bodewes Dec 7 '16 at 17:46
1
$\begingroup$

From what I understand, I should not be using the salt for this purpose, correct? I.e. I could use the salt as my "key file", and the password entered by the user as the password, but most literature I can find does not recommend using the salt this way.

While the term "salt" often connotes that the value is not secret, passing the contents of a secret, high-entropy keyfile as PBKDF2's "salt" parameter should be fine. PBKDF2 iterates a pseudorandom function keyed by the password, with the "salt" passed as the argument to the initial iteration. So if PBKDF2 is a pseudorandom function as well:

  1. An attacker who doesn't have either the keyfile nor the password has no choice but to perform a brute force search for the derived key.
  2. An attacker who has the keyfile but not the password would have to guess the password. (This is the same case as password cracking with public salts.)
  3. An attacker who has the password but not the keyfile would need to brute force search the content of the keyfile, which is only worthwhile if this search is smaller than the search for the derived key. (This tells you that the keyfile's content should be chosen uniformly at random, and the same size as the derived key's security level.)

There are good reasons for doing otherwise, though. For example, many programs instead use the password-derived key as a symmetric key encryption key for encrypting a randomly generated data encryption key. PBKDF2 produces uniform random bits as output, but keep in mind that not all cryptographic algorithms accept that as their keys. In particular, public key algorithms generally don't. So if you want a design that can work with any format of key you probably don't want to use PBKDF2 output directly as your data encryption key.

$\endgroup$
0
$\begingroup$

There are many ways to do this, when it comes to what is "best" that is determined by what you want out of the scheme, and security/performance tradeoffs. Maybe don't focus on the best method, but rather eliminating bad ones.

If the keyfile is large, it should be derived into a smaller key of appropriate size, such as 256-bits. I will call the keyfile $F$ and its derived key $K_F$, if $F$ is already an appropriate size, they are equivalent. The password $P$ and its derived key $K_P$.

You can use both derived keys as part of the encryption process. This can be done in a layered process, such as encrypting the data with $K_F$ and then with $K_P$. I am not a fan of this method. XEX modes of encryption that use one key for the X steps and one for the E steps may be more effective.

You can use $K_F$ as part of the password key derivation process. In this method you can use $K_F$ as the salt input to PBKDF2, making it a pepper rather than a salt. You can also use Ella's method of master key generation by hashing the derived keys together, either by concatenation or by using one as an HMAC key on the other.

As Luis says, what you do not want is a method that allows a single key to be easily recovered from the final master key if this and another derived key is known to the attacker. Say for example the attacker knows your password, and through some trickery was able to get the encryption key, they should not be able to recover $K_F$ through less than brute force. This may not seem important, as they already have the encryption key, but the more key material that remains secret the better, especially if you used the keyfile for something else. This means do not XOR the derived keys together to generate the master key.

The same rule applies to the password, if the keyfile is known, the workload on the password key derivation should not be decreased. This means not applying PBKDF2 to the keyfile, and then hashing that result with the password. The full password derivation process needs to be required to get the master key.

Whatever method you choose, you may want to change the password, or even the keyfile, at some future point in time. If you encrypt the data with the master key, you need to decrypt that data to change the password, and if you have gigabytes of data, this is a huge performance and time concern. The better method is to create a random data encryption key $K_D$, encrypt with that, then use the master derived key to encrypt that key. Now a password change only takes a fraction of a second longer than the key derivation process. It also means you get a true random key for your data encryption key, and you can use different keys for different pieces of data.

Combining that with the derivation process, we get something like this:

$K_F$ = SHA256($F$)
$K_P$ = PBKDF2($P$)
$K_M$ = SHA256($K_F$ || $K_P$)
$K_D$ = secure random 256-bits
$Dat_E$ = $E_{K_D}$(Data)
$Key_E$ = $E_{K_M}$($K_D$)

Now you store $Dat_E$ and $Key_E$, and use the keyfile and password to recover the data or to change the password.

For the keyfile, you can also do things to mess up attackers, like have 256 actual keyfiles on the flash drive, and choose 8 different files to derive $K_F$. This adds up to $2^{48}$ complexity to the derivation process, as there are more than 400 trillion different combinations to choose from. The files can be labeled 00.dat to FF.dat for example, and you need to choose the correct combo, such as FE 25 B6 A1 C8 D4 E9 0C. Think of it as a second password, but you would need to remember it. The software could even be programmed to display a dummy file if a specific combination of keyfiles is chosen, if you are worried about being forced to decode the data.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.