1
$\begingroup$

If you are encrypting some secrets (database passwords, access tokens, etc).

When it comes to key rotation, you'll need to store those encrypted values twice, for a short period of time (under the old and new keys).

Could the encrypted secrets be stored in two files named after the hash of their keys? maybe sha256?

Or could those file names (if known) be used in a malicious way? perhaps making it easier to determine the key values?

An alternative would be to use the keys to encrypt a known plain-text value, and use that for the file names.

I'm currently considering using libsodium, and "IETF ChaCha20-Poly1305" (aead_chacha20poly1305_ietf), to encrypt the secrets.


Why I'm not using 'secrets.new' and 'secrets.old'...

This will be used for some websites, where the script running will simply be given a key to decrypt the secrets (via an environment variable), I'd like to avoid having to also say if it's the new or old key.

Secondly, lets assume the website also has this security vulnerability:

<?php

    $path = '/path/to/file/' . $_GET['file'];

    readfile($path);

?>

It might not be that obvious, but the attacker won't know what the file will be called if it's based on the key (you see a similar thing with the Firefox profile directory).


As a very rough implementation:

<?php

//--------------------------------------------------
// Functions

    function secrets_set($key, $secrets) {

        $nonce = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES);

        $encrypted = sodium_crypto_aead_chacha20poly1305_ietf_encrypt(
                json_encode($secrets),
                $nonce,
                $nonce,
                $key
            );

        $name = hash('sha256', $key);

        $path = SECRETS_FOLDER . '/' . $name . '-' . bin2hex($nonce);

        file_put_contents($path, $encrypted);

    }

    function secrets_get($key) {

        $name = hash('sha256', $key);

        $prefix = SECRETS_FOLDER . '/' . $name . '-';
        $path = NULL;
        $nonce = NULL;
        $secrets = NULL;

        foreach (glob($prefix . '*') as $match) {
            if (($pos = strrpos($match, '-')) !== false) {
                $path = $match;
                $nonce = hex2bin(substr($match, ($pos + 1)));
            }
        }

        if ($path) {

            $secrets = file_get_contents($path);

            $secrets = sodium_crypto_aead_chacha20poly1305_ietf_decrypt(
                        $secrets,
                        $nonce,
                        $nonce,
                        $key
                    );

            $secrets = json_decode($secrets, true);

        }

        return $secrets;

    }

//--------------------------------------------------
// Store

    $key1 = sodium_crypto_aead_chacha20poly1305_ietf_keygen();

    secrets_set($key1, [
            'database' => 'u8syTtAvNWJbhwMNdFdRsfxJY',
            'api_key1' => 'y9f82wZdkahEPsyjXQAmy6nPh',
            'api_key2' => 'YtMp4pHR9EbHbGJXgRiniQixU',
        ]);

//--------------------------------------------------
// Get

    print_r(secrets_get($key1));

//--------------------------------------------------
// Re-key

    $key2 = sodium_crypto_aead_chacha20poly1305_ietf_keygen();

    secrets_set($key2, secrets_get($key1));

    print_r(secrets_get($key1));
    print_r(secrets_get($key2));

    // When $key1 is no longer in use, delete the old file.

?>
$\endgroup$

1 Answer 1

2
$\begingroup$

ChaCha20 is a stream cipher, therefore, knowledge can be used. Let see what one can do.

When it comes to key rotation, you'll need to store those encrypted values twice, for a short period of time (under the old and new keys).

This means that the attacker has a chance to see these $C_1 = P\oplus K_1$ and $C_2 = P\oplus K_2$. X-oring these two ciphertexts;

$$P\oplus K_1 \oplus P\oplus K_2 = K_1 \oplus K_2$$ results the x-or of the keys.

In other words, the attacker could get information about the X-or of new and old keys. As longs as the keys are select independently and uniformly from a good entropy source, this will give no information to the attacker.

Note that IV reuse under the same key will reveal information by crib dragging.

$$P_1\oplus K \oplus P_2\oplus K = P_1 \oplus P_2$$ this can ve exploited by crib-dragging.

Could the encrypted secrets be stored in two files named after the hash of their keys? maybe sha256?

Using hash of the keys give additional information for the attacker. Today, or maybe in a foreseeable this cannot be exploited, however, don't do this. Just use the file name with .old .new extensions during the operation.

Or could those file names (if known) be used in a malicious way? perhaps making it easier to determine the key values?

Already pointed above that the knowledge of the old and new file doesn't reveal the keys.

An alternative would be to use the keys to encrypt a known plain-text value, and use that for the file names.

This is fine.

$\endgroup$
4
  • $\begingroup$ Thanks @kelalaka, you're right to ask about the use of an .old/.new extension, so I've updated the question to explain my thinking for not doing so... I've also added a basic implementation of the hash version, but I think I agree with you about the additional information... I just need to think about how I can do a version that encrypts a known plain-text value, and where I can store it's nonce, while keeping the implementation fairly simple. $\endgroup$ Mar 23, 2020 at 13:30
  • 1
    $\begingroup$ @CraigFrancis usually, the nonce is prepended to the ciphertext and the authentication tag is appended. Prepended since the nonce is needed in the beginning, the tag is appended since it is lastly needed to check the tag. $\endgroup$
    – kelalaka
    Mar 23, 2020 at 15:26
  • $\begingroup$ I agree, but because this is a case of working out which file to use (in theory it should never be more than 2 files), the system will need to check each file to see if it's the correct one for the key provided... as in, take each filename, extract the nonce, encrypt the known plain-text value, and see if that matches the rest of the filename... I can't see a way to make it any easier, without doing the stupid thing of using the same nonce for all. $\endgroup$ Mar 23, 2020 at 15:41
  • 1
    $\begingroup$ Prepending the nonce/Iv is helpful when the file is big or streaming, So, before reading all, one can start decryption. Your question is a bit of software practice that I may not an expert about it/ and this is Cryptography. This can be a good question in Software Review with complete code. IMHO, you should be stored together that can reduce the query time. $\endgroup$
    – kelalaka
    Mar 23, 2020 at 17:01

Your Answer

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

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