From 093dea5197d1a66c9734aa20ff554ca8dc654d89 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sun, 23 Jun 2019 19:06:07 +0900 Subject: Initial commit --- encrypt.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 encrypt.py diff --git a/encrypt.py b/encrypt.py new file mode 100644 index 0000000..9809d45 --- /dev/null +++ b/encrypt.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +import secrets +from Cryptodome.Cipher import AES +import base64 +import struct +import subprocess +import binascii + +def _pad(data, blocksize): + return (data + b'\x80' + b'\x00'*blocksize)[:blocksize] + +def _unpad(data): + data = data.rstrip(b'\0') + if data[-1] == 0x80: + return data[:-1] + else: + raise ValueError('Invalid padding!') + +def encrypt_file(filename_in): + file_id = secrets.token_urlsafe(22) + auth_secret = secrets.token_bytes(16) + key = secrets.token_bytes(16) + cipher_nonce = secrets.token_bytes(8) + + token_cipher = AES.new(auth_secret, AES.MODE_GCM) + ciphertext, token_tag = token_cipher.encrypt_and_digest(key) + token = base64.b64encode(ciphertext) + + with open(filename_in, 'rb', buffering=8192) as fin, open(f'{file_id}.enc', 'wb', buffering=8192) as fout: + fout.write(token_cipher.nonce) # 16 bytes + fout.write(token_tag) # 16 bytes + fout.write(auth_secret) # 16 bytes + fout.write(cipher_nonce) # 8 bytes + + subprocess.check_call(['openssl', 'enc', '-aes-128-ctr', + '-K', binascii.hexlify(key), + '-iv', binascii.hexlify(cipher_nonce + '\0'*8) + + data = fin.read(cipher.block_size) or None + while data is not None: + next_data = fin.read(cipher.block_size) or None + if not next_data: # padding required + if len(data) == cipher.block_size: + next_data = b'' + else: + data = _pad(data, cipher.block_size) + fout.write(cipher.encrypt(data)) + data = next_data + + return file_id, token + +def decrypt_generator(file_id, token, seek=0): + + with open(f'{file_id}.enc', 'rb', buffering=8192) as fin: + token_nonce = fin.read(16) + token_tag = fin.read(16) + auth_secret = fin.read(16) + data_nonce = fin.read(8) + + ciphertext = base64.b64decode(token) + token_cipher = AES.new(auth_secret, AES.MODE_GCM, nonce=token_nonce) + key = token_cipher.decrypt_and_verify(ciphertext, token_tag) + + # Use token cipher's block size assuming the two are the same + cipher = AES.new(key, AES.MODE_CTR, + initial_value=struct.pack('