shared quality fixes
This commit is contained in:
parent
27be035e51
commit
6f91ba1b33
106
src/shared.py
106
src/shared.py
|
@ -1,21 +1,28 @@
|
||||||
from __future__ import division
|
"""
|
||||||
|
Some shared functions
|
||||||
|
|
||||||
|
.. deprecated:: 0.6.3
|
||||||
|
Should be moved to different places and this file removed,
|
||||||
|
but it needs refactoring.
|
||||||
|
"""
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
# Libraries.
|
# Libraries.
|
||||||
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import stat
|
import stat
|
||||||
import threading
|
import threading
|
||||||
import hashlib
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
from pyelliptic import arithmetic
|
from pyelliptic import arithmetic
|
||||||
|
|
||||||
# Project imports.
|
# Project imports.
|
||||||
import state
|
|
||||||
import highlevelcrypto
|
import highlevelcrypto
|
||||||
|
import state
|
||||||
|
from addresses import decodeAddress, encodeVarint
|
||||||
from bmconfigparser import BMConfigParser
|
from bmconfigparser import BMConfigParser
|
||||||
from debug import logger
|
from debug import logger
|
||||||
from addresses import decodeAddress, encodeVarint
|
|
||||||
from helper_sql import sqlQuery
|
from helper_sql import sqlQuery
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,6 +63,7 @@ maximumLengthOfTimeToBotherResendingMessages = 0
|
||||||
|
|
||||||
|
|
||||||
def isAddressInMyAddressBook(address):
|
def isAddressInMyAddressBook(address):
|
||||||
|
"""Is address in my addressbook?"""
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
'''select address from addressbook where address=?''',
|
'''select address from addressbook where address=?''',
|
||||||
address)
|
address)
|
||||||
|
@ -64,6 +72,7 @@ def isAddressInMyAddressBook(address):
|
||||||
|
|
||||||
# At this point we should really just have a isAddressInMy(book, address)...
|
# At this point we should really just have a isAddressInMy(book, address)...
|
||||||
def isAddressInMySubscriptionsList(address):
|
def isAddressInMySubscriptionsList(address):
|
||||||
|
"""Am I subscribed to this address?"""
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
'''select * from subscriptions where address=?''',
|
'''select * from subscriptions where address=?''',
|
||||||
str(address))
|
str(address))
|
||||||
|
@ -71,6 +80,7 @@ def isAddressInMySubscriptionsList(address):
|
||||||
|
|
||||||
|
|
||||||
def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
|
def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
|
||||||
|
"""Am I subscribed to this address, is it in my addressbook or whitelist?"""
|
||||||
if isAddressInMyAddressBook(address):
|
if isAddressInMyAddressBook(address):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -90,7 +100,8 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def decodeWalletImportFormat(WIFstring):
|
def decodeWalletImportFormat(WIFstring): # pylint: disable=inconsistent-return-statements
|
||||||
|
"""Convert private key from base58 that's used in the config file to 8-bit binary string"""
|
||||||
fullString = arithmetic.changebase(WIFstring, 58, 256)
|
fullString = arithmetic.changebase(WIFstring, 58, 256)
|
||||||
privkey = fullString[:-4]
|
privkey = fullString[:-4]
|
||||||
if fullString[-4:] != \
|
if fullString[-4:] != \
|
||||||
|
@ -101,7 +112,7 @@ def decodeWalletImportFormat(WIFstring):
|
||||||
' 6 characters of the PRIVATE key: %s',
|
' 6 characters of the PRIVATE key: %s',
|
||||||
str(WIFstring)[:6]
|
str(WIFstring)[:6]
|
||||||
)
|
)
|
||||||
os._exit(0)
|
os._exit(0) # pylint: disable=protected-access
|
||||||
# return ""
|
# return ""
|
||||||
elif privkey[0] == '\x80': # checksum passed
|
elif privkey[0] == '\x80': # checksum passed
|
||||||
return privkey[1:]
|
return privkey[1:]
|
||||||
|
@ -111,10 +122,11 @@ def decodeWalletImportFormat(WIFstring):
|
||||||
' the checksum passed but the key doesn\'t begin with hex 80.'
|
' the checksum passed but the key doesn\'t begin with hex 80.'
|
||||||
' Here is the PRIVATE key: %s', WIFstring
|
' Here is the PRIVATE key: %s', WIFstring
|
||||||
)
|
)
|
||||||
os._exit(0)
|
os._exit(0) # pylint: disable=protected-access
|
||||||
|
|
||||||
|
|
||||||
def reloadMyAddressHashes():
|
def reloadMyAddressHashes():
|
||||||
|
"""Reinitialise runtime data (e.g. encryption objects, address hashes) from the config file"""
|
||||||
logger.debug('reloading keys from keys.dat file')
|
logger.debug('reloading keys from keys.dat file')
|
||||||
myECCryptorObjects.clear()
|
myECCryptorObjects.clear()
|
||||||
myAddressesByHash.clear()
|
myAddressesByHash.clear()
|
||||||
|
@ -128,26 +140,21 @@ def reloadMyAddressHashes():
|
||||||
if isEnabled:
|
if isEnabled:
|
||||||
hasEnabledKeys = True
|
hasEnabledKeys = True
|
||||||
# status
|
# status
|
||||||
_, addressVersionNumber, streamNumber, hash = \
|
addressVersionNumber, streamNumber, hashobj = decodeAddress(addressInKeysFile)[1:]
|
||||||
decodeAddress(addressInKeysFile)
|
|
||||||
if addressVersionNumber in (2, 3, 4):
|
if addressVersionNumber in (2, 3, 4):
|
||||||
# Returns a simple 32 bytes of information encoded
|
# Returns a simple 32 bytes of information encoded
|
||||||
# in 64 Hex characters, or null if there was an error.
|
# in 64 Hex characters, or null if there was an error.
|
||||||
privEncryptionKey = hexlify(decodeWalletImportFormat(
|
privEncryptionKey = hexlify(decodeWalletImportFormat(
|
||||||
BMConfigParser().get(addressInKeysFile, 'privencryptionkey'))
|
BMConfigParser().get(addressInKeysFile, 'privencryptionkey')))
|
||||||
)
|
|
||||||
|
|
||||||
# It is 32 bytes encoded as 64 hex characters
|
# It is 32 bytes encoded as 64 hex characters
|
||||||
if len(privEncryptionKey) == 64:
|
if len(privEncryptionKey) == 64:
|
||||||
myECCryptorObjects[hash] = \
|
myECCryptorObjects[hashobj] = \
|
||||||
highlevelcrypto.makeCryptor(privEncryptionKey)
|
highlevelcrypto.makeCryptor(privEncryptionKey)
|
||||||
myAddressesByHash[hash] = addressInKeysFile
|
myAddressesByHash[hashobj] = addressInKeysFile
|
||||||
tag = hashlib.sha512(hashlib.sha512(
|
tag = hashlib.sha512(hashlib.sha512(
|
||||||
encodeVarint(addressVersionNumber) +
|
encodeVarint(addressVersionNumber) +
|
||||||
encodeVarint(streamNumber) + hash).digest()
|
encodeVarint(streamNumber) + hashobj).digest()).digest()[32:]
|
||||||
).digest()[32:]
|
|
||||||
myAddressesByTag[tag] = addressInKeysFile
|
myAddressesByTag[tag] = addressInKeysFile
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
'Error in reloadMyAddressHashes: Can\'t handle'
|
'Error in reloadMyAddressHashes: Can\'t handle'
|
||||||
|
@ -159,6 +166,7 @@ def reloadMyAddressHashes():
|
||||||
|
|
||||||
|
|
||||||
def reloadBroadcastSendersForWhichImWatching():
|
def reloadBroadcastSendersForWhichImWatching():
|
||||||
|
"""Reinitialise runtime data for the broadcasts I'm subscribed to from the config file"""
|
||||||
broadcastSendersForWhichImWatching.clear()
|
broadcastSendersForWhichImWatching.clear()
|
||||||
MyECSubscriptionCryptorObjects.clear()
|
MyECSubscriptionCryptorObjects.clear()
|
||||||
queryreturn = sqlQuery('SELECT address FROM subscriptions where enabled=1')
|
queryreturn = sqlQuery('SELECT address FROM subscriptions where enabled=1')
|
||||||
|
@ -166,9 +174,9 @@ def reloadBroadcastSendersForWhichImWatching():
|
||||||
for row in queryreturn:
|
for row in queryreturn:
|
||||||
address, = row
|
address, = row
|
||||||
# status
|
# status
|
||||||
_, addressVersionNumber, streamNumber, hash = decodeAddress(address)
|
addressVersionNumber, streamNumber, hashobj = decodeAddress(address)[1:]
|
||||||
if addressVersionNumber == 2:
|
if addressVersionNumber == 2:
|
||||||
broadcastSendersForWhichImWatching[hash] = 0
|
broadcastSendersForWhichImWatching[hashobj] = 0
|
||||||
# Now, for all addresses, even version 2 addresses,
|
# Now, for all addresses, even version 2 addresses,
|
||||||
# we should create Cryptor objects in a dictionary which we will
|
# we should create Cryptor objects in a dictionary which we will
|
||||||
# use to attempt to decrypt encrypted broadcast messages.
|
# use to attempt to decrypt encrypted broadcast messages.
|
||||||
|
@ -176,14 +184,14 @@ def reloadBroadcastSendersForWhichImWatching():
|
||||||
if addressVersionNumber <= 3:
|
if addressVersionNumber <= 3:
|
||||||
privEncryptionKey = hashlib.sha512(
|
privEncryptionKey = hashlib.sha512(
|
||||||
encodeVarint(addressVersionNumber) +
|
encodeVarint(addressVersionNumber) +
|
||||||
encodeVarint(streamNumber) + hash
|
encodeVarint(streamNumber) + hashobj
|
||||||
).digest()[:32]
|
).digest()[:32]
|
||||||
MyECSubscriptionCryptorObjects[hash] = \
|
MyECSubscriptionCryptorObjects[hashobj] = \
|
||||||
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
|
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
|
||||||
else:
|
else:
|
||||||
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(
|
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(
|
||||||
encodeVarint(addressVersionNumber) +
|
encodeVarint(addressVersionNumber) +
|
||||||
encodeVarint(streamNumber) + hash
|
encodeVarint(streamNumber) + hashobj
|
||||||
).digest()).digest()
|
).digest()).digest()
|
||||||
tag = doubleHashOfAddressData[32:]
|
tag = doubleHashOfAddressData[32:]
|
||||||
privEncryptionKey = doubleHashOfAddressData[:32]
|
privEncryptionKey = doubleHashOfAddressData[:32]
|
||||||
|
@ -192,21 +200,22 @@ def reloadBroadcastSendersForWhichImWatching():
|
||||||
|
|
||||||
|
|
||||||
def fixPotentiallyInvalidUTF8Data(text):
|
def fixPotentiallyInvalidUTF8Data(text):
|
||||||
|
"""Sanitise invalid UTF-8 strings"""
|
||||||
try:
|
try:
|
||||||
unicode(text, 'utf-8')
|
unicode(text, 'utf-8')
|
||||||
return text
|
return text
|
||||||
except:
|
except:
|
||||||
return 'Part of the message is corrupt. The message cannot be' \
|
return 'Part of the message is corrupt. The message cannot be' \
|
||||||
' displayed the normal way.\n\n' + repr(text)
|
' displayed the normal way.\n\n' + repr(text)
|
||||||
|
|
||||||
|
|
||||||
# Checks sensitive file permissions for inappropriate umask
|
|
||||||
# during keys.dat creation. (Or unwise subsequent chmod.)
|
|
||||||
#
|
|
||||||
# Returns true iff file appears to have appropriate permissions.
|
|
||||||
def checkSensitiveFilePermissions(filename):
|
def checkSensitiveFilePermissions(filename):
|
||||||
|
"""
|
||||||
|
:param str filename: path to the file
|
||||||
|
:return: True if file appears to have appropriate permissions.
|
||||||
|
"""
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
# TODO: This might deserve extra checks by someone familiar with
|
# .. todo:: This might deserve extra checks by someone familiar with
|
||||||
# Windows systems.
|
# Windows systems.
|
||||||
return True
|
return True
|
||||||
elif sys.platform[:7] == 'freebsd':
|
elif sys.platform[:7] == 'freebsd':
|
||||||
|
@ -214,30 +223,30 @@ def checkSensitiveFilePermissions(filename):
|
||||||
present_permissions = os.stat(filename)[0]
|
present_permissions = os.stat(filename)[0]
|
||||||
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
|
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
|
||||||
return present_permissions & disallowed_permissions == 0
|
return present_permissions & disallowed_permissions == 0
|
||||||
else:
|
try:
|
||||||
try:
|
# Skip known problems for non-Win32 filesystems
|
||||||
# Skip known problems for non-Win32 filesystems
|
# without POSIX permissions.
|
||||||
# without POSIX permissions.
|
fstype = subprocess.check_output(
|
||||||
fstype = subprocess.check_output(
|
'stat -f -c "%%T" %s' % (filename),
|
||||||
'stat -f -c "%%T" %s' % (filename),
|
shell=True,
|
||||||
shell=True,
|
stderr=subprocess.STDOUT
|
||||||
stderr=subprocess.STDOUT
|
)
|
||||||
)
|
if 'fuseblk' in fstype:
|
||||||
if 'fuseblk' in fstype:
|
logger.info(
|
||||||
logger.info(
|
'Skipping file permissions check for %s.'
|
||||||
'Skipping file permissions check for %s.'
|
' Filesystem fuseblk detected.', filename)
|
||||||
' Filesystem fuseblk detected.', filename)
|
return True
|
||||||
return True
|
except:
|
||||||
except:
|
# Swallow exception here, but we might run into trouble later!
|
||||||
# Swallow exception here, but we might run into trouble later!
|
logger.error('Could not determine filesystem type. %s', filename)
|
||||||
logger.error('Could not determine filesystem type. %s', filename)
|
present_permissions = os.stat(filename)[0]
|
||||||
present_permissions = os.stat(filename)[0]
|
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
|
||||||
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
|
return present_permissions & disallowed_permissions == 0
|
||||||
return present_permissions & disallowed_permissions == 0
|
|
||||||
|
|
||||||
|
|
||||||
# Fixes permissions on a sensitive file.
|
# Fixes permissions on a sensitive file.
|
||||||
def fixSensitiveFilePermissions(filename, hasEnabledKeys):
|
def fixSensitiveFilePermissions(filename, hasEnabledKeys):
|
||||||
|
"""Try to change file permissions to be more restrictive"""
|
||||||
if hasEnabledKeys:
|
if hasEnabledKeys:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'Keyfile had insecure permissions, and there were enabled'
|
'Keyfile had insecure permissions, and there were enabled'
|
||||||
|
@ -262,6 +271,7 @@ def fixSensitiveFilePermissions(filename, hasEnabledKeys):
|
||||||
|
|
||||||
|
|
||||||
def openKeysFile():
|
def openKeysFile():
|
||||||
|
"""Open keys file with an external editor"""
|
||||||
if 'linux' in sys.platform:
|
if 'linux' in sys.platform:
|
||||||
subprocess.call(["xdg-open", state.appdata + 'keys.dat'])
|
subprocess.call(["xdg-open", state.appdata + 'keys.dat'])
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user