dwallet-labs / dwallet-network

dWallet Network, a composable modular signature network that is the home of dWallets. A dWallet is a noncollusive and massively decentralized signing mechanism, used as a building block by builders on other networks to add native multi-chain interoperability to any smart contract.
https://docs.dwallet.io
Other
30 stars 29 forks source link

Don't Trust the Blockchain's DKG Output #164

Open ycscaly opened 3 weeks ago

ycscaly commented 3 weeks ago

A very serious issue that I came across during the encrypt user-share #123 feature.

Although the blockchain verifies that the proof the encrypter sends indeed encrypts the user-share, the user that decrypts the user-share cannot know this, for they didn't verify the proof themselves. At first, I wanted to add a verification that simply multiplies the secret share by the generator and checks if equals the public key share #155. But then we stumbled upon a problem: the receiving user don't know what public key share is to verify the proof against.

A trivial implementation would query the public key-share from the blockchain. However, who says we can trust the blockchain for this value? and indeed we can't.

This brought up an even larger issue. How can we trust any values that come out of an MPC computation (e.g. DKG output, dWallet user share, presign etc.) that are saved on the blockchain? The 2PC-MPC access structure guarntees that even in the case the entire network is malicious the user-share is not compromised and the dWallet is protercted. But in this case, the network can save invalid values in the state, and can cheat and lie and say that the public key share of the user is wrong, and temper with the transfer of the user-share.

True, this is solved if you run your own full-node, for it would verify the proof too, and it would verify that the user that generated the dWallet "agreed" with the DKG output when it finalized its creation and later on used it. But malicious full-nodes are part of the attack threats that we should cover, and indeed we should not have to trust that users run their own full-nodes or use an honest full-node for the dWallet security.

To further demonstrate the threat, consider the following attack: the user creates a dWallet, but doesn't take the DKG output from the MPC session, instead, to know the public key, they fetch it from the blockchain (this is, in fact, how we implemented our TS library and documentation code at the moment). In this case, the blockchain can simply generate a local key, and save its public key as the dWallet key. Since no verification occurs, an address on e.g. Bitcoin will be derived and funds deposited to it. But since the colluding validators hold the signing key (in the clear) they can steal the funds at any time (!)

Instead, we suggest the following principal to be implemented: any output from an MPC session should be signed by the user that participated in it, and saved on the blockchain. This signature should later be verified before using values from this output queried from the state. Although transactions are signed by default, unless you are a full-node they aren't easily accessible, so this should really be the signature saved on the state.

This applies for both DKG & presign. However, if the presign does not involve the user, than it is OK to trust those values.

Here I shall treat the DKG case. The most straight-forward way I can think to mitigate this threat is: add a signature over the dWallet public key, and the network and user public key-shares to the EncryptedUserShare object. In create_encrypted_user_share() verify that signature before saving it to the object. In the client side TS (#155), verify this signature in addition to verifying the user-share..

This covers transfer, but we should also do this for ourselves: when you create a dWallet, you add a signature by yourself to the encrypted user-share you submit on chain for backup (#163). When you fetch the dWallet public key or public key shares, first verify this signature.. We should expose an easy method that takes a dWallet ID and gets these values, so that everyone that ever do this have to go through the signature verification.

I believe that after you received a secret-share, you should also encrypt it to yourself so that it is straight-forward that you approved it, and later on when you use it you can query the public key and verify your own signature (and not someone elses', which will require you to remember who sent the dWallet to you and assure that they are indeed the correct owner and not a maliciously generated address that is controlled by colluding validators.)

ycscaly commented 3 weeks ago

For the other direction, in which a full node + user tries to be malicious without the network, we have to verify light client (either #168 or Sui's native light-client) for the EncryptedUserShare we pull from the network.

ycscaly commented 2 weeks ago

This big task is divided into sub-tasks as follows:

Finally, we have to sign our own EncryptionKeys and add that signature to the chain, and before we encrypt a user-share to someone else, we must make sure that it signed by that address (otherwise the blockchain/full-node/lightclient can create its own encryption key, and fool us to encrypt it to the malicious blockchain/full-node/lightclient) (#171)