diff --git a/veracrypt/verausbdrive.py b/veracrypt/verausbdrive.py new file mode 100755 index 0000000..cac5224 --- /dev/null +++ b/veracrypt/verausbdrive.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 + +import argparse +import getpass +import keyring +import os +import subprocess +import sys + +DESCRIPTION = "Wrapper for VeraCrypt to assist in working with data on an encrypted usb drive (e.g.\ a GIT repository)." +KEYRING_SERVICE = "de.tu-dortmund.cs.ls1.veracryptwrapper.hisdnp" +VERA_CONTAINER_NAME_ENV_VAR = "VERA_CONTAINER_NAME" + +DEFAULT_DRIVE_NAME = "KINGSTON" +DEFAULT_VERA_VOL_NAME = "NO NAME" +#TODO determine this based on OS +DEFAULT_MNT_PATH = "/Volumes" + +VERA_BINARIES = [ + 'veracrypt', + 'VeraCrypt', + '/Applications/VeraCrypt.app/Contents/MacOS/VeraCrypt', #MacOS +] + +VERA_PARAM_TEXT_MODE = '-t' +VERA_PARAM_STDIN = '--stdin' +VERA_PARAM_NON_INTERACTIVE = '--non-interactive' + +ARG_CONTAINER_NAME = "arg_container" +ARG_USB_DRIVE_NAME = "arg_usbstick" +ARG_VERA_VOL_NAME = "arg_volname" +ARG_MOUNT_PATH = "arg_mountpath" +ARG_UNMOUNT_USB_DRIVE = "arg_unmountusbdrive" +ARG_RESET_PASSWORD = "arg_resetpw" +ARG_VERA_DISMOUNT = "arg_vera_dismount" + +def __vera_binary(): + binaries = [ b for b in VERA_BINARIES if os.path.exists(b) ] + assert binaries, "Unable to find VeraCrypt executable!" + + return binaries[0] + +def __sync_io(): + #Sync all I/O devices (just in case) + res = subprocess.run([ + 'sync' + ]) + res.check_returncode() + +def __store_password(pw): + username = getpass.getuser() + keyring.set_password(KEYRING_SERVICE, username, pw) + +def __delete_password(): + username = getpass.getuser() + keyring.delete_password(KEYRING_SERVICE, username) + +def __get_password(reset_pw = False): + if reset_pw: + __delete_password() + + username = getpass.getuser() + pw = keyring.get_password(KEYRING_SERVICE, username) + if not pw: + pw = getpass.getpass() + return pw + +def __vera_exec(option_list, password = None): + args = [ __vera_binary(), VERA_PARAM_TEXT_MODE ] + + if password: + args = args + [ VERA_PARAM_STDIN ] + password = password.encode() + + return subprocess.run(args + option_list, input = password).returncode + +def vera_unmount(volume_path): + __sync_io() + res = __vera_exec(['-d', volume_path]) + assert res == 0, \ + "Failed to dismount VeraCrypt volume! VeraCrypt exited with status {}.".format(res) + __sync_io() + +#TODO the following function is MacOS specific +def unmount_usbdrive(usbdrive_mnt_path): + __sync_io() + res = subprocess.run([ + 'diskutil', + 'unmount', + usbdrive_mnt_path, + ]) + + res.check_returncode() + +def vera_mount(volume_path, container): + pw = __get_password() + + res = __vera_exec([ + VERA_PARAM_NON_INTERACTIVE, + '--mount', container, + ], pw) + + assert res == 0, \ + "Failed to mount VeraCrypt volume! VeraCrypt exited with status {}.".format(res) + __store_password(pw) + +def __parse_cmdline(): + parser = argparse.ArgumentParser(description = DESCRIPTION) + + container_name_default = os.environ.get(VERA_CONTAINER_NAME_ENV_VAR) + if container_name_default: + parser.add_argument(ARG_CONTAINER_NAME, default = container_name_default, nargs = '?', + help = "Path of the encrypted container file on the usb drive") + else: + parser.add_argument(ARG_CONTAINER_NAME, + help = "Path of the encrypted container file on the usb drive") + + parser.add_argument('--mount-path', '-m', dest = ARG_MOUNT_PATH, default = DEFAULT_MNT_PATH) + parser.add_argument('--vera-volume-name', '-v', dest = ARG_VERA_VOL_NAME, default = DEFAULT_VERA_VOL_NAME) + parser.add_argument('--usb-drive-part-name', '-n', dest = ARG_USB_DRIVE_NAME, default = DEFAULT_DRIVE_NAME) + + parser.add_argument('--reset-password', '-p', dest = ARG_RESET_PASSWORD, action = 'store_true') + parser.add_argument('--dismount', '-d', dest = ARG_VERA_DISMOUNT, action = 'store_true') + parser.add_argument('--unmount-usb-drive', '-u', dest = ARG_UNMOUNT_USB_DRIVE, action = 'store_true') + + return vars(parser.parse_args()) + +def __main(): + args = __parse_cmdline() + + if args[ARG_RESET_PASSWORD]: + __delete_password() + + volume_name = args[ARG_VERA_VOL_NAME] + mnt_path = args[ARG_MOUNT_PATH] + volume = os.path.join(mnt_path, volume_name) + + usbdrive_name = args[ARG_USB_DRIVE_NAME] + usbdrive = os.path.join(mnt_path, usbdrive_name) + + container_name = args[ARG_CONTAINER_NAME] + container = os.path.join(usbdrive, container_name) + + if args[ARG_VERA_DISMOUNT]: + vera_unmount(volume) + if args[ARG_UNMOUNT_USB_DRIVE]: + unmount_usbdrive(usbdrive) + return + + vera_mount(volume, container) + +if __name__ == '__main__': + __main()