Initial version

This commit is contained in:
EonaCat 2024-09-25 19:22:33 +02:00
parent 9bc0a10c53
commit 465bdad94b
20 changed files with 3208 additions and 58 deletions

16
.gitignore vendored
View File

@ -1,4 +1,3 @@
# ---> VisualStudio
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
@ -397,18 +396,3 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix

319
C#/EonaCatCipher.cs Normal file
View File

@ -0,0 +1,319 @@
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
/*
* 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
*/
public class EonaCatCipher : IDisposable
{
private const int DEFAULT_SALT_SIZE = 2048; // Salt size for key derivation
private const int DEFAULT_IV_SIZE = 2048; // IV size (16384 bits)
private const int DEFAULT_KEY_SIZE = 2048; // Key size (16384 bits)
private const int DEFAULT_ROUNDS = 2048; // Rounds
private const int DEFAULT_BLOCK_SIZE = 8192; // 8kb
private const int HMAC_KEY_SIZE = 32; // Key size for HMAC (256 bits)
private readonly byte[] _derivedKey; // Derived encryption key
private readonly byte[] _hmacKey; // HMAC key
private readonly int _ivSize; // IV size
private readonly int _keySize; // Key size
private readonly int _rounds; // Number of rounds for key derivation
private readonly int _blockSize; // The size of the block that is created
public EonaCatCipher(string password, int saltSize = DEFAULT_SALT_SIZE, int ivSize = DEFAULT_IV_SIZE, int keySize = DEFAULT_KEY_SIZE, int rounds = DEFAULT_ROUNDS, int blockSize = DEFAULT_BLOCK_SIZE)
{
if (string.IsNullOrEmpty(password))
{
throw new ArgumentException("EonaCatCipher: Password cannot be null or empty.");
}
_ivSize = ivSize;
_keySize = keySize;
_rounds = rounds;
_blockSize = blockSize;
// Derive encryption key and HMAC key
(_derivedKey, _hmacKey) = DeriveKeyAndHMAC(password, saltSize);
}
private static byte[] GenerateRandomBytes(int size)
{
var randomBytes = new byte[size];
RandomNumberGenerator.Fill(randomBytes);
return randomBytes;
}
private (byte[] encryptionKey, byte[] hmacKey) DeriveKeyAndHMAC(string password, int saltSize)
{
var salt = GenerateRandomBytes(saltSize);
var encryptionKey = PBKDF2(password, salt, _keySize, _rounds);
// Derive separate key for HMAC
var hmacKey = PBKDF2(password, salt, HMAC_KEY_SIZE, _rounds);
var keyWithSalt = new byte[saltSize + _keySize];
Buffer.BlockCopy(salt, 0, keyWithSalt, 0, saltSize);
Buffer.BlockCopy(encryptionKey, 0, keyWithSalt, saltSize, _keySize);
return (keyWithSalt, hmacKey);
}
private static byte[] PBKDF2(string password, byte[] salt, int keyLength, int iterations)
{
var hmac = new HMACSHA512(Encoding.UTF8.GetBytes(password));
int hashLength = hmac.HashSize / 8;
int requiredBytes = keyLength;
int blocksNeeded = (int)Math.Ceiling((double)requiredBytes / hashLength);
byte[] derivedKey = new byte[requiredBytes];
byte[] block = new byte[hashLength];
for (int blockIndex = 1; blockIndex <= blocksNeeded; blockIndex++)
{
// Step 1: F(blockIndex)
var currentBlock = new byte[salt.Length + 4];
Buffer.BlockCopy(salt, 0, currentBlock, 0, salt.Length);
BitConverter.GetBytes(blockIndex).CopyTo(currentBlock, salt.Length);
// Step 2: U1 = HMAC(password, salt + blockIndex)
byte[] u = hmac.ComputeHash(currentBlock);
Buffer.BlockCopy(u, 0, block, 0, hashLength);
// Step 3: Derived key starts with U1
Array.Copy(u, 0, derivedKey, (blockIndex - 1) * hashLength, Math.Min(hashLength, requiredBytes));
// Step 4: Iterations
for (int iteration = 1; iteration < iterations; iteration++)
{
// U2 = HMAC(password, U1)
u = hmac.ComputeHash(u);
// Step 5: XOR U2 with previous result
for (int i = 0; i < hashLength; i++)
{
block[i] ^= u[i];
}
// Step 6: Append result to derived key
Array.Copy(block, 0, derivedKey, (blockIndex - 1) * hashLength, Math.Min(hashLength, requiredBytes));
}
}
return derivedKey;
}
public byte[] Encrypt(string plaintext)
{
var iv = GenerateRandomBytes(_ivSize);
var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
var ciphertext = new byte[plaintextBytes.Length];
using var cipher = new EonaCatCrypto(_derivedKey, iv, _blockSize, _rounds);
cipher.Generate(plaintextBytes, ciphertext, true);
// Combine IV and ciphertext
var result = new byte[_ivSize + ciphertext.Length];
Buffer.BlockCopy(iv, 0, result, 0, _ivSize);
Buffer.BlockCopy(ciphertext, 0, result, _ivSize, ciphertext.Length);
// Generate HMAC for integrity check
var hmac = GenerateHMAC(result);
// Combine result and HMAC
var finalResult = new byte[result.Length + hmac.Length];
Buffer.BlockCopy(result, 0, finalResult, 0, result.Length);
Buffer.BlockCopy(hmac, 0, finalResult, result.Length, hmac.Length);
return finalResult;
}
public string Decrypt(byte[] ciphertextWithHMAC)
{
var hmacOffset = ciphertextWithHMAC.Length - HMAC_KEY_SIZE;
// Separate HMAC from the ciphertext
var providedHMAC = new byte[HMAC_KEY_SIZE];
Buffer.BlockCopy(ciphertextWithHMAC, hmacOffset, providedHMAC, 0, HMAC_KEY_SIZE);
var ciphertext = new byte[hmacOffset];
Buffer.BlockCopy(ciphertextWithHMAC, 0, ciphertext, 0, hmacOffset);
// Verify HMAC before decrypting
var calculatedHMAC = GenerateHMAC(ciphertext);
if (!AreEqual(providedHMAC, calculatedHMAC))
{
throw new CryptographicException("EonaCatCipher: HMAC validation failed. Data may have been tampered with.");
}
// Extract IV
var iv = new byte[_ivSize];
Buffer.BlockCopy(ciphertext, 0, iv, 0, _ivSize);
// Extract encrypted data
var encryptedData = new byte[ciphertext.Length - _ivSize];
Buffer.BlockCopy(ciphertext, _ivSize, encryptedData, 0, encryptedData.Length);
// Decrypt
var decryptedData = new byte[encryptedData.Length];
using var cipher = new EonaCatCrypto(_derivedKey, iv, _blockSize, _rounds);
cipher.Generate(encryptedData, decryptedData, false);
return Encoding.UTF8.GetString(decryptedData);
}
private byte[] GenerateHMAC(byte[] data)
{
using var hmac = new HMACSHA256(_hmacKey);
return hmac.ComputeHash(data);
}
private static bool AreEqual(byte[] a, byte[] b)
{
if (a.Length != b.Length) return false;
for (int i = 0; i < a.Length; i++)
{
if (a[i] != b[i]) return false;
}
return true;
}
public void Dispose()
{
if (_derivedKey != null)
{
Array.Clear(_derivedKey, 0, _derivedKey.Length);
}
if (_hmacKey != null)
{
Array.Clear(_hmacKey, 0, _hmacKey.Length);
}
}
private class EonaCatCrypto : IDisposable
{
private const long SECRET_SAUCE = 0x5DEECE66D;
private const uint UNSIGNED_INT = 0xFFFFFFFF;
private readonly int _blockSize;
private readonly int _rounds;
private readonly ulong[] _state;
private readonly uint[] _key;
private readonly uint[] _nonce;
private uint _blockCounter;
public EonaCatCrypto(byte[] keyWithSalt, byte[] nonce, int blockSize, int rounds)
{
_rounds = rounds;
_blockSize = blockSize / 4 > 0 ? blockSize : 128;
_key = new uint[keyWithSalt.Length / 4];
Buffer.BlockCopy(keyWithSalt, 0, _key, 0, keyWithSalt.Length);
_nonce = new uint[nonce.Length / 4];
Buffer.BlockCopy(nonce, 0, _nonce, 0, nonce.Length);
_state = new ulong[_blockSize / 4];
}
private void GenerateBlock(byte[] output)
{
// Initialize state using a combined operation
for (int i = 0; i < _state.Length; i++)
{
_state[i] = (_key[i % _key.Length] ^ _nonce[i % _nonce.Length]) + (ulong)i * SECRET_SAUCE;
}
// Mix the states according to the rounds
for (int round = 0; round < _rounds; round++)
{
for (int i = 0; i < _state.Length; i++)
{
_state[i] = (ulong)(((int)_state[i] + round) ^ (round * SECRET_SAUCE) + (i + _blockCounter));
}
}
// Output block
Buffer.BlockCopy(_state, 0, output, 0, output.Length);
_blockCounter++;
}
public void Generate(byte[] input, byte[] output, bool encrypt)
{
int totalBlocks = (input.Length + _blockSize - 1) / _blockSize;
for (int blockIndex = 0; blockIndex < totalBlocks; blockIndex++)
{
int inputOffset = blockIndex * _blockSize;
int outputOffset = blockIndex * _blockSize;
byte[] block = new byte[_blockSize];
// Generate a block based on the input
GenerateBlock(block);
// Perform XOR for encryption or decryption
for (int i = 0; i < block.Length && inputOffset + i < input.Length; i++)
{
output[outputOffset + i] = (byte)(input[inputOffset + i] ^ block[i]);
}
}
}
public void Dispose()
{
if (_state != null)
{
Array.Clear(_state, 0, _state.Length);
}
}
}
public static void Main(string[] args)
{
string password = "securePassword123!@#$";
string plaintext = "Thank you for using EonaCatCipher!";
Console.WriteLine($"Encrypting '{plaintext}' with password '{password}' (we do this 5 times)");
Console.WriteLine("================");
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Encryption round {i + 1}: ");
Console.WriteLine("================");
using var cipher = new EonaCatCipher(password);
var encrypted = cipher.Encrypt(plaintext);
Console.WriteLine("Encrypted (byte array): " + BitConverter.ToString(encrypted));
var decrypted = cipher.Decrypt(encrypted);
Console.WriteLine("Decrypted: " + decrypted);
Console.WriteLine("================");
}
}
}

239
C++/EonaCatCipher.cpp Normal file
View File

@ -0,0 +1,239 @@
#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;
}

35
C++/readme.txt Normal file
View File

@ -0,0 +1,35 @@
/*
* 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
*/
1. Compile the c++ code:
g++ -o EonaCatCipher EonaCatCipher.cpp -lssl -lcrypto
2. Run the executable:
./EonaCatCipher

257
Delphi/EonaCatCipher.pas Normal file
View File

@ -0,0 +1,257 @@
unit EonaCatCipher;
interface
uses
SysUtils, Classes, Hash, HMAC, Cryptography, Generics.Collections;
{*
* 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
*}
type
TEonaCatCipher = class(TObject)
private
const
DEFAULT_SALT_SIZE = 2048; // Salt size for key derivation
DEFAULT_IV_SIZE = 2048; // IV size (2048 bits)
DEFAULT_KEY_SIZE = 2048; // Key size (2048 bits)
DEFAULT_ROUNDS = 2048; // Rounds
DEFAULT_BLOCK_SIZE = 8192; // 8KB
HMAC_KEY_SIZE = 32; // Key size for HMAC (256 bits)
var
_derivedKey: TBytes; // Derived encryption key
_hmacKey: TBytes; // HMAC key
_ivSize: Integer; // IV size
_keySize: Integer; // Key size
_rounds: Integer; // Number of rounds for key derivation
_blockSize: Integer; // The size of the block that is created
function GenerateRandomBytes(size: Integer): TBytes;
function DeriveKeyAndHMAC(password: string; saltSize: Integer): TBytes;
function PBKDF2(password: string; salt: TBytes; keyLength: Integer; iterations: Integer): TBytes;
function GenerateHMAC(data: TBytes): TBytes;
function AreEqual(a, b: TBytes): Boolean;
public
constructor Create(password: string; saltSize: Integer = DEFAULT_SALT_SIZE;
ivSize: Integer = DEFAULT_IV_SIZE; keySize: Integer = DEFAULT_KEY_SIZE;
rounds: Integer = DEFAULT_ROUNDS; blockSize: Integer = DEFAULT_BLOCK_SIZE);
destructor Destroy; override;
function Encrypt(plaintext: string): TBytes;
function Decrypt(ciphertextWithHMAC: TBytes): string;
end;
implementation
constructor TEonaCatCipher.Create(password: string; saltSize: Integer;
ivSize: Integer; keySize: Integer; rounds: Integer; blockSize: Integer);
begin
inherited Create;
if password = '' then
raise Exception.Create('EonaCatCipher: Password cannot be null or empty.');
_ivSize := ivSize;
_keySize := keySize;
_rounds := rounds;
_blockSize := blockSize;
// Derive encryption key and HMAC key
(_derivedKey, _hmacKey) := DeriveKeyAndHMAC(password, saltSize);
end;
destructor TEonaCatCipher.Destroy;
begin
if Length(_derivedKey) > 0 then
FillChar(_derivedKey[0], Length(_derivedKey), 0);
if Length(_hmacKey) > 0 then
FillChar(_hmacKey[0], Length(_hmacKey), 0);
inherited Destroy;
end;
function TEonaCatCipher.GenerateRandomBytes(size: Integer): TBytes;
begin
SetLength(Result, size);
RandomBytes(Result);
end;
function TEonaCatCipher.DeriveKeyAndHMAC(password: string; saltSize: Integer): TBytes;
var
salt, encryptionKey, hmacKey: TBytes;
begin
salt := GenerateRandomBytes(saltSize);
encryptionKey := PBKDF2(password, salt, _keySize, _rounds);
hmacKey := PBKDF2(password, salt, HMAC_KEY_SIZE, _rounds);
SetLength(Result, saltSize + Length(encryptionKey));
Move(salt[0], Result[0], saltSize);
Move(encryptionKey[0], Result[saltSize], Length(encryptionKey));
// Combine encryptionKey and hmacKey if needed for further processing
end;
function TEonaCatCipher.PBKDF2(password: string; salt: TBytes; keyLength: Integer; iterations: Integer): TBytes;
var
hmac: IHMAC;
hashLength, requiredBytes, blocksNeeded, blockIndex: Integer;
derivedKey, block: TBytes;
currentBlock: TBytes;
u: TBytes;
begin
hmac := THashFactory.THMAC.CreateHMAC(THashFactory.TCrypto.CreateSHA512);
hmac.Key := TEncoding.UTF8.GetBytes(password);
hashLength := hmac.HashSize div 8;
requiredBytes := keyLength;
blocksNeeded := Ceil(requiredBytes / hashLength);
SetLength(derivedKey, requiredBytes);
SetLength(block, hashLength);
for blockIndex := 1 to blocksNeeded do
begin
SetLength(currentBlock, Length(salt) + SizeOf(Integer));
Move(salt[0], currentBlock[0], Length(salt));
Move(blockIndex, currentBlock[Length(salt)], SizeOf(Integer));
// U1 = HMAC(password, salt + blockIndex)
u := hmac.ComputeHash(currentBlock);
Move(u[0], block[0], hashLength);
Move(u[0], derivedKey[(blockIndex - 1) * hashLength], Min(hashLength, requiredBytes));
// Iterations
for var iteration := 1 to iterations - 1 do
begin
// U2 = HMAC(password, U1)
u := hmac.ComputeHash(u);
for var i := 0 to hashLength - 1 do
block[i] := block[i] xor u[i];
// Append result to derived key
Move(block[0], derivedKey[(blockIndex - 1) * hashLength], Min(hashLength, requiredBytes));
end;
end;
Result := derivedKey;
end;
function TEonaCatCipher.Encrypt(plaintext: string): TBytes;
var
iv, plaintextBytes: TBytes;
ciphertext: TBytes;
result: TBytes;
hmac: TBytes;
begin
iv := GenerateRandomBytes(_ivSize);
plaintextBytes := TEncoding.UTF8.GetBytes(plaintext);
SetLength(ciphertext, Length(plaintextBytes));
// Note: Implement the EonaCatCrypto class and its Generate method for encryption here
// using a similar approach to the C# version
// Combine IV and ciphertext
SetLength(result, _ivSize + Length(ciphertext));
Move(iv[0], result[0], _ivSize);
Move(ciphertext[0], result[_ivSize], Length(ciphertext));
// Generate HMAC for integrity check
hmac := GenerateHMAC(result);
// Combine result and HMAC
SetLength(Result, Length(result) + Length(hmac));
Move(result[0], Result[0], Length(result));
Move(hmac[0], Result[Length(result)], Length(hmac));
end;
function TEonaCatCipher.Decrypt(ciphertextWithHMAC: TBytes): string;
var
hmacOffset: Integer;
providedHMAC, ciphertext: TBytes;
calculatedHMAC: TBytes;
iv: TBytes;
encryptedData: TBytes;
decryptedData: TBytes;
begin
hmacOffset := Length(ciphertextWithHMAC) - HMAC_KEY_SIZE;
// Separate HMAC from the ciphertext
SetLength(providedHMAC, HMAC_KEY_SIZE);
Move(ciphertextWithHMAC[hmacOffset], providedHMAC[0], HMAC_KEY_SIZE);
SetLength(ciphertext, hmacOffset);
Move(ciphertextWithHMAC[0], ciphertext[0], hmacOffset);
// Verify HMAC before decrypting
calculatedHMAC := GenerateHMAC(ciphertext);
if not AreEqual(providedHMAC, calculatedHMAC) then
raise Exception.Create('EonaCatCipher: HMAC validation failed. Data may have been tampered with.');
// Extract IV
SetLength(iv, _ivSize);
Move(ciphertext[0], iv[0], _ivSize);
// Extract encrypted data
SetLength(encryptedData, Length(ciphertext) - _ivSize);
Move(ciphertext[_ivSize], encryptedData[0], Length(encryptedData));
// Decrypt
// Note: Implement the EonaCatCrypto class and its Generate method for decryption here
// using a similar approach to the C# version
Result := TEncoding.UTF8.GetString(decryptedData);
end;
function TEonaCatCipher.GenerateHMAC(data: TBytes): TBytes;
var
hmac: IHMAC;
begin
hmac := THashFactory.THMAC.CreateHMAC(THashFactory.TCrypto.CreateSHA256);
hmac.Key := _hmacKey;
Result := hmac.ComputeHash(data);
end;
function TEonaCatCipher.AreEqual(a, b: TBytes): Boolean;
var
i: Integer;
begin
Result := Length(a) = Length(b);
if Result then
begin
for i := 0 to High(a) do
if a[i] <> b[i] then
begin
Result := False;
Break;
end;
end;
end;
end.

50
Delphi/example.pas Normal file
View File

@ -0,0 +1,50 @@
program EonaCatCipherExample;
{$APPTYPE CONSOLE}
uses
SysUtils, EonaCatCipher;
{*
* 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
*}
var
cipher: TEonaCatCipher;
encrypted: TArray<Byte>;
decrypted: string;
begin
cipher := TEonaCatCipher.Create('securePassword123!@#$');
try
encrypted := cipher.Encrypt('Thank you for using EonaCatCipher!');
decrypted := cipher.Decrypt(encrypted);
WriteLn('Decrypted: ' + decrypted);
finally
cipher.Free;
end;
end;

312
Go/EonaCatCipher.go Normal file
View File

@ -0,0 +1,312 @@
package main
import (
"crypto/rand"
"crypto/sha512"
"crypto/hmac"
"encoding/binary"
"encoding/hex"
"encoding/base64"
"fmt"
"log"
)
/*
* 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
*/
// EonaCatCipher struct
type EonaCatCipher struct {
derivedKey []byte
hmacKey []byte
ivSize int
keySize int
rounds int
blockSize int
}
// Constants
const (
DEFAULT_SALT_SIZE = 2048 // Salt size for key derivation
DEFAULT_IV_SIZE = 2048 // IV size (16384 bits)
DEFAULT_KEY_SIZE = 2048 // Key size (16384 bits)
DEFAULT_ROUNDS = 2048
DEFAULT_BLOCK_SIZE = 8192 // 8KB
HMAC_KEY_SIZE = 32 // Key size for HMAC (256 bits)
SECRET_SAUCE = 0x5DEECE66D
)
// NewEonaCatCipher constructor
func NewEonaCatCipher(password string, saltSize int, ivSize int, keySize int, rounds int, blockSize int) *EonaCatCipher {
if password == "" {
log.Fatal("EonaCatCipher: Password cannot be null or empty.")
}
ec := &EonaCatCipher{
ivSize: ivSize,
keySize: keySize,
rounds: rounds,
blockSize: blockSize,
}
ec.derivedKey, ec.hmacKey = ec.DeriveKeyAndHMAC(password, saltSize)
return ec
}
// GenerateRandomBytes generates a slice of random bytes
func GenerateRandomBytes(size int) ([]byte, error) {
randomBytes := make([]byte, size)
_, err := rand.Read(randomBytes)
if err != nil {
return nil, err
}
return randomBytes, nil
}
// DeriveKeyAndHMAC derives the encryption key and HMAC key from the password
func (ec *EonaCatCipher) DeriveKeyAndHMAC(password string, saltSize int) ([]byte, []byte) {
salt, _ := GenerateRandomBytes(saltSize)
encryptionKey := ec.PBKDF2(password, salt, ec.keySize, ec.rounds)
// Derive separate key for HMAC
hmacKey := ec.PBKDF2(password, salt, HMAC_KEY_SIZE, ec.rounds)
keyWithSalt := append(salt, encryptionKey...)
return keyWithSalt, hmacKey
}
// PBKDF2 implementation of the PBKDF2 key derivation function
func (ec *EonaCatCipher) PBKDF2(password string, salt []byte, keyLength int, iterations int) []byte {
hmacHash := hmac.New(sha512.New, []byte(password))
hashLength := hmacHash.Size()
requiredBytes := keyLength
blocksNeeded := (requiredBytes + hashLength - 1) / hashLength
derivedKey := make([]byte, requiredBytes)
for blockIndex := 1; blockIndex <= blocksNeeded; blockIndex++ {
currentBlock := append(salt, intToBytes(blockIndex)...)
// U1 = HMAC(password, salt + blockIndex)
u := hmacHash.Sum(currentBlock)
block := make([]byte, hashLength)
copy(block, u)
// Derived key starts with U1
copy(derivedKey[(blockIndex-1)*hashLength:], block)
// Iterations
for iteration := 1; iteration < iterations; iteration++ {
u = hmacHash.Sum(u)
// XOR with previous result
for i := 0; i < hashLength; i++ {
block[i] ^= u[i]
}
// Append result to derived key
copy(derivedKey[(blockIndex-1)*hashLength:], block)
}
}
return derivedKey
}
// Encrypt encrypts the plaintext using the derived key and returns the ciphertext with HMAC
func (ec *EonaCatCipher) Encrypt(plaintext string) ([]byte, error) {
iv, err := GenerateRandomBytes(ec.ivSize)
if err != nil {
return nil, err
}
plaintextBytes := []byte(plaintext)
ciphertext := make([]byte, len(plaintextBytes))
cipher := NewEonaCatCrypto(ec.derivedKey, iv, ec.blockSize, ec.rounds)
cipher.Generate(plaintextBytes, ciphertext, true)
// Combine IV and ciphertext
result := append(iv, ciphertext...)
// Generate HMAC for integrity check
hmac := ec.GenerateHMAC(result)
// Combine result and HMAC
finalResult := append(result, hmac...)
return finalResult, nil
}
// Decrypt decrypts the ciphertext with HMAC
func (ec *EonaCatCipher) Decrypt(ciphertextWithHMAC []byte) (string, error) {
hmacOffset := len(ciphertextWithHMAC) - HMAC_KEY_SIZE
// Separate HMAC from the ciphertext
providedHMAC := ciphertextWithHMAC[hmacOffset:]
ciphertext := ciphertextWithHMAC[:hmacOffset]
// Verify HMAC before decrypting
calculatedHMAC := ec.GenerateHMAC(ciphertext)
if !AreEqual(providedHMAC, calculatedHMAC) {
return "", fmt.Errorf("EonaCatCipher: HMAC validation failed. Data may have been tampered with.")
}
// Extract IV
iv := ciphertext[:ec.ivSize]
// Extract encrypted data
encryptedData := ciphertext[ec.ivSize:]
// Decrypt
decryptedData := make([]byte, len(encryptedData))
cipher := NewEonaCatCrypto(ec.derivedKey, iv, ec.blockSize, ec.rounds)
cipher.Generate(encryptedData, decryptedData, false)
return string(decryptedData), nil
}
// GenerateHMAC generates HMAC for the data
func (ec *EonaCatCipher) GenerateHMAC(data []byte) []byte {
h := hmac.New(sha512.New, ec.hmacKey)
h.Write(data)
return h.Sum(nil)
}
// AreEqual checks if two byte slices are equal
func AreEqual(a, b []byte) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
// Convert an integer to bytes
func intToBytes(n int) []byte {
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, uint32(n))
return buf
}
// EonaCatCrypto struct for encryption and decryption
type EonaCatCrypto struct {
blockSize int
rounds int
state []uint64
key []uint32
nonce []uint32
blockCounter uint32
}
// NewEonaCatCrypto constructor
func NewEonaCatCrypto(keyWithSalt []byte, nonce []byte, blockSize int, rounds int) *EonaCatCrypto {
ec := &EonaCatCrypto{
blockSize: blockSize / 4,
rounds: rounds,
key: make([]uint32, len(keyWithSalt)/4),
nonce: make([]uint32, len(nonce)/4),
state: make([]uint64, blockSize/4),
}
copy(ec.key, keyWithSalt)
copy(ec.nonce, nonce)
return ec
}
// GenerateBlock generates a block for encryption/decryption
func (ec *EonaCatCrypto) GenerateBlock(output []byte) {
// Initialize state
for i := 0; i < len(ec.state); i++ {
ec.state[i] = uint64(ec.key[i%len(ec.key)]) ^ uint64(ec.nonce[i%len(ec.nonce)]) + uint64(i)*SECRET_SAUCE
}
// Mix the states according to the rounds
for round := 0; round < ec.rounds; round++ {
for i := 0; i < len(ec.state); i++ {
ec.state[i] = (ec.state[i] + uint64(round)) ^ (uint64(round) * SECRET_SAUCE) + uint64(i) + uint64(ec.blockCounter)
}
}
// Output block
for i := 0; i < len(output); i++ {
output[i] = byte(ec.state[i/4] >> (8 * (i % 4)))
}
ec.blockCounter++
}
// Generate performs encryption or decryption
func (ec *EonaCatCrypto) Generate(input []byte, output []byte, encrypt bool) {
totalBlocks := (len(input) + ec.blockSize - 1) / ec.blockSize
for blockIndex := 0; blockIndex < totalBlocks; blockIndex++ {
inputOffset := blockIndex * ec.blockSize
outputOffset := blockIndex * ec.blockSize
block := make([]byte, ec.blockSize)
// Generate a block based on the input
ec.GenerateBlock(block)
// Perform XOR for encryption or decryption
for i := 0; i < len(block) && inputOffset+i < len(input); i++ {
output[outputOffset+i] = input[inputOffset+i] ^ block[i]
}
}
}
// Main function for testing
func main() {
password := "securePassword123!@#$"
plaintext := "Thank you for using EonaCatCipher!"
fmt.Printf("Encrypting '%s' with password '%s' (we do this 5 times)\n", plaintext, password)
fmt.Println("================")
for i := 0; i < 5; i++ {
fmt.Printf("Encryption round %d:\n", i+1)
fmt.Println("================")
cipher := NewEonaCatCipher(password, DEFAULT_SALT_SIZE, DEFAULT_IV_SIZE, DEFAULT_KEY_SIZE, DEFAULT_ROUNDS, DEFAULT_BLOCK_SIZE)
encrypted, err := cipher.Encrypt(plaintext)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Encrypted (base64): %s\n", base64.StdEncoding.EncodeToString(encrypted))
decrypted, err := cipher.Decrypt(encrypted)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decrypted: %s\n", decrypted)
fmt.Println("================")
}
}

324
Java/EonaCatCipher.java Normal file
View File

@ -0,0 +1,324 @@
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
public class EonaCatCipher implements AutoCloseable {
private static final int DEFAULT_SALT_SIZE = 2048; // Salt size for key derivation
private static final int DEFAULT_IV_SIZE = 2048; // IV size (16384 bits)
private static final int DEFAULT_KEY_SIZE = 2048; // Key size (16384 bits)
private static final int DEFAULT_ROUNDS = 2048; // Rounds
private static final int DEFAULT_BLOCK_SIZE = 8192; // 8kb
private static final int HMAC_KEY_SIZE = 32; // Key size for HMAC (256 bits)
private final byte[] derivedKey; // Derived encryption key
private final byte[] hmacKey; // HMAC key
private final int ivSize; // IV size
private final int keySize; // Key size
private final int rounds; // Number of rounds for key derivation
private final int blockSize; // The size of the block that is created
/*
* 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
*/
public EonaCatCipher(String password, int saltSize, int ivSize, int keySize, int rounds, int blockSize) {
if (password == null || password.isEmpty()) {
throw new IllegalArgumentException("Password cannot be null or empty.");
}
this.ivSize = ivSize;
this.keySize = keySize;
this.rounds = rounds;
this.blockSize = blockSize;
// Derive encryption key and HMAC key
var keys = deriveKeyAndHMAC(password, saltSize);
this.derivedKey = keys[0];
this.hmacKey = keys[1];
}
private static byte[] generateRandomBytes(int size) {
byte[] randomBytes = new byte[size];
new SecureRandom().nextBytes(randomBytes);
return randomBytes;
}
private byte[][] deriveKeyAndHMAC(String password, int saltSize) {
byte[] salt = generateRandomBytes(saltSize);
byte[] encryptionKey = PBKDF2(password, salt, keySize, rounds);
// Derive separate key for HMAC
byte[] hmacKey = PBKDF2(password, salt, HMAC_KEY_SIZE, rounds);
byte[] keyWithSalt = new byte[saltSize + keySize];
System.arraycopy(salt, 0, keyWithSalt, 0, saltSize);
System.arraycopy(encryptionKey, 0, keyWithSalt, saltSize, keySize);
return new byte[][]{keyWithSalt, hmacKey};
}
private static byte[] PBKDF2(String password, byte[] salt, int keyLength, int iterations) {
try {
Mac mac = Mac.getInstance("HmacSHA512");
mac.init(new SecretKeySpec(password.getBytes(), "HmacSHA512"));
int hashLength = mac.getMacLength();
int requiredBytes = keyLength;
int blocksNeeded = (int) Math.ceil((double) requiredBytes / hashLength);
byte[] derivedKey = new byte[requiredBytes];
byte[] block = new byte[hashLength];
for (int blockIndex = 1; blockIndex <= blocksNeeded; blockIndex++) {
// Step 1: F(blockIndex)
byte[] currentBlock = new byte[salt.length + 4];
System.arraycopy(salt, 0, currentBlock, 0, salt.length);
System.arraycopy(intToBytes(blockIndex), 0, currentBlock, salt.length, 4);
// Step 2: U1 = HMAC(password, salt + blockIndex)
byte[] u = mac.doFinal(currentBlock);
System.arraycopy(u, 0, block, 0, hashLength);
// Step 3: Derived key starts with U1
System.arraycopy(u, 0, derivedKey, (blockIndex - 1) * hashLength, Math.min(hashLength, requiredBytes));
// Step 4: Iterations
for (int iteration = 1; iteration < iterations; iteration++) {
// U2 = HMAC(password, U1)
u = mac.doFinal(u);
// Step 5: XOR U2 with previous result
for (int i = 0; i < hashLength; i++) {
block[i] ^= u[i];
}
// Step 6: Append result to derived key
System.arraycopy(block, 0, derivedKey, (blockIndex - 1) * hashLength, Math.min(hashLength, requiredBytes));
}
}
return derivedKey;
} catch (Exception e) {
throw new RuntimeException("EonaCatCipher: Error during PBKDF2 processing", e);
}
}
public byte[] encrypt(String plaintext) {
byte[] iv = generateRandomBytes(ivSize);
byte[] plaintextBytes = plaintext.getBytes();
byte[] ciphertext = new byte[plaintextBytes.length];
try (EonaCatCrypto cipher = new EonaCatCrypto(derivedKey, iv, blockSize, rounds)) {
cipher.generate(plaintextBytes, ciphertext, true);
}
// Combine IV and ciphertext
byte[] result = new byte[ivSize + ciphertext.length];
System.arraycopy(iv, 0, result, 0, ivSize);
System.arraycopy(ciphertext, 0, result, ivSize, ciphertext.length);
// Generate HMAC for integrity check
byte[] hmac = generateHMAC(result);
// Combine result and HMAC
byte[] finalResult = new byte[result.length + hmac.length];
System.arraycopy(result, 0, finalResult, 0, result.length);
System.arraycopy(hmac, 0, finalResult, result.length, hmac.length);
return finalResult;
}
public String decrypt(byte[] ciphertextWithHMAC) {
int hmacOffset = ciphertextWithHMAC.length - HMAC_KEY_SIZE;
// Separate HMAC from the ciphertext
byte[] providedHMAC = new byte[HMAC_KEY_SIZE];
System.arraycopy(ciphertextWithHMAC, hmacOffset, providedHMAC, 0, HMAC_KEY_SIZE);
byte[] ciphertext = new byte[hmacOffset];
System.arraycopy(ciphertextWithHMAC, 0, ciphertext, 0, hmacOffset);
// Verify HMAC before decrypting
byte[] calculatedHMAC = generateHMAC(ciphertext);
if (!Arrays.equals(providedHMAC, calculatedHMAC)) {
throw new RuntimeException("EonaCatCipher: HMAC validation failed. Data may have been tampered with.");
}
// Extract IV
byte[] iv = new byte[ivSize];
System.arraycopy(ciphertext, 0, iv, 0, ivSize);
// Extract encrypted data
byte[] encryptedData = new byte[ciphertext.length - ivSize];
System.arraycopy(ciphertext, ivSize, encryptedData, 0, encryptedData.length);
// Decrypt
byte[] decryptedData = new byte[encryptedData.length];
try (EonaCatCrypto cipher = new EonaCatCrypto(derivedKey, iv, blockSize, rounds)) {
cipher.generate(encryptedData, decryptedData, false);
}
return new String(decryptedData);
}
private byte[] generateHMAC(byte[] data) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(hmacKey, "HmacSHA256"));
return mac.doFinal(data);
} catch (Exception e) {
throw new RuntimeException("EonaCatCipher: Error generating HMAC", e);
}
}
private static byte[] intToBytes(int value) {
return new byte[]{
(byte) (value >> 24),
(byte) (value >> 16),
(byte) (value >> 8),
(byte) value
};
}
@Override
public void close() {
if (derivedKey != null) {
Arrays.fill(derivedKey, (byte) 0);
}
if (hmacKey != null) {
Arrays.fill(hmacKey, (byte) 0);
}
}
private static class EonaCatCrypto implements AutoCloseable {
private static final long SECRET_SAUCE = 0x5DEECE66D;
private static final long UNSIGNED_INT = 0xFFFFFFFF;
private final int blockSize;
private final int rounds;
private final long[] state;
private final long[] key;
private final long[] nonce;
private long blockCounter;
public EonaCatCrypto(byte[] keyWithSalt, byte[] nonce, int blockSize, int rounds) {
this.rounds = rounds;
this.blockSize = blockSize / 4 > 0 ? blockSize : 128;
this.key = new long[keyWithSalt.length / 8];
for (int i = 0; i < key.length; i++) {
key[i] = bytesToLong(keyWithSalt, i * 8);
}
this.nonce = new long[nonce.length / 8];
for (int i = 0; i < nonce.length / 8; i++) {
nonce[i] = bytesToLong(nonce, i * 8);
}
this.state = new long[blockSize / 8];
}
private void generateBlock(byte[] output) {
// Initialize state using a combined operation
for (int i = 0; i < state.length; i++) {
state[i] = (key[i % key.length] ^ nonce[i % nonce.length]) + (long) i * SECRET_SAUCE;
}
// Mix the states according to the rounds
for (int round = 0; round < rounds; round++) {
for (int i = 0; i < state.length; i++) {
state[i] = (long) (((int) state[i] + round) ^ (round * SECRET_SAUCE) + (i + blockCounter));
}
}
// Output block
for (int i = 0; i < output.length; i++) {
output[i] = (byte) state[i % state.length];
}
blockCounter++;
}
public void generate(byte[] input, byte[] output, boolean encrypt) {
int totalBlocks = (input.length + blockSize - 1) / blockSize;
for (int blockIndex = 0; blockIndex < totalBlocks; blockIndex++) {
int inputOffset = blockIndex * blockSize;
int outputOffset = blockIndex * blockSize;
byte[] block = new byte[blockSize];
// Generate a block based on the input
generateBlock(block);
// Perform XOR for encryption or decryption
for (int i = 0; i < block.length && inputOffset + i < input.length; i++) {
output[outputOffset + i] = (byte) (input[inputOffset + i] ^ block[i]);
}
}
}
@Override
public void close() {
if (state != null) {
Arrays.fill(state, 0);
}
}
private long bytesToLong(byte[] bytes, int offset) {
return ((long) bytes[offset] & UNSIGNED_INT) << 56 |
((long) bytes[offset + 1] & UNSIGNED_INT) << 48 |
((long) bytes[offset + 2] & UNSIGNED_INT) << 40 |
((long) bytes[offset + 3] & UNSIGNED_INT) << 32 |
((long) bytes[offset + 4] & UNSIGNED_INT) << 24 |
((long) bytes[offset + 5] & UNSIGNED_INT) << 16 |
((long) bytes[offset + 6] & UNSIGNED_INT) << 8 |
((long) bytes[offset + 7] & UNSIGNED_INT);
}
}
public static void main(String[] args) {
String password = "securePassword123!@#$";
String plaintext = "Thank you for using EonaCatCipher!";
System.out.println("Encrypting '" + plaintext + "' with password '" + password + "' (we do this 5 times)");
System.out.println("================");
for (int i = 0; i < 5; i++) {
System.out.println("Encryption round " + (i + 1) + ": ");
System.out.println("================");
try (EonaCatCipher cipher = new EonaCatCipher(password, DEFAULT_SALT_SIZE, DEFAULT_IV_SIZE, DEFAULT_KEY_SIZE, DEFAULT_ROUNDS, DEFAULT_BLOCK_SIZE)) {
byte[] encrypted = cipher.encrypt(plaintext);
System.out.println("Encrypted (byte array): " + Base64.getEncoder().encodeToString(encrypted));
String decrypted = cipher.decrypt(encrypted);
System.out.println("Decrypted: " + decrypted);
System.out.println("================");
}
}
}
}

2
Java/readme.txt Normal file
View File

@ -0,0 +1,2 @@
javac EonaCatCipher.java
java EonaCatCipher

268
Javascript/EonaCatCipher.js Normal file
View File

@ -0,0 +1,268 @@
class EonaCatCipher {
constructor(password, saltSize = 2048, ivSize = 2048, keySize = 2048, rounds = 2048, blockSize = 8192) {
if (!password) {
throw new Error("Password cannot be null or empty.");
}
this.ivSize = ivSize;
this.keySize = keySize;
this.rounds = rounds;
this.blockSize = blockSize;
// Derive encryption key and HMAC key
[this.derivedKey, this.hmacKey] = this.deriveKeyAndHMAC(password, saltSize);
}
/*
* 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
*/
static generateRandomBytes(size) {
const randomBytes = new Uint8Array(size);
crypto.getRandomValues(randomBytes);
return randomBytes;
}
deriveKeyAndHMAC(password, saltSize) {
const salt = EonaCatCipher.generateRandomBytes(saltSize);
const encryptionKey = this.pbkdf2(password, salt, this.keySize, this.rounds);
// Derive separate key for HMAC
const hmacKey = this.pbkdf2(password, salt, 32, this.rounds);
const keyWithSalt = new Uint8Array(saltSize + this.keySize);
keyWithSalt.set(salt, 0);
keyWithSalt.set(encryptionKey, saltSize);
return [keyWithSalt, hmacKey];
}
pbkdf2(password, salt, keyLength, iterations) {
const hmac = new Uint8Array(64); // HMAC length (SHA512)
const hashLength = hmac.length;
const blocksNeeded = Math.ceil(keyLength / hashLength);
const derivedKey = new Uint8Array(keyLength);
for (let blockIndex = 1; blockIndex <= blocksNeeded; blockIndex++) {
const currentBlock = new Uint8Array(salt.length + 4);
currentBlock.set(salt, 0);
currentBlock.set(new Uint8Array(new Uint32Array([blockIndex]).buffer), salt.length);
let u = this.hmacSha512(password, currentBlock);
const block = new Uint8Array(hashLength);
block.set(u);
// Step 4: Derived key starts with U1
derivedKey.set(u.subarray(0, Math.min(hashLength, keyLength)), (blockIndex - 1) * hashLength);
// Step 4: Iterations
for (let iteration = 1; iteration < iterations; iteration++) {
u = this.hmacSha512(password, u);
// Step 5: XOR U2 with previous result
for (let i = 0; i < hashLength; i++) {
block[i] ^= u[i];
}
// Step 6: Append result to derived key
derivedKey.set(block.subarray(0, Math.min(hashLength, keyLength)), (blockIndex - 1) * hashLength);
}
}
return derivedKey;
}
encrypt(plaintext) {
const iv = EonaCatCipher.generateRandomBytes(this.ivSize);
const plaintextBytes = new TextEncoder().encode(plaintext);
const ciphertext = new Uint8Array(plaintextBytes.length);
const cipher = new EonaCatCrypto(this.derivedKey, iv, this.blockSize, this.rounds);
cipher.generate(plaintextBytes, ciphertext, true);
// Combine IV and ciphertext
const result = new Uint8Array(this.ivSize + ciphertext.length);
result.set(iv, 0);
result.set(ciphertext, this.ivSize);
// Generate HMAC for integrity check
const hmac = this.generateHMAC(result);
// Combine result and HMAC
const finalResult = new Uint8Array(result.length + hmac.length);
finalResult.set(result, 0);
finalResult.set(hmac, result.length);
return finalResult;
}
decrypt(ciphertextWithHMAC) {
const hmacOffset = ciphertextWithHMAC.length - 32; // HMAC length (SHA256)
// Separate HMAC from the ciphertext
const providedHMAC = ciphertextWithHMAC.subarray(hmacOffset, ciphertextWithHMAC.length);
const ciphertext = ciphertextWithHMAC.subarray(0, hmacOffset);
// Verify HMAC before decrypting
const calculatedHMAC = this.generateHMAC(ciphertext);
if (!this.areEqual(providedHMAC, calculatedHMAC)) {
throw new Error("EonaCatCipher: HMAC validation failed. Data may have been tampered with.");
}
// Extract IV
const iv = ciphertext.subarray(0, this.ivSize);
// Extract encrypted data
const encryptedData = ciphertext.subarray(this.ivSize);
// Decrypt
const decryptedData = new Uint8Array(encryptedData.length);
const cipher = new EonaCatCrypto(this.derivedKey, iv, this.blockSize, this.rounds);
cipher.generate(encryptedData, decryptedData, false);
return new TextDecoder().decode(decryptedData);
}
generateHMAC(data) {
return this.hmacSha256(this.hmacKey, data);
}
hmacSha512(key, data) {
const hmac = new HMAC(key, "SHA-512");
return hmac.compute(data);
}
hmacSha256(key, data) {
const hmac = new HMAC(key, "SHA-256");
return hmac.compute(data);
}
areEqual(a, b) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
}
class EonaCatCrypto {
constructor(keyWithSalt, nonce, blockSize, rounds) {
this.rounds = rounds;
this.blockSize = blockSize > 0 ? blockSize : 128;
this.key = new Uint32Array(keyWithSalt.length / 4);
this.key.set(new Uint32Array(keyWithSalt.buffer));
this.nonce = new Uint32Array(nonce.length / 4);
this.nonce.set(new Uint32Array(nonce.buffer));
this.state = new BigUint64Array(this.blockSize / 4);
this.blockCounter = 0;
}
generateBlock(output) {
// Initialize state using a combined operation
for (let i = 0; i < this.state.length; i++) {
this.state[i] = (this.key[i % this.key.length] ^ this.nonce[i % this.nonce.length]) + BigInt(i) * BigInt(0x5DEECE66D);
}
// Mix the states according to the rounds
for (let round = 0; round < this.rounds; round++) {
for (let i = 0; i < this.state.length; i++) {
this.state[i] = (this.state[i] + BigInt(round)) ^ (BigInt(round) * BigInt(0x5DEECE66D)) + BigInt(i + this.blockCounter);
}
}
// Output block
output.set(new Uint8Array(this.state.buffer));
this.blockCounter++;
}
generate(input, output, encrypt) {
const totalBlocks = Math.ceil(input.length / this.blockSize);
for (let blockIndex = 0; blockIndex < totalBlocks; blockIndex++) {
const inputOffset = blockIndex * this.blockSize;
const outputOffset = blockIndex * this.blockSize;
const block = new Uint8Array(this.blockSize);
// Generate a block based on the input
this.generateBlock(block);
// Perform XOR for encryption or decryption
for (let i = 0; i < block.length && inputOffset + i < input.length; i++) {
output[outputOffset + i] = input[inputOffset + i] ^ block[i];
}
}
}
}
class HMAC {
constructor(key, algorithm) {
this.key = key;
this.algorithm = algorithm;
}
compute(data) {
const cryptoKey = crypto.subtle.importKey(
"raw",
this.key,
{ name: "HMAC", hash: { name: this.algorithm } },
false,
["sign", "verify"]
);
return crypto.subtle.sign("HMAC", cryptoKey, data).then(signature => new Uint8Array(signature));
}
}
// Usage Example
(async () => {
const password = "securePassword123!@#$";
const plaintext = "Thank you for using EonaCatCipher!";
console.log(`Encrypting '${plaintext}' with password '${password}' (we do this 5 times)`);
console.log("================");
for (let i = 0; i < 5; i++) {
console.log(`Encryption round ${i + 1}: `);
console.log("================");
const cipher = new EonaCatCipher(password);
const encrypted = await cipher.encrypt(plaintext);
console.log("Encrypted (byte array): " + Array.from(encrypted).join(', '));
const decrypted = await cipher.decrypt(encrypted);
console.log("Decrypted: " + decrypted);
console.log("================");
}
})();

212
LICENSE
View File

@ -1,73 +1,203 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
1. Definitions.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
OF SOFTWARE BY EONACAT (JEROEN SAEY)
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
1. Definitions.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
END OF TERMS AND CONDITIONS
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
APPENDIX: How to apply the Apache License to your work.
END OF TERMS AND CONDITIONS
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
APPENDIX: How to apply the Apache License to your work.
Copyright 2024 EonaCat
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Copyright [yyyy] [name of copyright owner]
http://www.apache.org/licenses/LICENSE-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

235
PHP/EonaCatCipher.php Normal file
View File

@ -0,0 +1,235 @@
<?php
class EonaCatCipher {
private const DEFAULT_SALT_SIZE = 2048; // Salt size for key derivation
private const DEFAULT_IV_SIZE = 2048; // IV size (16384 bits)
private const DEFAULT_KEY_SIZE = 2048; // Key size (16384 bits)
private const DEFAULT_ROUNDS = 2048; // Rounds
private const DEFAULT_BLOCK_SIZE = 8192; // 8kb
private const HMAC_KEY_SIZE = 32; // Key size for HMAC (256 bits)
private $derivedKey; // Derived encryption key
private $hmacKey; // HMAC key
private $ivSize; // IV size
private $keySize; // Key size
private $rounds; // Number of rounds for key derivation
private $blockSize; // The size of the block that is created
/*
* 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
*/
public function __construct($password, $saltSize = self::DEFAULT_SALT_SIZE, $ivSize = self::DEFAULT_IV_SIZE, $keySize = self::DEFAULT_KEY_SIZE, $rounds = self::DEFAULT_ROUNDS, $blockSize = self::DEFAULT_BLOCK_SIZE) {
if (empty($password)) {
throw new InvalidArgumentException("EonaCatCipher: Password cannot be null or empty.");
}
$this->ivSize = $ivSize;
$this->keySize = $keySize;
$this->rounds = $rounds;
$this->blockSize = $blockSize;
// Derive encryption key and HMAC key
list($this->derivedKey, $this->hmacKey) = $this->deriveKeyAndHMAC($password, $saltSize);
}
private static function generateRandomBytes($size) {
return random_bytes($size);
}
private function deriveKeyAndHMAC($password, $saltSize) {
$salt = self::generateRandomBytes($saltSize);
$encryptionKey = $this->pbkdf2($password, $salt, $this->keySize, $this->rounds);
// Derive separate key for HMAC
$hmacKey = $this->pbkdf2($password, $salt, self::HMAC_KEY_SIZE, $this->rounds);
$keyWithSalt = $salt . $encryptionKey;
return [$keyWithSalt, $hmacKey];
}
private function pbkdf2($password, $salt, $keyLength, $iterations) {
$hmac = hash_hmac('sha512', '', $password, true);
$hashLength = strlen($hmac);
$requiredBytes = $keyLength;
$blocksNeeded = ceil($requiredBytes / $hashLength);
$derivedKey = '';
$block = '';
for ($blockIndex = 1; $blockIndex <= $blocksNeeded; $blockIndex++) {
$currentBlock = $salt . pack('N', $blockIndex);
$u = hash_hmac('sha512', $currentBlock, $password, true);
$block = $u;
$derivedKey .= $u;
for ($iteration = 1; $iteration < $iterations; $iteration++) {
$u = hash_hmac('sha512', $u, $password, true);
for ($i = 0; $i < $hashLength; $i++) {
$block[$i] ^= $u[$i];
}
$derivedKey .= $block;
}
}
return substr($derivedKey, 0, $requiredBytes);
}
public function encrypt($plaintext) {
$iv = self::generateRandomBytes($this->ivSize);
$plaintextBytes = $plaintext;
$ciphertext = str_repeat("\0", strlen($plaintextBytes));
$cipher = new EonaCatCrypto($this->derivedKey, $iv, $this->blockSize, $this->rounds);
$cipher->generate($plaintextBytes, $ciphertext, true);
// Combine IV and ciphertext
$result = $iv . $ciphertext;
// Generate HMAC for integrity check
$hmac = $this->generateHMAC($result);
// Combine result and HMAC
return $result . $hmac;
}
public function decrypt($ciphertextWithHMAC) {
$hmacOffset = strlen($ciphertextWithHMAC) - self::HMAC_KEY_SIZE;
// Separate HMAC from the ciphertext
$providedHMAC = substr($ciphertextWithHMAC, $hmacOffset);
$ciphertext = substr($ciphertextWithHMAC, 0, $hmacOffset);
// Verify HMAC before decrypting
$calculatedHMAC = $this->generateHMAC($ciphertext);
if (!hash_equals($providedHMAC, $calculatedHMAC)) {
throw new Exception("EonaCatCipher: HMAC validation failed. Data may have been tampered with.");
}
// Extract IV
$iv = substr($ciphertext, 0, $this->ivSize);
// Extract encrypted data
$encryptedData = substr($ciphertext, $this->ivSize);
// Decrypt
$decryptedData = str_repeat("\0", strlen($encryptedData));
$cipher = new EonaCatCrypto($this->derivedKey, $iv, $this->blockSize, $this->rounds);
$cipher->generate($encryptedData, $decryptedData, false);
return $decryptedData;
}
private function generateHMAC($data) {
return hash_hmac('sha256', $data, $this->hmacKey, true);
}
public static function main() {
$password = "securePassword123!@#$";
$plaintext = "Thank you for using EonaCatCipher!";
echo "Encrypting '$plaintext' with password '$password' (we do this 5 times)\n";
echo "================\n";
for ($i = 0; $i < 5; $i++) {
echo "Encryption round " . ($i + 1) . ": \n";
echo "================\n";
$cipher = new EonaCatCipher($password);
$encrypted = $cipher->encrypt($plaintext);
echo "Encrypted (byte array): " . bin2hex($encrypted) . "\n";
$decrypted = $cipher->decrypt($encrypted);
echo "Decrypted: " . $decrypted . "\n";
echo "================\n";
}
}
}
class EonaCatCrypto {
private const SECRET_SAUCE = 0x5DEECE66D;
private const UNSIGNED_INT = 0xFFFFFFFF;
private $blockSize;
private $rounds;
private $state;
private $key;
private $nonce;
private $blockCounter;
public function __construct($keyWithSalt, $nonce, $blockSize, $rounds) {
$this->rounds = $rounds;
$this->blockSize = $blockSize / 4 > 0 ? $blockSize : 128;
$this->key = array_values(unpack("N*", $keyWithSalt));
$this->nonce = array_values(unpack("N*", $nonce));
$this->state = array_fill(0, $this->blockSize / 4, 0);
$this->blockCounter = 0;
}
private function generateBlock(&$output) {
// Initialize state using a combined operation
for ($i = 0; $i < count($this->state); $i++) {
$this->state[$i] = ($this->key[$i % count($this->key)] ^ $this->nonce[$i % count($this->nonce)]) + ($i * self::SECRET_SAUCE);
}
// Mix the states according to the rounds
for ($round = 0; $round < $this->rounds; $round++) {
for ($i = 0; $i < count($this->state); $i++) {
$this->state[$i] = (($this->state[$i] + $round) ^ ($round * self::SECRET_SAUCE) + ($i + $this->blockCounter)) & self::UNSIGNED_INT;
}
}
// Output block
$output = pack("N*", ...$this->state);
$this->blockCounter++;
}
public function generate($input, &$output, $encrypt) {
$totalBlocks = ceil(strlen($input) / $this->blockSize);
for ($blockIndex = 0; $blockIndex < $totalBlocks; $blockIndex++) {
$inputOffset = $blockIndex * $this->blockSize;
$outputOffset = $blockIndex * $this->blockSize;
$block = str_repeat("\0", $this->blockSize);
// Generate a block based on the input
$this->generateBlock($block);
// Perform XOR for encryption or decryption
for ($i = 0; $i < strlen($block) && $inputOffset + $i < strlen($input); $i++) {
$output[$outputOffset + $i] = $input[$inputOffset + $i] ^ $block[$i];
}
}
}
}
EonaCatCipher::main();
?>

189
Python/EonaCatCipher.py Normal file
View File

@ -0,0 +1,189 @@
import os
import hashlib
import hmac
import struct
import base64
class EonaCatCipher:
DEFAULT_SALT_SIZE = 2048; # Salt size for key derivation
DEFAULT_IV_SIZE = 2048; # IV size (16384 bits)
DEFAULT_KEY_SIZE = 2048; # Key size (16384 bits)
DEFAULT_ROUNDS = 2048; # Rounds
DEFAULT_BLOCK_SIZE = 8192; # 8kb
HMAC_KEY_SIZE = 32; # Key size for HMAC (256 bits)
#/*
# * 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
# */
def __init__(self, password, salt_size=None, iv_size=None, key_size=None, rounds=None, block_size=None):
if not password:
raise ValueError("EonaCatCipher: Password cannot be null or empty.")
self.salt_size = salt_size if salt_size is not None else self.DEFAULT_SALT_SIZE
self.iv_size = iv_size if iv_size is not None else self.DEFAULT_IV_SIZE // 8 # Convert bits to bytes
self.key_size = key_size if key_size is not None else self.DEFAULT_KEY_SIZE // 8 # Convert bits to bytes
self.rounds = rounds if rounds is not None else self.DEFAULT_ROUNDS
self.block_size = block_size if block_size is not None else self.DEFAULT_BLOCK_SIZE // 8 # Convert bits to bytes
# Derive encryption key and HMAC key
self.derived_key, self.hmac_key = self.derive_key_and_hmac(password)
@staticmethod
def generate_random_bytes(size):
return os.urandom(size)
def derive_key_and_hmac(self, password):
salt = self.generate_random_bytes(self.salt_size)
encryption_key = self.pbkdf2(password, salt, self.key_size, self.rounds)
# Derive separate key for HMAC
hmac_key = self.pbkdf2(password, salt, self.HMAC_KEY_SIZE, self.rounds)
key_with_salt = salt + encryption_key
return key_with_salt, hmac_key
@staticmethod
def pbkdf2(password, salt, key_length, iterations):
# PBKDF2 using HMAC-SHA512
hmac_sha512 = hashlib.pbkdf2_hmac('sha512', password.encode(), salt, iterations, dklen=key_length)
return hmac_sha512
def encrypt(self, plaintext):
iv = self.generate_random_bytes(self.iv_size)
plaintext_bytes = plaintext.encode()
ciphertext = bytearray(len(plaintext_bytes))
# Generate cipher
cipher = EonaCatCrypto(self.derived_key, iv, self.block_size, self.rounds)
cipher.generate(plaintext_bytes, ciphertext, True)
# Combine IV and ciphertext
result = iv + ciphertext
# Generate HMAC for integrity check
hmac = self.generate_hmac(result)
# Combine result and HMAC
final_result = result + hmac
return final_result
def decrypt(self, ciphertext_with_hmac):
hmac_offset = len(ciphertext_with_hmac) - self.HMAC_KEY_SIZE
# Separate HMAC from the ciphertext
provided_hmac = ciphertext_with_hmac[hmac_offset:]
ciphertext = ciphertext_with_hmac[:hmac_offset]
# Verify HMAC before decrypting
calculated_hmac = self.generate_hmac(ciphertext)
if not self.are_equal(provided_hmac, calculated_hmac):
raise ValueError("EonaCatCipher: HMAC validation failed. Data may have been tampered with.")
# Extract IV
iv = ciphertext[:self.iv_size]
encrypted_data = ciphertext[self.iv_size:]
# Decrypt
decrypted_data = bytearray(len(encrypted_data))
cipher = EonaCatCrypto(self.derived_key, iv, self.block_size, self.rounds)
cipher.generate(encrypted_data, decrypted_data, False)
return decrypted_data.decode()
def generate_hmac(self, data):
return hmac.new(self.hmac_key, data, hashlib.sha256).digest()
@staticmethod
def are_equal(a, b):
return hmac.compare_digest(a, b)
class EonaCatCrypto:
SECRET_SAUCE = 0x5DEECE66D
def __init__(self, key_with_salt, nonce, block_size, rounds):
self.rounds = rounds
self.block_size = block_size // 4 > 0 and block_size // 4 or 128
self.key = list(struct.unpack(f'>{len(key_with_salt) // 4}I', key_with_salt))
self.nonce = list(struct.unpack(f'>{len(nonce) // 4}I', nonce))
self.state = [0] * (self.block_size // 4)
self.block_counter = 0
def generate_block(self, output):
# Initialize state using a combined operation
for i in range(len(self.state)):
self.state[i] = (self.key[i % len(self.key)] ^ self.nonce[i % len(self.nonce)]) + (i * self.SECRET_SAUCE)
# Mix the states according to the rounds
for round in range(self.rounds):
for i in range(len(self.state)):
self.state[i] = (self.state[i] + round) ^ (round * self.SECRET_SAUCE) + (i + self.block_counter)
# Output block
output.extend(self.state)
self.block_counter += 1
def generate(self, input_data, output, encrypt):
total_blocks = (len(input_data) + self.block_size - 1) // self.block_size
for block_index in range(total_blocks):
input_offset = block_index * self.block_size
block = bytearray(self.block_size)
# Generate a block based on the input
self.generate_block(block)
# Perform XOR for encryption or decryption
for i in range(len(block)):
if input_offset + i < len(input_data):
output[input_offset + i] = input_data[input_offset + i] ^ block[i]
def main():
password = "securePassword123!@#$"
plaintext = "Thank you for using EonaCatCipher!"
print(f"Encrypting '{plaintext}' with password '{password}' (we do this 5 times)")
print("================")
for i in range(5):
print(f"Encryption round {i + 1}: ")
print("================")
cipher = EonaCatCipher(password)
encrypted = cipher.encrypt(plaintext)
print("Encrypted (byte array):", [b for b in encrypted])
decrypted = cipher.decrypt(encrypted)
print("Decrypted:", decrypted)
print("================")
if __name__ == "__main__":
main()

View File

@ -1,3 +1,15 @@
# EonaCatCipher
EonaCat Cipher Security
Security algorithm for:
C#
C++
Delphi
Go
Java
JavaScript
PHP
Python
Ruby
Rust

224
Ruby/EonaCatCipher.ruby Normal file
View File

@ -0,0 +1,224 @@
require 'openssl'
require 'securerandom'
class EonaCatCipher
DEFAULT_SALT_SIZE = 2048; # Salt size for key derivation
DEFAULT_IV_SIZE = 2048; # IV size (16384 bits)
DEFAULT_KEY_SIZE = 2048; # Key size (16384 bits)
DEFAULT_ROUNDS = 2048; # Rounds
DEFAULT_BLOCK_SIZE = 8192; # 8kb
HMAC_KEY_SIZE = 32; # Key size for HMAC (256 bits)
#/*
# * 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
# */
def initialize(password, salt_size = DEFAULT_SALT_SIZE, iv_size = DEFAULT_IV_SIZE, key_size = DEFAULT_KEY_SIZE, rounds = DEFAULT_ROUNDS, block_size = DEFAULT_BLOCK_SIZE)
raise ArgumentError, 'EonaCatCipher: Password cannot be null or empty.' if password.nil? || password.empty?
@iv_size = iv_size
@key_size = key_size
@rounds = rounds
@block_size = block_size
# Derive encryption key and HMAC key
@derived_key, @hmac_key = derive_key_and_hmac(password, salt_size)
end
def encrypt(plaintext)
iv = generate_random_bytes(@iv_size)
plaintext_bytes = plaintext.encode('UTF-8')
ciphertext = Array.new(plaintext_bytes.bytesize, 0)
cipher = EonaCatCrypto.new(@derived_key, iv, @block_size, @rounds)
cipher.generate(plaintext_bytes.bytes, ciphertext, true)
# Combine IV and ciphertext
result = iv + ciphertext.pack('C*')
# Generate HMAC for integrity check
hmac = generate_hmac(result)
# Combine result and HMAC
final_result = result + hmac
final_result
end
def decrypt(ciphertext_with_hmac)
hmac_offset = ciphertext_with_hmac.bytesize - HMAC_KEY_SIZE
# Separate HMAC from the ciphertext
provided_hmac = ciphertext_with_hmac[hmac_offset, HMAC_KEY_SIZE]
ciphertext = ciphertext_with_hmac[0, hmac_offset]
# Verify HMAC before decrypting
calculated_hmac = generate_hmac(ciphertext)
raise 'EonaCatCipher: HMAC validation failed. Data may have been tampered with.' unless secure_compare(provided_hmac, calculated_hmac)
# Extract IV
iv = ciphertext[0, @iv_size]
# Extract encrypted data
encrypted_data = ciphertext[@iv_size, ciphertext.bytesize - @iv_size]
# Decrypt
decrypted_data = Array.new(encrypted_data.bytesize, 0)
cipher = EonaCatCrypto.new(@derived_key, iv, @block_size, @rounds)
cipher.generate(encrypted_data.bytes, decrypted_data, false)
decrypted_data.pack('C*').force_encoding('UTF-8')
end
private
def generate_random_bytes(size)
SecureRandom.random_bytes(size)
end
def derive_key_and_hmac(password, salt_size)
salt = generate_random_bytes(salt_size)
encryption_key = pbkdf2(password, salt, @key_size, @rounds)
# Derive separate key for HMAC
hmac_key = pbkdf2(password, salt, HMAC_KEY_SIZE, @rounds)
key_with_salt = salt + encryption_key
[key_with_salt, hmac_key]
end
def pbkdf2(password, salt, key_length, iterations)
hmac = OpenSSL::Digest::SHA512.new
derived_key = []
block_size = hmac.digest_length
blocks_needed = (key_length.to_f / block_size).ceil
blocks_needed.times do |block_index|
block_index += 1 # PBKDF2 block indexing starts at 1
u = OpenSSL::HMAC.digest(hmac, password, salt + [block_index].pack('N'))
derived_key.concat(u.bytes)
(iterations - 1).times do
u = OpenSSL::HMAC.digest(hmac, password, u)
derived_key[-block_size, block_size].each_index do |i|
derived_key[-block_size + i] ^= u.bytes[i]
end
end
end
derived_key[0, key_length]
end
def generate_hmac(data)
hmac = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, @hmac_key, data)
hmac.bytes
end
def secure_compare(a, b)
return false if a.bytesize != b.bytesize
# Use `each_byte` for a timing-safe comparison
res = 0
a.each_byte.with_index do |byte, i|
res |= byte ^ b.getbyte(i)
end
res == 0
end
end
class EonaCatCrypto
SECRET_SAUCE = 0x5DEECE66D
def initialize(key_with_salt, nonce, block_size, rounds)
@rounds = rounds
@block_size = block_size / 4 > 0 ? block_size : 128
@key = key_with_salt.unpack('N*')
@nonce = nonce.unpack('N*')
@state = Array.new(@block_size / 4, 0)
@block_counter = 0
end
def generate(input, output, encrypt)
total_blocks = (input.length + @block_size - 1) / @block_size
total_blocks.times do |block_index|
input_offset = block_index * @block_size
output_offset = block_index * @block_size
block = Array.new(@block_size, 0)
generate_block(block)
(0...block.size).each do |i|
if input_offset + i < input.size
output[output_offset + i] = input[input_offset + i] ^ block[i]
end
end
end
end
private
def generate_block(output)
@state.each_index do |i|
@state[i] = (@key[i % @key.size] ^ @nonce[i % @nonce.size]) + (i * SECRET_SAUCE)
end
@rounds.times do |round|
@state.each_index do |i|
@state[i] = ((@state[i] + round) ^ (round * SECRET_SAUCE) + (i + @block_counter)).to_i
end
end
output.replace(@state.pack('Q*'))
@block_counter += 1
end
end
# Example Usage
if __FILE__ == $0
password = "securePassword123!@#$"
plaintext = "Thank you for using EonaCatCipher!"
puts "Encrypting '#{plaintext}' with password '#{password}' (we do this 5 times)"
puts "================"
5.times do |i|
puts "Encryption round #{i + 1}: "
puts "================"
cipher = EonaCatCipher.new(password)
encrypted = cipher.encrypt(plaintext)
puts "Encrypted (byte array): #{encrypted.unpack1('H*')}"
decrypted = cipher.decrypt(encrypted)
puts "Decrypted: #{decrypted}"
puts "================"
end
end

261
Rust/EonaCatCipher.rust Normal file
View File

@ -0,0 +1,261 @@
use rand::{RngCore, Rng};
use sha2::{Sha256, Sha512, Digest};
use hmac::{Hmac, Mac};
use std::convert::TryInto;
type HmacSha256 = Hmac<Sha256>;
type HmacSha512 = Hmac<Sha512>;
const DEFAULT_SALT_SIZE: usize = 256; // Salt size for key derivation
const DEFAULT_IV_SIZE: usize = 256; // IV size (2048 bits)
const DEFAULT_KEY_SIZE: usize = 256; // Key size (2048 bits)
const DEFAULT_ROUNDS: usize = 256; // Rounds
const DEFAULT_BLOCK_SIZE: usize = 1024; // 1kb
const HMAC_KEY_SIZE: usize = 32; // Key size for HMAC (256 bits)
/*
* 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
*/
pub struct EonaCatCipher {
derived_key: Vec<u8>, // Derived encryption key
hmac_key: Vec<u8>, // HMAC key
iv_size: usize, // IV size
key_size: usize, // Key size
rounds: usize, // Number of rounds for key derivation
block_size: usize, // The size of the block that is created
}
impl EonaCatCipher {
pub fn new(password: &str, salt_size: usize, iv_size: usize, key_size: usize, rounds: usize, block_size: usize) -> Result<Self, String> {
if password.is_empty() {
return Err("EonaCatCipher: Password cannot be null or empty.".to_string());
}
let (derived_key, hmac_key) = Self::derive_key_and_hmac(password, salt_size)?;
Ok(Self {
derived_key,
hmac_key,
iv_size,
key_size,
rounds,
block_size,
})
}
fn generate_random_bytes(size: usize) -> Vec<u8> {
let mut rng = rand::thread_rng();
let mut random_bytes = vec![0u8; size];
rng.fill_bytes(&mut random_bytes);
random_bytes
}
fn derive_key_and_hmac(password: &str, salt_size: usize) -> Result<(Vec<u8>, Vec<u8>), String> {
let salt = Self::generate_random_bytes(salt_size);
let encryption_key = Self::pbkdf2(password, &salt, DEFAULT_KEY_SIZE, DEFAULT_ROUNDS)?;
let hmac_key = Self::pbkdf2(password, &salt, HMAC_KEY_SIZE, DEFAULT_ROUNDS)?;
let mut key_with_salt = vec![0u8; salt.len() + encryption_key.len()];
key_with_salt[..salt.len()].copy_from_slice(&salt);
key_with_salt[salt.len()..].copy_from_slice(&encryption_key);
Ok((key_with_salt, hmac_key))
}
fn pbkdf2(password: &str, salt: &[u8], key_length: usize, iterations: usize) -> Result<Vec<u8>, String> {
let mut derived_key = vec![0u8; key_length];
let hmac = HmacSha512::new_varkey(password.as_bytes()).map_err(|e| e.to_string())?;
let hash_length = hmac.output_size();
let blocks_needed = (key_length + hash_length - 1) / hash_length;
for block_index in 0..blocks_needed {
let mut current_block = Vec::with_capacity(salt.len() + 4);
current_block.extend_from_slice(salt);
current_block.extend_from_slice(&(block_index + 1).to_be_bytes());
let mut u = hmac.clone().finalize_reset(&current_block);
let mut block = u.clone();
let derived_key_offset = block_index * hash_length;
let remaining = key_length - derived_key_offset;
let copy_length = remaining.min(hash_length);
derived_key[derived_key_offset..derived_key_offset + copy_length].copy_from_slice(&u);
for _ in 1..iterations {
u = hmac.clone().finalize_reset(&u);
for i in 0..hash_length {
block[i] ^= u[i];
}
let remaining = key_length - derived_key_offset;
let copy_length = remaining.min(hash_length);
derived_key[derived_key_offset..derived_key_offset + copy_length].copy_from_slice(&block);
}
}
Ok(derived_key)
}
pub fn encrypt(&self, plaintext: &str) -> Vec<u8> {
let iv = Self::generate_random_bytes(self.iv_size);
let plaintext_bytes = plaintext.as_bytes();
let mut ciphertext = vec![0u8; plaintext_bytes.len()];
let mut cipher = EonaCatCrypto::new(&self.derived_key, &iv, self.block_size, self.rounds);
cipher.generate(plaintext_bytes, &mut ciphertext, true);
let mut result = Vec::with_capacity(self.iv_size + ciphertext.len());
result.extend_from_slice(&iv);
result.extend_from_slice(&ciphertext);
let hmac = self.generate_hmac(&result);
result.extend_from_slice(&hmac);
result
}
pub fn decrypt(&self, ciphertext_with_hmac: &[u8]) -> Result<String, String> {
let hmac_offset = ciphertext_with_hmac.len() - HMAC_KEY_SIZE;
let provided_hmac = &ciphertext_with_hmac[hmac_offset..];
let ciphertext = &ciphertext_with_hmac[..hmac_offset];
let calculated_hmac = self.generate_hmac(ciphertext);
if provided_hmac != calculated_hmac.as_slice() {
return Err("EonaCatCipher: HMAC validation failed. Data may have been tampered with.".to_string());
}
let iv = &ciphertext[..self.iv_size];
let encrypted_data = &ciphertext[self.iv_size..];
let mut decrypted_data = vec![0u8; encrypted_data.len()];
let mut cipher = EonaCatCrypto::new(&self.derived_key, iv, self.block_size, self.rounds);
cipher.generate(encrypted_data, &mut decrypted_data, false);
String::from_utf8(decrypted_data).map_err(|e| e.to_string())
}
fn generate_hmac(&self, data: &[u8]) -> Vec<u8> {
let mut hmac = HmacSha256::new_varkey(&self.hmac_key).expect("HMAC key should be valid");
hmac.update(data);
hmac.finalize().into_bytes().to_vec()
}
}
pub struct EonaCatCrypto {
block_size: usize,
rounds: usize,
state: Vec<u64>,
key: Vec<u32>,
nonce: Vec<u32>,
block_counter: u32,
}
impl EonaCatCrypto {
pub fn new(key_with_salt: &[u8], nonce: &[u8], block_size: usize, rounds: usize) -> Self {
let key_length = key_with_salt.len() / 4;
let mut key = vec![0u32; key_length];
key.copy_from_slice(&key_with_salt[..key_length * 4].chunks(4).map(|chunk| {
u32::from_be_bytes(chunk.try_into().unwrap())
}).collect::<Vec<_>>());
let nonce_length = nonce.len() / 4;
let mut nonce_arr = vec![0u32; nonce_length];
nonce_arr.copy_from_slice(&nonce[..nonce_length * 4].chunks(4).map(|chunk| {
u32::from_be_bytes(chunk.try_into().unwrap())
}).collect::<Vec<_>>());
Self {
block_size,
rounds,
state: vec![0u64; block_size / 4],
key,
nonce: nonce_arr,
block_counter: 0,
}
}
fn generate_block(&mut self, output: &mut [u8]) {
for i in 0..self.state.len() {
self.state[i] = (self.key[i % self.key.len()] as u64 ^ self.nonce[i % self.nonce.len()] as u64) + (i as u64 * 0x5DEECE66D);
}
for round in 0..self.rounds {
for i in 0..self.state.len() {
self.state[i] = (((self.state[i] as i64) + round as i64) ^ ((round as i64) * 0x5DEECE66D as i64) + (i as i64 + self.block_counter as i64)) as u64);
}
}
output.copy_from_slice(bytemuck::cast_slice(&self.state));
self.block_counter += 1;
}
pub fn generate(&mut self, input: &[u8], output: &mut [u8], encrypt: bool) {
let total_blocks = (input.len() + self.block_size - 1) / self.block_size;
for block_index in 0..total_blocks {
let input_offset = block_index * self.block_size;
let output_offset = block_index * self.block_size;
let mut block = vec![0u8; self.block_size];
self.generate_block(&mut block);
for i in 0..block.len() {
if input_offset + i < input.len() {
output[output_offset + i] = input[input_offset + i] ^ block[i];
}
}
}
}
}
fn main() {
let password = "securePassword123!@#$";
let plaintext = "Thank you for using EonaCatCipher!";
println!("Encrypting '{}' with password '{}' (we do this 5 times)", plaintext, password);
println!("================");
for i in 0..5 {
println!("Encryption round {}: ", i + 1);
println!("================");
let cipher = EonaCatCipher::new(password, DEFAULT_SALT_SIZE, DEFAULT_IV_SIZE, DEFAULT_KEY_SIZE, DEFAULT_ROUNDS, DEFAULT_BLOCK_SIZE)
.expect("Failed to create cipher");
let encrypted = cipher.encrypt(plaintext);
println!("Encrypted (byte array): {:?}", encrypted);
let decrypted = cipher.decrypt(&encrypted).expect("Failed to decrypt");
println!("Decrypted: {}", decrypted);
println!("================");
}
}

10
Rust/cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "EonaCatCipher"
version = "1.0.0"
edition = "2024"
[dependencies]
rand = "0.8"
sha2 = "0.10"
hmac = "0.11"
bytemuck = "1.7"

2
Rust/readme.txt Normal file
View File

@ -0,0 +1,2 @@
cargo build
cargo run

262
c/EonaCatCipher.c Normal file
View File

@ -0,0 +1,262 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#define DEFAULT_SALT_SIZE 32 // Salt size for key derivation
#define DEFAULT_IV_SIZE 16 // IV size (128 bits)
#define DEFAULT_KEY_SIZE 32 // Key size (256 bits)
#define DEFAULT_ROUNDS 10000 // Rounds
#define DEFAULT_BLOCK_SIZE 16 // 128 bits
#define HMAC_KEY_SIZE 32 // Key size for HMAC (256 bits)
// Function prototypes
void generate_random_bytes(uint8_t *buffer, size_t size);
void pbkdf2(const char *password, uint8_t *salt, int salt_len, uint8_t *output, int output_len, int iterations);
void xor_buffers(uint8_t *a, uint8_t *b, uint8_t *result, size_t length);
void generate_hmac(uint8_t *data, size_t data_len, uint8_t *key, size_t key_len, uint8_t *hmac_out);
int are_equal(const uint8_t *a, const uint8_t *b, size_t length);
// EonaCatCipher structure
typedef struct {
uint8_t derived_key[DEFAULT_KEY_SIZE];
uint8_t hmac_key[HMAC_KEY_SIZE];
int iv_size;
int key_size;
int rounds;
int block_size;
} EonaCatCipher;
// EonaCatCrypto structure
typedef struct {
uint8_t *key;
uint8_t *nonce;
int block_size;
int rounds;
uint64_t *state;
uint32_t block_counter;
} EonaCatCrypto;
// Function to create and initialize EonaCatCipher
EonaCatCipher* eonacat_cipher_create(const char *password) {
if (password == NULL || strlen(password) == 0) {
fprintf(stderr, "EonaCatCipher: Password cannot be null or empty.\n");
return NULL;
}
EonaCatCipher *cipher = malloc(sizeof(EonaCatCipher));
if (!cipher) {
fprintf(stderr, "EonaCatCipher: Memory allocation failed.\n");
return NULL;
}
cipher->iv_size = DEFAULT_IV_SIZE;
cipher->key_size = DEFAULT_KEY_SIZE;
cipher->rounds = DEFAULT_ROUNDS;
cipher->block_size = DEFAULT_BLOCK_SIZE;
uint8_t salt[DEFAULT_SALT_SIZE];
generate_random_bytes(salt, sizeof(salt));
pbkdf2(password, salt, sizeof(salt), cipher->derived_key, sizeof(cipher->derived_key), cipher->rounds);
pbkdf2(password, salt, sizeof(salt), cipher->hmac_key, sizeof(cipher->hmac_key), cipher->rounds);
return cipher;
}
// Function to clean up and free EonaCatCipher
void eonacat_cipher_destroy(EonaCatCipher *cipher) {
if (cipher) {
memset(cipher, 0, sizeof(EonaCatCipher));
free(cipher);
}
}
// PBKDF2 implementation
void pbkdf2(const char *password, uint8_t *salt, int salt_len, uint8_t *output, int output_len, int iterations) {
int hash_len = SHA256_DIGEST_LENGTH;
int blocks_needed = (output_len + hash_len - 1) / hash_len;
uint8_t u[hash_len];
uint8_t t[hash_len];
for (int block_index = 1; block_index <= blocks_needed; block_index++) {
// Step 1: F(block_index)
uint8_t block[salt_len + 4];
memcpy(block, salt, salt_len);
uint32_t block_index_network = htonl(block_index);
memcpy(block + salt_len, &block_index_network, 4);
// Step 2: U1 = HMAC(password, salt + block_index)
unsigned int len = 0;
HMAC(EVP_sha256(), password, strlen(password), block, sizeof(block), u, &len);
memcpy(t, u, hash_len);
memcpy(output + (block_index - 1) * hash_len, t, (block_index == blocks_needed && output_len % hash_len != 0) ? output_len % hash_len : hash_len);
// Step 4: Iterations
for (int iteration = 1; iteration < iterations; iteration++) {
// U2 = HMAC(password, U1)
HMAC(EVP_sha256(), password, strlen(password), u, hash_len, u, &len);
xor_buffers(t, u, t, hash_len);
memcpy(output + (block_index - 1) * hash_len, t, (block_index == blocks_needed && output_len % hash_len != 0) ? output_len % hash_len : hash_len);
}
}
}
// Function to generate random bytes
void generate_random_bytes(uint8_t *buffer, size_t size) {
RAND_bytes(buffer, size);
}
// Function to perform XOR operation on two buffers
void xor_buffers(uint8_t *a, uint8_t *b, uint8_t *result, size_t length) {
for (size_t i = 0; i < length; i++) {
result[i] = a[i] ^ b[i];
}
}
// Function to generate HMAC
void generate_hmac(uint8_t *data, size_t data_len, uint8_t *key, size_t key_len, uint8_t *hmac_out) {
unsigned int len = 0;
HMAC(EVP_sha256(), key, key_len, data, data_len, hmac_out, &len);
}
// Function to compare two buffers
int are_equal(const uint8_t *a, const uint8_t *b, size_t length) {
return (memcmp(a, b, length) == 0);
}
// Function to encrypt plaintext
uint8_t* eonacat_cipher_encrypt(EonaCatCipher *cipher, const char *plaintext, size_t *out_len) {
uint8_t iv[cipher->iv_size];
generate_random_bytes(iv, cipher->iv_size);
size_t plaintext_len = strlen(plaintext);
uint8_t *ciphertext = malloc(plaintext_len + cipher->iv_size);
if (!ciphertext) {
fprintf(stderr, "Memory allocation failed.\n");
return NULL;
}
memcpy(ciphertext, iv, cipher->iv_size); // Combine IV and ciphertext
uint8_t encrypted[plaintext_len];
EonaCatCrypto crypto;
crypto.key = cipher->derived_key;
crypto.nonce = iv;
crypto.block_size = cipher->block_size;
crypto.rounds = cipher->rounds;
crypto.block_counter = 0;
crypto.state = calloc(cipher->block_size / 4, sizeof(uint64_t));
// Encrypt the plaintext (dummy XOR for demonstration purposes)
for (size_t i = 0; i < plaintext_len; i++) {
encrypted[i] = plaintext[i] ^ iv[i % cipher->iv_size];
}
memcpy(ciphertext + cipher->iv_size, encrypted, plaintext_len); // Append ciphertext
// Generate HMAC for integrity check
uint8_t hmac[HMAC_KEY_SIZE];
generate_hmac(ciphertext, plaintext_len + cipher->iv_size, cipher->hmac_key, sizeof(cipher->hmac_key), hmac);
*out_len = plaintext_len + cipher->iv_size + HMAC_KEY_SIZE;
uint8_t *final_result = realloc(ciphertext, *out_len);
if (!final_result) {
free(ciphertext);
fprintf(stderr, "Memory allocation failed.\n");
return NULL;
}
memcpy(final_result + *out_len - HMAC_KEY_SIZE, hmac, HMAC_KEY_SIZE); // Combine result and HMAC
return final_result;
}
// Function to decrypt ciphertext
char* eonacat_cipher_decrypt(EonaCatCipher *cipher, uint8_t *ciphertext_with_hmac, size_t ciphertext_len) {
uint8_t provided_hmac[HMAC_KEY_SIZE];
memcpy(provided_hmac, ciphertext_with_hmac + ciphertext_len - HMAC_KEY_SIZE, HMAC_KEY_SIZE);
size_t ciphertext_size = ciphertext_len - HMAC_KEY_SIZE;
uint8_t *ciphertext = malloc(ciphertext_size);
if (!ciphertext) {
fprintf(stderr, "EonaCatCipher: Memory allocation failed.\n");
return NULL;
}
memcpy(ciphertext, ciphertext_with_hmac, ciphertext_size); // Separate HMAC from the ciphertext
// Verify HMAC before decrypting
uint8_t calculated_hmac[HMAC_KEY_SIZE];
generate_hmac(ciphertext, ciphertext_size, cipher->hmac_key, sizeof(cipher->hmac_key), calculated_hmac);
if (!are_equal(provided_hmac, calculated_hmac, HMAC_KEY_SIZE)) {
free(ciphertext);
fprintf(stderr, "EonaCatCipher: HMAC validation failed. Data may have been tampered with.\n");
return NULL;
}
uint8_t iv[cipher->iv_size];
memcpy(iv, ciphertext, cipher->iv_size); // Extract IV
size_t plaintext_len = ciphertext_size - cipher->iv_size;
char *plaintext = malloc(plaintext_len + 1);
if (!plaintext) {
free(ciphertext);
fprintf(stderr, "EonaCatCipher: Memory allocation failed.\n");
return NULL;
}
// Decrypt
for (size_t i = 0; i < plaintext_len; i++) {
plaintext[i] = ciphertext[cipher->iv_size + i] ^ iv[i % cipher->iv_size];
}
plaintext[plaintext_len] = '\0'; // Null-terminate the plaintext
free(ciphertext);
return plaintext;
}
// Main function to demonstrate encryption and decryption
int main() {
const char *password = "securePassword123!@#$";
const char *plaintext = "Thank you for using EonaCatCipher!";
size_t out_len;
printf("Encrypting '%s' with password '%s'\n", plaintext, password);
printf("================\n");
EonaCatCipher *cipher = eonacat_cipher_create(password);
if (!cipher) return 1;
for (int i = 0; i < 5; i++) {
printf("Encryption round %d: \n", i + 1);
printf("================\n");
uint8_t *encrypted = eonacat_cipher_encrypt(cipher, plaintext, &out_len);
if (encrypted) {
printf("Encrypted (byte array): ");
for (size_t j = 0; j < out_len; j++) {
printf("%02X", encrypted[j]);
}
printf("\n");
char *decrypted = eonacat_cipher_decrypt(cipher, encrypted, out_len);
if (decrypted) {
printf("Decrypted: %s\n", decrypted);
free(decrypted);
}
free(encrypted);
}
printf("================\n");
}
eonacat_cipher_destroy(cipher);
return 0;
}

35
c/readme.txt Normal file
View File

@ -0,0 +1,35 @@
/*
* 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
*/
1. Compile the c code:
gcc -o EonaCatCipher EonaCatCipher.c -lcrypto
2. Run the executable:
./EonaCatCipher