mirror of
https://github.com/onionshare/onionshare.git
synced 2024-10-01 01:35:40 -04:00
Early support for ClientAuth with v3 onions
This commit is contained in:
parent
34554414e9
commit
b48848eb04
@ -132,7 +132,14 @@ def main(cwd=None):
|
||||
action="store_true",
|
||||
dest="client_auth",
|
||||
default=False,
|
||||
help="Use client authorization (requires --legacy)",
|
||||
help="Use V2 client authorization (requires --legacy)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--client-auth-v3",
|
||||
action="store_true",
|
||||
dest="client_auth_v3",
|
||||
default=False,
|
||||
help="Use V3 client authorization",
|
||||
)
|
||||
# Share args
|
||||
parser.add_argument(
|
||||
@ -196,6 +203,7 @@ def main(cwd=None):
|
||||
autostop_timer = int(args.autostop_timer)
|
||||
legacy = bool(args.legacy)
|
||||
client_auth = bool(args.client_auth)
|
||||
client_auth_v3 = bool(args.client_auth_v3)
|
||||
autostop_sharing = not bool(args.no_autostop_sharing)
|
||||
data_dir = args.data_dir
|
||||
webhook_url = args.webhook_url
|
||||
@ -217,7 +225,14 @@ def main(cwd=None):
|
||||
# client_auth can only be set if legacy is also set
|
||||
if client_auth and not legacy:
|
||||
print(
|
||||
"Client authentication (--client-auth) is only supported with with legacy onion services (--legacy)"
|
||||
"Client authentication (--client-auth) is only supported with legacy onion services (--legacy)"
|
||||
)
|
||||
sys.exit()
|
||||
|
||||
# client_auth_v3 and legacy cannot be both set
|
||||
if client_auth_v3 and legacy:
|
||||
print(
|
||||
"V3 Client authentication (--client-auth-v3) cannot be used with legacy onion services (--legacy)"
|
||||
)
|
||||
sys.exit()
|
||||
|
||||
@ -243,6 +258,7 @@ def main(cwd=None):
|
||||
mode_settings.set("general", "autostop_timer", autostop_timer)
|
||||
mode_settings.set("general", "legacy", legacy)
|
||||
mode_settings.set("general", "client_auth", client_auth)
|
||||
mode_settings.set("general", "client_auth_v3", client_auth_v3)
|
||||
if mode == "share":
|
||||
mode_settings.set("share", "autostop_sharing", autostop_sharing)
|
||||
if mode == "receive":
|
||||
@ -364,9 +380,14 @@ def main(cwd=None):
|
||||
print("")
|
||||
if mode_settings.get("general", "client_auth"):
|
||||
print(
|
||||
f"Give this address and HidServAuth lineto your sender, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
||||
f"Give this address and HidServAuth line to your sender, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
||||
)
|
||||
print(app.auth_string)
|
||||
elif mode_settings.get("general", "client_auth_v3"):
|
||||
print(
|
||||
f"Give this address and ClientAuth line to your sender, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
||||
)
|
||||
print(app.auth_string_v3)
|
||||
else:
|
||||
print(
|
||||
f"Give this address to your sender, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
||||
@ -377,6 +398,11 @@ def main(cwd=None):
|
||||
f"Give this address and HidServAuth line to your recipient, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
||||
)
|
||||
print(app.auth_string)
|
||||
elif mode_settings.get("general", "client_auth_v3"):
|
||||
print(
|
||||
f"Give this address and ClientAuth line to your recipient, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
||||
)
|
||||
print(app.auth_string_v3)
|
||||
else:
|
||||
print(
|
||||
f"Give this address to your recipient, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
||||
@ -461,6 +487,10 @@ def main(cwd=None):
|
||||
print("Give this address and HidServAuth to the sender:")
|
||||
print(url)
|
||||
print(app.auth_string)
|
||||
elif mode_settings.get("general", "client_auth_v3"):
|
||||
print("Give this address and ClientAuth to the sender:")
|
||||
print(url)
|
||||
print(app.auth_string_v3)
|
||||
else:
|
||||
print("Give this address to the sender:")
|
||||
print(url)
|
||||
@ -469,6 +499,10 @@ def main(cwd=None):
|
||||
print("Give this address and HidServAuth line to the recipient:")
|
||||
print(url)
|
||||
print(app.auth_string)
|
||||
elif mode_settings.get("general", "client_auth_v3"):
|
||||
print("Give this address and ClientAuth line to the recipient:")
|
||||
print(url)
|
||||
print(app.auth_string_v3)
|
||||
else:
|
||||
print("Give this address to the recipient:")
|
||||
print(url)
|
||||
|
@ -39,6 +39,8 @@ class ModeSettings:
|
||||
"private_key": None,
|
||||
"hidservauth_string": None,
|
||||
"password": None,
|
||||
"client_auth_v3_priv_key": None,
|
||||
"client_auth_v3_pub_key": None,
|
||||
},
|
||||
"persistent": {"mode": None, "enabled": False},
|
||||
"general": {
|
||||
@ -48,6 +50,7 @@ class ModeSettings:
|
||||
"autostop_timer": False,
|
||||
"legacy": False,
|
||||
"client_auth": False,
|
||||
"client_auth_v3": False,
|
||||
"service_id": None,
|
||||
},
|
||||
"share": {"autostop_sharing": True, "filenames": []},
|
||||
|
@ -23,6 +23,7 @@ from stem import ProtocolError, SocketClosed
|
||||
from stem.connection import MissingPassword, UnreadableCookieFile, AuthenticationFailure
|
||||
from Crypto.PublicKey import RSA
|
||||
import base64
|
||||
import nacl.public
|
||||
import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
@ -166,10 +167,25 @@ class Onion(object):
|
||||
|
||||
# Assigned later if we are using stealth mode
|
||||
self.auth_string = None
|
||||
self.auth_string_v3 = None
|
||||
|
||||
# Keep track of onions where it's important to gracefully close to prevent truncated downloads
|
||||
self.graceful_close_onions = []
|
||||
|
||||
def key_str(self, key):
|
||||
"""
|
||||
Returns a base32 decoded string of a key.
|
||||
"""
|
||||
# bytes to base 32
|
||||
key_bytes = bytes(key)
|
||||
key_b32 = base64.b32encode(key_bytes)
|
||||
# strip trailing ====
|
||||
assert key_b32[-4:] == b'===='
|
||||
key_b32 = key_b32[:-4]
|
||||
# change from b'ASDF' to ASDF
|
||||
s = key_b32.decode('utf-8')
|
||||
return s
|
||||
|
||||
def connect(
|
||||
self,
|
||||
custom_settings=None,
|
||||
@ -570,7 +586,7 @@ class Onion(object):
|
||||
callable(list_ephemeral_hidden_services) and self.tor_version >= "0.2.7.1"
|
||||
)
|
||||
|
||||
# Do the versions of stem and tor that I'm using support stealth onion services?
|
||||
# Do the versions of stem and tor that I'm using support v2 stealth onion services?
|
||||
try:
|
||||
res = self.c.create_ephemeral_hidden_service(
|
||||
{1: 1},
|
||||
@ -586,11 +602,33 @@ class Onion(object):
|
||||
# ephemeral stealth onion services are not supported
|
||||
self.supports_stealth = False
|
||||
|
||||
# Do the versions of stem and tor that I'm using support v3 stealth onion services?
|
||||
try:
|
||||
res = self.c.create_ephemeral_hidden_service(
|
||||
{1: 1},
|
||||
basic_auth=None,
|
||||
await_publication=False,
|
||||
key_type="NEW",
|
||||
key_content="ED25519-V3",
|
||||
client_auth_v3="E2GOT5LTUTP3OAMRCRXO4GSH6VKJEUOXZQUC336SRKAHTTT5OVSA",
|
||||
)
|
||||
tmp_service_id = res.service_id
|
||||
self.c.remove_ephemeral_hidden_service(tmp_service_id)
|
||||
self.supports_stealth_v3 = True
|
||||
except:
|
||||
# ephemeral v3 stealth onion services are not supported
|
||||
self.supports_stealth_v3 = False
|
||||
|
||||
# Does this version of Tor support next-gen ('v3') onions?
|
||||
# Note, this is the version of Tor where this bug was fixed:
|
||||
# https://trac.torproject.org/projects/tor/ticket/28619
|
||||
self.supports_v3_onions = self.tor_version >= Version("0.3.5.7")
|
||||
|
||||
# Does this version of Tor support legacy ('v2') onions?
|
||||
# v2 onions have been phased out as of Tor 0.4.6.1.
|
||||
self.supports_v2_onions = self.tor_version < Version("0.4.6.1")
|
||||
|
||||
|
||||
def is_authenticated(self):
|
||||
"""
|
||||
Returns True if the Tor connection is still working, or False otherwise.
|
||||
@ -618,6 +656,12 @@ class Onion(object):
|
||||
)
|
||||
raise TorTooOldStealth()
|
||||
|
||||
if mode_settings.get("general", "client_auth_v3") and not self.supports_stealth_v3:
|
||||
print(
|
||||
"Your version of Tor is too old, stealth v3 onion services are not supported"
|
||||
)
|
||||
raise TorTooOldStealth()
|
||||
|
||||
auth_cookie = None
|
||||
if mode_settings.get("general", "client_auth"):
|
||||
if mode_settings.get("onion", "hidservauth_string"):
|
||||
@ -633,10 +677,11 @@ class Onion(object):
|
||||
else:
|
||||
# Not using client auth at all
|
||||
basic_auth = None
|
||||
client_auth_v3_pub_key = None
|
||||
|
||||
if mode_settings.get("onion", "private_key"):
|
||||
key_content = mode_settings.get("onion", "private_key")
|
||||
if self.is_v2_key(key_content):
|
||||
if self.is_v2_key(key_content) and self.supports_v2_onions:
|
||||
key_type = "RSA1024"
|
||||
else:
|
||||
# Assume it was a v3 key. Stem will throw an error if it's something illegible
|
||||
@ -644,19 +689,35 @@ class Onion(object):
|
||||
else:
|
||||
key_type = "NEW"
|
||||
# Work out if we can support v3 onion services, which are preferred
|
||||
if self.supports_v3_onions and not mode_settings.get("general", "legacy"):
|
||||
if self.supports_v3_onions and not mode_settings.get("general", "legacy") and not self.supports_v2_onions:
|
||||
key_content = "ED25519-V3"
|
||||
else:
|
||||
# fall back to v2 onion services
|
||||
key_content = "RSA1024"
|
||||
|
||||
# v3 onions don't yet support basic auth. Our ticket:
|
||||
# https://github.com/micahflee/onionshare/issues/697
|
||||
if (
|
||||
key_type == "NEW"
|
||||
and key_content == "ED25519-V3"
|
||||
and not mode_settings.get("general", "legacy")
|
||||
(key_type == "ED25519-V3"
|
||||
or key_content == "ED25519-V3")
|
||||
and mode_settings.get("general", "client_auth_v3")
|
||||
):
|
||||
if key_type == "NEW" or not mode_settings.get("onion", "client_auth_v3_priv_key"):
|
||||
# Generate a new key pair for Client Auth on new onions, or if
|
||||
# it's a persistent onion but for some reason we don't them
|
||||
client_auth_v3_priv_key_raw = nacl.public.PrivateKey.generate()
|
||||
client_auth_v3_priv_key = self.key_str(client_auth_v3_priv_key_raw)
|
||||
client_auth_v3_pub_key = self.key_str(client_auth_v3_priv_key_raw.public_key)
|
||||
else:
|
||||
# These should have been saved in settings from the previous run of a persistent onion
|
||||
client_auth_v3_priv_key = mode_settings.get("onion", "client_auth_v3_priv_key")
|
||||
client_auth_v3_pub_key = mode_settings.get("onion", "client_auth_v3_pub_key")
|
||||
|
||||
self.common.log(
|
||||
"Onion", "start_onion-service", f"ClientAuthV3 private key (for Tor Browser: {client_auth_v3_priv_key}"
|
||||
)
|
||||
self.common.log(
|
||||
"Onion", "start_onion-service", f"ClientAuthV3 public key (for Onion service: {client_auth_v3_pub_key}"
|
||||
)
|
||||
# basic_auth is only for v2 onions
|
||||
basic_auth = None
|
||||
|
||||
debug_message = f"key_type={key_type}"
|
||||
@ -670,6 +731,7 @@ class Onion(object):
|
||||
basic_auth=basic_auth,
|
||||
key_type=key_type,
|
||||
key_content=key_content,
|
||||
client_auth_v3=client_auth_v3_pub_key,
|
||||
)
|
||||
|
||||
except ProtocolError as e:
|
||||
@ -695,6 +757,19 @@ class Onion(object):
|
||||
self.auth_string = f"HidServAuth {onion_host} {auth_cookie}"
|
||||
mode_settings.set("onion", "hidservauth_string", self.auth_string)
|
||||
|
||||
# If using V3 onions and Client Auth, save both the private and public key
|
||||
# because we need to send the public key to ADD_ONION, and the private key
|
||||
# to the other user for their Tor Browser.
|
||||
if mode_settings.get("general", "client_auth_v3"):
|
||||
mode_settings.set("onion", "client_auth_v3_priv_key", client_auth_v3_priv_key)
|
||||
mode_settings.set("onion", "client_auth_v3_pub_key", client_auth_v3_pub_key)
|
||||
# If we were pasting the client auth directly into the filesystem behind a Tor client,
|
||||
# it would need to be in the format below. However, let's just set the private key
|
||||
# by itself, as this can be pasted directly into Tor Browser, which is likely to
|
||||
# be the most common use case.
|
||||
# self.auth_string_v3 = f"{onion_host}:x25519:{client_auth_v3_priv_key}"
|
||||
self.auth_string_v3 = client_auth_v3_priv_key
|
||||
|
||||
return onion_host
|
||||
|
||||
def stop_onion_service(self, mode_settings):
|
||||
|
@ -83,6 +83,9 @@ class OnionShare(object):
|
||||
if mode_settings.get("general", "client_auth"):
|
||||
self.auth_string = self.onion.auth_string
|
||||
|
||||
if mode_settings.get("general", "client_auth_v3"):
|
||||
self.auth_string_v3 = self.onion.auth_string_v3
|
||||
|
||||
def stop_onion_service(self, mode_settings):
|
||||
"""
|
||||
Stop the onion service
|
||||
|
393
cli/poetry.lock
generated
393
cli/poetry.lock
generated
@ -1,32 +1,33 @@
|
||||
[[package]]
|
||||
name = "atomicwrites"
|
||||
version = "1.4.0"
|
||||
description = "Atomic file writes."
|
||||
category = "dev"
|
||||
description = "Atomic file writes."
|
||||
marker = "sys_platform == \"win32\""
|
||||
name = "atomicwrites"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.4.0"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "20.3.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "dev"
|
||||
description = "Classes Without Boilerplate"
|
||||
name = "attrs"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "20.3.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"]
|
||||
dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"]
|
||||
docs = ["furo", "sphinx", "zope.interface"]
|
||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
|
||||
tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
|
||||
tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
|
||||
|
||||
[[package]]
|
||||
name = "bidict"
|
||||
version = "0.21.2"
|
||||
description = "The bidirectional mapping library for Python."
|
||||
category = "main"
|
||||
description = "The bidirectional mapping library for Python."
|
||||
name = "bidict"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "0.21.2"
|
||||
|
||||
[package.extras]
|
||||
coverage = ["coverage (<6)", "pytest-cov (<3)"]
|
||||
@ -36,56 +37,68 @@ precommit = ["pre-commit (<3)"]
|
||||
test = ["hypothesis (<6)", "py (<2)", "pytest (<7)", "pytest-benchmark (>=3.2.0,<4)", "sortedcollections (<2)", "sortedcontainers (<3)", "Sphinx (<4)", "sphinx-autodoc-typehints (<2)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2020.12.5"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
name = "certifi"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "2020.12.5"
|
||||
|
||||
[[package]]
|
||||
name = "chardet"
|
||||
version = "4.0.0"
|
||||
category = "main"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
name = "cffi"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "1.14.5"
|
||||
|
||||
[package.dependencies]
|
||||
pycparser = "*"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Universal encoding detector for Python 2 and 3"
|
||||
category = "main"
|
||||
name = "chardet"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "4.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "7.1.2"
|
||||
category = "main"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
name = "click"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "7.1.2"
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
description = "Cross-platform colored terminal text."
|
||||
marker = "sys_platform == \"win32\""
|
||||
name = "colorama"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "0.4.4"
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "1.16.0"
|
||||
description = "DNS toolkit"
|
||||
category = "main"
|
||||
description = "DNS toolkit"
|
||||
name = "dnspython"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.16.0"
|
||||
|
||||
[package.extras]
|
||||
DNSSEC = ["pycryptodome", "ecdsa (>=0.13)"]
|
||||
IDNA = ["idna (>=2.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "eventlet"
|
||||
version = "0.30.2"
|
||||
description = "Highly concurrent networking library"
|
||||
category = "main"
|
||||
description = "Highly concurrent networking library"
|
||||
name = "eventlet"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.30.2"
|
||||
|
||||
[package.dependencies]
|
||||
dnspython = ">=1.15.0,<2.0.0"
|
||||
@ -93,18 +106,18 @@ greenlet = ">=0.3"
|
||||
six = ">=1.10.0"
|
||||
|
||||
[[package]]
|
||||
name = "flask"
|
||||
version = "1.1.2"
|
||||
description = "A simple framework for building complex web applications."
|
||||
category = "main"
|
||||
description = "A simple framework for building complex web applications."
|
||||
name = "flask"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "1.1.2"
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=5.1"
|
||||
itsdangerous = ">=0.24"
|
||||
Jinja2 = ">=2.10.1"
|
||||
Werkzeug = ">=0.15"
|
||||
click = ">=5.1"
|
||||
itsdangerous = ">=0.24"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"]
|
||||
@ -112,86 +125,90 @@ docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-
|
||||
dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "flask-httpauth"
|
||||
version = "4.2.0"
|
||||
description = "Basic and Digest HTTP authentication for Flask routes"
|
||||
category = "main"
|
||||
description = "Basic and Digest HTTP authentication for Flask routes"
|
||||
name = "flask-httpauth"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "4.2.0"
|
||||
|
||||
[package.dependencies]
|
||||
Flask = "*"
|
||||
|
||||
[[package]]
|
||||
name = "flask-socketio"
|
||||
version = "5.0.1"
|
||||
description = "Socket.IO integration for Flask applications"
|
||||
category = "main"
|
||||
description = "Socket.IO integration for Flask applications"
|
||||
name = "flask-socketio"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "5.0.1"
|
||||
|
||||
[package.dependencies]
|
||||
Flask = ">=0.9"
|
||||
python-socketio = ">=5.0.2"
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "1.0.0"
|
||||
description = "Lightweight in-process concurrent programming"
|
||||
category = "main"
|
||||
description = "Lightweight in-process concurrent programming"
|
||||
name = "greenlet"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
|
||||
version = "1.0.0"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "2.10"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
name = "idna"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "2.10"
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "3.10.0"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "dev"
|
||||
description = "Read metadata from Python packages"
|
||||
marker = "python_version < \"3.8\""
|
||||
name = "importlib-metadata"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "3.10.0"
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
|
||||
zipp = ">=0.5"
|
||||
|
||||
[package.dependencies.typing-extensions]
|
||||
python = "<3.8"
|
||||
version = ">=3.6.4"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "1.1.1"
|
||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
||||
category = "dev"
|
||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
||||
name = "iniconfig"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "1.1.1"
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "1.1.0"
|
||||
description = "Various helpers to pass data to untrusted environments and back."
|
||||
category = "main"
|
||||
description = "Various helpers to pass data to untrusted environments and back."
|
||||
name = "itsdangerous"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "2.11.3"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "main"
|
||||
description = "A very fast and expressive template engine."
|
||||
name = "jinja2"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "2.11.3"
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=0.23"
|
||||
@ -200,122 +217,151 @@ MarkupSafe = ">=0.23"
|
||||
i18n = ["Babel (>=0.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "1.1.1"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "main"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
name = "markupsafe"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
|
||||
version = "1.1.1"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "20.9"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
description = "Core utilities for Python packages"
|
||||
name = "packaging"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "20.9"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2"
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "0.13.1"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
name = "pluggy"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "0.13.1"
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
||||
[package.dependencies.importlib-metadata]
|
||||
python = "<3.8"
|
||||
version = ">=0.12"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
|
||||
[[package]]
|
||||
name = "psutil"
|
||||
version = "5.8.0"
|
||||
description = "Cross-platform lib for process and system monitoring in Python."
|
||||
category = "main"
|
||||
description = "Cross-platform lib for process and system monitoring in Python."
|
||||
name = "psutil"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "5.8.0"
|
||||
|
||||
[package.extras]
|
||||
test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.10.0"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
category = "dev"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
name = "py"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.10.0"
|
||||
|
||||
[[package]]
|
||||
name = "pycryptodome"
|
||||
version = "3.10.1"
|
||||
description = "Cryptographic library for Python"
|
||||
category = "main"
|
||||
description = "C parser in Python"
|
||||
name = "pycparser"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "2.20"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Cryptographic library for Python"
|
||||
name = "pycryptodome"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "3.10.1"
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "2.4.7"
|
||||
description = "Python parsing module"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "pysocks"
|
||||
version = "1.7.1"
|
||||
description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
|
||||
category = "main"
|
||||
description = "Python binding to the Networking and Cryptography (NaCl) library"
|
||||
name = "pynacl"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "6.2.3"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "1.4.0"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
cffi = ">=1.4.1"
|
||||
six = "*"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"]
|
||||
tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)", "hypothesis (>=3.27.0)"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Python parsing module"
|
||||
name = "pyparsing"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
version = "2.4.7"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
|
||||
name = "pysocks"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.7.1"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
name = "pytest"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "6.2.3"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = ">=1.0"
|
||||
attrs = ">=19.2.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
||||
colorama = "*"
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<1.0.0a1"
|
||||
py = ">=1.8.2"
|
||||
toml = "*"
|
||||
|
||||
[package.dependencies.importlib-metadata]
|
||||
python = "<3.8"
|
||||
version = ">=0.12"
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "python-engineio"
|
||||
version = "4.0.1"
|
||||
description = "Engine.IO server"
|
||||
category = "main"
|
||||
description = "Engine.IO server"
|
||||
name = "python-engineio"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "4.0.1"
|
||||
|
||||
[package.extras]
|
||||
asyncio_client = ["aiohttp (>=3.4)"]
|
||||
client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "python-socketio"
|
||||
version = "5.1.0"
|
||||
description = "Socket.IO server"
|
||||
category = "main"
|
||||
description = "Socket.IO server"
|
||||
name = "python-socketio"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "5.1.0"
|
||||
|
||||
[package.dependencies]
|
||||
bidict = ">=0.21.0"
|
||||
@ -326,105 +372,109 @@ asyncio_client = ["aiohttp (>=3.4)", "websockets (>=7.0)"]
|
||||
client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.25.1"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
description = "Python HTTP for Humans."
|
||||
name = "requests"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "2.25.1"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
chardet = ">=3.0.2,<5"
|
||||
idna = ">=2.5,<3"
|
||||
PySocks = {version = ">=1.5.6,<1.5.7 || >1.5.7", optional = true, markers = "extra == \"socks\""}
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[package.dependencies.PySocks]
|
||||
optional = true
|
||||
version = ">=1.5.6,<1.5.7 || >1.5.7"
|
||||
|
||||
[package.extras]
|
||||
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
|
||||
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.15.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
category = "main"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
name = "six"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
version = "1.15.0"
|
||||
|
||||
[[package]]
|
||||
name = "stem"
|
||||
version = "1.8.0"
|
||||
description = "Stem is a Python controller library that allows applications to interact with Tor (https://www.torproject.org/)."
|
||||
category = "main"
|
||||
description = "Stem is a Python controller library that allows applications to interact with Tor (https://www.torproject.org/)."
|
||||
name = "stem"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "1.8.0"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
category = "dev"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
name = "toml"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
version = "0.10.2"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "3.7.4.3"
|
||||
description = "Backported and Experimental Type Hints for Python 3.5+"
|
||||
category = "dev"
|
||||
description = "Backported and Experimental Type Hints for Python 3.5+"
|
||||
marker = "python_version < \"3.8\""
|
||||
name = "typing-extensions"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "3.7.4.3"
|
||||
|
||||
[[package]]
|
||||
name = "unidecode"
|
||||
version = "1.2.0"
|
||||
description = "ASCII transliterations of Unicode text"
|
||||
category = "main"
|
||||
description = "ASCII transliterations of Unicode text"
|
||||
name = "unidecode"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
version = "1.2.0"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.4"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
name = "urllib3"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
version = "1.26.4"
|
||||
|
||||
[package.extras]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
brotli = ["brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "1.0.1"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
category = "main"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
name = "werkzeug"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "1.0.1"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"]
|
||||
watchdog = ["watchdog"]
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.4.1"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "dev"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
marker = "python_version < \"3.8\""
|
||||
name = "zipp"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "3.4.1"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
content-hash = "ace423d1b657b80c33a6fddb308d7d2a458847cfb14630c17da256c9e50f1f1d"
|
||||
python-versions = "^3.6"
|
||||
content-hash = "27f9680e537bbe672c9dc3e65a88e3d9f19c4f849135153580a4209773bbced5"
|
||||
|
||||
[metadata.files]
|
||||
atomicwrites = [
|
||||
@ -443,6 +493,45 @@ certifi = [
|
||||
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
|
||||
{file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
|
||||
]
|
||||
cffi = [
|
||||
{file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"},
|
||||
{file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"},
|
||||
{file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"},
|
||||
{file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"},
|
||||
{file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"},
|
||||
{file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"},
|
||||
{file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"},
|
||||
{file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"},
|
||||
{file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"},
|
||||
{file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"},
|
||||
{file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"},
|
||||
{file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"},
|
||||
{file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"},
|
||||
]
|
||||
chardet = [
|
||||
{file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
|
||||
{file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
|
||||
@ -617,6 +706,10 @@ py = [
|
||||
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
|
||||
{file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
|
||||
]
|
||||
pycparser = [
|
||||
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
|
||||
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
||||
]
|
||||
pycryptodome = [
|
||||
{file = "pycryptodome-3.10.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1c5e1ca507de2ad93474be5cfe2bfa76b7cf039a1a32fc196f40935944871a06"},
|
||||
{file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:6260e24d41149268122dd39d4ebd5941e9d107f49463f7e071fd397e29923b0c"},
|
||||
@ -649,6 +742,26 @@ pycryptodome = [
|
||||
{file = "pycryptodome-3.10.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:6bbf7fee7b7948b29d7e71fcacf48bac0c57fb41332007061a933f2d996f9713"},
|
||||
{file = "pycryptodome-3.10.1.tar.gz", hash = "sha256:3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673"},
|
||||
]
|
||||
pynacl = [
|
||||
{file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"},
|
||||
{file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"},
|
||||
{file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"},
|
||||
{file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"},
|
||||
{file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"},
|
||||
{file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"},
|
||||
{file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"},
|
||||
{file = "PyNaCl-1.4.0-cp35-abi3-win32.whl", hash = "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634"},
|
||||
{file = "PyNaCl-1.4.0-cp35-abi3-win_amd64.whl", hash = "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6"},
|
||||
{file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"},
|
||||
{file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"},
|
||||
{file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"},
|
||||
{file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"},
|
||||
{file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"},
|
||||
{file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"},
|
||||
{file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"},
|
||||
{file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"},
|
||||
{file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
||||
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
||||
|
@ -30,6 +30,7 @@ unidecode = "*"
|
||||
urllib3 = "*"
|
||||
eventlet = "*"
|
||||
setuptools = "*"
|
||||
pynacl = "^1.4.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "*"
|
||||
|
@ -25,11 +25,14 @@
|
||||
"gui_receive_flatpak_data_dir": "Because you installed OnionShare using Flatpak, you must save files to a folder in ~/OnionShare.",
|
||||
"gui_copy_url": "Copy Address",
|
||||
"gui_copy_hidservauth": "Copy HidServAuth",
|
||||
"gui_copy_client_auth_v3": "Copy ClientAuth",
|
||||
"gui_canceled": "Canceled",
|
||||
"gui_copied_url_title": "Copied OnionShare Address",
|
||||
"gui_copied_url": "OnionShare address copied to clipboard",
|
||||
"gui_copied_hidservauth_title": "Copied HidServAuth",
|
||||
"gui_copied_hidservauth": "HidServAuth line copied to clipboard",
|
||||
"gui_copied_client_auth_v3_title": "Copied ClientAuth",
|
||||
"gui_copied_client_auth_v3": "ClientAuth private key copied to clipboard",
|
||||
"gui_show_url_qr_code": "Show QR Code",
|
||||
"gui_qr_code_dialog_title": "OnionShare QR Code",
|
||||
"gui_waiting_to_start": "Scheduled to start in {}. Click to cancel.",
|
||||
@ -68,7 +71,7 @@
|
||||
"gui_settings_button_save": "Save",
|
||||
"gui_settings_button_cancel": "Cancel",
|
||||
"gui_settings_button_help": "Help",
|
||||
"settings_test_success": "Connected to the Tor controller.\n\nTor version: {}\nSupports ephemeral onion services: {}.\nSupports client authentication: {}.\nSupports next-gen .onion addresses: {}.",
|
||||
"settings_test_success": "Connected to the Tor controller.\n\nTor version: {}\nSupports ephemeral onion services: {}.\nSupports legacy .onion addresses: {}.\nSupports v2 client authentication: {}.\nSupports next-gen .onion addresses: {}.\nSupports next-gen client authentication: {}.",
|
||||
"connecting_to_tor": "Connecting to the Tor network",
|
||||
"update_available": "New OnionShare out. <a href='{}'>Click here</a> to get it.<br><br>You are using {} and the latest is {}.",
|
||||
"update_error_invalid_latest_version": "Could not check for new version: The OnionShare website is saying the latest version is the unrecognizable '{}'…",
|
||||
|
@ -695,8 +695,10 @@ class SettingsDialog(QtWidgets.QDialog):
|
||||
strings._("settings_test_success").format(
|
||||
onion.tor_version,
|
||||
onion.supports_ephemeral,
|
||||
onion.supports_v2_onions,
|
||||
onion.supports_stealth,
|
||||
onion.supports_v3_onions,
|
||||
onion.supports_stealth_v3,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -139,7 +139,7 @@ class ModeSettingsWidget(QtWidgets.QWidget):
|
||||
else:
|
||||
self.legacy_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||
|
||||
# Client auth
|
||||
# Client auth (v2)
|
||||
self.client_auth_checkbox = QtWidgets.QCheckBox()
|
||||
self.client_auth_checkbox.clicked.connect(self.client_auth_checkbox_clicked)
|
||||
self.client_auth_checkbox.clicked.connect(self.update_ui)
|
||||
@ -151,6 +151,18 @@ class ModeSettingsWidget(QtWidgets.QWidget):
|
||||
else:
|
||||
self.client_auth_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||
|
||||
# Client auth (v3)
|
||||
self.client_auth_v3_checkbox = QtWidgets.QCheckBox()
|
||||
self.client_auth_v3_checkbox.clicked.connect(self.client_auth_v3_checkbox_clicked)
|
||||
self.client_auth_v3_checkbox.clicked.connect(self.update_ui)
|
||||
self.client_auth_v3_checkbox.setText(
|
||||
strings._("mode_settings_client_auth_checkbox")
|
||||
)
|
||||
if self.settings.get("general", "client_auth_v3"):
|
||||
self.client_auth_v3_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||
else:
|
||||
self.client_auth_v3_checkbox.setCheckState(QtCore.Qt.Unchecked)
|
||||
|
||||
# Toggle advanced settings
|
||||
self.toggle_advanced_button = QtWidgets.QPushButton()
|
||||
self.toggle_advanced_button.clicked.connect(self.toggle_advanced_clicked)
|
||||
@ -167,6 +179,7 @@ class ModeSettingsWidget(QtWidgets.QWidget):
|
||||
advanced_layout.addLayout(autostop_timer_layout)
|
||||
advanced_layout.addWidget(self.legacy_checkbox)
|
||||
advanced_layout.addWidget(self.client_auth_checkbox)
|
||||
advanced_layout.addWidget(self.client_auth_v3_checkbox)
|
||||
self.advanced_widget = QtWidgets.QWidget()
|
||||
self.advanced_widget.setLayout(advanced_layout)
|
||||
self.advanced_widget.hide()
|
||||
@ -192,16 +205,19 @@ class ModeSettingsWidget(QtWidgets.QWidget):
|
||||
strings._("mode_settings_advanced_toggle_show")
|
||||
)
|
||||
|
||||
# Client auth is only a legacy option
|
||||
# v2 client auth is only a legacy option
|
||||
if self.client_auth_checkbox.isChecked():
|
||||
self.legacy_checkbox.setChecked(True)
|
||||
self.legacy_checkbox.setEnabled(False)
|
||||
self.client_auth_v3_checkbox.hide()
|
||||
else:
|
||||
self.legacy_checkbox.setEnabled(True)
|
||||
if self.legacy_checkbox.isChecked():
|
||||
self.client_auth_checkbox.show()
|
||||
self.client_auth_v3_checkbox.hide()
|
||||
else:
|
||||
self.client_auth_checkbox.hide()
|
||||
self.client_auth_v3_checkbox.show()
|
||||
|
||||
# If the server has been started in the past, prevent changing legacy option
|
||||
if self.settings.get("onion", "private_key"):
|
||||
@ -209,10 +225,12 @@ class ModeSettingsWidget(QtWidgets.QWidget):
|
||||
# If using legacy, disable legacy and client auth options
|
||||
self.legacy_checkbox.setEnabled(False)
|
||||
self.client_auth_checkbox.setEnabled(False)
|
||||
self.client_auth_v3_checkbox.hide()
|
||||
else:
|
||||
# If using v3, hide legacy and client auth options
|
||||
# If using v3, hide legacy and client auth options, show v3 client auth option
|
||||
self.legacy_checkbox.hide()
|
||||
self.client_auth_checkbox.hide()
|
||||
self.client_auth_v3_checkbox.show()
|
||||
|
||||
def title_editing_finished(self):
|
||||
if self.title_lineedit.text().strip() == "":
|
||||
@ -283,6 +301,11 @@ class ModeSettingsWidget(QtWidgets.QWidget):
|
||||
"general", "client_auth", self.client_auth_checkbox.isChecked()
|
||||
)
|
||||
|
||||
def client_auth_v3_checkbox_clicked(self):
|
||||
self.settings.set(
|
||||
"general", "client_auth_v3", self.client_auth_v3_checkbox.isChecked()
|
||||
)
|
||||
|
||||
def toggle_advanced_clicked(self):
|
||||
if self.advanced_widget.isVisible():
|
||||
self.advanced_widget.hide()
|
||||
|
@ -39,6 +39,7 @@ class ServerStatus(QtWidgets.QWidget):
|
||||
button_clicked = QtCore.Signal()
|
||||
url_copied = QtCore.Signal()
|
||||
hidservauth_copied = QtCore.Signal()
|
||||
client_auth_v3_copied = QtCore.Signal()
|
||||
|
||||
STATUS_STOPPED = 0
|
||||
STATUS_WORKING = 1
|
||||
@ -98,6 +99,9 @@ class ServerStatus(QtWidgets.QWidget):
|
||||
self.copy_hidservauth_button = QtWidgets.QPushButton(
|
||||
strings._("gui_copy_hidservauth")
|
||||
)
|
||||
self.copy_client_auth_v3_button = QtWidgets.QPushButton(
|
||||
strings._("gui_copy_client_auth_v3")
|
||||
)
|
||||
self.show_url_qr_code_button = QtWidgets.QPushButton(
|
||||
strings._("gui_show_url_qr_code")
|
||||
)
|
||||
@ -113,10 +117,15 @@ class ServerStatus(QtWidgets.QWidget):
|
||||
self.common.gui.css["server_status_url_buttons"]
|
||||
)
|
||||
self.copy_hidservauth_button.clicked.connect(self.copy_hidservauth)
|
||||
self.copy_client_auth_v3_button.setStyleSheet(
|
||||
self.common.gui.css["server_status_url_buttons"]
|
||||
)
|
||||
self.copy_client_auth_v3_button.clicked.connect(self.copy_client_auth_v3)
|
||||
url_buttons_layout = QtWidgets.QHBoxLayout()
|
||||
url_buttons_layout.addWidget(self.copy_url_button)
|
||||
url_buttons_layout.addWidget(self.show_url_qr_code_button)
|
||||
url_buttons_layout.addWidget(self.copy_hidservauth_button)
|
||||
url_buttons_layout.addWidget(self.copy_client_auth_v3_button)
|
||||
url_buttons_layout.addStretch()
|
||||
|
||||
url_layout = QtWidgets.QVBoxLayout()
|
||||
@ -218,6 +227,11 @@ class ServerStatus(QtWidgets.QWidget):
|
||||
else:
|
||||
self.copy_hidservauth_button.hide()
|
||||
|
||||
if self.settings.get("general", "client_auth_v3"):
|
||||
self.copy_client_auth_v3_button.show()
|
||||
else:
|
||||
self.copy_client_auth_v3_button.hide()
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Update the GUI elements based on the current state.
|
||||
@ -247,6 +261,7 @@ class ServerStatus(QtWidgets.QWidget):
|
||||
self.url.hide()
|
||||
self.copy_url_button.hide()
|
||||
self.copy_hidservauth_button.hide()
|
||||
self.copy_client_auth_v3_button.hide()
|
||||
self.show_url_qr_code_button.hide()
|
||||
|
||||
self.mode_settings_widget.update_ui()
|
||||
@ -454,6 +469,15 @@ class ServerStatus(QtWidgets.QWidget):
|
||||
|
||||
self.hidservauth_copied.emit()
|
||||
|
||||
def copy_client_auth_v3(self):
|
||||
"""
|
||||
Copy the ClientAuth v3 private key line to the clipboard.
|
||||
"""
|
||||
clipboard = self.qtapp.clipboard()
|
||||
clipboard.setText(self.app.auth_string_v3)
|
||||
|
||||
self.client_auth_v3_copied.emit()
|
||||
|
||||
def get_url(self):
|
||||
"""
|
||||
Returns the OnionShare URL.
|
||||
|
@ -276,6 +276,7 @@ class Tab(QtWidgets.QWidget):
|
||||
self.share_mode.server_status.button_clicked.connect(self.clear_message)
|
||||
self.share_mode.server_status.url_copied.connect(self.copy_url)
|
||||
self.share_mode.server_status.hidservauth_copied.connect(self.copy_hidservauth)
|
||||
self.share_mode.server_status.client_auth_v3_copied.connect(self.copy_client_auth_v3)
|
||||
|
||||
self.change_title.emit(self.tab_id, strings._("gui_tab_name_share"))
|
||||
|
||||
@ -313,6 +314,9 @@ class Tab(QtWidgets.QWidget):
|
||||
self.receive_mode.server_status.hidservauth_copied.connect(
|
||||
self.copy_hidservauth
|
||||
)
|
||||
self.receive_mode.server_status.client_auth_v3_copied.connect(
|
||||
self.copy_client_auth_v3
|
||||
)
|
||||
|
||||
self.change_title.emit(self.tab_id, strings._("gui_tab_name_receive"))
|
||||
|
||||
@ -350,6 +354,9 @@ class Tab(QtWidgets.QWidget):
|
||||
self.website_mode.server_status.hidservauth_copied.connect(
|
||||
self.copy_hidservauth
|
||||
)
|
||||
self.website_mode.server_status.client_auth_v3_copied.connect(
|
||||
self.copy_client_auth_v3
|
||||
)
|
||||
|
||||
self.change_title.emit(self.tab_id, strings._("gui_tab_name_website"))
|
||||
|
||||
@ -383,6 +390,7 @@ class Tab(QtWidgets.QWidget):
|
||||
self.chat_mode.server_status.button_clicked.connect(self.clear_message)
|
||||
self.chat_mode.server_status.url_copied.connect(self.copy_url)
|
||||
self.chat_mode.server_status.hidservauth_copied.connect(self.copy_hidservauth)
|
||||
self.chat_mode.server_status.client_auth_v3_copied.connect(self.copy_client_auth_v3)
|
||||
|
||||
self.change_title.emit(self.tab_id, strings._("gui_tab_name_chat"))
|
||||
|
||||
@ -604,6 +612,16 @@ class Tab(QtWidgets.QWidget):
|
||||
strings._("gui_copied_hidservauth"),
|
||||
)
|
||||
|
||||
def copy_client_auth_v3(self):
|
||||
"""
|
||||
When the v3 onion service ClientAuth private key gets copied to the clipboard, display this in the status bar.
|
||||
"""
|
||||
self.common.log("Tab", "copy_client_auth_v3")
|
||||
self.system_tray.showMessage(
|
||||
strings._("gui_copied_client_auth_v3_title"),
|
||||
strings._("gui_copied_client_auth_v3"),
|
||||
)
|
||||
|
||||
def clear_message(self):
|
||||
"""
|
||||
Clear messages from the status bar.
|
||||
|
Loading…
Reference in New Issue
Block a user