Initial commit.

This commit is contained in:
darko-poljak 2015-09-19 16:14:54 +02:00
parent 1b42cf554b
commit 25e17d32fd
4 changed files with 3984 additions and 0 deletions

4
.gitignore vendored Executable file
View file

@ -0,0 +1,4 @@
*.pyc
dist/
*.egg-info/
__pycache__/

88
README.rst Executable file
View 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.

3736
bottle.py Normal file

File diff suppressed because it is too large Load diff

156
x.py Normal file
View 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)