This repository has been archived on 2024-12-05. You can view files and clone it, but cannot push or open issues or pull requests.
PyBitmessage-2024-12-05/src/shared.py

234 lines
8.1 KiB
Python
Raw Normal View History

"""
2019-10-22 16:23:53 +02:00
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
2015-01-21 18:38:25 +01:00
# Libraries.
2019-10-22 16:23:53 +02:00
import hashlib
import os
import stat
2024-03-04 16:37:03 +01:00
import subprocess # nosec B404
import sys
2016-03-23 23:26:57 +01:00
from binascii import hexlify
# Project imports.
import highlevelcrypto
2019-10-22 16:23:53 +02:00
import state
from addresses import decodeAddress, encodeVarint
from bmconfigparser import config
2017-09-21 17:24:51 +02:00
from debug import logger
from helper_sql import sqlQuery
2017-09-21 17:24:51 +02:00
2013-05-02 17:53:54 +02:00
myECCryptorObjects = {}
MyECSubscriptionCryptorObjects = {}
2017-09-21 17:24:51 +02:00
# The key in this dictionary is the RIPE hash which is encoded
# in an address and value is the address itself.
myAddressesByHash = {}
# The key in this dictionary is the tag generated from the address.
myAddressesByTag = {}
2013-05-02 17:53:54 +02:00
broadcastSendersForWhichImWatching = {}
2017-09-21 17:24:51 +02:00
2013-05-02 17:53:54 +02:00
def isAddressInMyAddressBook(address):
2019-10-22 16:23:53 +02:00
"""Is address in my addressbook?"""
queryreturn = sqlQuery(
'''select address from addressbook where address=?''',
address)
2013-05-02 17:53:54 +02:00
return queryreturn != []
2017-09-21 17:24:51 +02:00
# At this point we should really just have a isAddressInMy(book, address)...
def isAddressInMySubscriptionsList(address):
2019-10-22 16:23:53 +02:00
"""Am I subscribed to this address?"""
queryreturn = sqlQuery(
'''select * from subscriptions where address=?''',
str(address))
return queryreturn != []
2013-06-14 04:03:03 +02:00
2017-09-21 17:24:51 +02:00
2013-05-02 17:53:54 +02:00
def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
"""
Am I subscribed to this address, is it in my addressbook or whitelist?
"""
2013-05-02 17:53:54 +02:00
if isAddressInMyAddressBook(address):
return True
2017-09-21 17:24:51 +02:00
queryreturn = sqlQuery(
'''SELECT address FROM whitelist where address=?'''
''' and enabled = '1' ''',
address)
if queryreturn != []:
2013-05-02 17:53:54 +02:00
return True
queryreturn = sqlQuery(
2017-09-21 17:24:51 +02:00
'''select address from subscriptions where address=?'''
''' and enabled = '1' ''',
address)
2017-09-21 17:24:51 +02:00
if queryreturn != []:
2013-05-02 17:53:54 +02:00
return True
return False
2017-09-21 17:24:51 +02:00
2013-05-02 17:53:54 +02:00
def reloadMyAddressHashes():
"""Reload keys for user's addresses from the config file"""
logger.debug('reloading keys from keys.dat file')
2013-05-02 17:53:54 +02:00
myECCryptorObjects.clear()
myAddressesByHash.clear()
2013-09-15 03:06:26 +02:00
myAddressesByTag.clear()
2017-09-21 17:24:51 +02:00
# myPrivateKeys.clear()
keyfileSecure = checkSensitiveFilePermissions(os.path.join(
state.appdata, 'keys.dat'))
hasEnabledKeys = False
for addressInKeysFile in config.addresses():
isEnabled = config.getboolean(addressInKeysFile, 'enabled')
if not isEnabled:
continue
hasEnabledKeys = True
addressVersionNumber, streamNumber, hashobj = decodeAddress(
addressInKeysFile)[1:]
if addressVersionNumber not in (2, 3, 4):
logger.error(
'Error in reloadMyAddressHashes: Can\'t handle'
' address versions other than 2, 3, or 4.')
continue
# Returns a simple 32 bytes of information encoded in 64 Hex characters
try:
privEncryptionKey = hexlify(
highlevelcrypto.decodeWalletImportFormat(
config.get(addressInKeysFile, 'privencryptionkey')
))
except ValueError:
logger.error(
'Error in reloadMyAddressHashes: failed to decode'
' one of the private keys for address %s', addressInKeysFile)
continue
# It is 32 bytes encoded as 64 hex characters
if len(privEncryptionKey) == 64:
myECCryptorObjects[hashobj] = \
highlevelcrypto.makeCryptor(privEncryptionKey)
myAddressesByHash[hashobj] = addressInKeysFile
tag = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + hashobj).digest()).digest()[32:]
myAddressesByTag[tag] = addressInKeysFile
2013-06-27 12:44:49 +02:00
if not keyfileSecure:
fixSensitiveFilePermissions(os.path.join(
state.appdata, 'keys.dat'), hasEnabledKeys)
2013-05-02 17:53:54 +02:00
2017-09-21 17:24:51 +02:00
2013-05-02 17:53:54 +02:00
def reloadBroadcastSendersForWhichImWatching():
"""
Reinitialize runtime data for the broadcasts I'm subscribed to
from the config file
"""
2013-05-02 17:53:54 +02:00
broadcastSendersForWhichImWatching.clear()
MyECSubscriptionCryptorObjects.clear()
queryreturn = sqlQuery('SELECT address FROM subscriptions where enabled=1')
2013-09-15 03:06:26 +02:00
logger.debug('reloading subscriptions...')
2013-05-02 17:53:54 +02:00
for row in queryreturn:
address, = row
2018-03-22 12:48:07 +01:00
# status
2019-10-22 16:23:53 +02:00
addressVersionNumber, streamNumber, hashobj = decodeAddress(address)[1:]
2013-05-02 17:53:54 +02:00
if addressVersionNumber == 2:
2019-10-22 16:23:53 +02:00
broadcastSendersForWhichImWatching[hashobj] = 0
2017-09-21 17:24:51 +02:00
# Now, for all addresses, even version 2 addresses,
# we should create Cryptor objects in a dictionary which we will
# use to attempt to decrypt encrypted broadcast messages.
2013-09-15 03:06:26 +02:00
if addressVersionNumber <= 3:
2017-09-21 17:24:51 +02:00
privEncryptionKey = hashlib.sha512(
encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + hashobj
2017-09-21 17:24:51 +02:00
).digest()[:32]
2019-10-22 16:23:53 +02:00
MyECSubscriptionCryptorObjects[hashobj] = \
2017-09-21 17:24:51 +02:00
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
2013-09-15 03:06:26 +02:00
else:
2017-09-21 17:24:51 +02:00
doubleHashOfAddressData = hashlib.sha512(hashlib.sha512(
encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + hashobj
2017-09-21 17:24:51 +02:00
).digest()).digest()
2013-09-15 03:06:26 +02:00
tag = doubleHashOfAddressData[32:]
privEncryptionKey = doubleHashOfAddressData[:32]
2017-09-21 17:24:51 +02:00
MyECSubscriptionCryptorObjects[tag] = \
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
2013-05-02 17:53:54 +02:00
def fixPotentiallyInvalidUTF8Data(text):
2019-10-22 16:23:53 +02:00
"""Sanitise invalid UTF-8 strings"""
try:
text.decode('utf-8')
return text
except UnicodeDecodeError:
2017-09-21 17:24:51 +02:00
return 'Part of the message is corrupt. The message cannot be' \
2019-10-22 16:23:53 +02:00
' displayed the normal way.\n\n' + repr(text)
2017-09-21 17:24:51 +02:00
def checkSensitiveFilePermissions(filename):
2019-10-22 16:23:53 +02:00
"""
:param str filename: path to the file
:return: True if file appears to have appropriate permissions.
"""
if sys.platform == 'win32':
2019-10-22 16:23:53 +02:00
# .. todo:: This might deserve extra checks by someone familiar with
# Windows systems.
2013-06-27 12:44:49 +02:00
return True
2013-11-29 01:20:16 +01:00
elif sys.platform[:7] == 'freebsd':
# FreeBSD file systems are the same as major Linux file systems
present_permissions = os.stat(filename)[0]
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
return present_permissions & disallowed_permissions == 0
2019-10-22 16:23:53 +02:00
try:
# Skip known problems for non-Win32 filesystems
# without POSIX permissions.
fstype = subprocess.check_output(
2024-02-29 18:48:54 +01:00
['/usr/bin/stat', '-f', '-c', '%T', filename],
2019-10-22 16:23:53 +02:00
stderr=subprocess.STDOUT
2024-02-29 18:48:54 +01:00
) # nosec B603
2019-10-22 16:23:53 +02:00
if 'fuseblk' in fstype:
logger.info(
'Skipping file permissions check for %s.'
' Filesystem fuseblk detected.', filename)
return True
except: # noqa:E722
2019-10-22 16:23:53 +02:00
# Swallow exception here, but we might run into trouble later!
logger.error('Could not determine filesystem type. %s', filename)
present_permissions = os.stat(filename)[0]
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
return present_permissions & disallowed_permissions == 0
2017-09-21 17:24:51 +02:00
# Fixes permissions on a sensitive file.
2013-06-27 12:44:49 +02:00
def fixSensitiveFilePermissions(filename, hasEnabledKeys):
2019-10-22 16:23:53 +02:00
"""Try to change file permissions to be more restrictive"""
2013-06-27 12:44:49 +02:00
if hasEnabledKeys:
2017-09-21 17:24:51 +02:00
logger.warning(
'Keyfile had insecure permissions, and there were enabled'
' keys. The truly paranoid should stop using them immediately.')
2013-06-27 12:44:49 +02:00
else:
2017-09-21 17:24:51 +02:00
logger.warning(
'Keyfile had insecure permissions, but there were no enabled keys.'
)
2013-06-27 12:44:49 +02:00
try:
present_permissions = os.stat(filename)[0]
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
2017-09-21 17:24:51 +02:00
allowed_permissions = ((1 << 32) - 1) ^ disallowed_permissions
2013-06-27 12:44:49 +02:00
new_permissions = (
allowed_permissions & present_permissions)
os.chmod(filename, new_permissions)
logger.info('Keyfile permissions automatically fixed.')
2017-09-21 17:24:51 +02:00
except Exception:
logger.exception('Keyfile permissions could not be fixed.')
2013-06-27 12:44:49 +02:00
raise