awnumar / memguard

Secure software enclave for storage of sensitive information in memory.
Apache License 2.0
2.49k stars 124 forks source link

Q: When to use Enclave and when LockedBuffer? #118

Closed ghost closed 4 years ago

ghost commented 4 years ago

I am not sure when I should be using the enclave object and when the locked buffer. My use case is that I have encrypted data in database and I am providing decryption key on boot via environment variable which is immediately unset and I am storing that key in enclave. When I need to decrypt the data in database, I get the key from the enclave as locked buffer, I decrypt the data and remove the locked buffer.

I am not sure if this is the correct use or if I should be simply keeping the locked buffer only and read the key from it when needed, not bothering with the enclave altogether?

awnumar commented 4 years ago

Generally use an enclave when you have lots of data to store or when you have some data that will hang around for a while in memory. Use a LockedBuffer when you need frequent access to the data such that decrypting it every time is too much of a performance penalty.

genezhang commented 4 years ago

Sorry to add more questions to this closed issue as my question is closest to this, instead of raising a new question. From your answer Awn, first it seems I can have lot of enclaves to store secrets such as encryption keys. Should I use one enclave for each key or one to contain many keys? One per key will be easiest to use but I have hundreds to thousands of keys potentially, would that be a performance concern? What will be the pattern to use one Enclave for many keys if I'd like to? Second, I was suggested to time out cached keys periodically if they are not in use. But I don't see a purge for specific enclaves, but purge for all. Since contents of enclaves are constantly encrypted using new random keys, I don't feel we need to purge/destroy individual enclaves (except for the number concerns). Can you confirm this is OK? Third, I'm interacting with Hashicorp Vault, which has HTTP(S)-based client, and req.BodyBytes and resp.Body will be in the plain in-memory. I keep the data in a LockedBuffer until last minute to the request body or fetch the response body directly to a LockedBuffer, hope the exposure is minimized. Is there a way better than this? Thanks in advance!

awnumar commented 4 years ago

@genezhang

Should I use one enclave for each key or one to contain many keys?

I would personally use one enclave per key.

One per key will be easiest to use but I have hundreds to thousands of keys potentially, would that be a performance concern?

Nope, Enclaves basically just look like this:

type Enclave struct {
    ciphertext []byte
}

There is a single global key that is used to protect all existing Enclaves, and resetting this key (for example by calling Purge) invalidates all of them at once by destroying this global key.

Second, I was suggested to time out cached keys periodically if they are not in use. But I don't see a purge for specific enclaves, but purge for all. Since contents of enclaves are constantly encrypted using new random keys, I don't feel we need to purge/destroy individual enclaves (except for the number concerns). Can you confirm this is OK?

This has come up before. There's no way currently to invalidate a single Enclave. There would be too much overhead in having a single key for each Enclave, and the ciphertext is stored in an ordinary byte slice provided by the runtime so wiping it wouldn't be effective.

I see two solutions to this in the future:

  1. Change the way keys are protected so that the overhead is small enough so that it's reasonable to have a single key per Enclave.
  2. Use specially allocated memory to store the ciphertext so that it can be wiped properly.

I think I'm in favour of (2) but first we need a more efficient allocator (#124).

In any case, ciphertext is not the easiest part of your system for an adversary to attack. It should be sufficient to just minimise the amount of time the keys spend decrypted inside LockedBuffer objects and let Enclave objects that you no longer need to fall out of scope.

I'm interacting with Hashicorp Vault, which has HTTP(S)-based client, and req.BodyBytes and resp.Body

You could use a Stream object to store the plaintext data. It implements the io.Reader and io.Writer interfaces so can be passed to a http Request as its body. LockedBuffers also have a Reader method that can be used to the same effect.

To read data from a http Response you could use this method or io.Copy into a Stream object.

genezhang commented 4 years ago

Thank you Awn for the detailed answer. I think I understand better now. I thought the content inside an Enclave was constantly being rekeyed (I didn't look inside the code earlier), which caused my concern on the overhead when the number of Enclaves are large. Now I know rekey applies to the encryption key representation. No worries there. And I don't see a need to destroy enclaves when Go GC can just reclaim them. I was using memguard.NewBufferFromEntireReader() when reading from http Response. For request body, it prefers []byte so I passed LockedBuffer.Bytes() to it. With slice reference, it should be good. Thanks again!