writing code and mails at the same time! woohooo - one-way encryption
apparently on mailing lists it's common to write mails, even cypherpunks do
that instead of code. allow me to combine the two things.
i thought it trough, and figured, that with openssl i have no AD crypto, with
gpg i have this MDC thingy and the rfc4880 strongselector beacon
strongselector, and with nacl and it's key derivation using ecdh make them all
unsuitable on first sight for the use-case.
re my 7 rules, i think i need to work on the threat model to have 100% score
pls enjoy (also as contributions to the copyleft/permissive licensing thread)
and comment:
#!/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, , 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'
# 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)
participants (1)
-
stef