bin-y / standard-ecies

Standard ECIES implemention for NodeJS based on crypto.ECDH with no other dependencies.
Creative Commons Zero v1.0 Universal
8 stars 2 forks source link

Reusing of the ephemeral private key (aka reusing the shared secret) #6

Open dko-slapdash opened 3 years ago

dko-slapdash commented 3 years ago

encrypt() uses a lot of CPU to generate a fresh private key for each new message it encrypts. It makes the encryption bullet proof and unbreakable even theoretically, but, on the other hand, it doesn't allow to encrypt a huge stream of messages in a CPU efficient way.

Would be cool if encrypt() accepted an optional ecdh object and cache all the sharedSecret related calculations in it. Or do something else to relax CPU usage in cases when reusing of the same private key is acceptable.

bin-y commented 3 years ago

I have noticed an implementation of ecies library reused a private key to improve its performance before I write this library. But I didn't follow that library because generating new keys for each message provides better security and I don't want my implementation less secure than standard. I didn't made it an optional way to improve performance because I believe many coders follow words like "Doing X can provide better performance" directly and ignore the cost behind the gain, which, in this case, is security.

I didn't test it but I believe it will reduce a lots of CPU usage. But still, I did not find a proper situation to reduce the security of encryption. I believe we could do better to the performance issue you met. Could you please explain your use case so we can find out a better solution or I can be persuaded to believe that offering a private key cache is necessary for my users.

dko-slapdash commented 3 years ago

The usecase (and the approach) I mention here is this:

Encryption algorithm for each [id, message]:

  1. random() -> [messagePublicKey, messagePrivateKey]
  2. DH(userPublicKey, messagePrivateKey) -> dhSecret
  3. sha256(dhSecret, id) -> secret
  4. aes256(secret, message) -> data
  5. ciphertext = version + messagePublicKey + data

We don't run step 1 for every message; instead, we cache [messagePublicKey, messagePrivateKey] for some time (1 second by default). Thus, notice that the ciphertext is deterministic by [id, message] if the same message is encoded multiple times in a raw. This is okay for the purposes of storing the ciphertext in a storage, because all messages there have different IDs, and thus all secrets will be different (so we don't need an IV).

Decryption algorithm for each [id, message]:

  1. split(ciphertext) -> [version, messagePublicKey, data]
  2. DH(userPrivateKey, messagePublicKey) -> dhSecret
  3. sha256(dhSecret, id) -> secret
  4. message = aes256(secret, data)

In practice, we expect that messages which are updated close to each other will have the same messagePublicKey, so we LRU-cache dhSecret.

bin-y commented 3 years ago

Sorry for the late response. I was busy on a private project. I believe the key to the problem here is whether the ECIES standard allows people to do something or not, rather than doing something is acceptable in someone's circumstance, due to the name of the library. Then I looked up some public document and now I believe the standard does not allow people to reuse that ephemeral key.

from https://www.researchgate.net/publication/255970113_A_Survey_of_the_Elliptic_Curve_Integrated_Encryption_Scheme

4 ECIES 1) Alice must create an ephemeral key pair consisting in the finite field element u and the elliptic curve point U=u·G. That key pair should be generated pseudo- randomly exclusively for the current process.

The ephemeral key should be used exclusively for current process.

from https://www.shoup.net/papers/iso-2_1.pdf

15.2 Encryption

  1. Chooser∈{1,...,μ−1}at random.

Choosing an random key pair is the first step of the encryption.

from https://www.secg.org/sec1-v2.pdf

5.1.3 Encryption Operation Input:The input to the encryption operation is:

  1. An octet string M which is the message to be encrypted.
  2. (Optional) Two octet stringsSharedInfo1andSharedInfo2which consist of some data sharedbyUandV.

"The ephemeral key to be reused" is not an input to be accepted for encryption process.

Actions : Encrypt M as follows:

  1. Select an ephemeral elliptic curve key pair (k, R) with R= (xR, yR) associated with the elliptic curve domain parameters T established during the setup procedure. Generate the key pair using the key pair generation primitive specified in Section 3.2.1.

Again, choosing an random key pair is the first step of the encryption.

According to all the documents referred above, I believe generating a new key pair for each encryption process is part of ECIES standard and I should not allow the users of this library to reuse that ephemeral key. However I'm not an cryptography expert nor a native English speaker. If you find a convincing basis or my understanding is wrong, please feel free to correct me.

dko-slapdash commented 3 years ago

BTW some more bits of info I'v dug:

  1. https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation - a very good explanations on how different modes of symmetric cipher work.
  2. Having aes-256-ecb as a default is not cool - see picture on the above page regarding ECB. (In short, if the plaintext consists of repetitive words, then ciphertext will also be repetitive). A better option would be CTR.
  3. It is safe in practice to use an empty IV in case the key for each message is different and is long enough - https://crypto.stackexchange.com/a/8601
  4. Here is a library which is similar to yours (has some cons though): https://pypi.org/project/eciespy/ - https://github.com/ecies/js