aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
blob: 2d04c7c6d427e86c697ab7c9eb3ba42a690e3fde (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*

  Copyright 2017 ZeroEx Intl.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

pragma solidity ^0.4.19;

import "./mixins/MSignatureValidator.sol";
import "./ISigner.sol";

/// @dev Provides MSignatureValidator
contract MixinSignatureValidator is
    MSignatureValidator
{
    enum SignatureType {
        Illegal, // Default value
        Invalid,
        Caller,
        Ecrecover,
        EIP712,
        Contract
    }
  
    function isValidSignature(
        bytes32 hash,
        address signer,
        bytes signature)
        public view
        returns (bool isValid)
    {
        // TODO: Domain separation: make hash depend on role. (Taker sig should not be valid as maker sig, etc.)
        
        require(signature.length >= 1);
        SignatureType signatureType = SignatureType(uint8(signature[0]));
        
        // Variables are not scoped in Solidity
        uint8 v;
        bytes32 r;
        bytes32 s;
        address recovered;
        
        // Always illegal signature
        // This is always an implicit option since a signer can create a
        // signature array with invalid type or length. We may as well make
        // it an explicit option. This aids testing and analysis. It is
        // also the initialization value for the enum type.
        if (signatureType == SignatureType.Illegal) {
            revert();
        
        // Always invalid signature
        // Like Illegal, this is always implicitly available and therefore
        // offered explicitly. It can be implicitly created by providing
        // a validly formatted but incorrect signature.
        } else if (signatureType == SignatureType.Invalid) {
            require(signature.length == 1);
            isValid = false;
            return isValid;
        
        // Implicitly signed by caller
        // The signer has initiated the call. In the case of non-contract
        // accounts it means the transaction itself was signed.
        // Example: let's say for a particular operation three signatures
        // A, B  are required. To submit the transaction, A and B can give
        // a signature to C, who can then submit the transaction using
        // `Caller` for his own signature. Or A and C can sign and B can
        // submit using `Caller`. Having `Caller` allows this flexibility.
        } else if (signatureType == SignatureType.Caller) {
            require(signature.length == 1);
            isValid = signer == msg.sender;
            return isValid;
        
        // Signed using web3.eth_sign
        } else if (signatureType == SignatureType.Ecrecover) {
            require(signature.length == 66);
            v = uint8(signature[1]);
            r = get32(signature, 2);
            s = get32(signature, 34);
            recovered = ecrecover(
                keccak256("\x19Ethereum Signed Message:\n32", hash),
                v,
                r,
                s
            );
            isValid = signer == recovered;
            return isValid;
        
        // Signature using EIP712
        } else if (signatureType == SignatureType.EIP712) {
            require(signature.length == 66);
            v = uint8(signature[1]);
            r = get32(signature, 2);
            s = get32(signature, 34);
            recovered = ecrecover(hash, v, r, s);
            isValid = signer == recovered;
            return isValid;
        
        // Signature verified by signer contract
        } else if (signatureType == SignatureType.Contract) {
            isValid = ISigner(signer).isValidSignature(hash, signature);
            return isValid;
        }
        
        // Anything else is illegal (We do not return false because
        // the signature may actually be valid, just not in a format
        // that we currently support. In this case returning false
        // may lead the caller to incorrectly believe that the
        // signature was invalid.)
        revert();
    }
    
    function get32(bytes b, uint256 index)
        private pure
        returns (bytes32 result)
    {
        require(b.length >= index + 32);
        
        // Arrays are prefixed by a 256 bit length parameter
        index += 32;
        
        // Read the bytes32 from array memory
        assembly {
            result := mload(add(b, index))
        }
        return result;
    }

}