0

I want to implement a service that can't read the data you store there.

The Idea is that I, like in a password manager, use the password to derive a vault key, which is different from the authentication key, that is used to encrypt/decrypt the data.

How can I encrypt and store data from multiple devices while only sharing the vault key in a way that allows new devices to join while all others are offline and while ensuring that no security issues arise from IV reuse in a setting where the client is unable to trust the server?

I cannot use the vault key directly because I can't know which IVs can be used when logging in from a new device, this creates the danger of IV reuse. When creating the IVs randomly, NIST recommends a strict limit of 2^32 encryptions per key, this feels a little too low for me. I can't trust the server with this either, so I must not rely on the server to generate/send the next fixed field to use as it might just send the same message to multiple devices.

One Idea I have would be to generate a key for each device on its own and store those keys, encrypted with the vault key, on the server (for other devices to be able to decrypt the data), and the IVs stay on the devices locally. Each package then would then need know which key is needed to decrypt it, but this feels like a reasonable compromise to me. I would have to think about what to do when the user changes the password, but I think I just encrypt the previous vault key with the new one and send it to the server ..

Is there a (better) way to share the vault-key-nonce between multiple devices in a way that no nonce is used twice without trusting the server with anything?

Gamer2015
  • 707
  • 5
  • 12
  • Is there a problem with putting your nonce along with the encrypted data with the ciphertext? You trust the server enough that it will not sabotage your files in detectable way otherwise you would not store anything there. You obviously do not trust server to not perform file modifications if it can get away with it. But GCM is just for that. It is authenticated encryption, giving both confidentiality and authentication and it also validates that the IV used is correct. You can put your use case and the uploader id etc as associated data used for gcm validation, server won't mess up with that. – Manish Adhikari May 28 '21 at 11:17
  • Yes, but this is not the Problem. The IV can be stored alongside the ciphertext. The problem is, how does a new device know what iv it can use for the next encryption that is done locally? I do not trust the server enough for it to assign a fixed field to the device as it might just assign the same field twice. – Gamer2015 May 28 '21 at 11:27
  • Can your new device not generate IV itself to use every time before encrypting? Be careful about nonce reuse tho, GCM IV is 96 bits, and you may expect IV collision if you use it about 2^48 times, if it is generated randomly. It can be avoided for more times with statefulness but it is very difficult to maintain especially in multi device scenarios like yours. With poor source of entropy, you may get collisions even earlier so you might want to use GCM-SIV. Using many different keys for different files might do. – Manish Adhikari May 28 '21 at 12:23
  • Yes I could do that, but I would prefer to use multiple keys in that case as I am not comfortable with accepting collisions. Also, not relevant to the question but as a remark: In the definition of AES-GCM it is stated that no more than 2^32 encryptions should be done in case of using an RBG, so 2^48 is a little too much but 2^32 might be enough for my usecase anyway .. – Gamer2015 May 28 '21 at 12:36
  • I just realized that using the vault key to encrypt all the keys might be a bad Idea because I do not have any nonce coordination for that scenario as well. I might end up randomly generating the IVs for the device keys when looking at it. – Gamer2015 May 28 '21 at 12:42
  • Would it be possible to derive a public/private key pair from a password to encrypt the device key before sending it to the server? As far as I am aware, there is no need for a nonce in an asymmetric setting so I would not have to communicate the next nonce to be used. – Gamer2015 May 28 '21 at 12:53
  • I still don't see the need to communicate nonce. And about 2^32 and 2^48, you got confused on where it applies, it seems. You can only encrypt up to 2^32 blocks of a single bulk of message (same IV) because only 32 bits is allocated for the counter. If you generate IV randomly then you expect at least one IV collision after about 2^48 encryptions (on messages) with different IVs. – Manish Adhikari May 28 '21 at 13:15
  • And it is not just being uncomfortable, You CANNOT have any collision in GCM mode, the consequences are devastating compared to IV collision in CBC for instance – Manish Adhikari May 28 '21 at 13:16
  • BTW assymetric encryption also uses nonce and they are not suitable for encrypting bulk messages. You don't need that. Why exactly do you need to communicate next nonce instead of generating it while encryption, is my question? – Manish Adhikari May 28 '21 at 13:19
  • P.S. I wanna add that collision after 2^48 encryptions is expected, you may get one well before that or maybe after that, it is probabilistic – Manish Adhikari May 28 '21 at 13:23
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/124799/discussion-between-gamer2015-and-manish-adhikari). – Gamer2015 May 28 '21 at 13:52

3 Answers3

0

I feel like this question deserves the standard "Don't roll your own" (also here).


I want to implement a service that can't read the data you store there.

Unless I'm grossly misunderstanding your question, you're trying to implement what we call "end-to-end encryption". This is something that is both surprisingly tricky to get right, and has also been solved many times. That wikipedia page gives a bit of the history and pitfalls associated with E2E Encrytion. I would also suggest that you base your implementation on the Signal Protocol, here are some sources:

Mike Ounsworth
  • 58,107
  • 21
  • 154
  • 209
  • Thank you for your answer, but the Signal Protocol does not completely fulfil my requirement of "no data loss in case of loosing all devices". I think that I will be able to use some parts of the Signal Protocol in this project but I do not yet see where .. – Gamer2015 May 28 '21 at 15:14
  • This Answer is marked "correct" as the "Don't roll your own" seems to be a good advice for most people. It does not solve my Problem and the Signal Protocol is not a valid alternative, but it seems like I was not able to communicate my issue properly. I got enough options to solve the problem from the discussion under the post itself, so I'll take some time to consider the options that were presented there. – Gamer2015 May 29 '21 at 06:22
0

I might have overlooked some things, but any additional input would be appreciated.

I think the answer to this is a plain and simple "no". The problem reminds me of a Man-In-The-Middle Attack, which it basically is, because the server can just act like it has some kind of old state for your data and simply deliver the same fixed field for the device again.

It is only possible to randomly generate the IV for each encryption done by the vault key and limit the number of encryptions for the key to 2^32 operations as defined by NIST.

It is not possible to know for sure how many operations have been performed though, otherwise a fixed field for the current device could also be retrieved securely, so the user must update the password early enough or, as mentioned in the post, the encryption with the vault key is only done for each device once. Having a Limit of 2^32 devices per password seems like it is enough, 2^96 messages per device should definitely be enough as well.

The only possibility, as mentioned by "Mike Ounsworth" would be an outbound verification like in the signal protocol.

Gamer2015
  • 707
  • 5
  • 12
0

Instead of using the derived vault key directly to encrypt data, use it as the key input to a key derivation function, like HKDF, and generate a random salt of 256 bits. Then, derive both a key and an IV for AES-256-GCM from that key derivation function, and use those for encryption. Prepend the salt to the encrypted data, and when decrypting, derive the keys the same way.

This avoids the problem of repeating IVs for a given key, because you're generating a new key and IV each time, just in a deterministic way given a random salt. These keys and IVs will be cryptographically independent, so you don't need to worry about problems with reuse.

bk2204
  • 8,695
  • 20
  • 19