Finally had a few hours this Sunday to think about the Key Management stuff.
We strive to be a private means of communicating, whether that is via text, media or transactions. However currently, it is easy to see a user’s entire transaction history (public blockchain’s yo) based on whisper identity. To improve this there is a desire to generate new keypairs for both on-chain transactions and communication.
Previous discussions have been thoughtful & fruitful, but have had some issues addressing backwards compatibility. In this document, I want to propose a slight variation on the view on keypairs, offer some thoughts on UI and point out some high impact low hanging fruit user feature ideas, while keeping backwards compatibility in mind.
Note: With these changes we do not solve the privacy issue, as transactions will still be traced, however this approach immediately discovering transactions from a user’s whisper identity is substantially harder and creates a solid foundation to support solving the issue with transaction privacy such as AZTEC protocol in the future.
Bonus Wall of Shame Item! Transaction Privacy!
For previous conversations see @gravityblast’s:
Keypair Security Models
I would like to propose viewing keypairs not in distinction of their logical use but instead of their security model. This is to say keypairs in Status live under hot, warm and cold policies.
We should recognise that first, all Keypairs are in-fact logically equivalent. However all public keys can derive addresses but addresses cannot be reverted to public keys (that’s literally the security feature design), in other words:
All ‘Whisper Contact Codes’ are Wallets, but not all Wallets are Whisper Contact Codes.
This cannot be stressed enough and is a necessary concept to understand to retain backwards compatibility.
These are in-memory generated keypairs, should be used for both Whisper & Wallets and do not explicitly require the user’s interaction for signing transactions or messages.
They are hardened child-derivations from the keypair currently used for Whisper in the application, derivations are deterministic based on;
- dapp/website hostname hash (open to alternative ways to do this)
- integer? (i.e. point of sales tx id)
If a (on-chain) balance exists on these keypairs, the application should strive to automatically sign a (private) transaction to an address managed by a warm or cold policy when it is appropriate to do so (???).
Nonetheless manual transactions from hot keypairs (in the wallet) should be treated under a warm keypair policy, the user should still confirm signing of the transaction when sending a transaction.
Except under the case of designation of a ‘pin-less’ address, maintaining feature parity with Keycard.
We can view these keypairs typically as traditional wallets. While whisper could be used here, it is not practical, as warm keypairs require explicit signing.
Note: Since all derviations come from master key, all usable wallets in Status are at least considered Warm keypairs.
This is a keypair that the Status application or Keycard has no control over. In Status it is a pointer to an address. It should ideally be a hardware wallet. The user can add as many cold wallet addresses as they like, for receiving only.
The hardware wallet may be another Keycard however this keypair should be refused to be loaded into the current application if designated under an account, and refuse to add the keypair as a cold wallet if it’s already loaded into the application, encouraging the user to maintain physical device separation.
hot root = current whisper key pair (afaik
m/44'/60'/0'/0/2?) or whatever derivation structure specified on recovery screen (see Recovery section)
If a user wants a wholly new identity, they can logout and create a new account. Otherwise we can treat the
hot root public key as their identity. This will allow us to maintain backwards compatibility, while providing unique identities per chat context moving forward.
All new clients should enter chats (1-1, private group, public) with a hardened (uses hot root private key) child-derivation off the hot root public key (iirc).
The user’s identity is unique to the chat context they are in. This allows the client to reveal the user’s true identity. Revealing is a signed message by the private key of the hot root authorising the unique identity, encrypted with the receiving clients public key, the receiving client decrypts & uses
ecrecover to verify.
The ENS username will most likely host the hot root public key and this key is likely the signer of reveals, the client listens on this keypair for inbound contact requests and shouldn’t be used for transmitting messages in the future, but will be during N future releases period.
Moving forward it will be impossible to directly chat (or transact?) with anyone based on the hot root, however we will introduce a transitionary phase that lasts N future releases where existing contacts can still converse directly with hot root public key, but encouraged to upgrade. New clients can still listen to messages received on the hot root public key, but older clients would see responses in new chats from new public keys.
When joining 1-1 or private group chats, the client can automatically reveal themselves to their friends/added contacts. And can selectively reveal themselves to unknown contacts.
An advanced option toggle would allow the user to intermediate all automatic functions, requiring explicit consent before automatic reveals.
A reveal to a friend could also automatically be responded to in-kind with a reveal, and subject to intermediation by advanced option.
Joining Public Chats is more complicated. Since child derivations are hardened, it is not possible, or desireable to automatically derive possible keypairs based on chat-id’s. Nor is requesting every contact every time you join a new event.
To minimise bandwidth, we could construct a bloom filter that aggregates all chat-id’s and passes it to the most recently seen/‘online’ contacts, this should be done on chat join events. This would allow clients to query bloom filters locally and give plausible deniability opportunity to the end client (assuming they are intermediating their reveals). If the user is no longer in that chat, the response is dropped.
The user should also be able to explicitly reveal to another user who they are in a public chat.
This would mean that a user will have a ‘wallet’ or address for every Chat context. (See Wallet section)
Each DApp would also have their own key, a hardened child-derivation off the hot root based on hostname hash (or better? open to ideas).
With this approach we can offer a ‘proxy’ address to the DApp, in which we send a (private) transaction to, to be used to the DApp, and, since it is a hot keypair, we can also offer a streamlined user experience with the ability to ‘Not ask me to sign transactions’, this would require ‘preloading’ the DApp Address with X value, this allows the user a degree of control without excessive transaction signing.
This would mean that a user will have a ‘wallet’ or address for every DApp context. (See Wallet section)
User Interface Considerations
Tangential but important & easy wallet improvements
- 24 word phrase recovery should be possible, please make it an option.
- Emoji Guard,
Seriously, it’s better than confusion between keyphrase and 3 words. and better than not having anything. Application proving itself to the user is important.
Edit: This has been an issue for over a year now with no new improvements from anyone, if no one has better ideas and the current version is tied to a linguistic background that not all users are guaranteed to have then I vote we go ahead with this.
- Omgplz let me add custom tokens already.
- Social Recovery by splitting Key. (okay this isn’t as easy, but gogogo!)
Chat & DApps
In this setup, chat remains largely unchanged, most of the work is ‘under the hood’ and is backwards compatible.
However we will need to develop UI flows to do reveal.
Older clients that did not upgrade within the ‘N future releases’ period will see mysterious responses in new chats from unknown public keys. I don’t think this should be a concern.
The important difference here is that now Chat & DApp tab items can essentially hold their own balances. (See Wallet section)
You should be able to send and receive from any key.
The wallet screen remains unchanged and is an aggregate of the user’s assets under all wallets.
The wallet should enumerate all addresses under hot, warm and cold that are used by the client, it should be a list ordered by their respective balances, displaying the top 3 token balances in each list item. To pull this off, wallet needs to be more responsive & intelligent in how it caches and queries balance data.
That means the list should be populated with 0 ETH balance DApp and Chat Addresses. It entirely possible to include this information in the current tab list and introduce a new Wallet tab list type. If these items are just generic new wallets, tapping them takes you to the existing wallet interface, but for that specific address, if it is a chat or dapp, it takes you do the chat/dapp, however we already have “View My Wallet” in 3 dots, conceptually this could be “View DApp Wallet, or View Chat Wallet”
Creating a new wallet is just an integer child-derivation from
warm root. Creating a new wallet is client-side only. On recovery these will be forgotten (unless they have balance) until we can encrypt the application database and upload to decentralised file storage.
Creating a cold wallet is read-only, receive only and requires the user to enter in an address. That is not under control of the application.
This also allows us to create smart contract based wallets, ie multisig, but I think this should be the next iteration.
A wallet address can be revealed in a chat context that is seperate from the unique identity and the real identity via the send/request commands.
This approach allows us to re-use many existing flows and UI elements without introducing major breaking changes or too many new UI elements. I imagine most of the work would be efficiently managing the balance data.
Since we made efforts to use similar key phrase recovery as other applications, Status should be compatible with any other HD key structures, such we should auto-detect what structure the recovered keypair uses based on other popular applications compatible with BIP44, and/or offer the user to select or define their own structure.
It might even make sense to support multiple HD key structures, as users might use the same keypair on different applications.
While I’m at it, by now we should also offer to import other private keys into Status
The above suggestions seem to achieve our goals while keeping minimal UI changes and retaining backwards compatibility, I have used some terms interchangeably and probably have some terms wrong (it’s been awhile)
(Also why does the Wallet say ‘Deposit’ instead of Receive? Not very accurate imo)
I’m sure I’ve missed some considerations and would love feedback!