added onion service key storage database to overcome limitations in normal torrc-based system

This commit is contained in:
Kevin Froman 2021-02-07 07:25:39 +00:00
parent ad103ee8b0
commit 18d4a87973
8 changed files with 188 additions and 6 deletions

View File

@ -0,0 +1,56 @@
from typing import NamedTuple
from base64 import b32decode, b64decode
from msgpack import packb, unpackb
from safedb import SafeDB
from utils.identifyhome import identify_home
from .servicecontrol import create_new_service, restore_service
ONION_KEY_DATABASE_FILE = identify_home() + "torgossip-onion-address-keys.db"
class OnionServiceTarget(NamedTuple):
virtual_port: int
unix_socket_path: str
def load_services(controller):
db = SafeDB(ONION_KEY_DATABASE_FILE, protected=True)
keys = db.keys()
if not keys:
db.close()
raise ValueError("No addresses to restore")
while keys:
# Not most pythonic but reduces mem usage as it runs
key = keys.pop()
if len(len) > 3:
try:
service = unpackb(db.get(key))
restore_service(
controller, service['k'], service['p'],
unix_socket=service['s'])
except Exception as _: # noqa
db.close()
raise
db.close()
def run_new_and_store_service(controller, target: OnionServiceTarget) -> bytes:
address, private_key = create_new_service(
controller, target.virtual_port, target.unix_socket_path)
db = SafeDB(ONION_KEY_DATABASE_FILE, protected=True)
service_info = {
'k': b64decode(private_key),
's': target.unix_socket_path,
'p': target.virtual_port}
decoded_address = b32decode(address.upper())
db.put(decoded_address, packb(service_info))
db.close()
return decoded_address

View File

@ -0,0 +1,74 @@
"""Onionr - Private P2P Communication.
Permanent onion service addresses without manual torrc
"""
from typing import TYPE_CHECKING
import base64
if TYPE_CHECKING:
from stem.control import Controller
from utils import identifyhome
"""
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 _add_ephemeral_service(
controller: 'Controller', virtual_port, target, key_content=None):
key_type = "ED25519-V3"
if not key_content:
key_type = "NEW"
hs = controller.create_ephemeral_hidden_service(
{virtual_port: target},
key_type=key_type,
key_content=key_content,
await_publication=True,
detached=True
)
return (hs.service_id, hs.private_key)
def create_new_service(
controller: 'Controller',
virtual_port: int,
unix_socket: str = None,
bind_location: str = None) -> bytes:
target = bind_location
if unix_socket and bind_location or (not unix_socket and not bind_location):
raise ValueError("Must pick unix socket or ip:port, and not both")
if unix_socket:
target = unix_socket
if not unix_socket.startswith("unix:"):
target = "unix:" + target
return _add_ephemeral_service(
controller, virtual_port, target)
def restore_service(
controller: 'Controller',
key: bytes,
virtual_port: int,
unix_socket: str = None,
bind_location: str = None):
if unix_socket and bind_location or (not unix_socket and not bind_location):
raise ValueError("Must pick unix socket or ip:port, and not both")
key = base64.b64encode(key)
target = unix_socket
if bind_location:
target = bind_location
return _add_ephemeral_service(controller, virtual_port, target, key)

View File

@ -0,0 +1,48 @@
"""Onionr - Private P2P Communication.
Detect if onion service has been online recently
"""
from typing import TYPE_CHECKING, Union
from base64 import b32encode
if TYPE_CHECKING:
from stem.control import Controller
from stem import DescriptorUnavailable, ProtocolError
"""
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 service_online_recently(
tor_controller: 'Controller',
onion_address: Union[str, bytes]) -> bool:
"""Detect if a .onion service is/recently online by getting descriptor
Does not detect if it is an Onionr node or actually has any TCP service
"""
if isinstance(onion_address, bytes):
# If address is "compressed"
# (raw bytes + no onion extension), restore it to b32 form
onion_address = b32encode(
onion_address).lower().decode('utf-8') + '.onion'
try:
tor_controller.get_hidden_service_descriptor(onion_address)
except DescriptorUnavailable:
return False
except ProtocolError:
raise ProtocolError(
onion_address + " is likely malformed. Or stem/tor malfunctioned")
return True

View File

@ -0,0 +1,2 @@
def test_service_online(testmanager):
return #testmanager.

View File

@ -10,11 +10,9 @@ from typing import TYPE_CHECKING
from random import SystemRandom from random import SystemRandom
import socks as socket import socks as socket
from stem import control
from netcontroller.torcontrol.onionserviceonline import service_online_recently from netcontroller.torcontrol.onionserviceonline import service_online_recently
from netcontroller.torcontrol import torcontroller from netcontroller.torcontrol import torcontroller
from .constants import GOSSIP_PORT
if TYPE_CHECKING: if TYPE_CHECKING:
from .peerdb import TorGossipPeers from .peerdb import TorGossipPeers

View File

@ -0,0 +1,3 @@
from utils.identifyhome import identify_home
SERVER_SOCKET = identify_home() + "torgossip.sock"
GOSSIP_PORT = 2020

View File

@ -10,10 +10,11 @@ import selectors
import socket import socket
from time import sleep from time import sleep
import filepaths
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
from commands import GossipCommands # noqa from commands import GossipCommands # noqa
import commandhandlers import commandhandlers
from .constants import SERVER_SOCKET
""" """
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -97,13 +98,13 @@ def start_server(shared_state):
do_close(conn) do_close(conn)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
socket_file = filepaths.identifyhome.identify_home() + "torgossip.sock"
try: try:
os.remove(socket_file) os.remove(SERVER_SOCKET)
except FileNotFoundError: except FileNotFoundError:
pass pass
sock.bind(socket_file) sock.bind(SERVER_SOCKET)
sock.listen(100) sock.listen(100)
sock.setblocking(False) sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept) sel.register(sock, selectors.EVENT_READ, accept)