diff --git a/source/app.d b/source/app.d index bcf2325..44334fb 100644 --- a/source/app.d +++ b/source/app.d @@ -11,6 +11,7 @@ import botan.hash.hash : HashFunction; import botan.hash.sha2_32 : SHA256; import cipher_utils; +import cli_utils; int main(string[] args) { Params params; @@ -20,10 +21,9 @@ int main(string[] args) { return result; } - BlockCipher cipher = null; // TODO: Use args to determine block cipher. - writeln("Enter a password:"); + writeln("Enter a passphrase:"); string password = readPassphrase(); if (password is null) { return 2; @@ -54,108 +54,6 @@ int main(string[] args) { return 0; } -enum Action { - ENCRYPT, - DECRYPT -} - -struct Params { - Action action = Action.ENCRYPT; - string target = "."; - bool recursive = false; - bool verbose = false; -} - -int parseParams(string[] args, ref Params params) { - getopt( - args, - "recursive|r", ¶ms.recursive, - "verbose|v", ¶ms.verbose - ); - if (args.length < 2) { - stderr.writeln("Missing required action."); - return 1; - } - string action = args[1].strip(); - if (action != "encrypt" && action != "decrypt") { - stderr.writeln("Invalid action."); - return 1; - } - if (action == "encrypt") { - params.action = Action.ENCRYPT; - } else if (action == "decrypt") { - params.action = Action.DECRYPT; - } - - if (args.length >= 3) { - params.target = args[2]; - if (!exists(params.target)) { - stderr.writefln!"Target \"%s\" doesn't exist."(params.target); - return 1; - } - } - return 0; -} - -string readPassphrase() { - version(Posix) { - import core.sys.posix.termios : termios, tcgetattr, tcsetattr, ECHO; - import core.sys.posix.stdio; - termios term; - tcgetattr(0, &term); - term.c_lflag &= ~ECHO; - tcsetattr(0, 0, &term); - - string passphrase = readln().strip(); - - term.c_lflag |= ECHO; - tcsetattr(0, 0, &term); - - return passphrase; - } else version(Windows) { - import core.sys.windows.windows; - DWORD con_mode; - DWORD dwRead; - HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hIn, &con_mode); - SetConsoleMode(hIn, con_mode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT)); - - string password = ""; - char BACKSPACE = 8; - char RETURN = 13; - char ch = 0; - while (ReadConsoleA(hIn, &ch, 1, &dwRead, NULL) && ch != RETURN) { - if (ch == BACKSPACE) { - if (password.length > 0) { - password = password[0 .. $ - 1]; - } - } else { - password ~= ch; - } - } - return password; - } else { - stderr.writeln("Unsupported password reading."); - return null; - } -} - -void printUsage() { - writeln(q"HELP -Usage: scrambler [target] [options] -Scramber is a command-line tool for "scrambling", or encrypting files with a -passphrase so they can only be read after they're decrypted using the same -passphrase. - -Provide either the "encrypt" or "decrypt" command, followed by a target that's -either a directory, or an individual file. By default, the target is the -directory that Scrambler was invoked from. - -The following options are available: - -r | --recursive Recursively encrypt/decrypt nested directories. - -v | --verbose Show verbose output during runtime. -HELP"); -} void encryptAndRemoveFile(string filename, BlockCipher cipher, ref ubyte[] buffer) { string encryptedFilename = filename ~ ".enc"; diff --git a/source/cli_utils.d b/source/cli_utils.d new file mode 100644 index 0000000..4dd5b39 --- /dev/null +++ b/source/cli_utils.d @@ -0,0 +1,121 @@ +module cli_utils; + +enum Action { + ENCRYPT, + DECRYPT +} + +struct Params { + Action action = Action.ENCRYPT; + string target = "."; + bool recursive = false; + bool verbose = false; +} + +int parseParams(string[] args, ref Params params) { + import std.getopt; + import std.stdio; + import std.file : exists; + import std.string : strip; + bool isEncrypting; + bool isDecrypting; + getopt( + args, + "recursive|r", ¶ms.recursive, + "verbose|v", ¶ms.verbose, + "encrypt|e", &isEncrypting, + "decrypt|d", &isDecrypting + ); + if (isEncrypting && isDecrypting) { + stderr.writeln("Invalid arguments: Cannot specify both the --encrypt and --decrypt flags."); + } + if (isEncrypting) { + params.action = Action.ENCRYPT; + } else if (isDecrypting) { + params.action = Action.DECRYPT; + } else { + writeln("Determining if we should encrypt or decrypt"); + params.action = Action.ENCRYPT; + } + + if (args.length > 1) { + params.target = args[1]; + if (!exists(params.target)) { + stderr.writefln!"Target \"%s\" doesn't exist."(params.target); + return 1; + } + } + return 0; +} + +void printUsage() { + import std.stdio : writeln; + writeln(q"HELP +Usage: scrambler [target] [options] +Scramber is a command-line tool for encrypting and decrypting files or entire +directories using a passphrase. + +The following options are available: + -e | --encrypt Do an encryption operation on the target file or + directory. + -d | --decrypt Do a decryption operation on the target file or + directory. + -r | --recursive Recursively encrypt/decrypt nested directories. + -v | --verbose Show verbose output during runtime. + -s | --no-suffix Do not add the ".enc" suffix to files. + +Encrypted files are suffixed with ".enc" to indicate that they're encrypted and +cannot be read as usual. If neither --encrypt nor --decrypt flags are provided, +Scrambler will try to determine which operation to do based on the presence of +".enc" file(s) at the target location. It's recommended that you always do +provide an explicit --encrypt or --decrypt flag. +HELP"); +} + +string readPassphrase() { + import std.stdio; + import std.string : strip; + version(Posix) { + import core.sys.posix.termios : termios, tcgetattr, tcsetattr, ECHO; + import core.sys.posix.stdio; + termios term; + tcgetattr(0, &term); + term.c_lflag &= ~ECHO; + tcsetattr(0, 0, &term); + + string passphrase = readln().strip(); + + term.c_lflag |= ECHO; + tcsetattr(0, 0, &term); + + return passphrase; + } else version(Windows) { + import core.sys.windows.windows; + DWORD prev_con_mode; + DWORD con_mode; + DWORD dwRead; + HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hIn, &con_mode); + GetConsoleMode(hIn, &prev_con_mode); + SetConsoleMode(hIn, con_mode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT)); + + string password = ""; + char BACKSPACE = 8; + char RETURN = 13; + char ch = 0; + while (ReadConsoleA(hIn, &ch, 1, &dwRead, NULL) && ch != RETURN) { + if (ch == BACKSPACE) { + if (password.length > 0) { + password = password[0 .. $ - 1]; + } + } else { + password ~= ch; + } + } + SetConsoleMode(hIn, prev_con_mode); + return password; + } else { + stderr.writeln("Cannot securely read password from terminal."); + return null; + } +}