SWC-117/签名的非唯一性
以太坊合约中的加密签名实现通常假定签名是唯一的,但是可以在不拥有私钥的情况下更改签名, 并且签名仍然有效。EVM规范定义了几个“预编译”合约,其中一个合约ecrecover执行椭圆曲线公钥恢复。 恶意用户可以稍微修改三个值v,r和s来创建其他有效签名。如果签名是已签名消息哈希的一部分, 则在合约级别执行签名验证的系统可能会受到攻击。恶意用户可能创建有效的签名,以重放以前签名的消息。
CWE漏洞分类
整改方案
在检查消息是否之前已由合约处理时,在消息哈希中不应包含签名。
参考文献
示例合约
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);
}
}
transaction_malleablity.yaml
description: Calculates transaction identifier with signature
issues:
- id: SWC-117
count: 1
locations:
- bytecode_offsets: {}
line_numbers:
transaction_malleablity.sol: [23]
transaction_malleablity_fixed.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 = 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);
}
}
transaction_malleablity_fixed.yaml
description: Calculates transaction identifier with signature
issues:
- id: SWC-117
count: 0
locations: []