116 lines
3.1 KiB
D
116 lines
3.1 KiB
D
import std.stdio;
|
|
import std.path;
|
|
import std.file;
|
|
import std.algorithm : endsWith;
|
|
import std.string : strip;
|
|
|
|
import botan.block.block_cipher : BlockCipher;
|
|
import botan.block.aes : AES256;
|
|
import botan.hash.hash : HashFunction;
|
|
import botan.hash.sha2_32 : SHA256;
|
|
|
|
import cipher_utils;
|
|
|
|
int main(string[] args) {
|
|
string action = "encrypt";
|
|
if (args.length >= 2) {
|
|
string command = args[1];
|
|
if (command == "encrypt") {
|
|
action = "encrypt";
|
|
} 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;
|
|
// TODO: Use args to determine block cipher.
|
|
writeln("Enter a password:");
|
|
string password = readln().strip();
|
|
|
|
HashFunction hash = new SHA256();
|
|
auto secureKeyVector = hash.process(password);
|
|
cipher = new AES256();
|
|
cipher.setKey(secureKeyVector);
|
|
|
|
ubyte[] buffer = new ubyte[cipher.blockSize];
|
|
if (isDir(target)) {
|
|
if (action == "encrypt") {
|
|
encryptDir(target, cipher, buffer, true);
|
|
} else if (action == "decrypt") {
|
|
decryptDir(target, cipher, buffer, true);
|
|
} else {
|
|
stderr.writefln!"Unsupported action: %s"(action);
|
|
return 1;
|
|
}
|
|
} else if (isFile(target)) {
|
|
if (action == "encrypt") {
|
|
encryptAndRemoveFile(target, cipher, buffer);
|
|
} else if (action == "decrypt") {
|
|
decryptAndRemoveFile(target, cipher, buffer);
|
|
} else {
|
|
stderr.writefln!"Unsupported action: %s"(action);
|
|
return 1;
|
|
}
|
|
} else {
|
|
stderr.writeln("Target is not a directory or file.");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void encryptAndRemoveFile(string filename, BlockCipher cipher, ref ubyte[] buffer) {
|
|
string encryptedFilename = filename ~ ".enc";
|
|
encryptFile(filename, encryptedFilename, cipher, buffer);
|
|
std.file.remove(filename);
|
|
}
|
|
|
|
void decryptAndRemoveFile(string filename, BlockCipher cipher, ref ubyte[] buffer) {
|
|
string decryptedFilename = filename[0 .. $-4];
|
|
decryptFile(filename, decryptedFilename, cipher, buffer);
|
|
std.file.remove(filename);
|
|
}
|
|
|
|
void encryptDir(string dirname, BlockCipher cipher, ref ubyte[] buffer, bool recursive) {
|
|
string[] dirsToTraverse;
|
|
foreach (DirEntry entry; dirEntries(dirname, SpanMode.shallow, false)) {
|
|
if (entry.isFile && !endsWith(entry.name, ".enc")) {
|
|
encryptAndRemoveFile(entry.name, cipher, buffer);
|
|
} else if (entry.isDir && recursive) {
|
|
dirsToTraverse ~= entry.name;
|
|
}
|
|
}
|
|
if (recursive) {
|
|
foreach (string childDirname; dirsToTraverse) {
|
|
encryptDir(childDirname, cipher, buffer, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void decryptDir(string dirname, BlockCipher cipher, ref ubyte[] buffer, bool recursive) {
|
|
string[] dirsToTraverse;
|
|
foreach (DirEntry entry; dirEntries(dirname, SpanMode.shallow, false)) {
|
|
if (entry.isFile && endsWith(entry.name, ".enc")) {
|
|
decryptAndRemoveFile(entry.name, cipher, buffer);
|
|
} else if (entry.isDir && recursive) {
|
|
dirsToTraverse ~= entry.name;
|
|
}
|
|
}
|
|
if (recursive) {
|
|
foreach (string childDirname; dirsToTraverse) {
|
|
decryptDir(childDirname, cipher, buffer, recursive);
|
|
}
|
|
}
|
|
}
|