Add main and serializer in Onion classes

master
Christophe Mehay 5 years ago committed by Christophe Mehay
parent 560fcddead
commit d7f0be237f

1
.gitignore vendored

@ -106,3 +106,4 @@ ENV/
# more # more
key/ key/
.vscode/* .vscode/*
test/*

@ -16,6 +16,7 @@ autopep8 = "*"
pycryptodome = "*" pycryptodome = "*"
numpy = "*" numpy = "*"
pytor = {path = "."} pytor = {path = "."}
fire = "*"
[requires] [requires]
python_version = "3" python_version = "3"

16
Pipfile.lock generated

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "98280bfddd663d905e9cac4c207929de6b28343792535c2b0076f75094081cd2" "sha256": "533e3ebed51a842c59590b22114b83da41d244def46dcceaae1899291dec379e"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -16,6 +16,13 @@
] ]
}, },
"default": { "default": {
"fire": {
"hashes": [
"sha256:c299d16064ff81cbb649b65988300d4a28b71ecfb789d1fb74d99ea98ae4d2eb"
],
"index": "pypi",
"version": "==0.1.3"
},
"numpy": { "numpy": {
"hashes": [ "hashes": [
"sha256:1980f8d84548d74921685f68096911585fee393975f53797614b34d4f409b6da", "sha256:1980f8d84548d74921685f68096911585fee393975f53797614b34d4f409b6da",
@ -81,6 +88,13 @@
}, },
"pytor": { "pytor": {
"path": "." "path": "."
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
} }
}, },
"develop": { "develop": {

@ -0,0 +1,41 @@
# pytor
`pytor` is a simple python librairy to create and manager tor hidden services in version 2 and 3.
```sh
$ pytor new
hostname:
cljfodghi4w5frc6.onion
private_key:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQD2Gza8HXzgDGo+YwyhjOdgD0GY7ti5en8YGXtcsBi/JIwHdKZo
iLC4e5pWzlmB2ACdTw93ASFTGpPFs7nRxk4NuXo1BnvTsqzzcrsd9HV6xuKO7BkS
aTEY3tgrSvB2nQtM1WR9FQoyxV+EWeE0Q9vrBVpEizO4kHqFXRanOJpJbwIDAQAB
AoGAA/axPXteGP+qMGIJAIsT6OSmAlAKdoZGCL3UUkxVwbJVfQNAcNuOuRHojPBa
2bAAZogw8BI5Fq0NZzg7TGkctazKbvrmIx6o22spx2MOQXEc7lj3R3CJ8B+F1moz
9lNxIhNmw4bHeL3Sw5XMTPnOhCy1OKmouWrrcOj7B59YKrkCQQD2pWkZih6Ijl0y
xG3vB8w22krpe0YOne94aXwdggkji6Cfne8YRNWU9y8FvxGZgwfXZKwGCOSOgq7r
0SP7vEoZAkEA/3CP8BGY1jThrLHLWNPKm5Vu1+YZClL4ibs4cdtxIs0J0l+dQcYW
LMSkQpOy1C/nIIYPJpq9x8sCXG2BsRgwxwJAR9NhqONVAvVaZKdZUEuYB71IJXgV
rboGe61UTI+Ks8Q8kV7/urSI8imNkwHSUT8cMHiLs/IxBOM/p0KvVOa/OQJAHlXY
0jLUysOW9XJb6t2kFxwFAODTonU+DOVOC796zR46h2BRhaknowNrWni96RMTSLqC
/BuuZBbI3f8nQsfTqwJBAMX/KjXO/MqcB8TAjyKWHNyVR4T8OJM5lgbk8IGLKE5/
w96jWD0AEePqKKdWofLImi074zMSyMKuu6RFrkBSUuI=
-----END RSA PRIVATE KEY-----
```
```
$ mkdir test
$ pytor new-hidden-service test --version 3
FYI: Binary data is base64 encoded
path:
test
hostname:
byb3bkhwi2ccbrctsqkowckpvk3tok36geddzg4l2m6yn6mrw626nqid.onion
hs_ed25519_secret_key:
PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAAAwIFsWaVtOk8r3RvXnkZcmxwIaDmmOdV8D7KaVf6yBWjVUIUTPpOWNQ9+hEiPKUclJ1RpflZ9FSdPgSj0j0tE3
hs_ed25519_public_key:
PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAAOA7Co9kaEIMRTlBTrCU+qtzcrfjEGPJuL0z2G+ZG3tQ==
```
(more doc soon, I'm tired right mow ~)

@ -0,0 +1,7 @@
from .onion import OnionV2
from .onion import OnionV3
__all__ = [
'OnionV2',
'OnionV3',
]

@ -0,0 +1,77 @@
import json
import sys
import fire
import yaml
from .onion import NonEmptyDirException
from .onion import OnionV2
from .onion import OnionV3
class Format(object):
def __init__(self, format: str):
attr = 'print_{format}'.format(format=format)
if not hasattr(self, attr):
raise Exception('Output format not valid')
self.method = getattr(self, attr)
print('FYI: Binary data is base64 encoded', file=sys.stderr)
def print(self, data: dict):
self.method(data)
def print_plain(self, data: dict):
for key, value in data.items():
print("{key}:\n{value}".format(key=key, value=value))
def print_json(self, data: dict):
print(json.dumps(data, indent=4))
def print_yaml(self, data: dict):
print(yaml.dump(data))
class Pytor(object):
_obj = {
2: OnionV2,
3: OnionV3,
}
def __init__(self, version: int = 2, format: str = 'plain'):
if version not in self._obj:
raise Exception('Onion version not valid')
self._version = version
self._print = Format(format).print
self._stderr: lambda x: print(x, file=sys.stderr)
@property
def _cls(self):
return self._obj[self._version]
def new(self):
obj = self._cls()
self._print(obj.serialize())
def new_hidden_service(self, path: str, force: bool = False):
obj = self._cls()
try:
obj.write_hidden_service(path=path, force=force)
except NonEmptyDirException:
s = input(
'Dir {path} not empty, override? [Y/n]'.format(path=path)
)
if not s or s.lower() == 'y':
obj.write_hidden_service(path=path, force=True)
else:
print('Canceled...')
self._print({'path': path, **obj.serialize()})
def main():
fire.Fire(Pytor)
if __name__ == '__main__':
main()

@ -3,6 +3,7 @@ import re
from abc import ABC from abc import ABC
from abc import abstractmethod from abc import abstractmethod
from base64 import b32encode from base64 import b32encode
from base64 import b64encode
from hashlib import sha1 from hashlib import sha1
from hashlib import sha3_256 from hashlib import sha3_256
from hashlib import sha512 from hashlib import sha512
@ -17,6 +18,8 @@ from .ed25519 import Ed25519
__all__ = [ __all__ = [
'OnionV2', 'OnionV2',
'OnionV3', 'OnionV3',
'EmptyDirException',
'NonEmptyDirException',
] ]
@ -24,6 +27,10 @@ class EmptyDirException(Exception):
pass pass
class NonEmptyDirException(Exception):
pass
class Onion(ABC): class Onion(ABC):
''' '''
Interface to implement hidden services keys managment Interface to implement hidden services keys managment
@ -92,10 +99,12 @@ class Onion(ABC):
raise Exception( raise Exception(
'{path} should be an existing directory'.format(path=path) '{path} should be an existing directory'.format(path=path)
) )
if os.path.exists( if (os.path.exists(
os.path.join(path, self._host_filename)
) or os.path.exists(
os.path.join(path, self._priv_key_filename) os.path.join(path, self._priv_key_filename)
) and not force: )) and not force:
raise Exception( raise NonEmptyDirException(
'Use force=True for non empty hidden service directory' 'Use force=True for non empty hidden service directory'
) )
with open(os.path.join(path, self._priv_key_filename), 'wb') as f: with open(os.path.join(path, self._priv_key_filename), 'wb') as f:
@ -187,6 +196,12 @@ class OnionV2(Onion):
'Compute onion address string' 'Compute onion address string'
return b32encode(sha1(self._pub[22:]).digest()[:10]).decode().lower() return b32encode(sha1(self._pub[22:]).digest()[:10]).decode().lower()
def serialize(self):
return {
self._host_filename: self.onion_hostname,
self._priv_key_filename: self.get_private_key().decode(),
}
class OnionV3(Onion): class OnionV3(Onion):
''' '''
@ -261,3 +276,12 @@ class OnionV3(Onion):
return b32encode( return b32encode(
self._pub + checksum(self._pub) + version_byte self._pub + checksum(self._pub) + version_byte
).decode().lower() ).decode().lower()
def serialize(self):
return {
self._host_filename: self.onion_hostname,
self._priv_key_filename: b64encode(
self.get_private_key()).decode(),
self._pub_key_filename: b64encode(
self.get_public_key()).decode(),
}

@ -6,7 +6,7 @@ from setuptools import setup
setup( setup(
name='pytor', name='pytor',
version='0.1.1', version='0.1.2',
packages=find_packages(), packages=find_packages(),
@ -33,11 +33,11 @@ setup(
install_requires=['pycryptodome==3.8.1'], install_requires=['pycryptodome==3.8.1'],
# entry_points={ entry_points={
# 'console_scripts': [ 'console_scripts': [
# 'pytor = pytor:main', 'pytor = pytor.__main__:main',
# ], ],
# }, },
license="WTFPL", license="WTFPL",
) )

Loading…
Cancel
Save