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
key/
.vscode/*
test/*

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

16
Pipfile.lock generated

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "98280bfddd663d905e9cac4c207929de6b28343792535c2b0076f75094081cd2"
"sha256": "533e3ebed51a842c59590b22114b83da41d244def46dcceaae1899291dec379e"
},
"pipfile-spec": 6,
"requires": {
@ -16,6 +16,13 @@
]
},
"default": {
"fire": {
"hashes": [
"sha256:c299d16064ff81cbb649b65988300d4a28b71ecfb789d1fb74d99ea98ae4d2eb"
],
"index": "pypi",
"version": "==0.1.3"
},
"numpy": {
"hashes": [
"sha256:1980f8d84548d74921685f68096911585fee393975f53797614b34d4f409b6da",
@ -81,6 +88,13 @@
},
"pytor": {
"path": "."
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
}
},
"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 abstractmethod
from base64 import b32encode
from base64 import b64encode
from hashlib import sha1
from hashlib import sha3_256
from hashlib import sha512
@ -17,6 +18,8 @@ from .ed25519 import Ed25519
__all__ = [
'OnionV2',
'OnionV3',
'EmptyDirException',
'NonEmptyDirException',
]
@ -24,6 +27,10 @@ class EmptyDirException(Exception):
pass
class NonEmptyDirException(Exception):
pass
class Onion(ABC):
'''
Interface to implement hidden services keys managment
@ -92,10 +99,12 @@ class Onion(ABC):
raise Exception(
'{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)
) and not force:
raise Exception(
)) and not force:
raise NonEmptyDirException(
'Use force=True for non empty hidden service directory'
)
with open(os.path.join(path, self._priv_key_filename), 'wb') as f:
@ -187,6 +196,12 @@ class OnionV2(Onion):
'Compute onion address string'
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):
'''
@ -261,3 +276,12 @@ class OnionV3(Onion):
return b32encode(
self._pub + checksum(self._pub) + version_byte
).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(
name='pytor',
version='0.1.1',
version='0.1.2',
packages=find_packages(),
@ -33,11 +33,11 @@ setup(
install_requires=['pycryptodome==3.8.1'],
# entry_points={
# 'console_scripts': [
# 'pytor = pytor:main',
# ],
# },
entry_points={
'console_scripts': [
'pytor = pytor.__main__:main',
],
},
license="WTFPL",
)

Loading…
Cancel
Save