Created
February 20, 2015 10:59
-
-
Save mrbitsdcf/3bf3f843cd17fe34913c to your computer and use it in GitHub Desktop.
One way cypher
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
# -*- coding: utf-8 -*- | |
#!/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 | |
import M2Crypto as m2c | |
from spongeshaker.spongewrap import SpongeWrap | |
from SecureString import clearmem | |
import sys | |
import 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' | |
# todo clear private RSA key from memory | |
# 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