/ src / shared.py.bak
shared.py.bak
  1  """
  2  Some shared functions
  3  
  4  .. deprecated:: 0.6.3
  5    Should be moved to different places and this file removed,
  6    but it needs refactoring.
  7  """
  8  from __future__ import division
  9  
 10  # Libraries.
 11  import hashlib
 12  import os
 13  import stat
 14  import subprocess  # nosec B404
 15  import sys
 16  from binascii import hexlify
 17  
 18  from six.moves.reprlib import repr
 19  
 20  # Project imports.
 21  import highlevelcrypto
 22  import state
 23  from addresses import decodeAddress, encodeVarint
 24  from bmconfigparser import config
 25  from debug import logger
 26  from helper_sql import sqlQuery
 27  
 28  myECCryptorObjects = {}
 29  MyECSubscriptionCryptorObjects = {}
 30  # The key in this dictionary is the RIPE hash which is encoded
 31  # in an address and value is the address itself.
 32  myAddressesByHash = {}
 33  # The key in this dictionary is the tag generated from the address.
 34  myAddressesByTag = {}
 35  broadcastSendersForWhichImWatching = {}
 36  
 37  
 38  def isAddressInMyAddressBook(address):
 39      """Is address in my addressbook?"""
 40      queryreturn = sqlQuery(
 41          '''select address from addressbook where address=?''',
 42          address)
 43      return queryreturn != []
 44  
 45  
 46  # At this point we should really just have a isAddressInMy(book, address)...
 47  def isAddressInMySubscriptionsList(address):
 48      """Am I subscribed to this address?"""
 49      queryreturn = sqlQuery(
 50          '''select * from subscriptions where address=?''',
 51          str(address))
 52      return queryreturn != []
 53  
 54  
 55  def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
 56      """
 57      Am I subscribed to this address, is it in my addressbook or whitelist?
 58      """
 59      if isAddressInMyAddressBook(address):
 60          return True
 61  
 62      queryreturn = sqlQuery(
 63          '''SELECT address FROM whitelist where address=?'''
 64          ''' and enabled = '1' ''',
 65          address)
 66      if queryreturn != []:
 67          return True
 68  
 69      queryreturn = sqlQuery(
 70          '''select address from subscriptions where address=?'''
 71          ''' and enabled = '1' ''',
 72          address)
 73      if queryreturn != []:
 74          return True
 75      return False
 76  
 77  
 78  def reloadMyAddressHashes():
 79      """Reload keys for user's addresses from the config file"""
 80      logger.debug('reloading keys from keys.dat file')
 81      myECCryptorObjects.clear()
 82      myAddressesByHash.clear()
 83      myAddressesByTag.clear()
 84      # myPrivateKeys.clear()
 85  
 86      keyfileSecure = checkSensitiveFilePermissions(os.path.join(
 87          state.appdata, 'keys.dat'))
 88      hasEnabledKeys = False
 89      for addressInKeysFile in config.addresses():
 90          if not config.getboolean(addressInKeysFile, 'enabled'):
 91              continue
 92  
 93          hasEnabledKeys = True
 94  
 95          addressVersionNumber, streamNumber, hashobj = decodeAddress(
 96              addressInKeysFile)[1:]
 97          if addressVersionNumber not in (2, 3, 4):
 98              logger.error(
 99                  'Error in reloadMyAddressHashes: Can\'t handle'
100                  ' address versions other than 2, 3, or 4.')
101              continue
102  
103          # Returns a simple 32 bytes of information encoded in 64 Hex characters
104          try:
105              privEncryptionKey = hexlify(
106                  highlevelcrypto.decodeWalletImportFormat(config.get(
107                      addressInKeysFile, 'privencryptionkey').encode()
108                  ))
109          except ValueError:
110              logger.error(
111                  'Error in reloadMyAddressHashes: failed to decode'
112                  ' one of the private keys for address %s', addressInKeysFile)
113              continue
114          # It is 32 bytes encoded as 64 hex characters
115          if len(privEncryptionKey) == 64:
116              myECCryptorObjects[hashobj] = \
117                  highlevelcrypto.makeCryptor(privEncryptionKey)
118              myAddressesByHash[hashobj] = addressInKeysFile
119              tag = highlevelcrypto.double_sha512(
120                  encodeVarint(addressVersionNumber)
121                  + encodeVarint(streamNumber) + hashobj)[32:]
122              myAddressesByTag[tag] = addressInKeysFile
123  
124      if not keyfileSecure:
125          fixSensitiveFilePermissions(os.path.join(
126              state.appdata, 'keys.dat'), hasEnabledKeys)
127  
128  
129  def reloadBroadcastSendersForWhichImWatching():
130      """
131      Reinitialize runtime data for the broadcasts I'm subscribed to
132      from the config file
133      """
134      broadcastSendersForWhichImWatching.clear()
135      MyECSubscriptionCryptorObjects.clear()
136      queryreturn = sqlQuery('SELECT address FROM subscriptions where enabled=1')
137      logger.debug('reloading subscriptions...')
138      for row in queryreturn:
139          address, = row
140          # status
141          addressVersionNumber, streamNumber, hashobj = decodeAddress(address)[1:]
142          if addressVersionNumber == 2:
143              broadcastSendersForWhichImWatching[hashobj] = 0
144          # Now, for all addresses, even version 2 addresses,
145          # we should create Cryptor objects in a dictionary which we will
146          # use to attempt to decrypt encrypted broadcast messages.
147  
148          if addressVersionNumber <= 3:
149              privEncryptionKey = hashlib.sha512(
150                  encodeVarint(addressVersionNumber)
151                  + encodeVarint(streamNumber) + hashobj
152              ).digest()[:32]
153              MyECSubscriptionCryptorObjects[hashobj] = \
154                  highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
155          else:
156              doubleHashOfAddressData = highlevelcrypto.double_sha512(
157                  encodeVarint(addressVersionNumber)
158                  + encodeVarint(streamNumber) + hashobj
159              )
160              tag = doubleHashOfAddressData[32:]
161              privEncryptionKey = doubleHashOfAddressData[:32]
162              MyECSubscriptionCryptorObjects[tag] = \
163                  highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
164  
165  
166  def fixPotentiallyInvalidUTF8Data(text):
167      """Sanitise invalid UTF-8 strings"""
168      try:
169          text.decode('utf-8')
170          return text
171      except UnicodeDecodeError:
172          return 'Part of the message is corrupt. The message cannot be' \
173              ' displayed the normal way.\n\n' + repr(text)
174  
175  
176  def checkSensitiveFilePermissions(filename):
177      """
178      :param str filename: path to the file
179      :return: True if file appears to have appropriate permissions.
180      """
181      if sys.platform == 'win32':
182          # .. todo:: This might deserve extra checks by someone familiar with
183          # Windows systems.
184          return True
185      elif sys.platform[:7] == 'freebsd':
186          # FreeBSD file systems are the same as major Linux file systems
187          present_permissions = os.stat(filename)[0]
188          disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
189          return present_permissions & disallowed_permissions == 0
190      try:
191          # Skip known problems for non-Win32 filesystems
192          # without POSIX permissions.
193          fstype = subprocess.check_output(
194              ['/usr/bin/stat', '-f', '-c', '%T', filename],
195              stderr=subprocess.STDOUT
196          )  # nosec B603
197          if 'fuseblk' in fstype:
198              logger.info(
199                  'Skipping file permissions check for %s.'
200                  ' Filesystem fuseblk detected.', filename)
201              return True
202      except:  # noqa:E722
203          # Swallow exception here, but we might run into trouble later!
204          logger.error('Could not determine filesystem type. %s', filename)
205      present_permissions = os.stat(filename)[0]
206      disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
207      return present_permissions & disallowed_permissions == 0
208  
209  
210  # Fixes permissions on a sensitive file.
211  def fixSensitiveFilePermissions(filename, hasEnabledKeys):
212      """Try to change file permissions to be more restrictive"""
213      if hasEnabledKeys:
214          logger.warning(
215              'Keyfile had insecure permissions, and there were enabled'
216              ' keys. The truly paranoid should stop using them immediately.')
217      else:
218          logger.warning(
219              'Keyfile had insecure permissions, but there were no enabled keys.'
220          )
221      try:
222          present_permissions = os.stat(filename)[0]
223          disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
224          allowed_permissions = ((1 << 32) - 1) ^ disallowed_permissions
225          new_permissions = (
226              allowed_permissions & present_permissions)
227          os.chmod(filename, new_permissions)
228  
229          logger.info('Keyfile permissions automatically fixed.')
230  
231      except Exception:
232          logger.exception('Keyfile permissions could not be fixed.')
233          raise