taint

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

account.py (4717B)


      1 # standard imports
      2 import os
      3 
      4 # local imports
      5 from .tag import Tag
      6 from .crypto import Salter
      7 
      8 
      9 class Account(Salter):
     10     """Represents a single account in the cache.
     11 
     12     An account is a blockchain address associated with one or more tags. It provides methods to compare addresses, view tags, merge tags from two accounts, as well as serializing and deserializing for storage.
     13 
     14     The provided chain_spec will be used to generate the salt to obfuscate the address in the cache.
     15 
     16     :param chain_spec: The chain spec the address is valid for
     17     :type chain_spec: chainlib.chain.ChainSpec
     18     :param account: The account address
     19     :type account: bytes
     20     :param label: Human-readable label for account used for logging
     21     :type label: str
     22     :param tags: Tags to associate account with 
     23     :type tags: list of bytes
     24     :param create_digest: If set to false, account obfuscation will be omitted
     25     :type create_digest: boolean
     26     """
     27     def __init__(self, chain_spec, account, label=None, tags=[], create_digest=True):
     28         super(Account, self).__init__(chain_spec)
     29 
     30         if label == None:
     31             label = str(account)
     32         self.label = label
     33         self.account_src = None
     34         self.create_digest = create_digest
     35         if self.create_digest:
     36             self.account_src = account
     37             self.account = self.sprinkle(self.account_src)
     38         else:
     39             self.account = account
     40         self.tags = Tag()
     41         for tag in tags:
     42             self.tags.create(tag)
     43 
     44 
     45     def tag(self, value):
     46         """Add a tag to the account.
     47 
     48         :param value: Literal tag value
     49         :type value: bytes
     50         """
     51         self.tags.create(value)
     52 
     53 
     54     def sum(self):
     55         """Get the sum of all the tags for the account.
     56 
     57         :rtype: bytes
     58         :returns: Tag sum
     59         """
     60         return self.tags.get()
     61 
     62 
     63     def connect(self, account):
     64         """Associate two accounts with each other. After this operation, both accounts will have the same tag sum.
     65 
     66         :param account: Account to merge with
     67         :type account: taint.account.Account
     68         """
     69         if not isinstance(account, Account):
     70             raise TypeError('account must be type taint.account.Account')
     71         self.tags.merge(account.tags)
     72 
     73 
     74     def is_same(self, account):
     75         """Compare two accounts.
     76 
     77         This will not compare the tag state of the accounts.
     78 
     79         :param account: Account to compare
     80         :type account: taint.account.Account
     81         :rtype: boolean
     82         :return: True if the account effectively represents the same underlying blockchain address
     83         """
     84         if not isinstance(account, Account):
     85             raise TypeError('account must be type crypto_account_cache.account.Account')
     86         return self.account == account.account
     87 
     88 
     89 #    def __eq__(self, account):
     90 #       return self.is_same(account)
     91 
     92 
     93     def is_account(self, address):
     94         """Compare blockchain address to address represented by account object.
     95 
     96         If account obfuscation is being used, the input value has to match the unobfuscated value.
     97 
     98         :param address: Address to compare with
     99         :type address: bytes
    100         :rtype: boolean
    101         :return: True on address match
    102         """
    103         if self.create_digest:
    104             return self.sprinkle(address) == self.account
    105         return address == self.account
    106 
    107 
    108     def serialize(self):
    109         """Serialize account object for storage.
    110 
    111         Account serialization consists of serialization of the account's tags, followed by the serialization of the underlying blockchain address.
    112 
    113         :rtype: bytes
    114         :return: Serialized data
    115         """
    116         b = self.tags.serialize() + self.account
    117         return b
    118 
    119 
    120     @staticmethod
    121     def from_serialized(b, chain_spec, label=None):
    122         """Deserialize account object from storage.
    123 
    124         BUG: deserialization may break if account is not obfuscated, since the address may not end on 32 byte boundary
    125 
    126         :param chain_spec: Chain spec to instantiate account for
    127         :type chain_spec: chainlib.chain.ChainSpec
    128         :param label: Human-readable label for logging
    129         :type label: str
    130         :rtype: taint.account.Account
    131         :returns: Deserialized account
    132         """
    133         l = len(b)
    134         if l % 32 > 0:
    135             raise ValueError('invalid data length; remainder {} of 32'.format(l % 32))
    136         if l < 64: 
    137             raise ValueError('invalid data length; expected minimum 64, got {}'.format(l))
    138 
    139         a = Account(chain_spec, b[-32:], label=label, create_digest=False)
    140         a.tags.deserialize(b[:-32])
    141         return a
    142 
    143 
    144     def __str__(self):
    145         return '{} [{}]'.format(self.account.hex(), str(self.tags))