105 lines
4.0 KiB
Python
105 lines
4.0 KiB
Python
#!/usr/bin/env python3.7
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
TFC - Onion-routed, endpoint secure messaging system
|
|
Copyright (C) 2013-2019 Markus Ottela
|
|
|
|
This file is part of TFC.
|
|
|
|
TFC 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.
|
|
|
|
TFC 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 TFC. If not, see <https://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
import os
|
|
import typing
|
|
|
|
import nacl.signing
|
|
|
|
from src.common.crypto import auth_and_decrypt, csprng, encrypt_and_sign
|
|
from src.common.encoding import pub_key_to_onion_address, pub_key_to_short_address
|
|
from src.common.exceptions import CriticalError
|
|
from src.common.misc import ensure_dir
|
|
from src.common.output import phase
|
|
from src.common.statics import CONFIRM_CODE_LENGTH, DIR_USER_DATA, DONE, ONION_SERVICE_PRIVATE_KEY_LENGTH, TX
|
|
|
|
if typing.TYPE_CHECKING:
|
|
from src.common.db_masterkey import MasterKey
|
|
|
|
|
|
class OnionService(object):
|
|
"""\
|
|
OnionService object manages the persistent Ed25519 key used
|
|
to create a v3 Tor Onion Service on the Networked Computer.
|
|
|
|
The reason the key is generated by Transmitter Program on Source
|
|
Computer, is this ensures that even when Networked Computer runs an
|
|
amnesic Linux distribution like Tails, the long term private
|
|
signing key is not lost between sessions.
|
|
|
|
The private key for Onion Service can not be kept as protected as
|
|
TFC's other private message/header keys (that never leave
|
|
Source/Destination computer). This is however OK, as the Onion
|
|
Service private key is only as secure as the networked endpoint
|
|
anyway.
|
|
"""
|
|
|
|
def __init__(self, master_key: 'MasterKey') -> None:
|
|
"""Create a new OnionService object."""
|
|
self.master_key = master_key
|
|
self.file_name = f'{DIR_USER_DATA}{TX}_onion_db'
|
|
self.is_delivered = False
|
|
self.conf_code = csprng(CONFIRM_CODE_LENGTH)
|
|
|
|
ensure_dir(DIR_USER_DATA)
|
|
if os.path.isfile(self.file_name):
|
|
self.onion_private_key = self.load_onion_service_private_key()
|
|
else:
|
|
self.onion_private_key = self.new_onion_service_private_key()
|
|
self.store_onion_service_private_key()
|
|
|
|
self.public_key = bytes(nacl.signing.SigningKey(seed=self.onion_private_key).verify_key)
|
|
|
|
self.user_onion_address = pub_key_to_onion_address(self.public_key)
|
|
self.user_short_address = pub_key_to_short_address(self.public_key)
|
|
|
|
@staticmethod
|
|
def new_onion_service_private_key() -> bytes:
|
|
"""Generate a new Onion Service private key and store it."""
|
|
phase("Generate Tor OS key")
|
|
onion_private_key = csprng(ONION_SERVICE_PRIVATE_KEY_LENGTH)
|
|
phase(DONE)
|
|
return onion_private_key
|
|
|
|
def store_onion_service_private_key(self) -> None:
|
|
"""Store Onion Service private key to an encrypted database."""
|
|
ct_bytes = encrypt_and_sign(self.onion_private_key, self.master_key.master_key)
|
|
|
|
ensure_dir(DIR_USER_DATA)
|
|
with open(self.file_name, 'wb+') as f:
|
|
f.write(ct_bytes)
|
|
|
|
def load_onion_service_private_key(self) -> bytes:
|
|
"""Load the Onion Service private key from the encrypted database."""
|
|
with open(self.file_name, 'rb') as f:
|
|
ct_bytes = f.read()
|
|
|
|
onion_private_key = auth_and_decrypt(ct_bytes, self.master_key.master_key, database=self.file_name)
|
|
|
|
if len(onion_private_key) != ONION_SERVICE_PRIVATE_KEY_LENGTH:
|
|
raise CriticalError("Invalid Onion Service private key length.")
|
|
|
|
return onion_private_key
|
|
|
|
def new_confirmation_code(self) -> None:
|
|
"""Generate new confirmation code for Onion Service data."""
|
|
self.conf_code = csprng(CONFIRM_CODE_LENGTH)
|