Project

General

Profile

Download (8.8 KB) Statistics
| Branch: | Revision:
// 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;
}
(6-6/7)