Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LSP11] SocialRecovery Improvement #216

Open
YamenMerhi opened this issue Jul 3, 2023 · 8 comments
Open

[LSP11] SocialRecovery Improvement #216

YamenMerhi opened this issue Jul 3, 2023 · 8 comments

Comments

@YamenMerhi
Copy link
Member

LSP11SocialRecovery Improvement

I would like to open a discussion on the LSP11 standard, which serves as a social recovery mechanism for users to regain access to their Smart Contracts. While this standard is fully functional, there are several aspects I believe could be improved upon to facilitate better adoption and integration with other LSPs.

Critiques

  • Lack of Interoperability: The current design of LSP11 fits primarily with an LSP0 owned by an LSPP6. The recovery mechanism functions through a voting system conducted by guardians, and once the secret word is provided in the recoverOwnership(..) function, the LSP11 contract sets LSP6 permissions on the LSP0 for the address voted for.

    This design limits the flexibility of the system and reduces the opportunities for interoperability. Particularly considering the recent integration of LSP20 within the LSP0, where the interaction with LSP0 became owner agnostic, we should aim to design the social recovery system in an owner-agnostic manner too.

    Ideally, the social recovery system should function irrespective of the owner and the contract type, potentially not limited to an LSP0. The person elected for recovery should be able to either set LSP6 permissions on the account post-recovery, transfer ownership, set LSPXXX permissions, or maybe even execute another function.

    In this way, the vote can happen based on a calldata to execute, or a hash of the calldata.

  • Deployment Costs: As a standard potentially useful to a wide range of users, ideally each account-contract should have an LSP11. However, the current requirement to deploy a unique contract for each account—even when using proxies—could create a significant cost barrier for companies aiming to provide social recovery options to their users.

    To overcome this, we should consider redesigning the standard in a way that enables a single instance of the standard to serve as a singleton. In such a design, a single instance of the social recovery contract could serve multiple users with differing guardian settings, thresholds, and configurations according to the linked contract.

    This approach would significantly reduce deployment costs and increase user adoption. If necessary, individuals who desire a unique contract can deploy a separate instance specific to their requirements, preventing the instance from being used with other accounts than the ones they specify.

Ideas and Concerns

  • Delays: Should there be a time range for a social recovery process that invalidates the votes of all previous guardians? Should it be part of the standard or left optional to be implemented without breaking the function specification ?

  • Privacy: Some of the social recovery contracts, emphasize on privacy where an account can set guardians but without revealing their addresses, in this case, the account will set a hash of the addresses, and when recovering the guardians are allowed based on their hash of addresses. This could be helpful for privacy of accounts (UniversalProfile)

  • Secret Word Leak: Since the standard use the secret word to recover access to the contract (either alone or with guardians), there is the possibility of having two people at the same time use the same hash, and one can reveal the plain secret before the other making the other vulnerable to be "hacked" by his guardians. Several ideas are being proposed such as double hashing, registry checking for a duplicate hash, etc .. This needs more research and opinions.

  • Accepting Guardian invitation: Many social recovery contracts implement the invitation mechanism, where first you invite a guardian and he should accept the invitation. I don't see the reason for implementing this mechanism, as it adds additional cost for the guardian to accept the invitation. As well, some of the contracts to be set as guardians maybe cannot execute until a certain condition is met.

  • Relay Execution: While it's true that providing functions that work on signed messages is good and makes the execution of a recovery frictionless, it may result in undesired action as a user can be tricked to sign a message and have this message not exposed can be executed later. This might go against the idea that the main account should have the ability of relay execution, not the targets contract. As well, in this way we need to integrate EIP-1271 as we should expect the use of smart contract accounts as guardians. To be discussed.

  • Cancel the recovery process: Making the account able to cancel a recovery process.

I can see the features listed above as 'standard', other custom features such as recovering based on specific logic can be implemented on top of the LSP11SocialRecovery contract. For example, a contract that works with Custom logic X for recovering can be added as a guardian for the main LSP11SocialRecovery contract.

While I see that these are lots of ideas and improvements, I don't see a reason to not implement these ideas together If they make sense, instead of standardizing several social recovery contracts with different features and interfaces which will not contribute to the Interoperability.

@skimaharvey
Copy link
Member

skimaharvey commented Jul 3, 2023

Looks very nice!
Cant agree more on the critiques part:

  • Deployment Costs: definitely could see an LSP17 extension shared cross UPs 😝 but yes also could make it work with non-lsp0 contracts.

Concerning the ideas and concerns. Just a few points:

  • Delays: personally I see them optional
  • Privacy: it is true that privacy can be a concern with UPs but worst case you dont have to use UP and can use normal EOAs if that's a concern (or even anonymous UP).
  • Secret Word Leak: Currently, there's an issue with how we're implementing 'Secret Word Leak.' The problem is, we don't include a salt (an additional random value) associated with the contract we're trying to recover. By using a commit-reveal scheme, we can automatically add this salt to the recovery string. This makes it much less likely for a "recovery" hash to be burnt, as the same string and salt would be needed (unless a hash collision occurs). Here's an example of how we could achieve this in code:
    function commit(bytes32 _commitment) public {
        commitments[msg.sender] = _commitment;
    }

    function reveal(string memory _value, bytes32 _salt) public returns (bool) {
        bytes32 valueHash = keccak256(abi.encodePacked(_value, _salt));
        if (commitments[msg.sender] == valueHash) {
            return true;
        } else {
            return false;
        }
    }

Using Zero-Knowledge Proofs (ZKPs) would be an even better solution, but they are challenging to implement in solidity and can be costly.

  • Risk in Relay Execution: For the worst-case scenario, we could use time-limited signatures as we are currently doing in lsp6. One of the significant issues with signatures is that they are vulnerable to front-running. For instance, someone could re-use a signature but change the bytes32 newSecretHash in the LSP11 context.
  • Cancel the recovery process: good point may be useful to be able to cancel the recovery process in one call (by the owner)

Glad to hear we are re-opening the LSP11 topic 🚀

@CJ42
Copy link
Member

CJ42 commented Jul 4, 2023

The current design of LSP11 fits primarily with an LSP0 owned by an LSPP6.

Yes we can consider this could be one implementation among many, for a specific architecture.

we should aim to design the social recovery system in an owner-agnostic manner too.

Definitely agree

Deployment Costs: As a standard potentially useful to a wide range of users, ideally each account-contract should have an LSP11. However, the current requirement to deploy a unique contract for each account—even when using proxies—could create a significant cost barrier for companies aiming to provide social recovery options to their users

Yes a singleton contract would lower the barrier of entry for projects. Giving them just the need to deploy a singleton contract onchain that acts as the "backbone of the social recovery service". But there might be risk around this to be looked at.

I will have a think in depth about the security concerns it and come back to the discussion.

@skimaharvey
Copy link
Member

skimaharvey commented Jul 4, 2023

was rethinking of Secret Word Leak:

  • singleton contract would be awesome as it would allow us to kind of use it as a "registry" and prevent the reuse of the same "password string"
  • going back to my previous example of "commit-reveal scheme" I think it would make it even better and more robust if we used the address of the contract we are recovering for instead of current salt params I gave above so that even if a recovery hash is burnt for a specific _value, it won't be burnt for another address as the hash is dependant of the address of the contract we are recovering for.
    For example:
    function commit(bytes32 _commitment) public {
        commitments[msg.sender] = _commitment;
    }

    function reveal(string memory _value) public returns (bool) {
        bytes32 valueHash = keccak256(abi.encodePacked(addresOfContractToRecoverFor, _value));
        if (commitments[msg.sender] == valueHash) {
            return true;
        } else {
            return false;
        }
    }

Might need to think it though more

Edit:
What I personally dont like with the approach above is that people could store all the _value and then "brute-forte" addresses until they find a match to find each one's password.
Maybe it is still relevant to have salt + addresOfContractToRecoverFor. Something like this

    function reveal(string memory _value, bytes32 _salt) public returns (bool) {
        bytes32 valueHash = keccak256(abi.encodePacked(addresOfContractToRecoverFor, _value, _salt));
        if (commitments[msg.sender] == valueHash) {
            return true;
        } else {
            return false;
        }
    }

@YamenMerhi
Copy link
Member Author

The discussion that happened yesterday with @NBR2807 covered these points:

  • Having guardians accept the invitation to be guardians will decrease user adoption as guardians need to pay either through a relayer or through gas fees the cost of accepting ownership. Even if subsidized by the owner setting the guardians, it's a cost that is not needed and won't introduce any extra layer of security.

  • The interoperability issue of having LSP11 strictly usable by LSP0 X LSP6 MUST be fixed and the LSP11 standard should be generic to support social recovery even for non-LSP0 contracts by having the ability to execute whatever function on the account at the process of recovery.

    After agreeing on this point, the question that should be asked here:

    • Should the guardians vote for a call data to be executed, or vote for a specific address that can later execute call data? The difference is:
      • When voting for an address, this address needs to execute the transaction itself, and you need to trust this address
      • When voting for a call data to be executed, anyone can execute this call data as long as it reaches the threshold and the caller passes the proper plain secret. But the problem is that voting on big call data could be costly, so a vote on a hash is needed, which may result in voting on something not clear. (Similar to how multi sig functions)
  • The idea of standardizing LSP11 to be close to recovery services (authy, biometrics) will not be standardized in the LSP11 itself, as it's hard to standardize topics related to web2 and how they should interact, and it was decided that special contract having a custom logic for recovering can be added as guardians, in this way, LSP11 is more generic and any recover system that uses a custom logic can be added as a guardian.

  • Singleton contract makes sense, in this way, we're removing the cost of having a separate LSP11 for each user which may decrease user adoption. The standard will take into consideration the need to deploy one instance for several accounts contracts but still, the LSP11 can be made or have a custom logic that doesn't break the standard specification that makes LSP11 usable by just 1 account.

  • Delays are not needed and can be used in a custom implementation by protocols.

  • Idea of double Hashing does not solve the security part but rather increases privacy. While we use EOAs for control of our accounts, these EOAs are backed by a seed phrase that we should memorize or store safely in some place.

    The social recovery is created for cases where the access for this seed phrase or private key is lost, that's why we should not expect that the plainSecret (password used for recovery in LSP11) to be that hard. It could be something specific to the owner such as his dog name or grandmother's name, etc Which could be used as well as a password for other web2 accounts. The idea is to instead of having the contract deal with the plain secret itself and exposing it on the blockchain which may risk the other web2 accounts of the owner, instead of setting the hash of the plain Secret first, you set the hash of the hash, and on the recovery instead of passing the plainSecret, you pass the hash of the plain secret.

    In the recover ownership(..) function, the hash gets passed as a parameter. It should be hashed again and should match the 'doubleHash' initially set by the account to recover.

  • It's good to find ways to have the ability to not expose the entity of the guardians, for example setting the hash of the address of the guardian instead of the address.

    Other stuff proposed, such as asking guardians to generate an address they control to keep their identity private does not make sense as you go back to the problem of holding private keys that have super power. Instead, you may want to set a UniversalProfile as a guardian but not expose that this UP is a guardian, to avoid having malicious users social engineer this UniversalProfiles. We see value in keeping the identity of the guardians secret which could be an optional feature.

  • Points to be discussed:

    • Opinion on points discussed above.
    • How to increase the security and avoid plainSecret/Hash leak and usage by other users.
    • Integration of relay execution.

@skimaharvey
Copy link
Member

skimaharvey commented Jul 7, 2023

Nice points were brought up!

Should the guardians vote for a call data to be executed, or vote for a specific address that can later execute call data? The difference is:
When voting for an address, this address needs to execute the transaction itself, and you need to trust this address

IMO it should be the recoverer calling the recovery function (after vote of the guardians). Since he will be the recoverer i don't see any pb having him pass any calldata it wants.

The social recovery is created for cases where the access for this seed phrase or private key is lost, that's why we should not expect that the plainSecret (password used for recovery in LSP11) to be that hard. It could be something specific to the owner such as his dog name or grandmother's name, etc Which could be used as well as a password for other web2 accounts. The idea is to instead of having the contract deal with the plain secret itself and exposing it on the blockchain which may risk the other web2 accounts of the owner, instead of setting the hash of the plain Secret first, you set the hash of the hash, and on the recovery instead of passing the plainSecret, you pass the hash of the plain secret.

Good point, even though it won't improve the security aspect. Makes sense that you may not want to expose the plan secret. Although in my opinion, we would still need to add salt and possibly address the contract we are recovering for to the recovery process to make it more robust. I am pretty sure we could add the view functions that could help with creating the hash (would just need to trust your RPC provider 😛 )

Integration of relay execution.

Why not have the option but not sure it should be part of the standard. Anyway, with LSP17 we might just make
some generic social recovery extensions. Can have two different for relay execution or not (with different function signatures). I think we relay execution possibly, we could have different logic where for example we dont even expose the plain secret nor hash but possibly the signature of the recoverer.

Let's see 👍

Edit: I think for the recovery process one should use wether it wants to :

  • require multisig threshold + password
  • only password
  • only multisig threshold

@CJ42
Copy link
Member

CJ42 commented Jul 11, 2023

@YamenMerhi @skimaharvey

I have looked at the discussion thread and posted all my ideas and comments below.

Firstly, I agree on the fact that LSP11 should be a standard not related directly to LSP6. Because access control could be implemented in any other form depending on the contract or dApp. (I have listed few ideas on top of my head below).

How about passing some calldata to the recoverOwnership function?
This calldata could be set in the contract storage for each linked accounts that register to the LSP11 Social Recovery service.

See for instance the two functions at the bottom of this file to see how contracts could register to the Social Recovery service (in this code, this is contract is for installing relayer addresses, but the principle is the same).

https://github.com/PISAresearch/contracts.any.sender/blob/master/versions/0.3.0/contracts/core/Relay.sol

How can we implement an LSP11 that interact directly with the LSP0 setData function? (Or any function really on the account that subscribed to the Social Recovery service).

We could put a social recovery in front of:

  • a UP
  • a Token
  • a DAO
  • a marketplace
  • A registry? To retrieve access to some data?
  • To re-access your social media account on a dApp?

Singleton pattern sounds relevant to me. Multiple services could deploy their own social recovery and compete between each other. This would enable users to register / deregister easily, or even subscribe to multiple recovery service at the same time for different purposes.

Privacy of addresses could make sense, as an optional extension. For instance, social recovery of vault that hold large amount of digital value would make sense. As you don’t want the guardians to be exposed, especially if they are UPs and their UP address is publicly known. They could be « tracked » and forced to hand over the secret to recover the account so to give access to malicious users.

Accepting Guardian invitation —> couldn’t we use here LSP14 (the generic standard interface) with the same functions. But the implementation actually is a contract that has multiple owners that are the guardians? So the owners / guardians are addresses that have to accept ownership on invitation. Something like this. This could be maybe an i teresting example of showing how the LSPs are composable, for different implementation (for different use cases), using the same LSP standard interfaces for interroperability.

Relay Execution: maybe the recovery process might not be good to be allowed as a relay execution (although debatable? Why not?)
I think it should be possible to allow to vote for guardians using meta transactions. The same way as you can elect someone to go to vote for you on your behalf for the presidential elections. This is actual implemented in existing protocols, such as the Uniswap governance token. User can delegate guardians votes the same way as the function delegateBySig(…) —> this could also be used as a way to implement a one time recovery vote for guardians (instead of allowing guardians to vote infinitely as long as they are guardians)

https://docs.uniswap.org//contracts/v2/reference/Governance/governance-reference

Very good point from @skimaharvey about "pluging-in" a Social Recovery services as a LSP17 Extension for instance. This could be one of many example of how this can be embedded in contracts and dApps.
For having it added as an LSP17 Extension, is it so that you can call the UP directly and re-route the call to the LSP11 contract? What would be the benefit? More gas still. Can’t you call the LSP11 contract directly?

The salt is definitely a MUST to add imo. As this would reduce the common types of attacks around password cracking like rainbow tables 🌈. However, we should ensure that salt cannot be reused after a social recovery process completed. Otherwise, the salt becomes publicly known (by looking at the transaction on the explorer), leading attackers to be more able to brute force the secret for the subsequent recovery process.

See some links about rainbow tables:

Having guardians accept the invitation to be guardians will decrease user adoption as guardians need to pay either through a relayer or through gas fees the cost of accepting ownership. Even if subsidized by the owner setting the guardians, it's a cost that is not needed and won't introduce any extra layer of security.

Good counter arguments here.

The interoperability issue of having LSP11 strictly usable by LSP0 X LSP6 MUST be fixed and the LSP11 standard should be generic to support social recovery even for non-LSP0 contracts by having the ability to execute whatever function on the account at the process of recovery.

I cannot agree more on this. This MUST be fixed.

Should the guardians vote for a call data to be executed, or vote for a specific address that can later execute call data?

That’s a good question here to be looked at. We can discuss this further tomorrow in our meeting.

@YamenMerhi
Copy link
Member Author

YamenMerhi commented Jul 13, 2023

The discussion that happened yesterday with @NBR2807 , @CJ42 , @skimaharvey and @b00ste covered these points:

  • Agree on the Singleton pattern. Multiple services could deploy their own social recovery. This would help increase the security where the singleton can act as a registry to check if the secretHash has been already used by other people to avoid having 2 people use the same secretHash and affect one the other while recovering.

  • Agree on having the ability to execute whatever call data by an address on recovering, not limiting LSP11 to call specific functions while recovering.

  • Agree on voting for address not a call data for the following reasons:

    • Calldata sometimes is not deterministic and needs to be crafted at a specific time, it's better to vote for an address that later executes this call data.

    • You need to trust the address you are voting for. Even this address can be restricted to execute specific call data based on what LSP11 is allowed to do.

      For instance with the example of recovering an LSP0 controlled by LSP6, if you grant the LSP11, for instance, the permission CHANGEOWNER, then the person voted for to recover can only execute call data that calls transferOwnership.

      If granted the permission ADDCONTROLLER, then the person voted for to recover can only execute call data that calls setData to set LSP6 Permissions.

      In this way LSP11 is kept generic, it is up to each account to set how LSP11 should interact and able to execute what type of call data on the target account to recover.

  • Relay execution: It is best to focus on security rather than usability. Tricking someone to sign a message could be easier than tricking someone to execute a transaction.

    Keeping in mind that recovering is a process that should happen once in a while not every month/year. It is okay if the person voted for recovering, does some extra work to get gas fees to pay for the transaction.

    Also with the notion of Smart contract Accounts, it is expected that the account has the ability to execute relay transactions, not the target contracts, for example, when you transfer a token with your UniversalProfile x LSP6, it is LSP6 that provides the relay call functionality, not the token contract. The same should apply here, the account should provide the relay functionality, not the LSP11.

  • Agree on the use of specific terms:

    • Recovery Protocol/dApp: It is the interface that allows someone to interact with the LSP11 to set up/vote in a recovery process.
    • Recovery Services: The services used as guardians, like biometric, authy, etc ..
  • Agree on making LSP11 generic in a process where the LSP11 can be used:

    • Only secret hash
    • Only guardians
    • Both
      It just depends on whether the user set the guardians, set a threshold, sets a secret hash, etc...
  • Accepting Guardianship will not be included in the specs, if protocols want to have a custom LSP11 contract that does so, then it will not break the specification and can be done by adding an extra function. We don't see value in adding it.

  • The team is still in an ongoing discussion regarding the secret guardians. While some people see it as an essential optional feature, some people argue that it will add complexity.
    Some ViewPoints from both sides:

    • Against This is something that can complicate the recovery process. It should be either private guardians or public guardians.
    • With If it doesn't complicate why not include it? I could have some recovery services that I trust no one can hack, but when setting other accounts (UniversalProfiles, Vaults, etc ..) I could be afraid of social engineering on their side, so it's better to keep their identity private.
    • With Social Engineering is the being the base for hacks in the Web3 space. It is essential to have the option to choose whether they want to keep the identity of the guardian private until recovery.
  • Agree on using a hash instead of a plainSecret to enforce not exposing the secret that could be used by the account owner on other web2 accounts. This will enhance the privacy and security of the user setting up recovery contracts.

  • To Investigate introducing batchCalls for setup functions, disallow other functions

  • The only final point needed to be discussed and we need people to participate in this issue, is how to increase security. Some of the ideas:

    • Salting on-chain or off-chain

@YamenMerhi
Copy link
Member Author

Recent Discussion:

  • Secret Guardian Concept: While necessary for phishing protection, there are various methods to keep guardians secret, such as hashing and salting using the profile address or employing alternatives like signatures and zero-knowledge proofs (zk). Therefore, it's proposed that the LSP11 standard remain neutral. Instead of integrating secret guardian logic within LSP11's core, it should be assigned to a separate 'guardian' contract. This approach allows each service or deployer to select their preferred method for maintaining guardian secrecy.

  • Password Security: Relying solely on passwords could be problematic due to the diverse ways of securing them in the blockchain space, including using multiple passwords or implementing multiple hashings with salting. To address this, it's suggested to externalize this logic from the LSP11 core and assign it to a guardian contract. This enables each service or deployer to choose their own method for securing passwords.

  • Recovery Function Access: Currently, only the voted address can initiate the recovery function, aiming for maximal security. However, this creates a user experience (UX) issue, as the address requires gas fees and a path for execution. For simplicity, it's decided to permit signatures from the voted address (using ECDSA and IERC1271) instead.

  • Addition of a Delay Option: There's a division in user preference regarding the recovery process timing. Some prefer a delay to cancel an invalid recovery attempt, while others want immediate profile recovery. To cater to both preferences, it's proposed to introduce an optional delay in the recovery setup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants