Skip to content
Closed

Main #13

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 193 additions & 7 deletions Crypt-Vault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,11 +537,132 @@ vector<unsigned char> hexToBytes(const string& hex) {
}

// ═══════════════════════════════════════════════════════════
// AES Cipher Class
// Security Primitives (HMAC, PBKDF2, Memory Safety)
// ═══════════════════════════════════════════════════════════

// Secure memory wipe - prevents compiler optimization
void secure_memzero(void* ptr, size_t len) {
volatile unsigned char* p = (volatile unsigned char*)ptr;
while (len--) *p++ = 0;
#if defined(__GNUC__) || defined(__clang__)
asm volatile("" ::: "memory");
#elif defined(_MSC_VER)
_ReadWriteBarrier();
#endif
}

// Constant-time comparison to prevent timing attacks
bool constant_time_compare(const unsigned char* a, const unsigned char* b, size_t len) {
unsigned char diff = 0;
for (size_t i = 0; i < len; i++) {
diff |= a[i] ^ b[i];
}
return diff == 0;
}

// HMAC-SHA256 implementation
vector<unsigned char> hmac_sha256(const unsigned char* key, size_t keyLen,
const unsigned char* data, size_t dataLen) {
const size_t BLOCK_SIZE = 64;
unsigned char keyBlock[BLOCK_SIZE] = {0};

// If key > block size, hash it first
if (keyLen > BLOCK_SIZE) {
auto h = SHA256Impl::hash(key, keyLen);
memcpy(keyBlock, h.data(), 32);
} else {
memcpy(keyBlock, key, keyLen);
}

// Create inner and outer padded keys
unsigned char ipad[BLOCK_SIZE], opad[BLOCK_SIZE];
for (size_t i = 0; i < BLOCK_SIZE; i++) {
ipad[i] = keyBlock[i] ^ 0x36;
opad[i] = keyBlock[i] ^ 0x5c;
}

// Inner hash: H(ipad || data)
vector<unsigned char> inner(ipad, ipad + BLOCK_SIZE);
inner.insert(inner.end(), data, data + dataLen);
auto innerHash = SHA256Impl::hash(inner.data(), inner.size());

// Outer hash: H(opad || innerHash)
vector<unsigned char> outer(opad, opad + BLOCK_SIZE);
outer.insert(outer.end(), innerHash.begin(), innerHash.end());

return SHA256Impl::hash(outer.data(), outer.size());
}

// PBKDF2-SHA256 key derivation
void pbkdf2_sha256(const string& password, const unsigned char* salt, size_t saltLen,
int iterations, unsigned char* output, size_t dkLen) {
const size_t HASH_LEN = 32;
size_t blocks = (dkLen + HASH_LEN - 1) / HASH_LEN;

for (size_t block = 1; block <= blocks; block++) {
// U1 = HMAC(password, salt || INT(block))
vector<unsigned char> saltBlock(salt, salt + saltLen);
saltBlock.push_back((block >> 24) & 0xFF);
saltBlock.push_back((block >> 16) & 0xFF);
saltBlock.push_back((block >> 8) & 0xFF);
saltBlock.push_back(block & 0xFF);

auto U = hmac_sha256((unsigned char*)password.data(), password.size(),
saltBlock.data(), saltBlock.size());
vector<unsigned char> T = U;

// Iterate: T = U1 ^ U2 ^ ... ^ Uc
for (int i = 1; i < iterations; i++) {
U = hmac_sha256((unsigned char*)password.data(), password.size(),
U.data(), U.size());
for (size_t j = 0; j < HASH_LEN; j++) T[j] ^= U[j];
}

// Copy result block
size_t offset = (block - 1) * HASH_LEN;
size_t copyLen = min(HASH_LEN, dkLen - offset);
memcpy(output + offset, T.data(), copyLen);
}
}

// ═══════════════════════════════════════════════════════════
// AES Cipher Class (PBKDF2 + HMAC-SHA256 Authentication)
// File format: salt(16) + iv(16) + ciphertext + hmac(32)
// ═══════════════════════════════════════════════════════════

class AESCipher {
private:
<<<<<<< HEAD
string storedPassword;
unsigned char encKey[32];
unsigned char authKey[32];
AES256Impl::Context ctx;

static const int SALT_SIZE = 16;
static const int IV_SIZE = 16;
static const int HMAC_SIZE = 32;
static const int PBKDF2_ITERATIONS = 100000;

// Derive encryption and authentication keys from password + salt
void deriveKeys(const unsigned char* salt) {
unsigned char derived[64];
pbkdf2_sha256(storedPassword, salt, SALT_SIZE, PBKDF2_ITERATIONS, derived, 64);
memcpy(encKey, derived, 32); // First 32 bytes for encryption
memcpy(authKey, derived + 32, 32); // Last 32 bytes for authentication
secure_memzero(derived, 64);
ctx.keyExpansion(encKey);
}

// Compute HMAC over salt + iv + ciphertext
vector<unsigned char> computeHMAC(const unsigned char* data, size_t len) {
return hmac_sha256(authKey, 32, data, len);
}

// Constant-time HMAC verification
bool verifyHMAC(const unsigned char* data, size_t dataLen, const unsigned char* expectedHmac) {
auto computed = computeHMAC(data, dataLen);
return constant_time_compare(computed.data(), expectedHmac, HMAC_SIZE);
=======
unsigned char key[32];
unsigned char currentSalt[PBKDF2::SALT_SIZE];
AES256Impl::Context ctx;
Expand All @@ -555,6 +676,7 @@ class AESCipher {
ctx.keyExpansion(key);
// Securely wipe derived key buffer
secure_memzero(derived.data(), derived.size());
>>>>>>> 047b5e4e722c960f9a3454e6d648784ab0757150
}

public:
Expand All @@ -571,6 +693,9 @@ class AESCipher {
}

void setKey(const string& password) {
<<<<<<< HEAD
storedPassword = password;
=======
// Generate random salt for encryption
if (!generateRandomBytes(currentSalt, PBKDF2::SALT_SIZE)) {
cerr << "Error: Could not generate random salt" << endl;
Expand All @@ -583,23 +708,39 @@ class AESCipher {
void setKeyWithSalt(const string& password, const unsigned char* salt) {
deriveKeyFromSalt(password, salt);
keyFromSalt = true;
>>>>>>> 047b5e4e722c960f9a3454e6d648784ab0757150
}

vector<unsigned char> encrypt(const vector<unsigned char>& plaintext) {
auto padded = pkcs7Pad(plaintext);
unsigned char iv[16];
if (!generateRandomBytes(iv, 16)) {
cerr << "Error: Could not generate random IV" << endl;
// Generate random salt and IV
unsigned char salt[SALT_SIZE];
unsigned char iv[IV_SIZE];
if (!generateRandomBytes(salt, SALT_SIZE) || !generateRandomBytes(iv, IV_SIZE)) {
cerr << "Error: Could not generate random salt/IV" << endl;
return {};
}

<<<<<<< HEAD
// Derive keys from password + salt
deriveKeys(salt);

// Pad plaintext
auto padded = pkcs7Pad(plaintext);

// Build result: salt + iv + ciphertext (HMAC added at end)
vector<unsigned char> result;
result.insert(result.end(), salt, salt + SALT_SIZE);
result.insert(result.end(), iv, iv + IV_SIZE);

// CBC encrypt
=======
// Prepend salt + IV to result
vector<unsigned char> result;
result.insert(result.end(), currentSalt, currentSalt + PBKDF2::SALT_SIZE);
result.insert(result.end(), iv, iv + 16);
>>>>>>> 047b5e4e722c960f9a3454e6d648784ab0757150
unsigned char prev[16];
memcpy(prev, iv, 16);

for (size_t i = 0; i < padded.size(); i += 16) {
unsigned char block[16];
memcpy(block, &padded[i], 16);
Expand All @@ -608,6 +749,33 @@ class AESCipher {
result.insert(result.end(), block, block + 16);
memcpy(prev, block, 16);
}
<<<<<<< HEAD

// Compute and append HMAC over salt + iv + ciphertext
auto hmac = computeHMAC(result.data(), result.size());
result.insert(result.end(), hmac.begin(), hmac.end());

// Secure cleanup
secure_memzero(salt, SALT_SIZE);
secure_memzero(iv, IV_SIZE);

return result;
}

vector<unsigned char> decrypt(const vector<unsigned char>& ciphertext) {
// Minimum size: salt(16) + iv(16) + one block(16) + hmac(32) = 80 bytes
if (ciphertext.size() < 80) return {};

size_t dataLen = ciphertext.size() - HMAC_SIZE;
if ((dataLen - SALT_SIZE - IV_SIZE) % 16 != 0) return {};

// Extract components
const unsigned char* salt = ciphertext.data();
const unsigned char* iv = ciphertext.data() + SALT_SIZE;
const unsigned char* encData = ciphertext.data() + SALT_SIZE + IV_SIZE;
const unsigned char* hmac = ciphertext.data() + dataLen;
size_t encLen = dataLen - SALT_SIZE - IV_SIZE;
=======

// Secure cleanup
secure_memzero(iv, sizeof(iv));
Expand All @@ -628,11 +796,29 @@ class AESCipher {
// Extract IV (after salt)
unsigned char prev[16];
memcpy(prev, ciphertext.data() + PBKDF2::SALT_SIZE, 16);
>>>>>>> 047b5e4e722c960f9a3454e6d648784ab0757150

// Derive keys from password + salt
deriveKeys(salt);

// Verify HMAC BEFORE decryption (Encrypt-then-MAC)
if (!verifyHMAC(ciphertext.data(), dataLen, hmac)) {
cerr << "\n❌ HMAC verification failed - file tampered or wrong password" << endl;
return {};
}

// CBC decrypt
vector<unsigned char> result;
<<<<<<< HEAD
unsigned char prev[16];
memcpy(prev, iv, 16);

for (size_t i = 0; i < encLen; i += 16) {
=======
for (size_t i = PBKDF2::SALT_SIZE + 16; i < ciphertext.size(); i += 16) {
>>>>>>> 047b5e4e722c960f9a3454e6d648784ab0757150
unsigned char block[16];
memcpy(block, &ciphertext[i], 16);
memcpy(block, encData + i, 16);
unsigned char enc[16];
memcpy(enc, block, 16);
ctx.decryptBlock(block);
Expand Down
Binary file modified build/crypt-vault.exe
Binary file not shown.
Loading