Onion v2 are ok

master
Christophe Mehay 5 years ago committed by Christophe Mehay
commit 68a4ebb08b

108
.gitignore vendored

@ -0,0 +1,108 @@
# Created by https://www.gitignore.io/api/python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# End of https://www.gitignore.io/api/python
# more
key/
.vscode/*

@ -0,0 +1,39 @@
repos:
- repo: git://github.com/pre-commit/pre-commit-hooks
rev: v2.1.0
hooks:
- id: check-added-large-files
- id: check-docstring-first
- id: check-merge-conflict
# - id: check-yaml
- id: end-of-file-fixer
- id: flake8
args:
- --exclude=__init__.py
- --exclude=settings*
language_version: python3
- id: requirements-txt-fixer
- id: trailing-whitespace
- repo: git://github.com/asottile/reorder_python_imports
rev: v1.3.4
hooks:
- id: reorder-python-imports
language_version: python3
- repo: https://github.com/pre-commit/mirrors-autopep8
rev: 'v1.4.3'
hooks:
- id: autopep8
args:
- -aaa
- --in-place
- --experimental
- repo: https://github.com/humitos/mirrors-autoflake.git
rev: 'v1.2'
hooks:
- id: autoflake
args:
- --in-place
- --remove-unused-variables
- --remove-all-unused-imports

@ -0,0 +1,19 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
tox = "*"
pre-commit = "*"
ptpython = "*"
pylama = "*"
pylint = "*"
pytest = "*"
autopep8 = "*"
[packages]
pycryptodome = "*"
[requires]
python_version = "3"

390
Pipfile.lock generated

@ -0,0 +1,390 @@
{
"_meta": {
"hash": {
"sha256": "352e3d3e8d72c1b9ced4c95a6c324e3f2a1255a21d5cd6bc8fdabc37ea85d0fc"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"pycryptodome": {
"hashes": [
"sha256:02838a7184803c05710ccb4f3c9129fe26b2634f04daf017a3490d8be5f55b62",
"sha256:04b710f55141c06388aa60d6d4a1d704b79f25b03fa9e61b0b1b9fcf0766e2ea",
"sha256:0c54852415567fc53b656b528612d3f8084c127086c38370a2985fbac4892298",
"sha256:0e59d4e06b0cdb57132387144051101eaa9bbde447b62db9660345d247212ff9",
"sha256:134479b4f4743402e7e8740f1bf3d779efd95b467c2535c600332b55d0b34686",
"sha256:1961a6cbd30926a812809efd0f17bd677f54857ac5fd0b0e2638ad4891407dd0",
"sha256:1e8f2d1b97578f0950a46128cca8b2dc44f224c4c08309a66ca53c4a33839fdd",
"sha256:213d360d5e145a2ff4d9d90afab9e2b2d99174114aa4b4c04888937730c7ef57",
"sha256:24dc9a6bc59d4e4fb182197327db1e92e15c598729889132195b420dbcc48468",
"sha256:3c24117d7fc65f5e299550abd7fa5c612813d0891b4db36d8b8cd02442674306",
"sha256:432b19da6106785825cbcaea50a8c3846ec9cd4678702f89e02985d27a73a314",
"sha256:5897e1e336c30274279f23a0f1ea81fdb43c51b514d6ac0f376ff835f420a33e",
"sha256:68ad0ce4a374577a26bb7f458575abe3c2a342818b5280de6e5738870b7761b3",
"sha256:68e50aa3230841dc296004e084ceb74225c92da951cff808122242af8a735501",
"sha256:74bd385d89aca3f8860d56caadead5dcb9e1abdf5774739c4aa9fe3cf56bb34f",
"sha256:783cffd29fb0dbf6498bd225c1b7194f8372258166c7585fb777075226b04b64",
"sha256:933750922193899d5601404c0d0637c4da12593ac2c54a16ba841b8afef44c9b",
"sha256:9871164f10b7bc87b88559b66e632528e766fcd0a84324a803860883b51f4c87",
"sha256:99a2f931e1bf87c95e465bc74ef0276ce735f1def575d52b67e117f712a63243",
"sha256:a53be389125c79728658bf42bf711a277c8ac1b3690d26303bb8b0a9217d8cb9",
"sha256:a68e85d19a870d8f54d7d78fbdefac640bb694aca216223b5987456cdf4d50ff",
"sha256:ae4070cfa271f78e9f74cde4f6b8c3869facfa4749b96e9bc7fbd1959d75f167",
"sha256:b3d3215c5a3392a6c1f537d38c13e014d93f9b33277048f06c7ede4c6926b8bd",
"sha256:df1d5540021b79230cfe8f80bb1f13ee1a626eab10161a26784b78c122c845db",
"sha256:dfdca3079aac696a25edb9a0b59595b9b776426f61587bf5a974596da217834e",
"sha256:e874188bb9c274de2dcc0995e885f15732fd0a43c7ef581eff54eef2b11d83c7",
"sha256:e9ff57287d899d2c4e17e523ac46d1e90902f3b36a81f0e6817238ff0219597f",
"sha256:ecb7718fc0087a67023fb4a78dd8ebb883d66c49f0465ad656b3213bb1841631"
],
"index": "pypi",
"version": "==3.8.1"
}
},
"develop": {
"aspy.yaml": {
"hashes": [
"sha256:ae249074803e8b957c83fdd82a99160d0d6d26dff9ba81ba608b42eebd7d8cd3",
"sha256:c7390d79f58eb9157406966201abf26da0d56c07e0ff0deadc39c8f4dbc13482"
],
"version": "==1.2.0"
},
"astroid": {
"hashes": [
"sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4",
"sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4"
],
"version": "==2.2.5"
},
"atomicwrites": {
"hashes": [
"sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
"sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
],
"version": "==1.3.0"
},
"attrs": {
"hashes": [
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
],
"version": "==19.1.0"
},
"autopep8": {
"hashes": [
"sha256:33d2b5325b7e1afb4240814fe982eea3a92ebea712869bfd08b3c0393404248c"
],
"index": "pypi",
"version": "==1.4.3"
},
"cfgv": {
"hashes": [
"sha256:6e9f2feea5e84bc71e56abd703140d7a2c250fc5ba38b8702fd6a68ed4e3b2ef",
"sha256:e7f186d4a36c099a9e20b04ac3108bd8bb9b9257e692ce18c8c3764d5cb12172"
],
"version": "==1.6.0"
},
"docopt": {
"hashes": [
"sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"
],
"version": "==0.6.2"
},
"filelock": {
"hashes": [
"sha256:b8d5ca5ca1c815e1574aee746650ea7301de63d87935b3463d26368b76e31633",
"sha256:d610c1bb404daf85976d7a82eb2ada120f04671007266b708606565dd03b5be6"
],
"version": "==3.0.10"
},
"identify": {
"hashes": [
"sha256:244e7864ef59f0c7c50c6db73f58564151d91345cd9b76ed793458953578cadd",
"sha256:8ff062f90ad4b09cfe79b5dfb7a12e40f19d2e68a5c9598a49be45f16aba7171"
],
"version": "==1.4.1"
},
"importlib-metadata": {
"hashes": [
"sha256:46fc60c34b6ed7547e2a723fc8de6dc2e3a1173f8423246b3ce497f064e9c3de",
"sha256:bc136180e961875af88b1ab85b4009f4f1278f8396a60526c0009f503a1a96ca"
],
"version": "==0.9"
},
"isort": {
"hashes": [
"sha256:08f8e3f0f0b7249e9fad7e5c41e2113aba44969798a26452ee790c06f155d4ec",
"sha256:4e9e9c4bd1acd66cf6c36973f29b031ec752cbfd991c69695e4e259f9a756927"
],
"version": "==4.3.16"
},
"jedi": {
"hashes": [
"sha256:2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b",
"sha256:2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"
],
"version": "==0.13.3"
},
"lazy-object-proxy": {
"hashes": [
"sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33",
"sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39",
"sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019",
"sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088",
"sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b",
"sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e",
"sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6",
"sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b",
"sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5",
"sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff",
"sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd",
"sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7",
"sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff",
"sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d",
"sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2",
"sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35",
"sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4",
"sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514",
"sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252",
"sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109",
"sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f",
"sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c",
"sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92",
"sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577",
"sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d",
"sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d",
"sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f",
"sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a",
"sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b"
],
"version": "==1.3.1"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"more-itertools": {
"hashes": [
"sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7",
"sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a"
],
"markers": "python_version > '2.7'",
"version": "==7.0.0"
},
"nodeenv": {
"hashes": [
"sha256:ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a"
],
"version": "==1.3.3"
},
"parso": {
"hashes": [
"sha256:17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33",
"sha256:2e9574cb12e7112a87253e14e2c380ce312060269d04bd018478a3c92ea9a376"
],
"version": "==0.4.0"
},
"pluggy": {
"hashes": [
"sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f",
"sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"
],
"version": "==0.9.0"
},
"pre-commit": {
"hashes": [
"sha256:75a9110eae00d009c913616c0fc8a6a02e7716c4a29a14cac9b313d2c7338ab0",
"sha256:f882c65316eb5b705fe4613e92a7c91055c1800102e4d291cfd18912ec9cf90e"
],
"index": "pypi",
"version": "==1.15.1"
},
"prompt-toolkit": {
"hashes": [
"sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
"sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1",
"sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"
],
"version": "==2.0.9"
},
"ptpython": {
"hashes": [
"sha256:51a74abe931f692360a32d650c2ba1ca329c08f3ed9b1de8abcd1164e0b0a6a7",
"sha256:938ee050e37d61c138dbbeb21383dfef8b9ed4ffb453a5f34041f42025bf5042",
"sha256:ebe9d68ea7532ec8ab306d4bdc7ec393701cd9bbd6eff0aa3067c821f99264d4"
],
"index": "pypi",
"version": "==2.0.4"
},
"py": {
"hashes": [
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
],
"version": "==1.8.0"
},
"pycodestyle": {
"hashes": [
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
],
"version": "==2.5.0"
},
"pydocstyle": {
"hashes": [
"sha256:2258f9b0df68b97bf3a6c29003edc5238ff8879f1efb6f1999988d934e432bd8",
"sha256:5741c85e408f9e0ddf873611085e819b809fca90b619f5fd7f34bd4959da3dd4",
"sha256:ed79d4ec5e92655eccc21eb0c6cf512e69512b4a97d215ace46d17e4990f2039"
],
"version": "==3.0.0"
},
"pyflakes": {
"hashes": [
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
],
"version": "==2.1.1"
},
"pygments": {
"hashes": [
"sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
"sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
],
"version": "==2.3.1"
},
"pylama": {
"hashes": [
"sha256:7e0327ee9b2a350ed73fe54c240894e534e2bccfb23a59ed5ce89f5a5689ee94",
"sha256:f81bf3bbd15db802b620903df491e5cd6469dcd542424ce6718425037dcc4d10"
],
"index": "pypi",
"version": "==7.6.6"
},
"pylint": {
"hashes": [
"sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09",
"sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1"
],
"index": "pypi",
"version": "==2.3.1"
},
"pytest": {
"hashes": [
"sha256:13c5e9fb5ec5179995e9357111ab089af350d788cbc944c628f3cde72285809b",
"sha256:f21d2f1fb8200830dcbb5d8ec466a9c9120e20d8b53c7585d180125cce1d297a"
],
"index": "pypi",
"version": "==4.4.0"
},
"pyyaml": {
"hashes": [
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
],
"version": "==5.1"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
},
"snowballstemmer": {
"hashes": [
"sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128",
"sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89"
],
"version": "==1.2.1"
},
"toml": {
"hashes": [
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
],
"version": "==0.10.0"
},
"tox": {
"hashes": [
"sha256:69620e19de33a6b7ee8aeda5478791b3618ff58f0b869dbd0319fb71aa903deb",
"sha256:e5cdb1653aa27b3e46b5c390de6b6d51d31afcfdbd9d1222d82d76b82ad03d9b"
],
"index": "pypi",
"version": "==3.8.6"
},
"typed-ast": {
"hashes": [
"sha256:035a54ede6ce1380599b2ce57844c6554666522e376bd111eb940fbc7c3dad23",
"sha256:037c35f2741ce3a9ac0d55abfcd119133cbd821fffa4461397718287092d9d15",
"sha256:049feae7e9f180b64efacbdc36b3af64a00393a47be22fa9cb6794e68d4e73d3",
"sha256:19228f7940beafc1ba21a6e8e070e0b0bfd1457902a3a81709762b8b9039b88d",
"sha256:2ea681e91e3550a30c2265d2916f40a5f5d89b59469a20f3bad7d07adee0f7a6",
"sha256:3a6b0a78af298d82323660df5497bcea0f0a4a25a0b003afd0ce5af049bd1f60",
"sha256:5385da8f3b801014504df0852bf83524599df890387a3c2b17b7caa3d78b1773",
"sha256:606d8afa07eef77280c2bf84335e24390055b478392e1975f96286d99d0cb424",
"sha256:69245b5b23bbf7fb242c9f8f08493e9ecd7711f063259aefffaeb90595d62287",
"sha256:6f6d839ab09830d59b7fa8fb6917023d8cb5498ee1f1dbd82d37db78eb76bc99",
"sha256:730888475f5ac0e37c1de4bd05eeb799fdb742697867f524dc8a4cd74bcecc23",
"sha256:9819b5162ffc121b9e334923c685b0d0826154e41dfe70b2ede2ce29034c71d8",
"sha256:9e60ef9426efab601dd9aa120e4ff560f4461cf8442e9c0a2b92548d52800699",
"sha256:af5fbdde0690c7da68e841d7fc2632345d570768ea7406a9434446d7b33b0ee1",
"sha256:b64efdbdf3bbb1377562c179f167f3bf301251411eb5ac77dec6b7d32bcda463",
"sha256:bac5f444c118aeb456fac1b0b5d14c6a71ea2a42069b09c176f75e9bd4c186f6",
"sha256:bda9068aafb73859491e13b99b682bd299c1b5fd50644d697533775828a28ee0",
"sha256:d659517ca116e6750101a1326107d3479028c5191f0ecee3c7203c50f5b915b0",
"sha256:eddd3fb1f3e0f82e5915a899285a39ee34ce18fd25d89582bc89fc9fb16cd2c6"
],
"markers": "implementation_name == 'cpython'",
"version": "==1.3.1"
},
"virtualenv": {
"hashes": [
"sha256:6aebaf4dd2568a0094225ebbca987859e369e3e5c22dc7d52e5406d504890417",
"sha256:984d7e607b0a5d1329425dd8845bd971b957424b5ba664729fab51ab8c11bc39"
],
"version": "==16.4.3"
},
"wcwidth": {
"hashes": [
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
],
"version": "==0.1.7"
},
"wrapt": {
"hashes": [
"sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533"
],
"version": "==1.11.1"
},
"zipp": {
"hashes": [
"sha256:55ca87266c38af6658b84db8cfb7343cdb0bf275f93c7afaea0d8e7a209c7478",
"sha256:682b3e1c62b7026afe24eadf6be579fb45fec54c07ea218bded8092af07a68c4"
],
"version": "==0.3.3"
}
}
}

@ -0,0 +1,199 @@
import os
import re
from abc import ABC
from abc import abstractmethod
from base64 import b32encode
from hashlib import sha1
from typing import BinaryIO
from Crypto.PublicKey import RSA
__all__ = [
'OnionV2',
'OnionV3',
]
class EmptyDirException(Exception):
pass
class Onion(ABC):
'''
Interface to implement hidden services keys managment
'''
_priv = None
_pub = None
_hidden_service_path = None
_version = None
def __init__(self,
private_key: bytes = None,
hidden_service_path: str = None):
if hidden_service_path:
try:
self.load_hidden_service(hidden_service_path)
except EmptyDirException:
pass
self._hidden_service_path = hidden_service_path
if private_key:
self.set_private_key(private_key)
if not self._priv:
self.gen_new_private_key()
@abstractmethod
def gen_new_private_key(self) -> None:
'Generate new private key'
...
@abstractmethod
def set_private_key_from_file(self, file: BinaryIO):
'Load private key from file'
...
@abstractmethod
def set_private_key(self, key: bytes) -> None:
'Add private key'
...
@abstractmethod
def _save_keypair(self, key) -> None:
'Generate pub key from priv key and save both in instance'
...
@abstractmethod
def load_hidden_service(self, path: str) -> None:
'Load key from hidden service'
...
@abstractmethod
def write_hidden_service(self, path: str, force: bool = False) -> None:
'Write hidden service keys to directory'
...
def get_available_private_key_formats(self) -> list:
'Get private key export availables formats'
r = re.compile(r'_get_private_key_has_([a-z]+)')
formats = []
for method in dir(self):
m = r.match(method)
if m:
formats.append(m[1])
return formats
def get_private_key(self, format: str = 'native'):
'Get the private key as specified format'
method = '_get_private_key_has_{format}'.format(
format=format
)
if not hasattr(self, method) and not callable(getattr(self, method)):
raise NotImplementedError('Method {method} if not implemented')
return getattr(self, method)()
@abstractmethod
def _get_private_key_has_native(self) -> bytes:
'Get private key like in tor native format'
...
@abstractmethod
def get_public_key(self) -> bytes:
'Compute public key'
if not self._priv:
raise Exception('No private key has been set')
@abstractmethod
def get_onion_str(self) -> str:
'Compute onion address string'
...
@property
def onion_address(self) -> str:
return "{onion}.onion".format(
onion=self.get_onion_str()
)
@property
def version(self) -> str:
return str(self._version)
class OnionV2(Onion):
'''
Tor onion address v2 implement
'''
_version = 2
def gen_new_private_key(self) -> None:
'Generate new 1024 bits RSA key for hidden service'
self._save_keypair(RSA.generate(bits=1024))
def _save_keypair(self, key: RSA.RsaKey) -> None:
self._priv = key.exportKey("PEM")
self._pub = key.publickey().exportKey("DER")
def set_private_key(self, key: bytes) -> None:
'Add private key'
self._save_keypair(RSA.importKey(key.strip()))
def set_private_key_from_file(self, file: BinaryIO):
'Load private key from file'
self.set_private_key(file.read())
def _get_private_key_has_native(self) -> bytes:
'Get RSA private key like in PEM'
return self._get_private_key_has_pem()
def _get_private_key_has_pem(self) -> bytes:
'Get RSA private key like in PEM'
return RSA.importKey(self._priv).exportKey("PEM")
def get_public_key(self) -> bytes:
'Compute public key'
super().get_public_key()
return self._pub
def load_hidden_service(self, path: str) -> None:
if not os.path.isdir(path):
raise Exception(
'{path} should be an existing directory'.format(path=path)
)
if 'private_key' not in os.listdir(path):
raise EmptyDirException(
'private_key file not found in {path}'.format(path=path)
)
with open(os.path.join(path, 'private_key'), 'rb') as f:
self.set_private_key_from_file(f)
def write_hidden_service(self, path: str = None,
force: bool = False) -> None:
path = path or self._hidden_service_path
if not path:
raise Exception('Missing hidden service path')
if not os.path.exists(path):
raise Exception(
'{path} should be an existing directory'.format(path=path)
)
if os.path.exists(os.path.join(path, 'private_key')) and not force:
raise Exception(
'Use force=True for non empty hidden service directory'
)
with open(os.path.join(path, 'private_key'), 'wb') as f:
f.write(self._get_private_key_has_native())
with open(os.path.join(path, 'hostname'), 'w') as f:
f.write(self.onion_address)
def get_onion_str(self) -> str:
'Compute onion address string'
return b32encode(sha1(self._pub[22:]).digest()[:10]).decode().lower()
class OnionV3(Onion):
'''
Tor onion address v3 implement
'''
_version = 3

@ -0,0 +1,124 @@
from base64 import b64decode
import pytest
from ..onion import OnionV2
from ..onion import OnionV3
class _testOnion:
private_key = None
public_key = None
onion_address = None
onion_cls = None
version = None
def generate_new_key(self):
return self.onion_cls().get_private_key()
def test_import_key(self):
o = self.onion_cls(private_key=self.private_key)
assert o.get_public_key() == self.public_key
assert o.onion_address == self.onion_address
assert o.get_private_key() == self.private_key.encode()
def test_generate_key(self):
o = self.onion_cls()
assert o.onion_address.endswith('.onion')
assert len(o.onion_address) == len(self.onion_address)
def test_import_file(self, fs):
fs.create_file('/test_tor/private_key', contents=self.private_key)
o = self.onion_cls()
with open('/test_tor/private_key', 'rb') as f:
o.set_private_key_from_file(f)
assert o.onion_address == self.onion_address
def test_import_hidden_directory(self, tmpdir):
d = tmpdir.mkdir("hidden_directory")
f = d.join('private_key')
f.write(self.private_key)
o = self.onion_cls(hidden_service_path=d)
assert o.onion_address == self.onion_address
def test_write_hidden_directory(self, tmpdir):
d = tmpdir.mkdir("hidden_directory")
o = self.onion_cls(private_key=self.private_key)
o.write_hidden_service(path=str(d))
assert d.join('private_key').read() == self.private_key
assert d.join('hostname').read() == o.onion_address
def test_import_empty_hidden_directory(self, tmpdir):
d = tmpdir.mkdir("hidden_directory")
o = self.onion_cls(hidden_service_path=d)
o.write_hidden_service()
assert d.join('private_key').read() == o.get_private_key().decode()
assert d.join('hostname').read() == o.onion_address
def test_import_hidden_directory_with_new_key(self, tmpdir):
d = tmpdir.mkdir("hidden_directory")
f = d.join('private_key')
f.write(self.generate_new_key())
o = self.onion_cls(hidden_service_path=d,
private_key=self.private_key)
with pytest.raises(Exception):
o.write_hidden_service()
o.write_hidden_service(force=True)
assert d.join('private_key').read() == o.get_private_key().decode()
assert d.join('hostname').read() == o.onion_address
def test_version(self):
assert self.onion_cls().version == str(self.version)
class TestOnionV2(_testOnion):
private_key = '''
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCsMP4gl6g1Q313miPhb1GnDr56ZxIWGsO2PwHM1infkbhlBakR
6DGQfpE31L1ZKTUxY0OexKbW088v8qCOfjD9Zk1i80JP4xzfWQcwFZ5yM/0fkhm3
zLXqXdEahvRthmFsS8OWusRs/04U247ryTm4k5S0Ch5OTBuvMLzQ8W0yDwIDAQAB
AoGAAZr3U5B2ZgC6E7phKUHjbf5KMlPxrDkVqAZQWvuIKmhuYqq518vlYmZ7rhyS
o1kqAMrfH4TP1WLmJJlLe+ibRk2aonR4e0GbW4x151wcJdT1V3vdWAsVSzG3+dqX
PiGT//DIe0OPSH6ecI8ftFRLODd6f5iGkF4gsUSTcVzAFgkCQQDTY67dRpOD9Ozw
oYH48xe0B9NQCw7g4NSH85jPurJXnpn6lZ6bcl8x8ioAdgLyomR7fO/dJFYLw6uV
LZLqZsVbAkEA0Iei3QcpsJnYgcQG7l5I26Sq3LwoiGRDFKRI6k0e+en9JQJgA3Ay
tsLpyCHv9jQ762F6AVXFru5DmZX40F6AXQJBAIHoKac8Xx1h4FaEuo4WPkPZ50ey
dANIx/OAhTFrp3vnMPNpDV60K8JS8vLzkx4vJBcrkXDSirqSFhkIN9grLi8CQEO2
l5MQPWBkRKK2pc2Hfj8cdIMi8kJ/1CyCwE6c5l8etR3sbIMRTtZ76nAbXRFkmsRv
La/7Syrnobngsh/vX90CQB+PSSBqiPSsK2yPz6Gsd6OLCQ9sdy2oRwFTasH8sZyl
bhJ3M9WzP/EMkAzyW8mVs1moFp3hRcfQlZHl6g1U9D8=
-----END RSA PRIVATE KEY-----
'''.strip()
public_key = b64decode('''
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsMP4gl6g1Q313miPhb1GnDr56
ZxIWGsO2PwHM1infkbhlBakR6DGQfpE31L1ZKTUxY0OexKbW088v8qCOfjD9Zk1i
80JP4xzfWQcwFZ5yM/0fkhm3zLXqXdEahvRthmFsS8OWusRs/04U247ryTm4k5S0
Ch5OTBuvMLzQ8W0yDwIDAQAB
''')
onion_address = 'wcet3bgkj4purdfx.onion'
onion_cls = OnionV2
version = 2
class TestOnionV3(_testOnion):
private_key = b64decode('''
PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACArobDQYyZAWXei4QZwr++j96H1X/gq14N
wLRZ2O5DXuL0EzYKkdhZSILY85q+kfwZH8z4ceqe7u1F+0pQi/sM
''')
public_key = b64decode('''
PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAC9kzftiea/kb+TWlCEVNpfUJLVk+rFIoMG
m9/hW13isA==
''')
onion_address = (
'xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion'
)
onion_cls = OnionV3
version = 3

@ -0,0 +1,45 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from setuptools import find_packages
from setuptools import setup
setup(
name='pytor',
version='0.1.0',
packages=find_packages(),
author="Christophe Mehay",
author_email="cmehay@nospam.student.42.fr",
description="Manage Tor hidden services keys",
include_package_data=True,
url='http://github.com/cmehay/pytor',
classifiers=[
"Programming Language :: Python",
"Development Status :: 1 - Planning",
"License :: OSI Approved :: BSD License",
"Natural Language :: English",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
],
install_requires=['pycryptodome==3.8.1'],
# entry_points={
# 'console_scripts': [
# 'pytor = pytor:main',
# ],
# },
license="WTFPL",
)

@ -0,0 +1,10 @@
[tox]
envlist = py34, py35, py36, py37
skip_missing_interpreters = true
[testenv]
deps=
pytest
pyfakefs
pycryptodome
commands=pytest -v
Loading…
Cancel
Save