Added proper password reading.

This commit is contained in:
Andrew Lalis 2023-03-21 08:48:12 +01:00
parent 27979d58e5
commit f2df5a48d2
1 changed files with 121 additions and 34 deletions

View File

@ -3,6 +3,7 @@ import std.path;
import std.file; import std.file;
import std.algorithm : endsWith; import std.algorithm : endsWith;
import std.string : strip; import std.string : strip;
import std.getopt;
import botan.block.block_cipher : BlockCipher; import botan.block.block_cipher : BlockCipher;
import botan.block.aes : AES256; import botan.block.aes : AES256;
@ -12,32 +13,21 @@ import botan.hash.sha2_32 : SHA256;
import cipher_utils; import cipher_utils;
int main(string[] args) { int main(string[] args) {
string action = "encrypt"; Params params;
if (args.length >= 2) { int result = parseParams(args, params);
string command = args[1]; if (result != 0) {
if (command == "encrypt") { printUsage();
action = "encrypt"; return result;
} else if (command == "decrypt") {
action = "decrypt";
} else {
stderr.writefln!"Invalid action: %s"(command);
return 1;
}
} }
string target = ".";
if (args.length >= 3) {
target = args[2];
if (!exists(target)) {
stderr.writefln!"Target \"%s\" doesn't exist."(target);
return 1;
}
}
BlockCipher cipher = null; BlockCipher cipher = null;
// TODO: Use args to determine block cipher. // TODO: Use args to determine block cipher.
writeln("Enter a password:"); writeln("Enter a password:");
string password = readln().strip(); string password = readPassphrase();
if (password is null) {
return 2;
}
HashFunction hash = new SHA256(); HashFunction hash = new SHA256();
auto secureKeyVector = hash.process(password); auto secureKeyVector = hash.process(password);
@ -45,23 +35,17 @@ int main(string[] args) {
cipher.setKey(secureKeyVector); cipher.setKey(secureKeyVector);
ubyte[] buffer = new ubyte[cipher.blockSize]; ubyte[] buffer = new ubyte[cipher.blockSize];
if (isDir(target)) { if (isDir(params.target)) {
if (action == "encrypt") { if (params.action == Action.ENCRYPT) {
encryptDir(target, cipher, buffer, true); encryptDir(params.target, cipher, buffer, params.recursive);
} else if (action == "decrypt") {
decryptDir(target, cipher, buffer, true);
} else { } else {
stderr.writefln!"Unsupported action: %s"(action); decryptDir(params.target, cipher, buffer, params.recursive);
return 1;
} }
} else if (isFile(target)) { } else if (isFile(params.target)) {
if (action == "encrypt") { if (params.action == Action.ENCRYPT) {
encryptAndRemoveFile(target, cipher, buffer); encryptAndRemoveFile(params.target, cipher, buffer);
} else if (action == "decrypt") {
decryptAndRemoveFile(target, cipher, buffer);
} else { } else {
stderr.writefln!"Unsupported action: %s"(action); decryptAndRemoveFile(params.target, cipher, buffer);
return 1;
} }
} else { } else {
stderr.writeln("Target is not a directory or file."); stderr.writeln("Target is not a directory or file.");
@ -70,6 +54,109 @@ int main(string[] args) {
return 0; 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", &params.recursive,
"verbose|v", &params.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 <encrypt|decrypt> [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) { void encryptAndRemoveFile(string filename, BlockCipher cipher, ref ubyte[] buffer) {
string encryptedFilename = filename ~ ".enc"; string encryptedFilename = filename ~ ".enc";
encryptFile(filename, encryptedFilename, cipher, buffer); encryptFile(filename, encryptedFilename, cipher, buffer);