taint

Crypto forensics for private use
git clone git://git.defalsify.org/taint.git
Log | Files | Refs | LICENSE

taint.py (5932B)


      1 # standard imports
      2 import logging
      3 
      4 # external imports
      5 from hexathon import strip_0x
      6 
      7 # local imports
      8 from .cache import Cache
      9 from .account import Account
     10 from .crypto import Salter
     11 
     12 logg = logging.getLogger().getChild(__name__)
     13 
     14 
     15 def noop_process_actors(self, inputs, outputs):
     16     return (inputs, outputs,)
     17 
     18 
     19 class Tainter(Cache):
     20     """Frontend object containing code to load and save state of a cache, aswell as chain sync handling.
     21 
     22     :param chain_spec: Chain spec context for the cache
     23     :type chain_spec: chainlib.chain.ChainSpec
     24     :param bits_size: Bitsize of bloom filter used for cache
     25     :type bits_size: int
     26     :param result_handler: Callback called once for each registered account found in a transaction.
     27     :type result_handler: function
     28     :param store: State storage for cache
     29     :type store: taint.store.base.BaseStore
     30     :param cache_bloom: Cache bloom filter to instantiate
     31     :type cache_bloom: taint.cache.CacheBloom
     32     """
     33     def __init__(self, chain_spec, bits_size, result_handler=None, store=None, cache_bloom=None):
     34         super(Tainter, self).__init__(chain_spec, bits_size, cache_bloom=cache_bloom)
     35         self.store = store
     36         self.result_handler = result_handler
     37         self.processors = [noop_process_actors]
     38 
     39 
     40     def register_actor_processor(self, processor):
     41         self.processors.insert(0, processor)
     42 
     43 
     44     def add_account(self, account, label):
     45         """Add account to be tracked in cache.
     46 
     47         If registered, the result handler will be called with the initial state of the added account.
     48 
     49         The label will only be stored in memory for the given session, and will not be part of state storage.
     50 
     51         :param account: Account to add
     52         :type account: taint.account.Account
     53         :param label: Filter section to add account to
     54         :type label: taint.cache.CacheAccountEnum
     55         """
     56         super(Tainter, self).add_account(account, label)
     57         if self.result_handler != None:
     58             self.result_handler.register(account)
     59 
     60 
     61     def filter(self, conn, block, tx, storer):
     62         """Transaction callback for chainsyncer.
     63 
     64         :param conn: RPC connection object
     65         :type conn: chainlib.connection.RPCConnection
     66         :param block: Block object
     67         :type block: chainlib.block.Block
     68         :param tx: Transaction object
     69         :type tx: chainlib.tx.Tx
     70         :param storer: State storage object (e.g. a sql database session)
     71         :type storer: any
     72         """
     73         outputs = None
     74         inputs = None
     75 
     76         for p in self.processors:
     77             r = p(tx.outputs, tx.inputs)
     78             if r != None:
     79                 outputs = r[0]
     80                 inputs = r[1]
     81                 break
     82 
     83         for output in outputs:
     84             for inpt in inputs:
     85                 sender = bytes.fromhex(strip_0x(output))
     86                 recipient = bytes.fromhex(strip_0x(inpt))
     87                 r = None
     88                 try:
     89                     r = self.add_tx(
     90                             self.sprinkle(sender),
     91                             self.sprinkle(recipient),
     92                             block.number,
     93                             tx.index,
     94                             block_hash=block.hash,
     95                             tx_hash=tx.hash,
     96                             )
     97                 except KeyError:
     98                     logg.debug('false positive match tx {}'.format(tx.hash))
     99                     continue
    100 
    101                 if r == None:
    102                     continue
    103 
    104                 subjects = r[0]
    105                 objects = r[1]
    106                 
    107                 if self.result_handler != None:
    108                     for account in subjects:
    109                         self.result_handler.register(account)
    110                     for account in objects:
    111                         self.result_handler.register(account)
    112 
    113 
    114     def save(self):
    115         """Save state of all accounts and the salt used for the session to the cache store.
    116         """
    117         for account in self.subjects.values():
    118             self.store.put(account.account, account.serialize())
    119         self.store.put(self.root_key(), self.serialize())
    120 
    121 
    122     def load_account(self, k, label=None):
    123         """Load state for an accounts from a cache store.
    124 
    125         :param k: Account to load, by obfuscated value.
    126         :type k: bytes
    127         :param label: Label to associate with account, for display use.
    128         """
    129         try:
    130             b = self.store.get(k)
    131         except FileNotFoundError:
    132             return None
    133         return Account.from_serialized(b, self.chain_spec, label)
    134 
    135 
    136     def load_subject(self, k, label=None):
    137         """Load state for an account as subject from the cache store.
    138 
    139         A subject will always merge tags with any other subject or object in the same transaction.
    140 
    141         :param k: Account to load, by obfuscated value.
    142         :type k: bytes
    143         :param label: Label to associate with account, for display use.
    144         """
    145         a = self.load_account(k, label)
    146         if a == None:
    147             return False
    148         self.add_subject(a)
    149         return True
    150 
    151 
    152     def load_object(self, k, label=None):
    153         """Load state for an account as object from the cache store.
    154 
    155         An object will only merge tags with other subjects in the same transaction.
    156 
    157         :param k: Account to load, by obfuscated value.
    158         :type k: bytes
    159         :param label: Label to associate with account, for display use.
    160         """
    161         a = self.load_account(k, label)
    162         if a == None:
    163             return False
    164         self.add_object(a)
    165         return True
    166 
    167 
    168     @staticmethod
    169     def load(store, chain_spec, result_handler=None):
    170         """Instantiate new Tainter object with salt stored from previous session.
    171         """
    172         a = Salter(chain_spec)
    173         b = store.get(a.root_key())
    174         c = Tainter.from_serialized(chain_spec, b)
    175         c.store = store
    176         c.result_handler = result_handler
    177         return c
    178