2

I am building an E2EE chat app where there is one asymmetric key pair per group. Each user also has one asymmetric key pair. All messages in a group chat are encrypted with the group public key and decrypted with the group private key.

When Alice is added to the group, Bob (one of the group admins) encrypts the group's private key for Alice with her public key. For want of a better term, let's call this encrypted form of the group private key the "encrypted group private key". The encrypted group private key can only be decrypted using Alice's private key. It is therefore safe to store on my server. Bob creates this encrypted group private key locally and then sends it up for storage on my server.

// on Bob's device
encryptedGroupPrivateKey = encrypt(data: groupPrivateKey, publicKey: alicePublicKey)

My server in this case is just a plain key-value store of encrypted private keys on the cloud. Later, Alice can download the encrypted group private key and decrypt it to reveal the raw group private key and use that to read messages of the group.

When Bob sends up the encrypted group private key to my server, how can I verify that what Bob has sent me does indeed contain the group private key and not some garbage? Otherwise, my server would dutifully store the garbage for Alice but when Alice comes to decrypt the encrypted group private key she finds out the contents are not the group private key and is not able to read the messages of the group.

My first thought is to hash the group private key when the group is created and store the hashed version on my server and somehow later compare this hash with that of the encrypted group private key. How might this work?

I'm looking to have a verify function on my server which is able to

// on my cloud server
verify(encryptedGroupPrivateKey, groupPrivateKeyHash) // returns true
verify("anything else", groupPrivateKeyHash) // returns false

Putting it all together and more generally this is the set of functions I'm looking for:

messageHash = hash(message)
cipherText = encrypt(message, publicKey)

verify(cipherText, messageHash) # returns true
verify("anything else", messageHash) # returns false
4
  • 1
    You should not send private keys around, that's horrible design. Alice should generate her private key and have her public key added to the group.
    – MechMK1
    Dec 15 '21 at 11:41
  • Unfortunately that doesn't work because (A) Alice should be able to see previous messages (B) The group could have hundreds of participants, the message size quickly escalates to an unreasonable size if it is encrypted for each participant. Finally Alice does indeed generate her own private key, the wrapped private key is encrypted for her public key. Only she can read it. Dec 15 '21 at 11:51
  • 1
    Have you looked at the design patterns for e2ee on how to manage keys in group chats?
    – schroeder
    Dec 15 '21 at 12:09
  • My cloud service needs verification that Bob's message is indeed the valid group private key. In case he is a bad actor trying to destroy Alice's group private key backup with some invalid private key. I'm not so concerned with verifying that Bob was the one who sent the wrapped private key. More important is verifying that the wrapped private key is indeed wrapping the underlying group private key. And once again my server should never see the raw private key. Dec 15 '21 at 12:40
1

There are a few things to unpack here.

First, as @MechMK1 points out in the comments, private keys are normally not sent over the network (even after they've been encrypted). Usually in group chat applications, a random symmetric key is generated by one of the participants (e.g. Bob), then as other participants join (e.g. Alice), Bob encrypts the symmetric key using Alice's public key and sends the encrypted symmetric key to Alice. This way, all of the participants share the same symmetric key, and this symmetric key is used to encrypt and decrypt messages sent by participants to the group using a symmetric encryption cipher such as AES.

In this case, your server is simply acting as a conduit for passing encrypted messages and encrypted keys between the participants, and therefore your server has 'zero knowledge' of these messages and keys. If one of the participants tries to distribute a bogus key, the other participants will notices this when they try to use this bogus key to decrypt a message, as @schroeder pointed out. To this end, you might want to consider using an authenticated encryption algorithm (e.g. AES-GCM), which combines both integrity verification and confidentiality.

If you still prefer that your server does some sort of integrity checking, then the only other solution that comes to mind is that the group admin (e.g. Bob) must be trusted by the server, and Bob signs the encrypted key with his private key. Then, the server can verify the signature using Bob's public key.

7
  • Thanks for your response. 🙏 I think you've perfectly paraphrased the problem "If one of the participants tries to distribute a bogus key, the other participants will notices this when they try to use this bogus key to decrypt a message". I want to stop this from ever happening. How can my server prevent that is the real question. I don't see how using AES-GCM will ever prevent this because all the signing does is verifies that Bob sent it but I already know that! I need to verify the contents not verify who sent it! Dec 15 '21 at 13:28
  • I also don't see why distributing a symmetric key is any better than distributing a private key Dec 15 '21 at 13:34
  • I'm not sure it's possible for an untrusted server to verify the integrity of information passing through it, while having zero knowledge of this information, and without trusting a party that does have knowledge of this information. WRT key distribution - nothing jumps out at me as being inherently insecure about distributing an encrypted private key; but it's definitely unconventional, and it breaks the second law of cryptography, which is 'never roll your own crypto protocols' (the first law is Kerckhoffs's principle). It may raise a few eyebrows and hinder adoption.
    – mti2935
    Dec 15 '21 at 16:02
  • appreciate the response anyway "I'm not sure it's possible" 👍 Dec 15 '21 at 16:17
  • 1
    See security.stackexchange.com/questions/99920/… for a similar question.
    – mti2935
    Dec 15 '21 at 16:39
1

What you are asking is for Bob (the prover) proves to a server (the verifier) that he is sharing the correct secret, while not revealing said secret.

This is called a Zero-knowledge proof.

You can read about the Fiat-Shamir heuristic which enables a non-interactive verification: Bob - or Peggy in the article - send a sets of elements to the server once, instead of having a back and forth with the server.

I have quickly checked for existing implementation of this on GitHub, and while some exists, this does not seems to be widely used, so I would take extra care using it ; and implementing your own cryptography is almost always a bad idea.

3
  • that's really cool! now I know what it's called! (Definitely not planning on building my own). I wonder if there are any others similar to Fiat-Shamir Dec 16 '21 at 9:36
  • github.com/matter-labs/awesome-zero-knowledge-proofs found this wonder if I might be able to use some of the implementations there? Dec 16 '21 at 9:57
  • I do not know about the implementation you found, but that is definitely worth a look
    – Lou_is
    Dec 17 '21 at 6:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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