mirror of https://github.com/ConsenSys/mythril
commit
ed06c3148e
@ -0,0 +1,117 @@ |
|||||||
|
from ethereum import utils |
||||||
|
from mythril.laser.smt import ( |
||||||
|
BitVec, |
||||||
|
Function, |
||||||
|
URem, |
||||||
|
symbol_factory, |
||||||
|
ULE, |
||||||
|
And, |
||||||
|
ULT, |
||||||
|
Bool, |
||||||
|
Or, |
||||||
|
) |
||||||
|
from typing import Dict, Tuple, List |
||||||
|
|
||||||
|
TOTAL_PARTS = 10 ** 40 |
||||||
|
PART = (2 ** 256 - 1) // TOTAL_PARTS |
||||||
|
INTERVAL_DIFFERENCE = 10 ** 30 |
||||||
|
hash_matcher = "fffffff" # This is usually the prefix for the hash in the output |
||||||
|
|
||||||
|
|
||||||
|
class KeccakFunctionManager: |
||||||
|
""" |
||||||
|
A bunch of uninterpreted functions are considered like keccak256_160 ,... |
||||||
|
where keccak256_160 means the input of keccak256() is 160 bit number. |
||||||
|
the range of these functions are constrained to some mutually disjoint intervals |
||||||
|
All the hashes modulo 64 are 0 as we need a spread among hashes for array type data structures |
||||||
|
All the functions are kind of one to one due to constraint of the existence of inverse |
||||||
|
for each encountered input. |
||||||
|
For more info https://files.sri.inf.ethz.ch/website/papers/sp20-verx.pdf |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self): |
||||||
|
self.store_function = {} # type: Dict[int, Tuple[Function, Function]] |
||||||
|
self.interval_hook_for_size = {} # type: Dict[int, int] |
||||||
|
self._index_counter = TOTAL_PARTS - 34534 |
||||||
|
self.quick_inverse = {} # type: Dict[BitVec, BitVec] # This is for VMTests |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def find_concrete_keccak(data: BitVec) -> BitVec: |
||||||
|
""" |
||||||
|
Calculates concrete keccak |
||||||
|
:param data: input bitvecval |
||||||
|
:return: concrete keccak output |
||||||
|
""" |
||||||
|
keccak = symbol_factory.BitVecVal( |
||||||
|
int.from_bytes( |
||||||
|
utils.sha3(data.value.to_bytes(data.size() // 8, byteorder="big")), |
||||||
|
"big", |
||||||
|
), |
||||||
|
256, |
||||||
|
) |
||||||
|
return keccak |
||||||
|
|
||||||
|
def get_function(self, length: int) -> Tuple[Function, Function]: |
||||||
|
""" |
||||||
|
Returns the keccak functions for the corresponding length |
||||||
|
:param length: input size |
||||||
|
:return: tuple of keccak and it's inverse |
||||||
|
""" |
||||||
|
try: |
||||||
|
func, inverse = self.store_function[length] |
||||||
|
except KeyError: |
||||||
|
func = Function("keccak256_{}".format(length), length, 256) |
||||||
|
inverse = Function("keccak256_{}-1".format(length), 256, length) |
||||||
|
self.store_function[length] = (func, inverse) |
||||||
|
return func, inverse |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def get_empty_keccak_hash() -> BitVec: |
||||||
|
""" |
||||||
|
returns sha3("") |
||||||
|
:return: |
||||||
|
""" |
||||||
|
val = 89477152217924674838424037953991966239322087453347756267410168184682657981552 |
||||||
|
return symbol_factory.BitVecVal(val, 256) |
||||||
|
|
||||||
|
def create_keccak(self, data: BitVec) -> Tuple[BitVec, Bool]: |
||||||
|
""" |
||||||
|
Creates Keccak of the data |
||||||
|
:param data: input |
||||||
|
:return: Tuple of keccak and the condition it should satisfy |
||||||
|
""" |
||||||
|
length = data.size() |
||||||
|
func, inverse = self.get_function(length) |
||||||
|
|
||||||
|
condition = self._create_condition(func_input=data) |
||||||
|
self.quick_inverse[func(data)] = data |
||||||
|
return func(data), condition |
||||||
|
|
||||||
|
def _create_condition(self, func_input: BitVec) -> Bool: |
||||||
|
""" |
||||||
|
Creates the constraints for hash |
||||||
|
:param func_input: input of the hash |
||||||
|
:return: condition |
||||||
|
""" |
||||||
|
length = func_input.size() |
||||||
|
func, inv = self.get_function(length) |
||||||
|
try: |
||||||
|
index = self.interval_hook_for_size[length] |
||||||
|
except KeyError: |
||||||
|
self.interval_hook_for_size[length] = self._index_counter |
||||||
|
index = self._index_counter |
||||||
|
self._index_counter -= INTERVAL_DIFFERENCE |
||||||
|
|
||||||
|
lower_bound = index * PART |
||||||
|
upper_bound = lower_bound + PART |
||||||
|
|
||||||
|
cond = And( |
||||||
|
inv(func(func_input)) == func_input, |
||||||
|
ULE(symbol_factory.BitVecVal(lower_bound, 256), func(func_input)), |
||||||
|
ULT(func(func_input), symbol_factory.BitVecVal(upper_bound, 256)), |
||||||
|
URem(func(func_input), symbol_factory.BitVecVal(64, 256)) == 0, |
||||||
|
) |
||||||
|
return cond |
||||||
|
|
||||||
|
|
||||||
|
keccak_function_manager = KeccakFunctionManager() |
@ -0,0 +1,25 @@ |
|||||||
|
from typing import cast |
||||||
|
import z3 |
||||||
|
|
||||||
|
from mythril.laser.smt.bitvec import BitVec |
||||||
|
|
||||||
|
|
||||||
|
class Function: |
||||||
|
"""An uninterpreted function.""" |
||||||
|
|
||||||
|
def __init__(self, name: str, domain: int, value_range: int): |
||||||
|
"""Initializes an uninterpreted function. |
||||||
|
|
||||||
|
:param name: Name of the Function |
||||||
|
:param domain: The domain for the Function (10 -> all the values that a bv of size 10 could take) |
||||||
|
:param value_range: The range for the values of the function (10 -> all the values that a bv of size 10 could take) |
||||||
|
""" |
||||||
|
self.domain = z3.BitVecSort(domain) |
||||||
|
self.range = z3.BitVecSort(value_range) |
||||||
|
self.raw = z3.Function(name, self.domain, self.range) |
||||||
|
|
||||||
|
def __call__(self, item: BitVec) -> BitVec: |
||||||
|
"""Function accessor, item can be symbolic.""" |
||||||
|
return BitVec( |
||||||
|
cast(z3.BitVecRef, self.raw(item.raw)), annotations=item.annotations |
||||||
|
) |
@ -0,0 +1,138 @@ |
|||||||
|
from mythril.laser.smt import Solver, symbol_factory, And |
||||||
|
from mythril.laser.ethereum.keccak_function_manager import keccak_function_manager |
||||||
|
import z3 |
||||||
|
import pytest |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize( |
||||||
|
"input1, input2, expected", |
||||||
|
[ |
||||||
|
(symbol_factory.BitVecVal(100, 8), symbol_factory.BitVecVal(101, 8), z3.unsat), |
||||||
|
(symbol_factory.BitVecVal(100, 8), symbol_factory.BitVecVal(100, 16), z3.unsat), |
||||||
|
(symbol_factory.BitVecVal(100, 8), symbol_factory.BitVecVal(100, 8), z3.sat), |
||||||
|
( |
||||||
|
symbol_factory.BitVecSym("N1", 256), |
||||||
|
symbol_factory.BitVecSym("N2", 256), |
||||||
|
z3.sat, |
||||||
|
), |
||||||
|
( |
||||||
|
symbol_factory.BitVecVal(100, 256), |
||||||
|
symbol_factory.BitVecSym("N1", 256), |
||||||
|
z3.sat, |
||||||
|
), |
||||||
|
( |
||||||
|
symbol_factory.BitVecVal(100, 8), |
||||||
|
symbol_factory.BitVecSym("N1", 256), |
||||||
|
z3.unsat, |
||||||
|
), |
||||||
|
], |
||||||
|
) |
||||||
|
def test_keccak_basic(input1, input2, expected): |
||||||
|
s = Solver() |
||||||
|
|
||||||
|
o1, c1 = keccak_function_manager.create_keccak(input1) |
||||||
|
o2, c2 = keccak_function_manager.create_keccak(input2) |
||||||
|
s.add(And(c1, c2)) |
||||||
|
|
||||||
|
s.add(o1 == o2) |
||||||
|
assert s.check() == expected |
||||||
|
|
||||||
|
|
||||||
|
def test_keccak_symbol_and_val(): |
||||||
|
""" |
||||||
|
check keccak(100) == keccak(n) && n == 10 |
||||||
|
:return: |
||||||
|
""" |
||||||
|
s = Solver() |
||||||
|
hundred = symbol_factory.BitVecVal(100, 256) |
||||||
|
n = symbol_factory.BitVecSym("n", 256) |
||||||
|
o1, c1 = keccak_function_manager.create_keccak(hundred) |
||||||
|
o2, c2 = keccak_function_manager.create_keccak(n) |
||||||
|
s.add(And(c1, c2)) |
||||||
|
s.add(o1 == o2) |
||||||
|
s.add(n == symbol_factory.BitVecVal(10, 256)) |
||||||
|
assert s.check() == z3.unsat |
||||||
|
|
||||||
|
|
||||||
|
def test_keccak_complex_eq(): |
||||||
|
""" |
||||||
|
check for keccak(keccak(b)*2) == keccak(keccak(a)*2) && a != b |
||||||
|
:return: |
||||||
|
""" |
||||||
|
s = Solver() |
||||||
|
a = symbol_factory.BitVecSym("a", 160) |
||||||
|
b = symbol_factory.BitVecSym("b", 160) |
||||||
|
o1, c1 = keccak_function_manager.create_keccak(a) |
||||||
|
o2, c2 = keccak_function_manager.create_keccak(b) |
||||||
|
s.add(And(c1, c2)) |
||||||
|
two = symbol_factory.BitVecVal(2, 256) |
||||||
|
o1 = two * o1 |
||||||
|
o2 = two * o2 |
||||||
|
o1, c1 = keccak_function_manager.create_keccak(o1) |
||||||
|
o2, c2 = keccak_function_manager.create_keccak(o2) |
||||||
|
|
||||||
|
s.add(And(c1, c2)) |
||||||
|
s.add(o1 == o2) |
||||||
|
s.add(a != b) |
||||||
|
|
||||||
|
assert s.check() == z3.unsat |
||||||
|
|
||||||
|
|
||||||
|
def test_keccak_complex_eq2(): |
||||||
|
""" |
||||||
|
check for keccak(keccak(b)*2) == keccak(keccak(a)*2) |
||||||
|
This isn't combined with prev test because incremental solving here requires extra-extra work |
||||||
|
(solution is literally the opposite of prev one) so it will take forever to solve. |
||||||
|
:return: |
||||||
|
""" |
||||||
|
s = Solver() |
||||||
|
a = symbol_factory.BitVecSym("a", 160) |
||||||
|
b = symbol_factory.BitVecSym("b", 160) |
||||||
|
o1, c1 = keccak_function_manager.create_keccak(a) |
||||||
|
o2, c2 = keccak_function_manager.create_keccak(b) |
||||||
|
s.add(And(c1, c2)) |
||||||
|
two = symbol_factory.BitVecVal(2, 256) |
||||||
|
o1 = two * o1 |
||||||
|
o2 = two * o2 |
||||||
|
o1, c1 = keccak_function_manager.create_keccak(o1) |
||||||
|
o2, c2 = keccak_function_manager.create_keccak(o2) |
||||||
|
|
||||||
|
s.add(And(c1, c2)) |
||||||
|
s.add(o1 == o2) |
||||||
|
|
||||||
|
assert s.check() == z3.sat |
||||||
|
|
||||||
|
|
||||||
|
def test_keccak_simple_number(): |
||||||
|
""" |
||||||
|
check for keccak(b) == 10 |
||||||
|
:return: |
||||||
|
""" |
||||||
|
s = Solver() |
||||||
|
a = symbol_factory.BitVecSym("a", 160) |
||||||
|
ten = symbol_factory.BitVecVal(10, 256) |
||||||
|
o, c = keccak_function_manager.create_keccak(a) |
||||||
|
|
||||||
|
s.add(c) |
||||||
|
s.add(ten == o) |
||||||
|
|
||||||
|
assert s.check() == z3.unsat |
||||||
|
|
||||||
|
|
||||||
|
def test_keccak_other_num(): |
||||||
|
""" |
||||||
|
check keccak(keccak(a)*2) == b |
||||||
|
:return: |
||||||
|
""" |
||||||
|
s = Solver() |
||||||
|
a = symbol_factory.BitVecSym("a", 160) |
||||||
|
b = symbol_factory.BitVecSym("b", 256) |
||||||
|
o, c = keccak_function_manager.create_keccak(a) |
||||||
|
two = symbol_factory.BitVecVal(2, 256) |
||||||
|
o = two * o |
||||||
|
s.add(c) |
||||||
|
o, c = keccak_function_manager.create_keccak(o) |
||||||
|
s.add(c) |
||||||
|
s.add(b == o) |
||||||
|
|
||||||
|
assert s.check() == z3.sat |
Loading…
Reference in new issue