Compare commits

..

3 Commits

Author SHA1 Message Date
Kevin F
d915a2aaed handle unix peer socket file not existing 2022-09-17 01:24:11 -05:00
Kevin F
25e705c0b2 Removed sqlite as dependency 2022-09-17 01:23:27 -05:00
Kevin F
6f3e5aebd9 Removed defunct Onionrusers 2022-09-17 01:23:01 -05:00
6 changed files with 9 additions and 360 deletions

View File

@ -1,9 +0,0 @@
# onionrusers
onionrusers is a small collection of classes for interacting with onionr public keys, such as encrypting messages to them with forward secrecy, interacting with their settings, or else.
## Files
onionrusers.py: OnionrUsers class can be used to encrypt/decrypt messages to a particular Onionr user (incl. forward secrecy), view information about them, and get our friend list.
contactmanager.py: Inheriting from OnionrUsers, ContactManager allows arbitrary information to be associated with an Onionr user.

View File

@ -1,89 +0,0 @@
"""Onionr - Private P2P Communication.
Set more abstract information related to a peer.
Can be thought of as traditional 'contact' system
"""
import os
import ujson as json
import unpaddedbase32
import onionrexceptions
from onionrusers import onionrusers
from onionrutils import bytesconverter, epoch
from utils import identifyhome
from onionrutils import mnemonickeys
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
class ContactManager(onionrusers.OnionrUser):
def __init__(self, publicKey, saveUser=False, recordExpireSeconds=5):
try:
if mnemonickeys.DELIMITER in publicKey:
publicKey = mnemonickeys.get_base32(publicKey)
#publicKey = unpaddedbase32.b32encode(bytesconverter.str_to_bytes(publicKey))
except ValueError:
pass
publicKey = bytesconverter.bytes_to_str(unpaddedbase32.repad(bytesconverter.str_to_bytes(publicKey)))
super(ContactManager, self).__init__(publicKey, saveUser=saveUser)
home = identifyhome.identify_home()
self.dataDir = home + '/contacts/'
self.dataFile = '%s/contacts/%s.json' % (home, publicKey)
self.lastRead = 0
self.recordExpire = recordExpireSeconds
self.data = self._loadData()
self.deleted = False
if not os.path.exists(self.dataDir):
os.mkdir(self.dataDir)
def _writeData(self):
data = json.dumps(self.data)
with open(self.dataFile, 'w') as dataFile:
dataFile.write(data)
def _loadData(self):
self.lastRead = epoch.get_epoch()
retData = {}
if os.path.exists(self.dataFile):
with open(self.dataFile, 'r') as dataFile:
retData = json.loads(dataFile.read())
return retData
def set_info(self, key, value, autoWrite=True):
if self.deleted:
raise onionrexceptions.ContactDeleted
self.data[key] = value
if autoWrite:
self._writeData()
return
def get_info(self, key, forceReload=False):
if self.deleted:
raise onionrexceptions.ContactDeleted
if (epoch.get_epoch() - self.lastRead >= self.recordExpire) or forceReload:
self.data = self._loadData()
try:
return self.data[key]
except KeyError:
return None
def delete_contact(self):
self.deleted = True
if os.path.exists(self.dataFile):
os.remove(self.dataFile)

View File

@ -1,259 +0,0 @@
"""Onionr - Private P2P Communication.
Contains abstractions for interacting with users of Onionr
"""
import sqlite3
import time
import onionrexceptions
from onionrutils import stringvalidators, bytesconverter, epoch
import unpaddedbase32
import nacl.exceptions
from coredb import keydb, dbfiles
import onionrcrypto
from onionrcrypto import getourkeypair
from onionrvalues import DATABASE_LOCK_TIMEOUT
"""
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
def deleteExpiredKeys():
# Fetch the keys we generated for the peer, that are still around
conn = sqlite3.connect(
dbfiles.forward_keys_db, timeout=DATABASE_LOCK_TIMEOUT)
c = conn.cursor()
curTime = epoch.get_epoch()
c.execute("DELETE from myForwardKeys where expire <= ?", (curTime,))
conn.commit()
conn.execute("VACUUM")
conn.close()
return
def deleteTheirExpiredKeys(pubkey):
conn = sqlite3.connect(
dbfiles.user_id_info_db, timeout=DATABASE_LOCK_TIMEOUT)
c = conn.cursor()
# Prepare the insert
command = (pubkey, epoch.get_epoch())
c.execute("DELETE from forwardKeys where peerKey = ? and expire <= ?", command)
conn.commit()
conn.close()
DEFAULT_KEY_EXPIRE = 604800
class OnionrUser:
def __init__(self, publicKey, saveUser=False):
"""
OnionrUser is an abstraction for "users" of the network.
Takes a base32 encoded ed25519 public key, and a bool saveUser
saveUser determines if we should add a user to our peer database or not.
"""
publicKey = unpaddedbase32.repad(
bytesconverter.str_to_bytes(publicKey)).decode()
self.trust = 0
self.publicKey = publicKey
if saveUser and not publicKey == getourkeypair.get_keypair():
try:
keydb.addkeys.add_pub_key(publicKey)
except (AssertionError, ValueError) as _:
pass
self.trust = keydb.userinfo.get_user_info(self.publicKey, 'trust')
return
def setTrust(self, newTrust):
"""Set the peers trust. 0 = not trusted, 1 = friend, 2 = ultimate"""
keydb.userinfo.set_user_info(self.publicKey, 'trust', newTrust)
def isFriend(self):
if keydb.userinfo.get_user_info(self.publicKey, 'trust') == 1:
return True
return False
def getName(self):
retData = 'anonymous'
name = keydb.userinfo.get_user_info(self.publicKey, 'name')
try:
if len(name) > 0:
retData = name
except ValueError:
pass
return retData
def encrypt(self, data):
encrypted = onionrcrypto.encryption.pub_key_encrypt(
data, self.publicKey, encodedData=True)
return encrypted
def decrypt(self, data):
decrypted = onionrcrypto.encryption.pub_key_decrypt(
data, self.publicKey, encodedData=True)
return decrypted
def forwardEncrypt(self, data):
deleteTheirExpiredKeys(self.publicKey)
deleteExpiredKeys()
retData = ''
forwardKey = self._getLatestForwardKey()
if stringvalidators.validate_pub_key(forwardKey[0]):
retData = onionrcrypto.encryption.pub_key_encrypt(
data, forwardKey[0], encodedData=True)
else:
raise onionrexceptions.InvalidPubkey(
"No valid forward secrecy key available for this user")
return (retData, forwardKey[0], forwardKey[1])
def forwardDecrypt(self, encrypted):
retData = ""
for key in self.getGeneratedForwardKeys(False):
try:
retData = onionrcrypto.encryption.pub_key_decrypt(
encrypted, privkey=key[1], encodedData=True)
except nacl.exceptions.CryptoError:
retData = False
else:
break
else:
raise onionrexceptions.DecryptionError(
"Could not decrypt forward secrecy content")
return retData
def _getLatestForwardKey(self):
# Get the latest forward secrecy key for a peer
key = ""
conn = sqlite3.connect(
dbfiles.user_id_info_db, timeout=DATABASE_LOCK_TIMEOUT)
c = conn.cursor()
# TODO: account for keys created at the same time (same epoch)
for row in c.execute(
"SELECT forwardKey, max(EXPIRE) FROM forwardKeys WHERE peerKey = ? ORDER BY expire DESC", # noqa
(self.publicKey,)):
key = (row[0], row[1])
break
conn.commit()
conn.close()
return key
def _getForwardKeys(self):
conn = sqlite3.connect(
dbfiles.user_id_info_db, timeout=DATABASE_LOCK_TIMEOUT)
c = conn.cursor()
keyList = []
for row in c.execute(
"SELECT forwardKey, date FROM forwardKeys WHERE peerKey = ? ORDER BY expire DESC", # noqa
(self.publicKey,)):
keyList.append((row[0], row[1]))
conn.commit()
conn.close()
return list(keyList)
def generateForwardKey(self, expire=DEFAULT_KEY_EXPIRE):
# Generate a forward secrecy key for the peer
conn = sqlite3.connect(
dbfiles.forward_keys_db, timeout=DATABASE_LOCK_TIMEOUT)
c = conn.cursor()
# Prepare the insert
time = epoch.get_epoch()
newKeys = onionrcrypto.generate()
newPub = bytesconverter.bytes_to_str(newKeys[0])
newPriv = bytesconverter.bytes_to_str(newKeys[1])
command = (self.publicKey, newPub, newPriv, time, expire + time)
c.execute("INSERT INTO myForwardKeys VALUES(?, ?, ?, ?, ?);", command)
conn.commit()
conn.close()
return newPub
def getGeneratedForwardKeys(self, genNew=True):
# Fetch the keys we generated for the peer, that are still around
conn = sqlite3.connect(
dbfiles.forward_keys_db, timeout=DATABASE_LOCK_TIMEOUT)
c = conn.cursor()
pubkey = self.publicKey
pubkey = bytesconverter.bytes_to_str(pubkey)
command = (pubkey,)
keyList = [] # list of tuples containing pub, private for peer
for result in c.execute(
"SELECT * FROM myForwardKeys WHERE peer = ?", command):
keyList.append((result[1], result[2]))
if len(keyList) == 0:
if genNew:
self.generateForwardKey()
keyList = self.getGeneratedForwardKeys()
return list(keyList)
def addForwardKey(self, newKey, expire=DEFAULT_KEY_EXPIRE):
newKey = bytesconverter.bytes_to_str(
unpaddedbase32.repad(bytesconverter.str_to_bytes(newKey)))
if not stringvalidators.validate_pub_key(newKey):
# Do not add if something went wrong with the key
raise onionrexceptions.InvalidPubkey(newKey)
conn = sqlite3.connect(
dbfiles.user_id_info_db, timeout=DATABASE_LOCK_TIMEOUT)
c = conn.cursor()
# Get the time we're inserting the key at
timeInsert = epoch.get_epoch()
# Look at our current keys for duplicate key data or time
for entry in self._getForwardKeys():
if entry[0] == newKey:
return False
if entry[1] == timeInsert:
timeInsert += 1
# Sleep if our time is the same to prevent dupe time records
time.sleep(1)
# Add a forward secrecy key for the peer
# Prepare the insert
command = (self.publicKey, newKey, timeInsert, timeInsert + expire)
c.execute("INSERT INTO forwardKeys VALUES(?, ?, ?, ?);", command)
conn.commit()
conn.close()
return True
@classmethod
def list_friends(cls):
friendList = []
for x in keydb.listkeys.list_pub_keys(trust=1):
friendList.append(cls(x))
return list(friendList)

View File

@ -2,7 +2,7 @@
size related utilities
"""
import sqlite3, os
import os
from onionrutils import stringvalidators
"""
This program is free software: you can redistribute it and/or modify

View File

@ -1,6 +1,8 @@
from os.path import exists
from socket import AF_UNIX, SOCK_STREAM, socket
from gossip.peerset import gossip_peer_set
class UnixPeer:
def __init__(self, socket_file):
@ -14,8 +16,12 @@ class UnixPeer:
def get_socket(self, connect_timeout) -> socket:
s = socket(AF_UNIX, SOCK_STREAM)
#s.settimeout(connect_timeout)
s.connect(self.transport_address)
return s
try:
s.connect(self.transport_address)
except FileNotFoundError:
gossip_peer_set.remove(self)
else:
return s
def __hash__(self):
return hash(self.transport_address)