0
$\begingroup$

I'm thinking about a page-level encryption scheme for a database, the general structure would be something like the following:

  • Derive KEY from PASSWORD and SALT using libsodium's Argon2ID
  • Encrypt page with KEY, unique NONCE per page using libsodium's xsalsa20poly1305
  • Store (per page) CIPHERTEXT, NONCE (in clear), TAG (in clear)

Now, my question is, how do I store the SALT? My first idea would be to store it at some known offset in the file. In this way I can read the SALT and then using the cleartext NONCEs and TAGs, do my little decryption dance. One problem I see with this approach is that an attacker observing multiple iterations of the encrypted file will notice that the part of the file containing the SALT remains unchanged.

Regenerating a SALT every time something is stored to disk is obviously too expensive as I would need to reencrypt the whole database.

Is there some other approach possible that doesn't lead to this information leak? Maybe I can add an option for paranoid users to provide high entropy KEYs themselves so that I don't need to perform KEY derivation?

Thanks

$\endgroup$

1 Answer 1

1
+100
$\begingroup$

Now, my question is, how do I store the SALT?

The initial salt used by a cryptographic system which is using it to derive initial key material from entropic inputs of unknown quality (passwords, passphrases) is not typically available for encryption. This is due to there likely being no suitable encryption key available. Encrypting such an initial salt with weakly entropic initial inputs defeats the point of using the memory-hard hashing algorithm (argon2id), since decryption attempts will be much easier to brute-force than the memory-hard hash function.

For the above reason, such an initial salt, while it may not be treated as a fully public value, still cannot be treated with the full care that's ideal for a secret value. This means that keeping the initial salt static and viewable by an attacker with access to the contents of the storage medium is often the best that can be done.


One problem I see with this approach is that an attacker observing multiple iterations of the encrypted file will notice that the part of the file containing the SALT remains unchanged.

This is easy to interpret to mean that this initial salt for the password hash is being encrypted. If so, it needs to be decrypted to be read, thus the weakly entropic inputs must somehow be being used in a raw, preprocessed form to encrypt it. If this is the case, I'd highly recommend against encrypting this salt, for the above stated reason (eg. doing so provides a more easily brute-forceable artifact that undermines the hardness of argon2id).


Regenerating a SALT every time something is stored to disk is obviously too expensive as I would need to reencrypt the whole database.

The only justifiable reason to change this salt that I can see is if the password changes, or if the password hashing algorithm changes. Even in the later case, if the password hash is being used as the only key for encryption, changing the salt will update the ciphertexts, but an adversary with access to any previous password hash can still perform offline attacks to uncover the underlying common secret needed to derive the new keys which will decrypt all ciphertexts.


Is there some other approach possible that doesn't lead to this information leak? Maybe I can add an option for paranoid users to provide high entropy KEYs themselves so that I don't need to perform KEY derivation?

There are other options to consider and many footguns to avoid. However, providing this kind of separate interface opens up the possibility of users mistakenly, or out of convenience/performance, using the weaker interface with their passwords. This is a self-inflicted downgrade attack, that should be avoided, even if such interfaces may be common due to necessity for reasons of backwards compatibility. Whether or not a user has a strong key, or a weak password, the easiest path, and hardest path to misuse, is to just use strong key derivation for all cases during initialization. Doing so once at startup is a potentially negligible cost to pay for the robustness it offers against user mistakes.

What I would suggest is the use of a key-derivation function to make a new subkey for each page. This is generally recommended under the "one use one key" rule. There should also be context strings canonically incorporated into the KDF routine which specify the encryption method, the unique page number/ID, the application name, the nonce, and any other context that could be considered additional authenticated associated data. When unique context information is combined with initial key material in a KDF to create an encryption key, at least in context committing encryption schemes, they can act as more heavy-weight authenticated tweaks.

Relatedly, key rotation is something to consider and plan for. This is difficult to do when the password hash is the main key that encrypts (or derives the encryption key for) everything. The use of page specific keys, that are stored encrypted, can simplify the situation by allowing the page key to update independently, or the password to update and only require the encrypted page keys to be updated, instead of the entire database. But, the downside is that compromise of an isolated page key, that isn't combined in a KDF with the main key and context, would be enough for an adversary to access page contents.

DISCLAIMER: The task you seem to be attempting does not seem trivial. Have you read through guidelines like these before considering such a cryptography engineering task?

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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