Initial commit.
This commit is contained in:
parent
1b42cf554b
commit
25e17d32fd
4 changed files with 3984 additions and 0 deletions
4
.gitignore
vendored
Executable file
4
.gitignore
vendored
Executable file
|
@ -0,0 +1,4 @@
|
|||
*.pyc
|
||||
dist/
|
||||
*.egg-info/
|
||||
__pycache__/
|
88
README.rst
Executable file
88
README.rst
Executable file
|
@ -0,0 +1,88 @@
|
|||
cloak
|
||||
=====
|
||||
|
||||
Download URL target encrypted then compressed and base64 encoded as
|
||||
DATA.txt file.
|
||||
|
||||
Target URL is expected to be base64 encoded zlib compressed AES
|
||||
encrypted URL. URL can be prepared using this same tool (see usage
|
||||
below).
|
||||
URL content is fetched and then AES encrypted, zlib compressed and
|
||||
base64 encoded. Content is downloaded as DATA.txt. It can be
|
||||
decoded, uncompressed and decrypted using this same tool (see usage
|
||||
below).
|
||||
Content size can be limited.
|
||||
It uses lock which allows only one request at a time.
|
||||
It is implemented with bottle microframework.
|
||||
It is a hack! :)
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Print usage:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python x.py -h
|
||||
|
||||
Prepare target URL:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python x.py c TARGET
|
||||
python x.py c 'http://www.google.com'
|
||||
|
||||
Decode, decompress and decrypt target content saved as DATA.txt and
|
||||
save it to data.tar.xz:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
cat DATA.txt | python x.py d > data.tar.xz
|
||||
|
||||
Run it in development mode:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python x.py ANY-ARG
|
||||
python x.py x
|
||||
|
||||
Run it in production mode:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python x.py
|
||||
|
||||
When you use this same tool for prepareing target URL and decrypting
|
||||
target content on the client then x.py need to be defined with the same AES
|
||||
KEY and IV values as on the server.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Copy bottle.py and x.py to the desired server and directory and run it.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
this README.rst, code itself, docstrings
|
||||
|
||||
It can be found on github.com at:
|
||||
|
||||
https://github.com/darko-poljak/cloak
|
||||
|
||||
Tested With
|
||||
===========
|
||||
|
||||
Python2.7, Python3.4
|
||||
|
||||
Further development ideas
|
||||
=========================
|
||||
|
||||
* Implement key, iv rotation or something similar.
|
||||
|
||||
* Add configuration file.
|
||||
|
||||
* Add apache mod_wsgi support.
|
||||
|
||||
* Support defined maximum requests at a time instead of one lock.
|
||||
|
156
x.py
Normal file
156
x.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
from __future__ import print_function
|
||||
import bottle as b
|
||||
import base64
|
||||
import ssl
|
||||
import threading
|
||||
import zlib
|
||||
import sys
|
||||
import functools
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
# fill with your values
|
||||
# number of bytes; if less than or equals to zero then unlimited
|
||||
MAXSIZE = 104857600 # max 100MB
|
||||
PRODUCTION_HOST = '188.226.169.172'
|
||||
PRODUCTION_PORT = 80
|
||||
DEV_HOST = 'localhost'
|
||||
DEV_PORT = 8080
|
||||
URL_PREFIX = 'x'
|
||||
# AES encryption stuff
|
||||
# fill with your values
|
||||
AES_MODE = AES.MODE_CFB
|
||||
AES_IV = r'A123B890JKL @%#$'
|
||||
AES_KEY = r')(*+AKIM 313 321kjah.;klk@sfsd%$'
|
||||
|
||||
py3k = sys.version_info >= (3, 0, 0)
|
||||
if py3k:
|
||||
import urllib.request as urlreq
|
||||
import urllib.parse as urlpar
|
||||
|
||||
stdout_write = sys.stdout.buffer.write
|
||||
def get_content_length(r):
|
||||
foo = r.getheader('Content-Length')
|
||||
if foo:
|
||||
return int(foo)
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
import urllib2 as urlreq
|
||||
import urllib as urlpar
|
||||
|
||||
stdout_write = sys.stdout.write
|
||||
def get_content_length(r):
|
||||
foo = r.info().getheaders('Content-Length')
|
||||
if foo and len(foo) >= 1:
|
||||
return int(foo[0])
|
||||
else:
|
||||
return None
|
||||
|
||||
urlunquote = urlpar.unquote
|
||||
|
||||
if hasattr(ssl, 'create_default_context'):
|
||||
SSL_NO_VERIFY_CTX = ssl.create_default_context()
|
||||
SSL_NO_VERIFY_CTX.check_hostname = False
|
||||
SSL_NO_VERIFY_CTX.verify_mode = ssl.CERT_NONE
|
||||
urlopen = functools.partial(urlreq.urlopen, context=SSL_NO_VERIFY_CTX)
|
||||
else:
|
||||
urlopen = urlreq.urlopen
|
||||
|
||||
|
||||
lock = threading.Lock()
|
||||
|
||||
|
||||
# TODO: implement key rotation or something similar
|
||||
def aes_key():
|
||||
return AES_KEY
|
||||
|
||||
|
||||
# TODO: implement IV rotation or something similar
|
||||
def aes_iv():
|
||||
return AES_IV
|
||||
|
||||
|
||||
def aes_new():
|
||||
return AES.new(aes_key(), AES_MODE, aes_iv())
|
||||
|
||||
|
||||
@b.route('/')
|
||||
def x():
|
||||
return "/" + URL_PREFIX + "/TARGET"
|
||||
|
||||
|
||||
@b.route('/' + URL_PREFIX + '/<url:re:.+>')
|
||||
def x(url):
|
||||
global debug
|
||||
# allow only one request at a time
|
||||
with lock:
|
||||
durl = str.encode(url)
|
||||
durl = base64.b64decode(durl)
|
||||
durl = zlib.decompress(durl)
|
||||
aes = aes_new()
|
||||
durl = aes.decrypt(durl)
|
||||
durl = urlunquote(durl.decode())
|
||||
if debug:
|
||||
print('fetching {}'.format(durl))
|
||||
foo = urlopen(durl)
|
||||
content_length = get_content_length(foo)
|
||||
if debug:
|
||||
print('content-length: {}'.format(content_length))
|
||||
if content_length is None:
|
||||
x = str.encode('No Content-Length\n')
|
||||
elif MAXSIZE > 0 and content_length > MAXSIZE:
|
||||
x = str.encode('Too big\n')
|
||||
else:
|
||||
x = foo.read()
|
||||
b.response.set_header('Content-Type', "text/plain")
|
||||
b.response.set_header('Content-Disposition',
|
||||
"attachment; filename=DATA.txt")
|
||||
aes = aes_new()
|
||||
return base64.b64encode(zlib.compress(aes.encrypt(x)))
|
||||
|
||||
argc = len(sys.argv)
|
||||
if argc > 1:
|
||||
aes = aes_new()
|
||||
if sys.argv[1] == '-h':
|
||||
print("usage: {0} -h print help".format(sys.argv[0]))
|
||||
print("usage: {0} c TARGET encode TARGET".format(sys.argv[0]))
|
||||
print("usage: {0} d decode stdin".format(sys.argv[0]))
|
||||
print("usage: {0} ANY run server in debug mode on 8080".format(
|
||||
sys.argv[0]))
|
||||
print("usage: {0} run server on 80".format(sys.argv[0]))
|
||||
sys.exit(0)
|
||||
elif sys.argv[1] == 'c':
|
||||
if argc < 3:
|
||||
print("missing target")
|
||||
sys.exit(1)
|
||||
else:
|
||||
# encode target url
|
||||
if sys.argv[1] == 'c':
|
||||
foo = str.encode(sys.argv[2])
|
||||
foo = aes.encrypt(foo)
|
||||
foo = zlib.compress(foo)
|
||||
foo = base64.b64encode(foo)
|
||||
stdout_write(foo)
|
||||
print()
|
||||
sys.exit(0)
|
||||
# decode data from stdin
|
||||
elif sys.argv[1] == 'd':
|
||||
foo = str.encode(sys.stdin.read())
|
||||
foo = base64.b64decode(foo)
|
||||
foo = zlib.decompress(foo)
|
||||
foo = aes.decrypt(foo)
|
||||
stdout_write(foo)
|
||||
sys.exit(0)
|
||||
else:
|
||||
# run dev server
|
||||
host = DEV_HOST
|
||||
port = DEV_PORT
|
||||
debug = True
|
||||
else:
|
||||
# run production server
|
||||
host = PRODUCTION_HOST
|
||||
port = PRODUCTION_PORT
|
||||
debug = False
|
||||
|
||||
b.run(host=host, port=port, debug=debug, reloader=True)
|
||||
|
Loading…
Reference in a new issue