Last active
August 29, 2015 14:15
-
-
Save stef/3b3b6db83cf6be5d6cf4 to your computer and use it in GitHub Desktop.
implements simple one-way encryption pipe using rsa and keccak-based spongewrap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# | |
# implements simple one-way encryption pipe using rsa and keccak-based | |
# spongewrap | |
# | |
# useful at least in the following use-case: you have an untrusted | |
# host on which plaintext data arrives, which you want to encrypt | |
# before it is forwarded in a hostile environment to the final | |
# recipient holding a private key in a safe location. In this one-way | |
# setting the recipient is never talking to the host doing the | |
# encryption. | |
# | |
# Example: take photos in a hostile situation, encrypt the the photos | |
# and being unable to recover them until arrival in the save location | |
# with the the private key. (note, this does not protect against | |
# forensics!) | |
# | |
# crypto: a random 32 byte message key is encrypted with the public | |
# key of the recipient in oaep padded RSA, then this message key is | |
# fed into SpongeWrap, which is then used to authenticated encrypt the | |
# message. | |
# | |
# output format: | |
# 2 bytes - the length of the RSA encrypted message key | |
# n bytes - the RSA encrypted message key | |
# m bytes - the encrypted message | |
# 16 bytes - the "MAC" | |
# | |
# depends: `pip install m2crypto spongeshaker SecureString` | |
# | |
# create keys using openssl: | |
# `openssl genrsa -out my.key 4096` | |
# `openssl rsa -in my.key -pubout >> my.pub` | |
# `cat my.key my.pub >>my.pem` | |
# `srm -fll my.key` | |
# | |
# deploy my.pub on the encrypting host, secure my.pem in a safe | |
# location for decryption. | |
# | |
# test with: | |
# | |
# for i in {0..42} {8170..8210} 1000000; do | |
# echo -ne "\r$i " | |
# dd if=/dev/zero bs=$i count=1 2>/dev/null | | |
# ./ondir.py e my.pub | | |
# ./ondir.py d my.pem >/dev/null || | |
# break | |
# done | |
# | |
# (C) 2015 by Stefan Marsiske, <s@ctrlc.hu>, GPLv3 | |
import M2Crypto as m2c | |
from spongeshaker.spongewrap import SpongeWrap | |
from SecureString import clearmem | |
import sys, struct | |
TAGLEN = 16 | |
BUFLEN = 8192 | |
def encrypt(to): | |
# load recipient pk | |
key = m2c.RSA.load_pub_key(to) | |
# gen message key | |
mkey = m2c.Rand.rand_bytes(32) | |
# encrypt message key | |
cmkey = key.public_encrypt(mkey, m2c.RSA.pkcs1_oaep_padding) | |
# output message key | |
sys.stdout.write(struct.pack("H", len(cmkey))) | |
sys.stdout.write(cmkey) | |
# encrypt message | |
ctx = SpongeWrap(1536) | |
# with mkey | |
ctx.add_header(mkey) | |
# mkey not needed anymore | |
clearmem(mkey) | |
# buffered encrypt of stdin to stdout | |
while 1: | |
buf = sys.stdin.read(BUFLEN) | |
if not buf: | |
break | |
sys.stdout.write(ctx.encrypt_body(buf)) | |
# calculate tag | |
tag=ctx.digest(TAGLEN) | |
# output tag | |
sys.stdout.write(tag) | |
def decrypt(to): | |
# load recipient pk | |
key = m2c.RSA.load_key(to) | |
# read msg key | |
klen = struct.unpack('H', sys.stdin.read(2))[0] | |
if klen>1024: | |
print >>sys.stderr, "probably corrupt file" | |
sys.exit(1) | |
cmkey = sys.stdin.read(klen) | |
# decrypt message key | |
try: | |
mkey = key.private_decrypt(cmkey, m2c.RSA.pkcs1_oaep_padding) | |
except: | |
# twarth timing attacks | |
mkey = 'couldntdecryptkey' | |
# decrypt with mkey | |
ctx = SpongeWrap(1536) | |
ctx.add_header(mkey) | |
# mkey not needed anymore | |
clearmem(mkey) | |
zero = True # to detect empty files | |
rest = '' | |
# buffered reading of stdin, since we need to catch the last | |
# bytes for the tag, we always retain the last n bytes for | |
# this purpose in rest | |
while 1: | |
buf = sys.stdin.read(BUFLEN) | |
if zero and len(buf)>TAGLEN: zero=False | |
tag=buf[-TAGLEN:] | |
if len(buf)>TAGLEN: | |
# prepend the retained last bytes to the next decrypt, if | |
# there's enough more bytes read | |
sys.stdout.write(ctx.decrypt_body(rest+buf[:-TAGLEN])) | |
elif len(buf)>0: | |
if len(tag)==TAGLEN: | |
# we have exactly the tag read in buf, decrypt the | |
# rest | |
sys.stdout.write(ctx.decrypt_body(rest)) | |
else: | |
# truncate the last bytes, as we have a boundary | |
# spanning tag | |
sys.stdout.write(ctx.decrypt_body(rest[:-TAGLEN+len(tag)])) | |
if len(buf)<BUFLEN: | |
# this is the last buffer | |
if len(tag)<TAGLEN: | |
# we have a tag spanning a buffer boundary, we must | |
# patch it up | |
tag = ''.join((rest, buf[:-TAGLEN], tag))[-TAGLEN:] | |
# verify if tag is valid | |
if not zero and tag!=ctx.digest(TAGLEN): | |
print >>sys.stderr, "couldn't decrypt message" | |
sys.exit(1) | |
break | |
rest=tag | |
if __name__ == '__main__': | |
if sys.argv[1]=='e': | |
encrypt(sys.argv[2]) | |
elif sys.argv[1]=='d': | |
decrypt(sys.argv[2]) | |
else: | |
print "usage: %s <e|d> <pub|key>" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment