Sending anonymous messages

We would like to give users the ability to send anonymous messages:

This is captured by the following user stories:

As a user 
I want to able to send anonymous messages in public chats
So that I can communicate without disclosing my identity

And another that is not so obvious is:

As a user
I want to see anonymous messages sent by me on a different device as originating from myself
So that I can easily understand the interactions when using a different device

Basically anonymous messages should be anonymous for everyone, but for the sender (the owner of a given key pair).

Here I describe two potential solutions to this issue.

Solution 1

The first solution that came to mind was to generate a key pair using a subpath of the chat key of the user (authorKeyPair1), for example:

Our current path for the chat key is m / 43' / 60' / 1581' / 0' / 0.

When sending an anonymous message we derive the path from m / 43' / 60' / 1581' / 0' / 0' / x and sign the message using that key (randomKeyPair1).

x is the added to the message, in the payload, to indicate this is an anonymous message

https://github.com/status-im/specs/blob/master/docs/stable/6-payloads.md#payload

message ChatMessage {
  ...
  uint anonymous_derivation_path =  20;`
  ...
}

Upon receiving the message, each client will understand that this is meant to be an anoymous message by the presence of anonymous_derivation_path.

It will then extract the public key of the sender from the signature (as with any other message), which is just a randomly generated key.

To check that this message is not originating from a different device, it will then take the key specified at the derivation path:

m / 43' / 60' / 1581' / 0' / 0' / x

and check if it matches the random pk generated by the sender. If it matches, we can tell that this is an anonymous message originating from one of our devices, otherwise is just an anonymous message originating from someone else.

The important things we are validating here are:

  1. The content of the message has been authored by randomKeyPair1 and not tempered with (this is given by the already existing protocol, thanks to the signature of the payload)
  2. Through checking the derivation, we can tell that the sender of the message has access to authorKeyPair1

As a further protection:

uint anonymous_derivation_path = 20;

Can actually be encrypted using authorKeyPair1.PublicKey, so that the actual path is not disclosed to everyone, and it would be matched if it can be decrypted.

Attack vectors

One potential attack vector that comes to mind is:

What happens if an attacker take an existing anonymous_derivation_path and associate it with a different message (sort of a replay attack)?

The public key won’t match so it will be considered an anonymous message by everyone, the message ID will be different.

Because the new field is inside the payload, which is signed, we can rest assured that any modification will result in either the signature not being valid or a different public key and messageID would be derived.
It’s important that this new field is put inside the payload and not on the wrapper, otherwise we would be exposed to ID clashes.

Thanks to @gravityblast for the help and suggestions.

Backward compatibility

Current clients will see a random name instead of Anonymous in the UI, but will be able to read and receive those messages.

Solution 2

Simplifying this further, instead of using the derivation path, why not instead put a piece of information that we can use to reveal the original author (authorKeyPair1)?

Here, we would generate any random key pair, and sign the message with it. In the payload we include a new field:

message ChatMessage {
  ...
  bytes anonymous_signature =  20;`
  ...
}

This is generated:

signedPublicKey = Signature(authorKeyPair1.PrivateKey, []byte(randomKeyPair1.PublicKey))
chatMessage.anonymous_signature = Encryption(authorKeyPair1.PublicKey, signedPublicKey)

On receiving the message:

  1. a client will derive the public key of the sender (randomKeyPair1.PublicKey)
  2. It will try to decrypt anonmous_signature. If not able to do so, it will be marking as an anonymous message not sent by us. otherwise it will continue.
  3. It will then extract the public key from the inner signature signedPublicKey and check if it matches its own private key. If not, the message will be discarded, otherwise is an anonymous message sent by us.

At this point we know that:

  1. The message was signed by randomKeyPair1 and it wasn’t modified (that given from the existing protocol)
  2. authorKeyPair1 has signed randomKeyPair1, which means that it showed the intention (does not prove ownership though), to send a message using that key pair.

Attack vectors

What happens if we take an existing anonymous_signature and reply it in a different message?

The public key extracted from the payload won’t match the inner signature in step 3, and the message will be discarded. This is because we already know was intended for us, as we can decrypt the field, but if the key does not match, it means it had been tampered with.

Any other tampering of the content will give similar scenarios.

Backward compatibility

Current clients will see a random name instead of Anonymous in the UI, but will be able to read and receive those messages.

Other approaches considered

A more naive approach is to just mark the message as anonymous with a boolean flag and send a separate message to your device disclosing the fact that you sent the message. This is operationally harder and might lead to inconsistencies (have to send two messages, what happens if you don’t receive one).

Another suggested by @ricardo3

  • Allow users to send messages in public chats as “anonymous”: use a keyless method of signature (use the hash of the message as r,s of signature) - if r and s are a exactly the same, and/or are hash of the message, the message can be considered “signed by anonymous”

This is very neat and the most elegant method (no extra piece of data is needed to tell is an anonymous message), but requires more fundamental changes to the protocol and is too generic for our use case (we only want user to be able to send anonymous messages in public chats, narrowing down the options limits the potential for attacks/issues), and we still need to solve the multiple devices problem, so more info will have to be propagated, likely in the payload following a method as described above.

Of all the 2 solutions proposed above, I think 1 looks more elegant, but it’s a bit more complex and not sure it’s justified, 2 seems to serve our purpose just fine, although the geek inside of me would love to code 1, 2 seems to be the safest choice.

Opinions are welcome.
Thanks

5 Likes

Great work @cammellos!
With this feature, can I use the same anonymous key for many messages or would each message have a different key?
With Solution 1 I would be able to continue the conversation from my second device using the same anonymous key. With solution 2 you could maybe encrypt the private key to add this feature, would it make sense? In both cases if the main chat key is compomised, the messages are not anonymous anymore right?

1 Like

With this feature, can I use the same anonymous key for many messages or would each message have a different key?

I think that’s to be decided, my impression is that if you want to send anonymous messages, those should be completely anonymous, so each one should be using a different (random) key, and we should not be able to associate 2 messages to the same sender, even if they are sent in quick succession by the same device.

Otherwise, I would probably see the feature from a slightly different angle, you want to be able to send messages from a different (temporary) account when logged in with a user. But it’s a more complex feature (i.e you need to show to users that this messages are “anonymous”, but they come from the same account, you want probably give the user the ability to “release” an account and start afresh etc).

With Solution 1 I would be able to continue the conversation from my second device using the same anonymous key. With solution 2 you could maybe encrypt the private key to add this feature, would it make sense?

Correct, if we want user to be able to have continuity of keys, solution 1 seems like a better option, so we don’t have to pass the private key, as probably inherently less safe.

While it’s true that if you compromise the main chat key you’d be accessing both in both solutions, in solution 2 if we send the private key over the wire we’d have to also store it on the other end, which is another place where the key might be exposed, so passing only the path looks like a safer option and it would now justify the added complexity imo.

In both cases if the main chat key is compomised, the messages are not anonymous anymore right?

Yes, to overcome this we could encrypt the revealing data by doing a ratchet step, but at that point you’d be only revealing your identity only to the devices you are aware of, as it’s device to device encryption. So a new device that has the same seed phrase won’t be able to understand that this message is coming from its private key.

1 Like

Nice one! From my reading number 2 seems simpler as well.

May I ask, is this a must have requirement? It isn’t clear to me that it is one. Personally I generally remember what I sent and it doesn’t seem like a big deal to me. It seems like that might simplify the design a lot and we could essentially just use the moot account like before (but package it in the UI etc). Am I missing something?

As a user
I want to see anonymous messages sent by me on a different device as originating from myself
So that I can easily understand the interactions when using a different device
1 Like

May I ask, is this a must have requirement?

I take this is a question for UX, (Should anonymous message sent by you by shown as sent by you when received on a different device?) I assumed it would be for consistency, but happy to go with anything really. cc @maciej @hester

If that’s not a requirement, using a moot account is an option, although I’d favor using randomly generated keys and passing the information on-band that is to be considered an anonymous message, rather than having the agreement on a specific account, but either are probably fine.

In such case, I would also consider just not sign the message at all, whisper/waku both support this, although we’d get into some issues with compatibility, as those messages would be dropped by older clients.

1 Like

Should anonymous message sent by you by shown as sent by you when received on a different device?)

Yes, as a user I would expect they would be shown as mine regardless on which of my devices they’re viewed. I find the alternative where that gets lost somehow the worse experience for the person using it, not a deal breaker tho.

I’m trying to wrap my head around some use scenarios in which anonymous messages would be used; In order to decide whether you’d need access on multiple devices or be able to send multiple messages.

Scenario 1 - Organizer coordinating a protest

Context

Reaching a lot of people in a volatile environment; Expecting to be surveilled

Use cases

  • As a protest organizer I want to communicate time and location on multiple days in a public channel
  • As a protest organizer I want to send this message anonymously with no risk of association with existing profiles, because those profiles may already contain enough information to track or identify me
  • As a protest organizer I want to be the sole user of the profile with which my messages are displayed, because I don’t want anyone pretending to be me spreading false information, further disorganizing
  • As a protest organizer I want to proof that I am the organizer, because I want to know for certain that no one can pretend to be me
  • As a protest participant I want to know that messages originate from the same account, because after day 1, I trust their coordination and do not want to risk being tricked by fake accounts

Scenario 2 - Whistleblower sharing classified information

Context

Communicating with 1 or more news outlets about wrongdoings, wanting to do so anonymously to avoid retaliation on personal life and safety

Use cases

  • As a whistleblower I want to send the same message anonymously to 1 or more individuals or a public chat
  • As a whistleblower I want to control if and how a recipient is able to contact me, because I don’t want to risk them sharing ways to contact me with others
  • As a whistleblower I want to be certain that the information I shared has been received
  • As a recipient I want to protect my sources anonymity if requested

Conclusion on requirements

  • Scenario 1 seems to cancel out #moot as it needs to exclude others from spreading information
  • Both options seem to require sending multiple messages
  • However, in case of scenario 1 proof of ownership of the same key is necessary, in scenario 2 it’s not
  • Viewing send messages on multiple devices does not seem critical for either scenario

I am not sure this scenarios capture the feature correctly,

I think we are conflating two different things, probably we should have been more precise about naming this:

  1. Sending messages without disclosing your real world identity. This is already fulfilled by the app in most cases, as anyone can create a new random account.

  2. Sending messages as an anonymous collective group (this feature), which is the equivalent of #moot. To me, that’s not a security feature, but it’s the equivalent of 4chan Anonmyous FAQ - 4chan. It’s not meant really to shield your real identity in terms of security, but to have a collective voice.

Scenario 1 is already satisfied by the app, anyone can create a second account and use that. We could make this more convenient for the user to have a “quick switch” in a chat, but that’s a different feature all together.

Similarly in scenario 2, a whisteblower would not necessarily send a “collective” anonymous message, the use case imo is better fulfilled by just creating a different account and using that one, especially as you might want to give continuity to your messages.

TLDR, the primary use case in my opinion is not serious stuff, most user would prefer a randomly generated account. It’s just allowing people a different way to shape a conversation by conflating multiple voice under a single shared account (#moot, but explicit).

2 Likes

I agree with cammellos, that choosing to use another identity is better done by simply creating a new one, and here we could use a better interface to quickly switch between them. Something I started drafting here.
Now having that out of the way, I have no clue what moot is about and why would people want that xD Which imo makes it a candidate for a chat extension similar to how send / receive are if we’re searching for a suitable interface.

Adding a 3rd scenario to understand the goal of an equivalent to #moot. Agree with scenario 1 and 2 mostly being covered by existing features, and there are better ways to handle these scenarios in creating multiple chat keys to create anonymity. Which is why I struggle to understand the user goal of sending messages from a profile represented as a collective.

Scenario 3 - Any user communicating on behalf of a collective

Context

Any user wanting to represent themselves as a collective voice

Use cases

  • As a user I want to send a message using an generic account, because I don’t want what I share being traced back to my personal profile
  • As a user I want to lean on the perception and trust of a collective profile to add validity to my message, because what I say will mean more to people reading it from a profile that has a history and reputation

Anonymous is not a single person, but rather, represents the collective whole of 4chan. He is a god amongst men. Anonymous invented the moon, assassinated former President David Palmer, and is also harder than the hardest metal known the man: diamond. His power level is rumored to be over nine thousand. He currently resides with his auntie and uncle in a town called Bel-Air (however, he is West Philadelphia born and raised). He does not forgive.

You can be Anonymous. Isn’t that enough of a user story?

1 Like

Cross posting my concern voiced on Discord here:

Happy to be convinced that this is not a concern or benefits outweigh risks.

I mostly see how this feature can be misused by individuals hijacking what comes across as a collective voice. Limiting discourse and diversity of views. Question for me is how can we distinguish a collective voice from an amplified voice

Meaning, how can we prevent a representation (e.g. Anonymous ) from being hijacked by a single or limited set of keys, through the shear volume of messages send from those keys. And if we can’t, how can we provide users with insight into how collective the representation actually is. I can imagine showing the number of keys that have used the representation to send a message can help distinguish. However, I understand that there’s no way for us to know if a message send from a key is sent by one, two or a thousands different users sharing a key.

Whether we should actually work on this feature or no, for me is hard to say, I don’t really feel the need for it, but my understanding is that is one of the top requested features by users, and I can see the benefit as this would be a differentiating feature from other messenger apps ( I am not aware of messenger apps that offer this feature, but haven’t really spent time researching).

I can see why there would be some concerns about this issue, and I think the easiest way to see the benefits and drawbacks is to look at 4chan.

For one side it generated a huge amount of very influential culture, it has shaped so much the our current culture, a lot of which has been normalized and is present in different media.
Part of it success is definitely the fact that giving a sense of anonymity to the user allows for a different kind of conversation (same as this feature, not to be confused with our randomly generate keys which can’t be tracked down to an identity).
It allows people to push boundaries without being fearful of the consequences.

I believe that even a 3 random name might give users some inhibition and a sense of identity.

Whether this is a good thing, it’s harder to say.

4Chan is also a nest of the worst ideas and behaviors of mankind (racism, bullying, homophobia), which are often more visible than good behaviors (the overwhelming majority of users in 4chan are well behaved, but a tiny vocal minority is the one we are most aware of).

Meaning, how can we prevent a representation (e.g. Anonymous ) from being hijacked by a single or limited set of keys

This I am a bit less concerned about, there are communities in 4chan that are very welcoming, and other that are not, so I would expect “Anonymous” on status to be a reflection of the community, and I don’t expect users to give “Anonymous” a true identity, in the same way we don’t attach an identity to graffiti on a bathroom wall.

Anonymous as a movement is a distinct thing from anonymous the poster, in the former you can sort of ascribe an identity to it, or at least a set of morals, while in the latter is just another “Foo was here” on a bathroom wall, and this is more what this feature represent.

Overall, I don’t know whether this is a feature worth having, I think it’s cute and likely harmless, but definitely I would consider it a bit of a social experiment, ready to pull the plug if clearly misused or takes a dark turn.

1 Like

Let me add the feature to https://discuss.status.im/c/features/51 so we can get a sense of popularity by votes. I actually haven’t seen it as a frequent request in #status and Support so far

1 Like

Why using a keyless method looks complex? Seems very straight forward, instead of having a hardcoded key as “moot”, it would have be proven to be anonymous because is computationally impossible to figure out the private key which can sign a message with the r and s of the hash of the message itself.

Anyway, this is all backend, and the average user would not be affected by which method is used, but the use of a hardcoded shared key does not seem the best solution.

For the keyless method, a message can be considered anonymous when it’s hash matches r and/or s of elliptic curve, or even when a fixed r/s is used, e.g. use 0x01 and/or 0xFF as r/s.

The random name would not be used when this conditions are detected.

The advantages of keyless method is:

  • no private key
  • no public key
  • not possible to register/use an ens name pointing to this chatid

I agree keyless method is not the most complex.
I was referring to the second method I proposed, while I agree that keyless is simpler (“This is very neat and the most elegant method”), it does not solve completely both use cases, and in order to minimize changes, a randomly generated throw-away key is more palatable as requires less effort and minimize the scope of changes imo, while keyless is a superior generic solution.

I would not also use an hardcoded key.

In our case though, there’s an even simpler solution if we want to go that route and make changes so low level at the protocol, waku/whisper already allow you to send unsigned messages, in which case messages are not signed at all, although backward compatibility would be an issue, as currently they would be discarded, but maybe it’s ok to have anonymous messages only received by new client, as that’s a new feature.

1 Like

Ah, if you are really considering changing the protocol, then better. I wasn’t considering this as an option.

In regards of the Options 1 and 2, If I understood them right, I considered them out of scope.
If the user want a pseudonymous, they can create another account. Anonymous messages should not be potentially pseudonymous. Maybe is the case of multi-account also support multi-chatids? Status.app
e.g: if the sender is forced to put their finger in the scanner, or reveal their password/seedphrase, will be possible to proof the authorship? While this would be an interesting tool, its a double-edged sword. By default, anonymous messages should be 100% anonymous and technically impossible to proof authorship. By best, totally unsigned.

1 Like

I want to see anonymous messages sent by me on a different device as originating from myself
So that I can easily understand the interactions when using a different device

In case of using a keyless method, either by change of protocol, or by a static signature in EC, this can be accomplished by device sync:

  • any user can can “favorite”/“flag” any message.
  • when a user sends an anonymous message, its automatically “favorited”.

Notice: an anonymous message is never sent by any user, because the user is not anonymous, so it would be preferred that another tool help user following up on their anonymous messages. So anonymous messages are always “received messages”, never a “sent message”.

Anonymous Sessions, not messages

When we introduced multiaccount I also wanted to introduce the concept of multiple public keys for usage in chat so we could have anonymous messages. Rather than trying to create anonymous messages, I think it is better to create anonymous sessions.

This allows us to join public chats anonymously, hides the identity of a user in usernames, spin up burner sessions or periodically rotate keys with contacts and allows the user to selectively accept contact requests.

Also this session scheme generalises to any kind of interaction/transaction the user does, whether it’s chat, dapps (or wallets for folk thinking in those terms). This is important because creating burner addresses (while harder to do in Ethereum’s account model, requiring something like Tornado.cash to do properly) is an important feature for maintaining privacy in dapps and making transactions with other users.

Similar to how we generate a new key/address for each dapp (or at least we should be), we should also be doing the same for chat session. I should be able to join a public chat without revealing myself, and either revealing my identity to the entire public chat, or selectively to participants (my contacts)

To me Anonymous Chat Sessions and Wallet per DApp is the same problem, and my hope is we think of them the same way.

Username public key as an inbox

The public key that is published for usernames or shared via url is no longer the users identity, instead this public key is an inbox for messages and contact requests, and is read-only by the user (and write during upgrade transition to maintain backwards compatibility)

When the user receives a message/contact request on this inbox key, they then deterministically generate a new keypair, and sign a message with the inbox key, so the receiver knows who the message belongs to. This has some parallels with Solution 2.

Alternatively the requester could send the contact request from their public key, which would allow for instant username lookup, however then after the receiver has sent their confirmation message to the requesters inbox, the requester would have to reply with their chat session key.

After this the chat session can act as normal, and we can have some randomization in the rotation of keys if we wanted to, we could create new chat sessions with our friends without revealing our identity

Revealing oneself in multi-participant chat

Revealing oneself in a multi-participant chat is more complex, because the initial reveal has to be done out of band. The problem is you don’t know which of your friends are part of that chat, and you don’t necessarily want to broadcast all the chats you’re in to your friends. I don’t have any strong opinions on how to best solve this. My knee-jerk response to this would be to construct a bloom filter of all the chats your in and share it with friends, so at least the bandwidth is minimised.

4 Likes