root/src/crypt_util.cc @ 95c2e096
| f16b75e3 | david.sorber | // Modifiy from source found here: https://gist.github.com/leto/1a984b1daa710ed55464c093640d6769
|
|
#include <cstdint>
|
|||
#include <cstdio>
|
|||
#include <cstring>
|
|||
#include <iostream>
|
|||
#include <string>
|
|||
#include <termios.h>
|
|||
#include <unistd.h>
|
|||
#include <sodium.h>
|
|||
#include "StreamUtils.h"
|
|||
const std::string BOLD{"\033[1m"};
|
|||
const std::string ENDC{"\033[0m"};
|
|||
const std::string RED{"\033[31m"};
|
|||
const std::string YELLOW{"\033[33m"};
|
|||
const std::string PURPLE{"\033[35m"};
|
|||
const std::string LBLUE{"\033[1;34m"};
|
|||
const std::string UP_ONE = "\033[1A";
|
|||
const uint32_t CHUNK_SIZE{4096};
|
|||
#define ERROR_MSG BOLD << RED << "ERROR: " << ENDC
|
|||
void get_password(char* password)
|
|||
{
|
|||
static struct termios old_terminal;
|
|||
static struct termios new_terminal;
|
|||
// get settings of the actual terminal
|
|||
tcgetattr(STDIN_FILENO, &old_terminal);
|
|||
// do not echo the characters
|
|||
new_terminal = old_terminal;
|
|||
new_terminal.c_lflag &= ~(ECHO);
|
|||
// set this as the new terminal options
|
|||
tcsetattr(STDIN_FILENO, TCSANOW, &new_terminal);
|
|||
// get the password
|
|||
// the user can add chars and delete if he puts it wrong
|
|||
// the input process is done when he hits the enter
|
|||
// the \n is stored, we replace it with \0
|
|||
if (fgets(password, BUFSIZ, stdin) == NULL)
|
|||
{
|
|||
password[0] = '\0';
|
|||
}
|
|||
else
|
|||
{
|
|||
password[strlen(password) - 1] = '\0';
|
|||
}
|
|||
// go back to the old settings
|
|||
tcsetattr(STDIN_FILENO, TCSANOW, &old_terminal);
|
|||
}
|
|||
static int encrypt(
|
|||
const char *target_file,
|
|||
const char *source_file,
|
|||
const uint8_t key[crypto_secretstream_xchacha20poly1305_KEYBYTES],
|
|||
const uint8_t salt[crypto_pwhash_SALTBYTES])
|
|||
{
|
|||
// NOTE: the salt value is not used directly in encrpytion but is written
|
|||
// to the first XYZ bytes of the output file
|
|||
uint8_t buf_in[CHUNK_SIZE]{};
|
|||
uint8_t buf_out[CHUNK_SIZE + crypto_secretstream_xchacha20poly1305_ABYTES]{};
|
|||
uint8_t header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]{};
|
|||
crypto_secretstream_xchacha20poly1305_state st;
|
|||
FILE* fp_s = std::fopen(source_file, "rb");
|
|||
FILE* fp_t = std::fopen(target_file, "wb");
|
|||
// First write out salt bytes to the output file
|
|||
std::fwrite(salt, 1, crypto_pwhash_SALTBYTES, fp_t);
|
|||
// Next write out header
|
|||
crypto_secretstream_xchacha20poly1305_init_push(&st, header, key);
|
|||
std::fwrite(header, 1, sizeof(header), fp_t);
|
|||
// Iterate over the input file a chunk at a time, encrypt each, and then
|
|||
// write it to the output file
|
|||
size_t rlen;
|
|||
long long unsigned out_len;
|
|||
int eof;
|
|||
uint8_t tag;
|
|||
do
|
|||
{
|
|||
rlen = std::fread(buf_in, 1, sizeof(buf_in), fp_s);
|
|||
eof = std::feof(fp_s);
|
|||
tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0;
|
|||
crypto_secretstream_xchacha20poly1305_push(&st, buf_out, &out_len, buf_in,
|
|||
rlen,nullptr, 0, tag);
|
|||
std::fwrite(buf_out, 1, (size_t)out_len, fp_t);
|
|||
}
|
|||
while (! eof);
|
|||
std::fclose(fp_t);
|
|||
std::fclose(fp_s);
|
|||
return 0;
|
|||
}
|
|||
static int decrypt(
|
|||
const char *target_file,
|
|||
const char *source_file,
|
|||
const uint8_t key[crypto_secretstream_xchacha20poly1305_KEYBYTES],
|
|||
uint32_t saltLength)
|
|||
{
|
|||
// NOTE: the "saltLength" parameter is used to skip past the salt value at
|
|||
// the beginning of the file
|
|||
int ret = -1;
|
|||
uint8_t buf_in[CHUNK_SIZE + crypto_secretstream_xchacha20poly1305_ABYTES]{};
|
|||
uint8_t buf_out[CHUNK_SIZE]{};
|
|||
uint8_t header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]{};
|
|||
crypto_secretstream_xchacha20poly1305_state st;
|
|||
FILE* fp_s = std::fopen(source_file, "rb");
|
|||
FILE* fp_t = std::fopen(target_file, "wb");
|
|||
// Skip past the salt value at the beginning of the file
|
|||
std::fseek(fp_s, saltLength, SEEK_SET);
|
|||
std::fread(header, 1, sizeof header, fp_s);
|
|||
if (crypto_secretstream_xchacha20poly1305_init_pull(&st, header, key) != 0)
|
|||
{
|
|||
std::cerr << ERROR_MSG << "during decrypt: incomplete header" << std::endl;
|
|||
goto ret;
|
|||
}
|
|||
size_t rlen;
|
|||
long long unsigned out_len;
|
|||
int eof;
|
|||
uint8_t tag;
|
|||
do
|
|||
{
|
|||
rlen = fread(buf_in, 1, sizeof buf_in, fp_s);
|
|||
eof = feof(fp_s);
|
|||
int rc = crypto_secretstream_xchacha20poly1305_pull(&st, buf_out,
|
|||
&out_len, &tag,
|
|||
buf_in, rlen,
|
|||
nullptr, 0);
|
|||
if (rc != 0)
|
|||
{
|
|||
std::cerr << ERROR_MSG << "during decrypt: corrupted chunk" << std::endl;
|
|||
goto ret;
|
|||
}
|
|||
if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && ! eof)
|
|||
{
|
|||
std::cerr << ERROR_MSG << "during decrypt: premature end of file" << std::endl;
|
|||
goto ret;
|
|||
}
|
|||
std::fwrite(buf_out, 1, (size_t) out_len, fp_t);
|
|||
}
|
|||
while (! eof);
|
|||
ret = 0;
|
|||
ret:
|
|||
std::fclose(fp_t);
|
|||
std::fclose(fp_s);
|
|||
return ret;
|
|||
}
|
|||
int main(int argc, char** argv)
|
|||
{
|
|||
const uint32_t saltLen{crypto_pwhash_SALTBYTES};
|
|||
const uint32_t keyLen{crypto_secretstream_xchacha20poly1305_KEYBYTES};
|
|||
char passwordBuffer[BUFSIZ]{};
|
|||
if (sodium_init() != 0)
|
|||
{
|
|||
return 1;
|
|||
}
|
|||
uint8_t key[keyLen]{};
|
|||
#if 1
|
|||
uint8_t salt[saltLen]{};
|
|||
#else
|
|||
uint8_t salt[saltLen]{0x1A, 0x19, 0x2F, 0x62, 0x65, 0x9A,
|
|||
0x9C, 0x64, 0x99, 0xA7, 0xD1, 0x3B,
|
|||
0x02, 0xC4, 0xA1, 0xC9};
|
|||
#endif
|
|||
// DEBUGGERY
|
|||
// std::cout << "SALT: " << HexBytesInlineBE(salt, saltLen) << std::endl;
|
|||
// std::cout << "KEY: " << HexBytesInlineBE(key, keyLen) << std::endl;
|
|||
if (argc < 2)
|
|||
{
|
|||
std::cerr << ERROR_MSG << "please provide correct number of arguments!"
|
|||
<< std::endl;
|
|||
return -1;
|
|||
}
|
|||
if (std::strlen(argv[1]) == 3 && std::strncmp(argv[1], "enc", 3) == 0)
|
|||
{
|
|||
// Encrypt the file
|
|||
// TODO: confirm input file exists
|
|||
// Generate random salt
|
|||
randombytes_buf(salt, saltLen);
|
|||
std::cout << "Password: ";
|
|||
get_password(passwordBuffer);
|
|||
std::cout << std::endl;
|
|||
// Generate key from password and salt
|
|||
if (crypto_pwhash(key, sizeof key, passwordBuffer,
|
|||
std::strlen(passwordBuffer), salt,
|
|||
crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|||
crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|||
crypto_pwhash_ALG_ARGON2ID13) != 0)
|
|||
{
|
|||
std::cerr << ERROR_MSG << "crypto_pwhash - out of memory" << std::endl;
|
|||
return -1;
|
|||
}
|
|||
// Explicitly zeroize the password buffer
|
|||
explicit_bzero(passwordBuffer, BUFSIZ);
|
|||
// Create a name for the cipher text file by appending ".enc" to the
|
|||
// name of the plain text file
|
|||
std::string plainTextFile(argv[2]);
|
|||
std::string cipherTextFile(plainTextFile + ".enc");
|
|||
std::cout << "PT: " << plainTextFile << std::endl;
|
|||
std::cout << "CT: " << cipherTextFile << std::endl;
|
|||
if (encrypt(cipherTextFile.c_str(),
|
|||
plainTextFile.c_str(),
|
|||
key, salt) != 0)
|
|||
{
|
|||
return 1;
|
|||
}
|
|||
}
|
|||
else if (std::strlen(argv[1]) == 3 && std::strncmp(argv[1], "dec", 3) == 0)
|
|||
{
|
|||
// Decrypt the file
|
|||
// TODO: confirm input file exists
|
|||
// Create a name for the cipher text file by appending ".enc" to the
|
|||
// name of the plain text file
|
|||
std::string cipherTextFile(argv[2]);
|
|||
std::string plainTextFile(cipherTextFile + ".dec");
|
|||
std::cout << "CT: " << cipherTextFile << std::endl;
|
|||
std::cout << "PT: " << plainTextFile << std::endl;
|
|||
std::cout << "Password: ";
|
|||
get_password(passwordBuffer);
|
|||
std::cout << std::endl;
|
|||
// Read in salt value
|
|||
FILE* ctFile = std::fopen(cipherTextFile.c_str(), "rb");
|
|||
std::fread(salt, 1, saltLen, ctFile);
|
|||
std::fclose(ctFile);
|
|||
// Generate key from password and salt
|
|||
if (crypto_pwhash(key, sizeof key, passwordBuffer,
|
|||
std::strlen(passwordBuffer), salt,
|
|||
crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|||
crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|||
crypto_pwhash_ALG_ARGON2ID13) != 0)
|
|||
{
|
|||
std::cerr << ERROR_MSG << "crypto_pwhash - out of memory" << std::endl;
|
|||
return -1;
|
|||
}
|
|||
// Explicitly zeroize the password buffer
|
|||
explicit_bzero(passwordBuffer, BUFSIZ);
|
|||
if (decrypt(plainTextFile.c_str(),
|
|||
cipherTextFile.c_str(),
|
|||
key, saltLen) != 0)
|
|||
{
|
|||
return 1;
|
|||
}
|
|||
}
|
|||
else
|
|||
{
|
|||
std::cerr << ERROR_MSG << "invalid mode \"" << argv[1] << "\"!" << std::endl;
|
|||
return 2;
|
|||
}
|
|||
return 0;
|
|||
}
|