240 lines
9.0 KiB
C++
240 lines
9.0 KiB
C++
|
#include <iostream>
|
||
|
#include <stdexcept>
|
||
|
#include <vector>
|
||
|
#include <openssl/evp.h>
|
||
|
#include <openssl/rand.h>
|
||
|
#include <openssl/hmac.h>
|
||
|
#include <openssl/sha.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
/*
|
||
|
* EonaCatCipher - Because security is key!
|
||
|
*
|
||
|
* Copyright (c) 2024 EonaCat (Jeroen Saey)
|
||
|
*
|
||
|
* https://eonacat.com/license
|
||
|
*
|
||
|
* TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||
|
* OF SOFTWARE BY EONACAT (JEROEN SAEY)
|
||
|
*
|
||
|
* This software is provided "as is", without any express or implied warranty.
|
||
|
* In no event shall the authors or copyright holders be liable for any claim,
|
||
|
* damages or other liability, whether in an action of contract, tort or otherwise,
|
||
|
* arising from, out of or in connection with the software or the use or other
|
||
|
* dealings in the software.
|
||
|
*
|
||
|
* You may use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and permit persons to whom the Software is furnished
|
||
|
* to do so, subject to the following conditions:
|
||
|
*
|
||
|
* 1. The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* 2. The software must not be used for any unlawful purpose.
|
||
|
*
|
||
|
* For any inquiries, please contact: eonacat@gmail.com
|
||
|
*/
|
||
|
|
||
|
class EonaCatCrypto {
|
||
|
private:
|
||
|
static const uint64_t SECRET_SAUCE = 0x5DEECE66D;
|
||
|
const int _blockSize;
|
||
|
const int _rounds;
|
||
|
uint64_t* _state;
|
||
|
uint32_t* _key;
|
||
|
uint32_t* _nonce;
|
||
|
uint32_t _blockCounter;
|
||
|
|
||
|
void GenerateBlock(uint8_t* output) {
|
||
|
for (int i = 0; i < _blockSize / 4; i++) {
|
||
|
_state[i] = (_key[i % (_blockSize / 4)] ^ _nonce[i % (_blockSize / 4)]) + (uint64_t)i * SECRET_SAUCE;
|
||
|
}
|
||
|
|
||
|
for (int round = 0; round < _rounds; round++) {
|
||
|
for (int i = 0; i < _blockSize / 4; i++) {
|
||
|
_state[i] = (uint64_t)(((int)_state[i] + round) ^ (round * SECRET_SAUCE) + (i + _blockCounter));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
memcpy(output, _state, _blockSize);
|
||
|
_blockCounter++;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
EonaCatCrypto(const uint8_t* keyWithSalt, const uint8_t* nonce, int blockSize, int rounds)
|
||
|
: _blockSize(blockSize), _rounds(rounds), _blockCounter(0) {
|
||
|
_key = new uint32_t[_blockSize / 4];
|
||
|
memcpy(_key, keyWithSalt, _blockSize / 4 * sizeof(uint32_t));
|
||
|
|
||
|
_nonce = new uint32_t[blockSize / 4];
|
||
|
memcpy(_nonce, nonce, blockSize / 4 * sizeof(uint32_t));
|
||
|
|
||
|
_state = new uint64_t[_blockSize / 4];
|
||
|
}
|
||
|
|
||
|
~EonaCatCrypto() {
|
||
|
delete[] _key;
|
||
|
delete[] _nonce;
|
||
|
delete[] _state;
|
||
|
}
|
||
|
|
||
|
void Generate(const uint8_t* input, uint8_t* output, bool encrypt) {
|
||
|
int totalBlocks = (strlen((const char*)input) + _blockSize - 1) / _blockSize;
|
||
|
|
||
|
for (int blockIndex = 0; blockIndex < totalBlocks; blockIndex++) {
|
||
|
int inputOffset = blockIndex * _blockSize;
|
||
|
int outputOffset = blockIndex * _blockSize;
|
||
|
uint8_t block[_blockSize];
|
||
|
|
||
|
GenerateBlock(block);
|
||
|
|
||
|
for (int i = 0; i < _blockSize && inputOffset + i < strlen((const char*)input); i++) {
|
||
|
output[outputOffset + i] = input[inputOffset + i] ^ block[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class EonaCatCipher {
|
||
|
private:
|
||
|
const int DEFAULT_SALT_SIZE = 2048; // Salt size for key derivation
|
||
|
const int DEFAULT_IV_SIZE = 2048; // IV size (16384 bits)
|
||
|
const int DEFAULT_KEY_SIZE = 2048; // Key size (16384 bits)
|
||
|
const int DEFAULT_ROUNDS = 2048; // Rounds
|
||
|
const int DEFAULT_BLOCK_SIZE = 8192; // 8kb
|
||
|
const int HMAC_KEY_SIZE = 32; // Key size for HMAC (256 bits)
|
||
|
|
||
|
uint8_t* _derivedKey; // Derived encryption key
|
||
|
uint8_t* _hmacKey; // HMAC key
|
||
|
int _ivSize; // IV size
|
||
|
int _keySize; // Key size
|
||
|
int _rounds; // Number of rounds for key derivation
|
||
|
int _blockSize; // The size of the block that is created
|
||
|
|
||
|
static std::vector<uint8_t> GenerateRandomBytes(int size) {
|
||
|
std::vector<uint8_t> randomBytes(size);
|
||
|
if (!RAND_bytes(randomBytes.data(), size)) {
|
||
|
throw std::runtime_error("EonaCatCipher: Failed to generate random bytes.");
|
||
|
}
|
||
|
return randomBytes;
|
||
|
}
|
||
|
|
||
|
void DeriveKeyAndHMAC(const std::string& password, int saltSize) {
|
||
|
std::vector<uint8_t> salt = GenerateRandomBytes(saltSize);
|
||
|
_derivedKey = PBKDF2(password, salt.data(), salt.size(), _keySize);
|
||
|
_hmacKey = PBKDF2(password, salt.data(), salt.size(), HMAC_KEY_SIZE);
|
||
|
}
|
||
|
|
||
|
uint8_t* PBKDF2(const std::string& password, const uint8_t* salt, int saltLen, int keyLength) {
|
||
|
uint8_t* derivedKey = new uint8_t[keyLength];
|
||
|
PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), salt, saltLen, _rounds, EVP_sha256(), keyLength, derivedKey);
|
||
|
return derivedKey;
|
||
|
}
|
||
|
|
||
|
std::vector<uint8_t> GenerateHMAC(const uint8_t* data, size_t dataLen) {
|
||
|
std::vector<uint8_t> hmac(EVP_MAX_MD_SIZE);
|
||
|
unsigned int hmacLen;
|
||
|
HMAC(EVP_sha256(), _hmacKey, HMAC_KEY_SIZE, data, dataLen, hmac.data(), &hmacLen);
|
||
|
hmac.resize(hmacLen);
|
||
|
return hmac;
|
||
|
}
|
||
|
|
||
|
bool AreEqual(const uint8_t* a, const uint8_t* b, size_t length) {
|
||
|
return CRYPTO_memcmp(a, b, length) == 0;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
EonaCatCipher(const std::string& password, int saltSize = 32, int ivSize = 16, int keySize = 32, int rounds = 1000, int blockSize = 128)
|
||
|
: _ivSize(ivSize), _keySize(keySize), _rounds(rounds), _blockSize(blockSize), _derivedKey(nullptr), _hmacKey(nullptr) {
|
||
|
if (password.empty()) {
|
||
|
throw std::invalid_argument("EonaCatCipher: Password cannot be null or empty.");
|
||
|
}
|
||
|
DeriveKeyAndHMAC(password, saltSize);
|
||
|
}
|
||
|
|
||
|
~EonaCatCipher() {
|
||
|
delete[] _derivedKey;
|
||
|
delete[] _hmacKey;
|
||
|
}
|
||
|
|
||
|
std::vector<uint8_t> Encrypt(const std::string& plaintext) {
|
||
|
auto iv = GenerateRandomBytes(_ivSize);
|
||
|
std::vector<uint8_t> plaintextBytes(plaintext.begin(), plaintext.end());
|
||
|
std::vector<uint8_t> ciphertext(plaintextBytes.size());
|
||
|
|
||
|
EonaCatCrypto cipher(_derivedKey, iv.data(), _blockSize, _rounds);
|
||
|
cipher.Generate(plaintextBytes.data(), ciphertext.data(), true);
|
||
|
|
||
|
std::vector<uint8_t> result(_ivSize + ciphertext.size());
|
||
|
memcpy(result.data(), iv.data(), _ivSize);
|
||
|
memcpy(result.data() + _ivSize, ciphertext.data(), ciphertext.size());
|
||
|
|
||
|
auto hmac = GenerateHMAC(result.data(), result.size());
|
||
|
result.insert(result.end(), hmac.begin(), hmac.end());
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
std::string Decrypt(const std::vector<uint8_t>& ciphertextWithHMAC) {
|
||
|
size_t hmacOffset = ciphertextWithHMAC.size() - HMAC_KEY_SIZE;
|
||
|
|
||
|
std::vector<uint8_t> providedHMAC(HMAC_KEY_SIZE);
|
||
|
memcpy(providedHMAC.data(), ciphertextWithHMAC.data() + hmacOffset, HMAC_KEY_SIZE);
|
||
|
|
||
|
std::vector<uint8_t> ciphertext(hmacOffset);
|
||
|
memcpy(ciphertext.data(), ciphertextWithHMAC.data(), hmacOffset);
|
||
|
|
||
|
auto calculatedHMAC = GenerateHMAC(ciphertext.data(), ciphertext.size());
|
||
|
if (!AreEqual(providedHMAC.data(), calculatedHMAC.data(), HMAC_KEY_SIZE)) {
|
||
|
throw std::runtime_error("EonaCatCipher: HMAC validation failed. Data may have been tampered with.");
|
||
|
}
|
||
|
|
||
|
std::vector<uint8_t> iv(_ivSize);
|
||
|
memcpy(iv.data(), ciphertext.data(), _ivSize);
|
||
|
|
||
|
std::vector<uint8_t> encryptedData(ciphertext.size() - _ivSize);
|
||
|
memcpy(encryptedData.data(), ciphertext.data() + _ivSize, encryptedData.size());
|
||
|
|
||
|
std::vector<uint8_t> decryptedData(encryptedData.size());
|
||
|
EonaCatCrypto decryptCipher(_derivedKey, iv.data(), _blockSize, _rounds);
|
||
|
decryptCipher.Generate(encryptedData.data(), decryptedData.data(), false);
|
||
|
|
||
|
return std::string(decryptedData.begin(), decryptedData.end());
|
||
|
}
|
||
|
|
||
|
static void Main() {
|
||
|
std::string password = "securePassword123!@#$";
|
||
|
std::string plaintext = "Thank you for using EonaCatCipher!";
|
||
|
|
||
|
std::cout << "Encrypting '" << plaintext << "' with password '" << password << "' (we do this 5 times)" << std::endl;
|
||
|
std::cout << "================" << std::endl;
|
||
|
|
||
|
for (int i = 0; i < 5; i++) {
|
||
|
std::cout << "Encryption round " << (i + 1) << ": " << std::endl;
|
||
|
std::cout << "================" << std::endl;
|
||
|
|
||
|
try {
|
||
|
EonaCatCipher cipher(password);
|
||
|
auto encrypted = cipher.Encrypt(plaintext);
|
||
|
std::cout << "Encrypted (byte array): ";
|
||
|
for (auto byte : encrypted) {
|
||
|
printf("%02X ", byte);
|
||
|
}
|
||
|
std::cout << std::endl;
|
||
|
|
||
|
std::string decrypted = cipher.Decrypt(encrypted);
|
||
|
std::cout << "Decrypted: " << decrypted << std::endl;
|
||
|
std::cout << "================" << std::endl;
|
||
|
}
|
||
|
catch (const std::exception& ex) {
|
||
|
std::cerr << "Error: " << ex.what() << std::endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
int main() {
|
||
|
EonaCatCipher::Main();
|
||
|
return 0;
|
||
|
}
|