Last active
May 8, 2020 16:56
-
-
Save mmerickel/aee97620e92f4d73bb3e2ea297e7e8b7 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
from nacl.bindings.crypto_secretstream import ( | |
crypto_secretstream_xchacha20poly1305_ABYTES, | |
crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
crypto_secretstream_xchacha20poly1305_TAG_MESSAGE, | |
crypto_secretstream_xchacha20poly1305_TAG_FINAL, | |
crypto_secretstream_xchacha20poly1305_init_pull, | |
crypto_secretstream_xchacha20poly1305_init_push, | |
crypto_secretstream_xchacha20poly1305_pull, | |
crypto_secretstream_xchacha20poly1305_push, | |
crypto_secretstream_xchacha20poly1305_state, | |
) | |
from nacl.utils import random | |
import struct | |
# version, chunk size | |
_header_struct = struct.Struct('<BQ') | |
def encrypt_stream(srcfp, destfp, symmetric_key, chunk_size=4096): | |
if len(symmetric_key) != crypto_secretstream_xchacha20poly1305_KEYBYTES: | |
raise ValueError('symmetric key is too short') | |
state = crypto_secretstream_xchacha20poly1305_state() | |
hdr = crypto_secretstream_xchacha20poly1305_init_push(state, symmetric_key) | |
destfp.write(hdr) | |
frame = crypto_secretstream_xchacha20poly1305_push( | |
state, | |
_header_struct.pack(1, chunk_size), | |
None, | |
crypto_secretstream_xchacha20poly1305_TAG_MESSAGE, | |
) | |
destfp.write(frame) | |
eof = False | |
while not eof: | |
msg = srcfp.read(chunk_size) | |
eof = len(msg) < chunk_size | |
tag = crypto_secretstream_xchacha20poly1305_TAG_FINAL if eof else 0 | |
frame = crypto_secretstream_xchacha20poly1305_push(state, msg, tag=tag) | |
destfp.write(frame) | |
def decrypt_stream(srcfp, destfp, symmetric_key): | |
hdr = srcfp.read(crypto_secretstream_xchacha20poly1305_HEADERBYTES) | |
if not hdr: | |
raise ValueError('corrupted stream, missing header') | |
state = crypto_secretstream_xchacha20poly1305_state() | |
crypto_secretstream_xchacha20poly1305_init_pull(state, hdr, symmetric_key) | |
# read the header version and chunk size | |
hdr = srcfp.read( | |
_header_struct.size + crypto_secretstream_xchacha20poly1305_ABYTES, | |
) | |
chunk, tag = crypto_secretstream_xchacha20poly1305_pull(state, hdr, None) | |
version, chunk_size = _header_struct.unpack(chunk) | |
if version != 1: | |
raise ValueError('unsupported stream version={0}'.format(version)) | |
frame_size = chunk_size + crypto_secretstream_xchacha20poly1305_ABYTES | |
while tag != crypto_secretstream_xchacha20poly1305_TAG_FINAL: | |
frame = srcfp.read(frame_size) | |
if not frame: | |
raise ValueError('corrupted stream, missing chunks') | |
chunk, tag = crypto_secretstream_xchacha20poly1305_pull(state, frame) | |
destfp.write(chunk) | |
def main(): | |
import sys | |
key = random(crypto_secretstream_xchacha20poly1305_KEYBYTES) | |
with open(sys.argv[1], 'rb') as srcfp, open('foo.enc', 'wb') as dstfp: | |
encrypt_stream(srcfp, dstfp, key) | |
with open('foo.enc', 'rb') as srcfp, open('foo.dec', 'wb') as dstfp: | |
decrypt_stream(srcfp, dstfp, key) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Not sure if I was correct about that size, maybe example just breaks with anything above ~10K:
EDIT:
Tracked the issue down to this line -
frame = crypto_secretstream_xchacha20poly1305_push(state, msg, tag=tag)
- error above happens with zero-length msg there, i.e. if file size divides into blocks without remainder.To fix that, guess either different eof check should be used (e.g. C examples use
eof = feof(fp_s);
), or last block to always contain some dummy byte to be deliberately discarded.