有名合同与无名合同的区别(有名合同和无名合同的区别)

文章前言

以太坊合约中的加密签名实现通常假定签名是唯一的,但是可以在不具备私钥的情况下实现对签名的更改,并且签名仍然有效,在EVM规范定义了几个\\”预编译\\”合约,其中一个合约ecrecover主要用于执行椭圆曲线公钥恢复,恶意用户可以稍微修改三个值r,v,s来创建其他有效签名,此时如果签名是已签名消息哈希的一部分,则在合约级别执行签名验证的系统可能会受到攻击,恶意用户可能创建有效的签名,以重放以前签名的消息来获利或实施恶意攻击。

漏洞示例

transaction_malleablity.sol

    pragma solidity ^0.4.24;
    contract transaction_malleablity{ mapping(address => uint256) balances; mapping(bytes32 => bool) signatureUsed;
    constructor(address[] owners, uint[] init){ require(owners.length == init.length); for(uint i=0; i < owners.length; i ++){ balances[owners[i]] = init[i]; } }
    function transfer( bytes _signature, address _to, uint256 _value, uint256 _gasPrice, uint256 _nonce) public returns (bool){ bytes32 txid = keccak256(abi.encodePacked(getTransferHash(_to, _value, _gasPrice, _nonce), _signature)); require(!signatureUsed[txid]);
    address from = recoverTransferPreSigned(_signature, _to, _value, _gasPrice, _nonce);
    require(balances[from] > _value); balances[from] -= _value; balances[_to] += _value;
    signatureUsed[txid] = true; }
    function recoverTransferPreSigned( bytes _sig, address _to, uint256 _value, uint256 _gasPrice, uint256 _nonce) public view returns (address recovered){ return ecrecoverFromSig(getSignHash(getTransferHash(_to, _value, _gasPrice, _nonce)), _sig); }
    function getTransferHash( address _to, uint256 _value, uint256 _gasPrice, uint256 _nonce) public view returns (bytes32 txHash) { return keccak256(address(this), bytes4(0x1296830d), _to, _value, _gasPrice, _nonce); }
    function getSignHash(bytes32 _hash) public pure returns (bytes32 signHash){ return keccak256(\\\"\\\\x19Ethereum Signed Message:\\\\n32\\\", _hash); }
    function ecrecoverFromSig(bytes32 hash, bytes sig) public pure returns (address recoveredAddress){ bytes32 r; bytes32 s; uint8 v; if (sig.length != 65) return address(0); assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) } if (v < 27) { v += 27; } if (v != 27 && v != 28) return address(0); return ecrecover(hash, v, r, s); }}

    防御措施

    在检查消息是否之前已由合约处理时,在消息哈希中不应包含签名,修改后的代码如下:

      pragma solidity ^0.4.24;
      contract transaction_malleablity{ mapping(address => uint256) balances; mapping(bytes32 => bool) signatureUsed;
      constructor(address[] owners, uint[] init){ require(owners.length == init.length); for(uint i=0; i < owners.length; i ++){ balances[owners[i]] = init[i]; } }
      function transfer( bytes _signature, address _to, uint256 _value, uint256 _gasPrice, uint256 _nonce) public returns (bool){ bytes32 txid = getTransferHash(_to, _value, _gasPrice, _nonce); require(!signatureUsed[txid]);
      address from = recoverTransferPreSigned(_signature, _to, _value, _gasPrice, _nonce);
      require(balances[from] > _value); balances[from] -= _value; balances[_to] += _value;
      signatureUsed[txid] = true; }
      function recoverTransferPreSigned( bytes _sig, address _to, uint256 _value, uint256 _gasPrice, uint256 _nonce) public view returns (address recovered){ return ecrecoverFromSig(getSignHash(getTransferHash(_to, _value, _gasPrice, _nonce)), _sig); }
      function getTransferHash( address _to, uint256 _value, uint256 _gasPrice, uint256 _nonce) public view returns (bytes32 txHash) { return keccak256(address(this), bytes4(0x1296830d), _to, _value, _gasPrice, _nonce); }
      function getSignHash(bytes32 _hash) public pure returns (bytes32 signHash){ return keccak256(\\\"\\\\x19Ethereum Signed Message:\\\\n32\\\", _hash); }
      function ecrecoverFromSig(bytes32 hash, bytes sig) public pure returns (address recoveredAddress){ bytes32 r; bytes32 s; uint8 v; if (sig.length != 65) return address(0); assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) } if (v < 27) { v += 27; } if (v != 27 && v != 28) return address(0); return ecrecover(hash, v, r, s); }}

      修改前后的代码差异对比如下:

      原创文章,作者:七芒星实验室,如若转载,请注明出处:https://www.sudun.com/ask/34258.html

      Like (0)
      七芒星实验室的头像七芒星实验室
      Previous 2024年4月6日
      Next 2024年4月6日

      相关推荐

      发表回复

      您的邮箱地址不会被公开。 必填项已用 * 标注