Added reset.sh, cleaned up readme and CLI.
This commit is contained in:
parent
6fd042f594
commit
332a07b7a4
43
README.md
43
README.md
|
@ -1,2 +1,45 @@
|
||||||
# scrambler
|
# scrambler
|
||||||
Tool for encrypting and decrypting entire directories on-the-fly.
|
Tool for encrypting and decrypting entire directories on-the-fly.
|
||||||
|
|
||||||
|
scrambler uses a block cipher and passphrase to encrypt and decrypt files in-place, as a means for quickly securing content without having to move things and/or create archives. scrambler appends a `.enc` extension to encrypted files, so it can use context clues to determine whether you want to perform encryption or decryption.
|
||||||
|
|
||||||
|
As of writing, this application uses an AES-256 block cipher, with a passphrase that's hashed by SHA-256.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
```shell
|
||||||
|
# Show help information.
|
||||||
|
scrambler -h
|
||||||
|
scrambler --help
|
||||||
|
|
||||||
|
# Encrypt or decrypt the current directory.
|
||||||
|
scrambler
|
||||||
|
|
||||||
|
# Encrypt or decrypt a specified directory.
|
||||||
|
scrambler my-dir
|
||||||
|
|
||||||
|
# Encrypt or decrypt a single file.
|
||||||
|
scrambler path/to/my/file.txt
|
||||||
|
|
||||||
|
# Encrypt or decrypt a directory recursively.
|
||||||
|
scrambler my-dir -r
|
||||||
|
|
||||||
|
# Add -v for verbose output.
|
||||||
|
scrambler myfile.txt -v
|
||||||
|
|
||||||
|
# Explicitly choose to encrypt with -e.
|
||||||
|
scrambler my-dir -e
|
||||||
|
|
||||||
|
# Explicitly choose to decrypt with -d.
|
||||||
|
scrambler my-encrypted-dir -d
|
||||||
|
|
||||||
|
# Supply a passphrase from a file.
|
||||||
|
scrambler my-dir -p my-passphrase.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Download
|
||||||
|
You can download a compatible release from the [releases](https://github.com/andrewlalis/scrambler/releases) page. If you can't find any release that's available for your system, you can build it yourself:
|
||||||
|
```shell
|
||||||
|
git clone git@github.com:andrewlalis/scrambler.git
|
||||||
|
cd scrambler
|
||||||
|
dub build --build=release
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
rm -rf test-dir
|
||||||
|
cp -R test-dir-tpl test-dir
|
78
source/app.d
78
source/app.d
|
@ -14,6 +14,10 @@ import cipher_utils;
|
||||||
import cli_utils;
|
import cli_utils;
|
||||||
|
|
||||||
int main(string[] args) {
|
int main(string[] args) {
|
||||||
|
if (args.length >= 2 && (args[1] == "-h" || args[1] == "--help")) {
|
||||||
|
printUsage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Params params;
|
Params params;
|
||||||
int result = parseParams(args, params);
|
int result = parseParams(args, params);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
|
@ -21,80 +25,98 @@ int main(string[] args) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockCipher cipher = null;
|
string passphrase;
|
||||||
// TODO: Use args to determine block cipher.
|
if (params.passphraseFile !is null && exists(params.passphraseFile) && isFile(params.passphraseFile)) {
|
||||||
writeln("Enter a passphrase:");
|
if (params.verbose) {
|
||||||
string password = readPassphrase();
|
writefln!"Reading passphrase from \"%s\""(params.passphraseFile);
|
||||||
if (password is null) {
|
}
|
||||||
|
passphrase = readText(params.passphraseFile).strip();
|
||||||
|
} else {
|
||||||
|
write("Enter passphrase: ");
|
||||||
|
passphrase = readPassphrase();
|
||||||
|
writeln();
|
||||||
|
}
|
||||||
|
if (passphrase is null || passphrase.length == 0) {
|
||||||
|
stderr.writeln("Invalid or missing passphrase.");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
HashFunction hash = new SHA256();
|
HashFunction hash = new SHA256();
|
||||||
auto secureKeyVector = hash.process(password);
|
auto secureKeyVector = hash.process(passphrase);
|
||||||
cipher = new AES256();
|
BlockCipher cipher = new AES256();
|
||||||
cipher.setKey(secureKeyVector);
|
cipher.setKey(secureKeyVector);
|
||||||
|
|
||||||
ubyte[] buffer = new ubyte[cipher.blockSize];
|
ubyte[] buffer = new ubyte[cipher.blockSize];
|
||||||
if (isDir(params.target)) {
|
if (isDir(params.target)) {
|
||||||
if (params.action == Action.ENCRYPT) {
|
if (params.action == Action.ENCRYPT) {
|
||||||
encryptDir(params.target, cipher, buffer, params.recursive);
|
encryptDir(params.target, cipher, buffer, params.recursive, params.verbose);
|
||||||
} else {
|
} else {
|
||||||
decryptDir(params.target, cipher, buffer, params.recursive);
|
bool success = decryptDir(params.target, cipher, buffer, params.recursive, params.verbose);
|
||||||
|
if (!success) {
|
||||||
|
stderr.writeln("Decryption failed.");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (isFile(params.target)) {
|
} else if (isFile(params.target)) {
|
||||||
if (params.action == Action.ENCRYPT) {
|
if (params.action == Action.ENCRYPT) {
|
||||||
encryptAndRemoveFile(params.target, cipher, buffer);
|
encryptAndRemoveFile(params.target, cipher, buffer, params.verbose);
|
||||||
} else {
|
} else {
|
||||||
decryptAndRemoveFile(params.target, cipher, buffer);
|
bool success = decryptAndRemoveFile(params.target, cipher, buffer, params.verbose);
|
||||||
|
if (!success) {
|
||||||
|
stderr.writeln("Decryption failed.");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
stderr.writeln("Target is not a directory or file.");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void encryptAndRemoveFile(string filename, BlockCipher cipher, ref ubyte[] buffer) {
|
void encryptAndRemoveFile(string filename, BlockCipher cipher, ref ubyte[] buffer, bool verbose) {
|
||||||
string encryptedFilename = filename ~ ".enc";
|
string encryptedFilename = filename ~ ENCRYPTED_SUFFIX;
|
||||||
encryptFile(filename, encryptedFilename, cipher, buffer);
|
encryptFile(filename, encryptedFilename, cipher, buffer, verbose);
|
||||||
std.file.remove(filename);
|
std.file.remove(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void decryptAndRemoveFile(string filename, BlockCipher cipher, ref ubyte[] buffer) {
|
bool decryptAndRemoveFile(string filename, BlockCipher cipher, ref ubyte[] buffer, bool verbose) {
|
||||||
string decryptedFilename = filename[0 .. $-4];
|
string decryptedFilename = filename[0 .. $-ENCRYPTED_SUFFIX.length];
|
||||||
decryptFile(filename, decryptedFilename, cipher, buffer);
|
bool success = decryptFile(filename, decryptedFilename, cipher, buffer, verbose);
|
||||||
|
if (!success) return false;
|
||||||
std.file.remove(filename);
|
std.file.remove(filename);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void encryptDir(string dirname, BlockCipher cipher, ref ubyte[] buffer, bool recursive) {
|
void encryptDir(string dirname, BlockCipher cipher, ref ubyte[] buffer, bool recursive, bool verbose) {
|
||||||
string[] dirsToTraverse;
|
string[] dirsToTraverse;
|
||||||
foreach (DirEntry entry; dirEntries(dirname, SpanMode.shallow, false)) {
|
foreach (DirEntry entry; dirEntries(dirname, SpanMode.shallow, false)) {
|
||||||
if (entry.isFile && !endsWith(entry.name, ".enc")) {
|
if (entry.isFile && !endsWith(entry.name, ENCRYPTED_SUFFIX)) {
|
||||||
encryptAndRemoveFile(entry.name, cipher, buffer);
|
encryptAndRemoveFile(entry.name, cipher, buffer, verbose);
|
||||||
} else if (entry.isDir && recursive) {
|
} else if (entry.isDir && recursive) {
|
||||||
dirsToTraverse ~= entry.name;
|
dirsToTraverse ~= entry.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
foreach (string childDirname; dirsToTraverse) {
|
foreach (string childDirname; dirsToTraverse) {
|
||||||
encryptDir(childDirname, cipher, buffer, recursive);
|
encryptDir(childDirname, cipher, buffer, recursive, verbose);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void decryptDir(string dirname, BlockCipher cipher, ref ubyte[] buffer, bool recursive) {
|
bool decryptDir(string dirname, BlockCipher cipher, ref ubyte[] buffer, bool recursive, bool verbose) {
|
||||||
string[] dirsToTraverse;
|
string[] dirsToTraverse;
|
||||||
foreach (DirEntry entry; dirEntries(dirname, SpanMode.shallow, false)) {
|
foreach (DirEntry entry; dirEntries(dirname, SpanMode.shallow, false)) {
|
||||||
if (entry.isFile && endsWith(entry.name, ".enc")) {
|
if (entry.isFile && endsWith(entry.name, ENCRYPTED_SUFFIX)) {
|
||||||
decryptAndRemoveFile(entry.name, cipher, buffer);
|
bool success = decryptAndRemoveFile(entry.name, cipher, buffer, verbose);
|
||||||
|
if (!success) return false;
|
||||||
} else if (entry.isDir && recursive) {
|
} else if (entry.isDir && recursive) {
|
||||||
dirsToTraverse ~= entry.name;
|
dirsToTraverse ~= entry.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
foreach (string childDirname; dirsToTraverse) {
|
foreach (string childDirname; dirsToTraverse) {
|
||||||
decryptDir(childDirname, cipher, buffer, recursive);
|
bool success = decryptDir(childDirname, cipher, buffer, recursive, verbose);
|
||||||
|
if (!success) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,31 @@ module cipher_utils;
|
||||||
|
|
||||||
import botan.block.block_cipher : BlockCipher;
|
import botan.block.block_cipher : BlockCipher;
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
|
import std.file;
|
||||||
|
|
||||||
public void encryptFile(string filename, string outputFilename, BlockCipher cipher, ref ubyte[] buffer) {
|
public const string ENCRYPTED_SUFFIX = ".enc";
|
||||||
|
|
||||||
|
public void encryptFile(string filename, string outputFilename, BlockCipher cipher, ref ubyte[] buffer, bool verbose) {
|
||||||
assert(buffer.length == cipher.blockSize, "Buffer length must match cipher block size.");
|
assert(buffer.length == cipher.blockSize, "Buffer length must match cipher block size.");
|
||||||
|
if (verbose) {
|
||||||
|
writefln!"Encrypting file \"%s\" of %d bytes to \"%s\" using cipher %s."(
|
||||||
|
filename,
|
||||||
|
getSize(filename),
|
||||||
|
outputFilename,
|
||||||
|
cipher.name
|
||||||
|
);
|
||||||
|
}
|
||||||
File fIn = File(filename, "rb");
|
File fIn = File(filename, "rb");
|
||||||
File fOut = File(outputFilename, "wb");
|
File fOut = File(outputFilename, "wb");
|
||||||
// First, write one block containing the file's size.
|
// First, write one block containing the file's size.
|
||||||
writeSizeBytes(buffer, fIn.size);
|
writeSizeBytes(buffer, fIn.size);
|
||||||
|
// Fill the rest of the block with an incrementing series of bytes, so we can easily validate decryption.
|
||||||
|
if (buffer.length > 8) {
|
||||||
|
ubyte marker = 1;
|
||||||
|
for (size_t i = 8; i < buffer.length; i++) {
|
||||||
|
buffer[i] = marker++;
|
||||||
|
}
|
||||||
|
}
|
||||||
cipher.encrypt(buffer);
|
cipher.encrypt(buffer);
|
||||||
fOut.rawWrite(buffer);
|
fOut.rawWrite(buffer);
|
||||||
// Then write the rest of the file.
|
// Then write the rest of the file.
|
||||||
|
@ -18,17 +36,49 @@ public void encryptFile(string filename, string outputFilename, BlockCipher ciph
|
||||||
}
|
}
|
||||||
fIn.close();
|
fIn.close();
|
||||||
fOut.close();
|
fOut.close();
|
||||||
|
if (verbose) {
|
||||||
|
writefln!" Encrypted file has a size of %d bytes."(getSize(outputFilename));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decryptFile(string filename, string outputFilename, BlockCipher cipher, ref ubyte[] buffer) {
|
public bool decryptFile(string filename, string outputFilename, BlockCipher cipher, ref ubyte[] buffer, bool verbose) {
|
||||||
assert(buffer.length == cipher.blockSize, "Buffer length must match cipher block size.");
|
assert(buffer.length == cipher.blockSize, "Buffer length must match cipher block size.");
|
||||||
|
if (verbose) {
|
||||||
|
writefln!"Decrypting file \"%s\" of %d bytes to \"%s\" using cipher %s."(
|
||||||
|
filename,
|
||||||
|
getSize(filename),
|
||||||
|
outputFilename,
|
||||||
|
cipher.name
|
||||||
|
);
|
||||||
|
}
|
||||||
File fIn = File(filename, "rb");
|
File fIn = File(filename, "rb");
|
||||||
File fOut = File(outputFilename, "wb");
|
|
||||||
// First, read one block containing the file's size.
|
// First, read one block containing the file's size.
|
||||||
fIn.rawRead(buffer);
|
fIn.rawRead(buffer);
|
||||||
cipher.decrypt(buffer);
|
cipher.decrypt(buffer);
|
||||||
|
// Verify the sequence of values to ensure decryption was successful.
|
||||||
|
if (buffer.length > 8) {
|
||||||
|
ubyte expectedMarker = 1;
|
||||||
|
for (size_t i = 8; i < buffer.length; i++) {
|
||||||
|
if (buffer[i] != expectedMarker) {
|
||||||
|
if (verbose) {
|
||||||
|
writefln!" Decryption validation failed. Expected byte at index %d to be %d, but got %d."(
|
||||||
|
i,
|
||||||
|
expectedMarker,
|
||||||
|
buffer[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fIn.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
expectedMarker++;
|
||||||
|
}
|
||||||
|
}
|
||||||
ulong size = readSizeBytes(buffer);
|
ulong size = readSizeBytes(buffer);
|
||||||
|
if (verbose) {
|
||||||
|
writefln!" Original file had size of %d bytes."(size);
|
||||||
|
}
|
||||||
ulong bytesWritten = 0;
|
ulong bytesWritten = 0;
|
||||||
|
File fOut = File(outputFilename, "wb");
|
||||||
// Then read the rest of the file.
|
// Then read the rest of the file.
|
||||||
foreach (ubyte[] chunk; fIn.byChunk(buffer)) {
|
foreach (ubyte[] chunk; fIn.byChunk(buffer)) {
|
||||||
cipher.decrypt(buffer);
|
cipher.decrypt(buffer);
|
||||||
|
@ -41,27 +91,48 @@ public void decryptFile(string filename, string outputFilename, BlockCipher ciph
|
||||||
}
|
}
|
||||||
fIn.close();
|
fIn.close();
|
||||||
fOut.close();
|
fOut.close();
|
||||||
|
if (verbose) {
|
||||||
|
writefln!" Decrypted file has a size of %d bytes."(getSize(outputFilename));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
union LongByteArrayUnion {
|
||||||
|
ulong longValue;
|
||||||
|
ubyte[8] bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeSizeBytes(ref ubyte[] bytes, ulong size) {
|
private void writeSizeBytes(ref ubyte[] bytes, ulong size) {
|
||||||
assert(bytes.length >= 4, "Array length must be at least 4.");
|
assert(bytes.length >= 8, "Array length must be at least 8.");
|
||||||
bytes[0] = size & 0xFF;
|
LongByteArrayUnion u;
|
||||||
bytes[1] = (size << 8) & 0xFF;
|
u.longValue = size;
|
||||||
bytes[2] = (size << 16) & 0xFF;
|
for (size_t i = 0; i < 8; i++) bytes[i] = u.bytes[i];
|
||||||
bytes[3] = (size << 24) & 0xFF;
|
if (bytes.length > 8) {
|
||||||
if (bytes.length > 4) {
|
for (size_t i = 8; i < bytes.length; i++) {
|
||||||
for (size_t i = 4; i < bytes.length; i++) {
|
|
||||||
bytes[i] = 0;
|
bytes[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong readSizeBytes(ref ubyte[] bytes) {
|
private ulong readSizeBytes(ref ubyte[] bytes) {
|
||||||
assert(bytes.length >= 4, "Array length must be at least 4.");
|
assert(bytes.length >= 8, "Array length must be at least 8.");
|
||||||
ulong size = 0;
|
LongByteArrayUnion u;
|
||||||
size += bytes[0];
|
for (size_t i = 0; i < 8; i++) u.bytes[i] = bytes[i];
|
||||||
size += bytes[1] << 8;
|
return u.longValue;
|
||||||
size += bytes[2] << 16;
|
}
|
||||||
size += bytes[3] << 24;
|
|
||||||
return size;
|
unittest {
|
||||||
|
ubyte[] buffer = new ubyte[16];
|
||||||
|
|
||||||
|
void doAssert(ulong size) {
|
||||||
|
import std.format;
|
||||||
|
writeSizeBytes(buffer, size);
|
||||||
|
ulong r = readSizeBytes(buffer);
|
||||||
|
assert(r == size, format!"Value read: %d does not match expected: %d."(r, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
doAssert(0);
|
||||||
|
doAssert(1);
|
||||||
|
doAssert(42);
|
||||||
|
doAssert(74_092_382_742_030);
|
||||||
}
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
module cli_utils;
|
module cli_utils;
|
||||||
|
|
||||||
|
import cipher_utils : ENCRYPTED_SUFFIX;
|
||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
ENCRYPT,
|
ENCRYPT,
|
||||||
DECRYPT
|
DECRYPT
|
||||||
|
@ -10,6 +12,7 @@ struct Params {
|
||||||
string target = ".";
|
string target = ".";
|
||||||
bool recursive = false;
|
bool recursive = false;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
string passphraseFile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parseParams(string[] args, ref Params params) {
|
int parseParams(string[] args, ref Params params) {
|
||||||
|
@ -24,19 +27,12 @@ int parseParams(string[] args, ref Params params) {
|
||||||
"recursive|r", ¶ms.recursive,
|
"recursive|r", ¶ms.recursive,
|
||||||
"verbose|v", ¶ms.verbose,
|
"verbose|v", ¶ms.verbose,
|
||||||
"encrypt|e", &isEncrypting,
|
"encrypt|e", &isEncrypting,
|
||||||
"decrypt|d", &isDecrypting
|
"decrypt|d", &isDecrypting,
|
||||||
|
"passphrase-file|p", ¶ms.passphraseFile
|
||||||
);
|
);
|
||||||
if (isEncrypting && isDecrypting) {
|
if (isEncrypting && isDecrypting) {
|
||||||
stderr.writeln("Invalid arguments: Cannot specify both the --encrypt and --decrypt flags.");
|
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) {
|
if (args.length > 1) {
|
||||||
params.target = args[1];
|
params.target = args[1];
|
||||||
|
@ -45,9 +41,33 @@ int parseParams(string[] args, ref Params params) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isEncrypting) {
|
||||||
|
params.action = Action.ENCRYPT;
|
||||||
|
} else if (isDecrypting) {
|
||||||
|
params.action = Action.DECRYPT;
|
||||||
|
} else {
|
||||||
|
params.action = determineBestAction(params.target);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Action determineBestAction(string target) {
|
||||||
|
import std.file;
|
||||||
|
import std.algorithm : endsWith;
|
||||||
|
|
||||||
|
if (isFile(target)) {
|
||||||
|
return endsWith(target, ENCRYPTED_SUFFIX) ? Action.DECRYPT : Action.ENCRYPT;
|
||||||
|
} else if (isDir(target)) {
|
||||||
|
foreach (DirEntry entry; dirEntries(target, SpanMode.breadth, false)) {
|
||||||
|
if (entry.isFile && endsWith(entry.name, ENCRYPTED_SUFFIX)) return Action.DECRYPT;
|
||||||
|
}
|
||||||
|
return Action.ENCRYPT;
|
||||||
|
} else {
|
||||||
|
return Action.ENCRYPT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void printUsage() {
|
void printUsage() {
|
||||||
import std.stdio : writeln;
|
import std.stdio : writeln;
|
||||||
writeln(q"HELP
|
writeln(q"HELP
|
||||||
|
@ -60,6 +80,9 @@ The following options are available:
|
||||||
directory.
|
directory.
|
||||||
-d | --decrypt Do a decryption operation on the target file or
|
-d | --decrypt Do a decryption operation on the target file or
|
||||||
directory.
|
directory.
|
||||||
|
-p | --passphrase-file A file to read the passphrase from, instead of
|
||||||
|
prompting the user for a passphrase in the command
|
||||||
|
line.
|
||||||
-r | --recursive Recursively encrypt/decrypt nested directories.
|
-r | --recursive Recursively encrypt/decrypt nested directories.
|
||||||
-v | --verbose Show verbose output during runtime.
|
-v | --verbose Show verbose output during runtime.
|
||||||
-s | --no-suffix Do not add the ".enc" suffix to files.
|
-s | --no-suffix Do not add the ".enc" suffix to files.
|
||||||
|
@ -67,8 +90,7 @@ The following options are available:
|
||||||
Encrypted files are suffixed with ".enc" to indicate that they're encrypted and
|
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,
|
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
|
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
|
".enc" file(s) at the target location.
|
||||||
provide an explicit --encrypt or --decrypt flag.
|
|
||||||
HELP");
|
HELP");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
Loading…
Reference in New Issue