There are various other posts which talk about grabbing a key from ram such as
- https://electronics.stackexchange.com/questions/198274/storing-a-secure-key-in-an-embedded-devices-memory
- How would you know a certificate/private key if you extracted it from RAM? Or would you?
- https://crypto.stackexchange.com/questions/30281/storing-a-secure-key-in-an-embedded-devices-memory
My question is a bit different. Assume that we have the key and for a brief window we'll use it to initialize a cipher (in this case ChaCha20) and then wipe the key from memory (using best practices, memset_s etc.)[*edit2]
Once the cipher is initialized, is that equivalent to having the key in memory?
Specifically, looking at the ChaCha20 reference code: https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/chacha.c
void ECRYPT_keysetup(ECRYPT_ctx *x,const u8 *k,u32 kbits,u32 ivbits)
{
const char *constants;
x->input[4] = U8TO32_LITTLE(k + 0);
x->input[5] = U8TO32_LITTLE(k + 4);
x->input[6] = U8TO32_LITTLE(k + 8);
x->input[7] = U8TO32_LITTLE(k + 12);
if (kbits == 256) { /* recommended */
k += 16;
constants = sigma;
} else { /* kbits == 128 */
constants = tau;
}
x->input[8] = U8TO32_LITTLE(k + 0);
x->input[9] = U8TO32_LITTLE(k + 4);
x->input[10] = U8TO32_LITTLE(k + 8);
x->input[11] = U8TO32_LITTLE(k + 12);
x->input[0] = U8TO32_LITTLE(constants + 0);
x->input[1] = U8TO32_LITTLE(constants + 4);
x->input[2] = U8TO32_LITTLE(constants + 8);
x->input[3] = U8TO32_LITTLE(constants + 12);
}
It seems that one simply needs to get the memory location pointed at by x->input, read in like 48 bytes of data from that point, and you've got it.
- Is this correct?
- How can this be prevented in software? (best practices)
EDIT: I see that at least input[12] and input[13] are adjusted as bytes are crunched... and that the cipher can be "seeked" by adjusting input[8] and input[9] (or at least in salsa20, I guess chacha20 is similar)... so therefore it would probably be enough to simply seek to a random "location", and encrypt some random filler bytes, upon initialization
- Is this correct, would it improve defense against grabbing the key (or initialization state)?
EDIT 2:
I believe the key can be wiped from memory once the cipher is initialized since the reference implementation does not use it for decryption... or am I missing something?
Example:
ECRYPT_init();
ECRYPT_keysetup(&ctx, MyKey, ECRYPT_MAXKEYSIZE, ECRYPT_MAXIVSIZE);
ECRYPT_ivsetup(&ctx, MyIv);
//notice neither MyKey nor MyIv are used here:
ECRYPT_decrypt_bytes(&ctx, ciphertext, plaintext, ciphertext_len);