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

MessageHash, StakeIntentHash and RedeemIntentHash should be hashed as per EIP-712. #415

Closed
deepesh-kn opened this issue Oct 26, 2018 · 9 comments
Assignees

Comments

@deepesh-kn
Copy link
Contributor

deepesh-kn commented Oct 26, 2018

User Story

Staker / Redeemer needs to sign the structured data. This data should be hashed as per the standard specified in EIP-712.

Following structure exists in the code.
In EIP20Gateways there is struct Stake.
In EIP20Gateways there is struct Redeem.
In MessageBus there is struct Message.

This means that

  • Stake intent hash will include STAKE_TYPEHASH.
  • Redeem intent hash will include REDEEM_TYPEHASH.
  • Message hash will include MESSAGE_TYPEHASH.
  • Remove GATEWAY_LINK_TYPEHASH from the code.

The type hash are as per the EIP-712 standards.

  • STAKE_TYPEHASH is type hash of Stake.
  • REDEEM_TYPEHASH is type hash of Redeem.
  • MESSAGE_TYPEHASH is type hash of Message.
@deepesh-kn deepesh-kn changed the title Gateway: MessageHash, StakingIntentHash, RedemptionIntentHash, GatewayLinkingIntentHash should be hashed as per EIP-712. MessageHash, StakingIntentHash and RedemptionIntentHash should be hashed as per EIP-712. Nov 20, 2018
@deepesh-kn deepesh-kn changed the title MessageHash, StakingIntentHash and RedemptionIntentHash should be hashed as per EIP-712. MessageHash, StakeIntentHash and RedeemIntentHash should be hashed as per EIP-712. Nov 29, 2018
@gulshanvasnani gulshanvasnani self-assigned this Dec 3, 2018
@deepesh-kn
Copy link
Contributor Author

  • Initially, I was in favor of updating the structs storage to hold all the data, but after today's discussion, it clears my understanding that we do not need the actual struct to store all the params.
  • Rather the type hash defines the expected structure of the data for hashing.

Now the current challenge is that the data user will sign should be readable. In the current implementation, it is not and the user will see hashed data.

So we can have the following two approaches so that data can be presented to the user.
First
example:

        bytes32 STAKE_INTENT_TYPE_HASH = keccak256(
            abi.encodePacked(
            "StakeIntentHash(address staker,uint256 amount,address beneficiary,uint256 gasPrice,uint256 gasLimit,uint256 bounty,uint256 nonce,address gateway)"
            )
        );
        bytes32 MESSAGE_TYPE_HASH = keccak256(
            abi.encodePacked(
                "Message(bytes32 intentHash,address sender,uint256 nonce,address gateway,bytes32 hashLock)"
            )
        );

        bytes32 stakeIntentHash =  keccak256(
            abi.encodePacked(
                STAKE_INTENT_TYPE_HASH,
                staker,
                amount,
                beneficiary,
                gasPrice,
                gasLimit,
                bounty,
                nonce,
                gateway
            )
        );
        bytes32 messageHash = keccak256(
            MESSAGE_TYPE_HASH, 
            stakeIntentHash,
            sender,
            nonce,
            gateway,
            hashLock
        );
  • Message hash is independent of the intent hashes and uses the MESSAGE_TYPE_HASH.
  • In this case, the user needs to sign the IntentHash (e.g: StakeIntentHash.)
  • Message bus has to verify the signature with IntentHash as data.
  • StakeIntentHash and MessageHash data have repeated params.
  • In this example, we can remove staker param from the stakeIntentHash, as it will be the signer.

Second
example:

bytes32 STAKE_INTENT_TYPE_HASH = keccak256(
            abi.encodePacked(
            "StakeIntentHash(uint256 amount,address beneficiary,uint256 gasPrice,uint256 gasLimit,uint256 bounty)"
            )
        );

        bytes32 STAKE_MESSAGE_TYPE_HASH = keccak256(
            abi.encodePacked(
                "Message(StakeIntentHash intentHash,address sender,uint256 nonce,address gateway)StakeIntentHash(uint256 amount,address beneficiary,uint256 gasPrice,uint256 gasLimit,uint256 bounty)"
            )
        )

 bytes32 stakeIntentHash =  keccak256(
            abi.encodePacked(
                STAKE_INTENT_TYPE_HASH,
                amount,
                beneficiary,
                gasPrice,
                gasLimit,
                bounty                
            )
        );
        bytes32 messageHash = keccak256(
            STAKE_MESSAGE_TYPE_HASH, // this will change for different message types
            stakeIntentHash,
            sender,
            nonce,
            gateway,
        );
  • (stake and redeem)Context-specific message hash.
  • Here we have the nested structured data.
  • Hashed data do not have repeated params.
  • hashLock cannot be part of the message hash in this case.
  • User signs the messageHash.
  • Message bus has to verify the signature with messageHash as data.

Let me know what is your opinion.
cc: @benjaminbollen , @schemar , @sarvesh-ost , @gulshanvasnani

@schemar
Copy link
Contributor

schemar commented Dec 4, 2018

Why can we not add the hash lock?

@gulshanvasnani
Copy link
Contributor

Second example seems to be aligned with the example at EIP712 below :-
https://github.com/ethereum/EIPs/blob/master/assets/eip-712/Example.sol

To me it make sense to move ahead with second approach(without changing Stake and Message struct) if anyone doesn't have any concern.

cc: @benjaminbollen @schemar @deepesh-kn @sarvesh-ost

@schemar
Copy link
Contributor

schemar commented Dec 4, 2018

Yes, the second approach looks better.

@deepesh-kn
Copy link
Contributor Author

deepesh-kn commented Dec 4, 2018

Why can we not add the hash lock?

In this case, message hash is signed by the staker/redeemer. Hashlock is provided by the facilitator, so the hash lock is not part of the message hash.
I strongly feel that the hash lock somehow should be part of message hash so that it can be cryptographically proved on the other chain. Otherwise, any facilitator can just put any Hashlock on the other chain. 🤔

If we create a new hash using message hash and the unlock key than we are not getting ant major benefit as compared to the first approach.

Does anyone have a 3rd approach? I am feeling more inclined towards the first approach.

@schemar
Copy link
Contributor

schemar commented Dec 7, 2018

OK that makes sense. The hash lock definitely has to be there, then. Another question: if the stake intent is part of the message. And the nonce and gateway are part of the stake intent. Then why do we need to add them again to the message?

Would the following not work? The sender could sign the intent hash, the facilitator could sign the message hash, and still all data could be "proven" with the hashes. Am I missing something? 🤔

bytes32 STAKE_INTENT_TYPE_HASH = keccak256(
    abi.encodePacked(
        "StakeIntentHash(address staker,uint256 amount,address beneficiary,uint256 gasPrice,uint256 gasLimit,uint256 bounty,uint256 nonce,address gateway)"
    )
);
bytes32 MESSAGE_TYPE_HASH = keccak256(
    abi.encodePacked(
        "Message(bytes32 intentHash,address sender,bytes32 hashLock)"
    )
);

bytes32 stakeIntentHash = keccak256(
    abi.encodePacked(
        STAKE_INTENT_TYPE_HASH,
        staker,
        amount,
        beneficiary,
        gasPrice,
        gasLimit,
        bounty,
        nonce,
        gateway
    )
);
bytes32 messageHash = keccak256(
    abi.encodePacked(
        MESSAGE_TYPE_HASH, 
        stakeIntentHash,
        sender,
        hashLock
    )
);

@deepesh-kn
Copy link
Contributor Author

Message bus does not know how the intentHash is formed. MessageBus is abstracted in such a way that it can work with any intentHash, which may not even include the nonce.

  • To stop the replay attacks staker's nonce was part of the messageHash.
  • Similarly gateway address was added in the messageHash so as to MessageBus can verify that the message belongs to correct gateway.

@schemar
Copy link
Contributor

schemar commented Dec 10, 2018

In that case I think it makes sense to add the parameters in both places. They are there for "different" reasons.

@gulshanvasnani gulshanvasnani removed their assignment Dec 14, 2018
@schemar schemar self-assigned this Dec 18, 2018
@schemar
Copy link
Contributor

schemar commented Dec 19, 2018

After discussing the topic one more time, we agreed to:

  • remove redundant parameters, as the hashes won't be signed
  • have two separate message hash types, based on the intent hash

/cc @deepesh-kn @sarvesh-ost

schemar added a commit to schemar/mosaic-contracts that referenced this issue Dec 20, 2018
The Message, StakeIntent, and RedeemIntent now have a new structure and
different fields that are part of the hash. Stake and redeem intent now
consist of amount, beneficiary, and (originating) gateway address. The
Message has as first parameter a hash which is not specific to staking
or redeeming.
All three pre-images now include a type hash as per EIP712.

This means that I was able to remove a lot of stake/redeem specific code
for message hashing. A message's hash is now generated regardless of the
kind of intent it holds.

I added a method `storeMessage` which creates a message hash and stores
the message at the hash in the mapping. The reason was to circumvent a
"stack too deep" error which prevented me from creating the hash
directly in confirm stake/redeem intent.

I restructured the tests in an effort to make them more easily
understandable. I spent quite some time wrapping my head around the
construct.

I did not add the domain separator yet, as the hashes are never signed
or the signature proven/verified.

:warning: Please check that the new logic of how and what is hashed
still guarantees correctness and prevents fraud when relaying the
messages :warning:

Fixes OpenST#415
schemar added a commit to schemar/mosaic-contracts that referenced this issue Dec 20, 2018
The Message, StakeIntent, and RedeemIntent now have a new structure and
different fields that are part of the hash. Stake and redeem intent now
consist of amount, beneficiary, and (originating) gateway address. The
Message has as first parameter a hash which is not specific to staking
or redeeming.
All three pre-images now include a type hash as per EIP712.

This means that I was able to remove a lot of stake/redeem specific code
for message hashing. A message's hash is now generated regardless of the
kind of intent it holds.

I added a method `storeMessage` which creates a message hash and stores
the message at the hash in the mapping. The reason was to circumvent a
"stack too deep" error which prevented me from creating the hash
directly in confirm stake/redeem intent.

I restructured the tests in an effort to make them more easily
understandable. I spent quite some time wrapping my head around the
construct.

I did not add the domain separator yet, as the hashes are never signed
or the signature proven/verified.

:warning: Please check that the new logic of how and what is hashed
still guarantees correctness and prevents fraud when relaying the
messages :warning:

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

No branches or pull requests

4 participants