Skip to content

Commit

Permalink
1.0-beta
Browse files Browse the repository at this point in the history
  • Loading branch information
Lutymane committed Jun 29, 2023
1 parent 65cc001 commit 9d3bfa3
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 41 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
main
mars
test-suite

.vscode

*.mars
*.tar.br
*.tar.gz
*.tar.gz

tf
6 changes: 5 additions & 1 deletion architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ v2:
v2 prevents from searching for file prefix signatures, since it starts with a random hash

folder preparation:
https://unix.stackexchange.com/questions/533463/tar-contents-in-current-directory-to-stdout
tar will only write stdout to smth
* `tar -c data` (`tar -x data.tar`)
* `brotli --best data.tar -o data.tar.br` (`brotli -d`)

Expand All @@ -34,4 +36,6 @@ https://www.ibm.com/docs/en/zos/2.4.0?topic=rules-pkcs-padding-method

https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption

https://www.openssl.org/docs/man3.1/man3/EVP_EncryptUpdate.html
https://www.openssl.org/docs/man3.1/man3/EVP_EncryptUpdate.html

# overwrite files with 0s
12 changes: 5 additions & 7 deletions integrity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,30 @@
const auto DIGEST_ALG = EVP_sha3_512();
const auto DIGEST_SIZE = EVP_MD_get_size(DIGEST_ALG);

void hash(byte *data, size_t data_size, byte *&digest_buffer)
void hash(const byte *data, const size_t data_size, byte *digest_buffer)
{
EVP_MD_CTX *ctx = EVP_MD_CTX_new();

EVP_DigestInit(ctx, DIGEST_ALG);

EVP_DigestUpdate(ctx, data, data_size);

digest_buffer = new byte[DIGEST_SIZE];

unsigned int digest_size;

EVP_DigestFinal(ctx, digest_buffer, &digest_size);

EVP_MD_CTX_free(ctx);
}

bool verify(byte *data, size_t data_size, byte *digest_buffer)
bool verify(const byte *data, const size_t data_size, const byte *digest_buffer)
{
byte *current_digest_buffer;
byte data_digest_buffer[DIGEST_SIZE];

hash(data, data_size, current_digest_buffer);
hash(data, data_size, data_digest_buffer);

for (size_t b = 0; b < DIGEST_SIZE; b += 1)
{
if (digest_buffer[b] != current_digest_buffer[b])
if (digest_buffer[b] != data_digest_buffer[b])
return false;
}

Expand Down
6 changes: 4 additions & 2 deletions kdf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
// aes256
#define KEY_SIZE (256 / 8)

#define SALT_SIZE (128 / 8)

#include <chrono>
using namespace std::chrono;

#define MB(x) 1024ull * x
#define GB(x) MB(1024ull) * x

void kdf(byte *password, byte password_size, byte *salt, byte salt_size, byte *&key)
void kdf(byte *password, byte password_size, byte *salt, byte salt_size, byte *key)
{
EVP_KDF *kdf = EVP_KDF_fetch(NULL, "ARGON2D", NULL);

Expand Down Expand Up @@ -48,7 +50,7 @@ void kdf(byte *password, byte password_size, byte *salt, byte salt_size, byte *&

printf("[KDF] Iterations: %zu, memcost: %zu, lanes: %zu\n", iterations, memcost, lanes);

key = new byte[KEY_SIZE];
// key = new byte[KEY_SIZE];

// @todo error, undefined
/* required if threads > 1 */
Expand Down
205 changes: 176 additions & 29 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,64 @@
#include "./integrity.hpp"
#include "./pcbc.hpp"

#define SALT_SIZE (128 / 8)
#define ENCRYPTED_EXT std::string(".mars")

int main(void)
void encrypt(const std::string filename, const std::string password)
{

#pragma region File digest
#pragma region Create brotli compressed archive

std::string tar_path = filename + ".tar.br";
std::string command = "tar -cO " + filename + " | brotli --best -f - -o " + tar_path;

// size_t file_size = 500;
// byte *file = new byte[file_size];
// RAND_bytes(file, file_size);
system(command.c_str());

byte *file = (byte *)"some test file content";
size_t file_size = strlen((char *)file);
#pragma endregion

byte *digest;
#pragma region Read tar.br
std::ifstream fs(tar_path, std::ios::binary);

hash(file, file_size, digest);
// extra parens are required????
std::string file((std::istreambuf_iterator<char>(fs)),
(std::istreambuf_iterator<char>()));

// printf("Digest = %s\n", OPENSSL_buf2hexstr(digest_buffer, DIGEST_SIZE));
fs.close();

size_t file_size = file.length();
#pragma endregion

#pragma region Password input and key derivation
printf("Input password:\n");
#pragma region Overwrite file and delete it

std::string password;
getline(std::cin, password);
std::ofstream of(tar_path);
for (size_t f = 0; f < file_size; f += 1)
{
of.put(0);
}
of.close();

system("clear");
// remove file
std::remove(tar_path.c_str());
#pragma endregion

#pragma region File digest

byte digest[DIGEST_SIZE];

hash((byte *)file.data(), file_size, digest);

// printf("Digest = %s\n", OPENSSL_buf2hexstr(digest_buffer, DIGEST_SIZE));

#pragma endregion

#pragma region Key derivation

byte salt[SALT_SIZE];
RAND_bytes(salt, SALT_SIZE);

// printf("Password (%zu) = %s\n", password.length(), password.c_str());
// printf("Salt = %s\n", OPENSSL_buf2hexstr(salt, SALT_SIZE));

byte *key;
byte key[KEY_SIZE];

kdf((byte *)password.c_str(), password.length(), salt, SALT_SIZE, key);

Expand All @@ -58,36 +79,162 @@ int main(void)

#pragma region File encryption

byte *iv = new byte[IV_SIZE];
byte iv[IV_SIZE];
RAND_bytes(iv, IV_SIZE);

byte *ciphertext;
size_t ciphertext_size;

encrypt_aes256_pcbc(file, file_size, key, iv, ciphertext, ciphertext_size);
encrypt_aes256_pcbc((byte *)file.data(), file_size, key, iv, ciphertext, ciphertext_size);

// erase key
memset(key, 0, KEY_SIZE);
OPENSSL_cleanse(key, KEY_SIZE);
OPENSSL_cleanse(file.data(), file_size);

#pragma endregion

#pragma region Writing to disk

std::ofstream encrypted_tar;
encrypted_tar.open("encrypted.mars", std::ios::binary | std::ios::trunc);
std::ofstream encrypted(filename + ".mars", std::ios::binary | std::ios::trunc);

// << can't be used
encrypted_tar.write((char *)salt, SALT_SIZE);
encrypted_tar.write((char *)digest, DIGEST_SIZE);
encrypted_tar.write((char *)iv, IV_SIZE);
encrypted_tar.write((char *)ciphertext, ciphertext_size);
encrypted.write((char *)salt, SALT_SIZE);
encrypted.write((char *)digest, DIGEST_SIZE);
encrypted.write((char *)iv, IV_SIZE);
encrypted.write((char *)ciphertext, ciphertext_size);

// vs code doesn't immediately update files in side bar, so it may appear ofstream didn't create the file
encrypted_tar.close();
encrypted.close();

printf("File written! %zu bytes\n", SALT_SIZE + DIGEST_SIZE + IV_SIZE + ciphertext_size);

#pragma endregion
}

void decrypt(const std::string filename, const std::string password)
{
#pragma region Read data

byte salt[SALT_SIZE];
byte digest[DIGEST_SIZE];
byte iv[IV_SIZE];

std::ifstream encrypted(filename, std::ios::binary);
encrypted.read((char *)salt, SALT_SIZE);
encrypted.read((char *)digest, DIGEST_SIZE);
encrypted.read((char *)iv, IV_SIZE);

std::string ciphertext((std::istreambuf_iterator<char>(encrypted)),
(std::istreambuf_iterator<char>()));

size_t ciphertext_size = ciphertext.length();

#pragma endregion

#pragma region Key derivation

byte key[KEY_SIZE];

kdf((byte *)password.c_str(), password.length(), salt, SALT_SIZE, key);

#pragma endregion

#pragma region Decryption

byte *plaintext;
size_t plaintext_size;

decrypt_aes256_pcbc((byte *)ciphertext.data(), ciphertext_size, key, iv, plaintext, plaintext_size);

OPENSSL_cleanse(key, KEY_SIZE);

#pragma endregion

#pragma Integrity check

if (!verify(plaintext, plaintext_size, digest))
{
printf("Integrity check failed!\n");
abort();
}

#pragma endregion

#pragma region Decompress and untar

std::string path = filename.substr(0, filename.length() - ENCRYPTED_EXT.length()) + ".tar.br";

std::ofstream fs;

fs.open(path, std::ios::binary);
fs.write((char *)plaintext, plaintext_size);
fs.close();

OPENSSL_cleanse(plaintext, plaintext_size);

std::string command = "brotli -d " + path + " --stdout | tar -x";

system(command.c_str());

// overwrite with 0s
fs.open(path, std::ios::binary);
for (size_t f = 0; f < plaintext_size; f += 1)
{
fs.put(0);
}
fs.close();

std::remove((path).c_str());

printf("File decrypted!\n");

#pragma endregion
}

int main(int argc, char **argv)
{
if (argc != 2)
{
printf(
"\e[33m\e[3m\e[1m"
"Usage:"
"\e[0m\n"

" \e[41m"
"mars"
"\e[49m \e[36m"
"encrypted.mars\n"

"\e[39m\e[3m"
"or"
"\e[0m\n"

" \e[41m"
"mars"
"\e[49m \e[33m"
"path/to/folder\n");
return 0;
}

std::string argument(argv[1]);

#pragma region Password input
printf("\e[33m\e[3m\e[1m"
"Input password:\n"
"\e[0m");

std::string password;
getline(std::cin, password);

system("clear");

#pragma endregion

return 0;
if (argument.ends_with(ENCRYPTED_EXT))
{
decrypt(argument, password);
}
else
{
encrypt(argument, password);
}
}
3 changes: 2 additions & 1 deletion makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ OPENSSL_PATH=/home/ceo/src/openssl
build:
# source should go before libs, otherwise they are skipped
# -L/usr/local/lib64/
g++ main.cpp -pthread -L/usr/local/lib64/ -lcrypto -o main
# -std=c++2a for string.endsWith
g++ main.cpp -std=c++2a -pthread -L/usr/local/lib64/ -lcrypto -o mars

test:
g++ test.cpp -pthread -L/usr/local/lib64/ -lcrypto -o test-suite && ./test-suite
10 changes: 10 additions & 0 deletions pcbc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ void decrypt_aes256_pcbc(const byte *ciphertext, const size_t ciphertext_size, c
}

size_t padding_size = plaintext[ciphertext_size - 1];

// @note sometimes incorrect password may lead to incorrect decryption and thus
// extremely big plaintext_size values, when padding_size turns out to be bigger
// than ciphertext size (overflow)
if (padding_size > BLOCK_SIZE)
{
printf("Integrity is broken\n");
abort();
}

plaintext_size = ciphertext_size - padding_size;

EVP_CIPHER_CTX_free(ctx);
Expand Down

0 comments on commit 9d3bfa3

Please sign in to comment.