17#ifdef DATABASE_HAS_OPENSSL
18#include <openssl/evp.h>
19#include <openssl/rand.h>
20#include <openssl/err.h>
36 std::ostringstream oss;
42 <<
static_cast<int>(credentials.
auth_method) <<
"\n"
45 std::string serialized = oss.str();
52 const std::string& connection_id)
const
63 if (decrypted.empty())
69 std::istringstream iss(decrypted);
77 int auth_method_int = 0;
78 int encryption_int = 0;
79 iss >> auth_method_int >> encryption_int;
109 std::random_device rd;
110 std::mt19937 gen(rd());
111 std::uniform_int_distribution<int> dist(33, 126);
114 for (
int i = 0; i < 32; ++i)
116 new_key.push_back(
static_cast<char>(dist(gen)));
120 std::unordered_map<std::string, std::string> rotated;
125 if (decrypted.empty())
135 if (re_encrypted.empty())
139 rotated[id] = re_encrypted;
150 std::string bytes_to_hex(
const std::vector<uint8_t>& bytes)
152 std::ostringstream oss;
153 for (uint8_t b : bytes)
155 oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(b);
160 std::vector<uint8_t> hex_to_bytes(
const std::string& hex)
162 std::vector<uint8_t> bytes;
163 bytes.reserve(hex.size() / 2);
164 for (
size_t i = 0; i + 1 < hex.size(); i += 2)
166 unsigned int val = 0;
167 std::istringstream iss(hex.substr(i, 2));
168 iss >> std::hex >> val;
169 bytes.push_back(
static_cast<uint8_t
>(val));
182#ifdef DATABASE_HAS_OPENSSL
184 constexpr int iterations = 100000;
185 constexpr int salt_len = 16;
186 constexpr int hash_len = 32;
188 std::vector<uint8_t> salt(salt_len);
189 RAND_bytes(salt.data(), salt_len);
191 std::vector<uint8_t> hash(hash_len);
194 salt.data(), salt_len,
197 hash_len, hash.data()
201 std::ostringstream oss;
202 oss <<
"pbkdf2:" << iterations <<
":" << bytes_to_hex(salt) <<
":" << bytes_to_hex(hash);
207 static bool warned =
false;
210 std::cerr <<
"[database_system] WARNING: Using FNV1a placeholder for password hashing. "
211 <<
"Build with OpenSSL for PBKDF2-HMAC-SHA256 support.\n";
215 uint64_t hash = 0xcbf29ce484222325ULL;
218 hash ^=
static_cast<uint64_t
>(c);
219 hash *= 0x100000001b3ULL;
222 std::ostringstream oss;
223 oss << std::hex << std::setfill(
'0') << std::setw(16) << hash;
224 return "fnv1a:" + oss.str();
229 const std::string& hash)
const
231 if (
password.empty() || hash.empty())
236#ifdef DATABASE_HAS_OPENSSL
238 if (hash.substr(0, 7) ==
"pbkdf2:")
240 auto first_colon = hash.find(
':', 7);
241 auto second_colon = hash.find(
':', first_colon + 1);
242 if (first_colon == std::string::npos || second_colon == std::string::npos)
247 int iterations = std::stoi(hash.substr(7, first_colon - 7));
248 auto salt = hex_to_bytes(hash.substr(first_colon + 1, second_colon - first_colon - 1));
249 auto expected_hash = hex_to_bytes(hash.substr(second_colon + 1));
251 std::vector<uint8_t> computed(expected_hash.size());
254 salt.data(),
static_cast<int>(salt.size()),
257 static_cast<int>(computed.size()), computed.data()
260 return computed == expected_hash;
264 if (hash.substr(0, 6) ==
"fnv1a:")
266 uint64_t h = 0xcbf29ce484222325ULL;
269 h ^=
static_cast<uint64_t
>(c);
270 h *= 0x100000001b3ULL;
272 std::ostringstream oss;
273 oss << std::hex << std::setfill(
'0') << std::setw(16) << h;
274 return hash == (
"fnv1a:" + oss.str());
289#ifdef DATABASE_HAS_OPENSSL
293 key.resize(32,
'\0');
295 constexpr int iv_len = 12;
296 constexpr int tag_len = 16;
298 std::vector<uint8_t> iv(iv_len);
299 RAND_bytes(iv.data(), iv_len);
301 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
304 std::vector<uint8_t> ciphertext(data.size() + EVP_MAX_BLOCK_LENGTH);
305 std::vector<uint8_t> tag(tag_len);
306 int len = 0, ciphertext_len = 0;
308 EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(),
nullptr,
nullptr,
nullptr);
309 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len,
nullptr);
310 EVP_EncryptInit_ex(ctx,
nullptr,
nullptr,
311 reinterpret_cast<const unsigned char*
>(key.data()), iv.data());
312 EVP_EncryptUpdate(ctx, ciphertext.data(), &len,
313 reinterpret_cast<const unsigned char*
>(data.data()),
314 static_cast<int>(data.size()));
315 ciphertext_len = len;
316 EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len);
317 ciphertext_len += len;
318 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_len, tag.data());
319 EVP_CIPHER_CTX_free(ctx);
321 ciphertext.resize(ciphertext_len);
324 return "aes:" + bytes_to_hex(iv) +
":" + bytes_to_hex(ciphertext) +
":" + bytes_to_hex(tag);
327 static bool warned =
false;
330 std::cerr <<
"[database_system] WARNING: Using XOR placeholder for data encryption. "
331 <<
"Build with OpenSSL for AES-256-GCM support.\n";
336 std::string result = data;
337 for (
size_t i = 0; i < result.size(); ++i)
339 result[i] ^= key[i % key.size()];
342 std::ostringstream oss;
343 for (
unsigned char c : result)
345 oss << std::hex << std::setfill(
'0') << std::setw(2)
346 <<
static_cast<int>(c);
348 return "xor:" + oss.str();
354 if (encrypted_data.empty())
359#ifdef DATABASE_HAS_OPENSSL
361 if (encrypted_data.substr(0, 4) ==
"aes:")
363 auto first = encrypted_data.find(
':', 4);
364 auto second = encrypted_data.find(
':', first + 1);
365 if (first == std::string::npos || second == std::string::npos)
370 auto iv = hex_to_bytes(encrypted_data.substr(4, first - 4));
371 auto ciphertext = hex_to_bytes(encrypted_data.substr(first + 1, second - first - 1));
372 auto tag = hex_to_bytes(encrypted_data.substr(second + 1));
375 key.resize(32,
'\0');
377 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
380 std::vector<uint8_t> plaintext(ciphertext.size() + EVP_MAX_BLOCK_LENGTH);
381 int len = 0, plaintext_len = 0;
383 EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(),
nullptr,
nullptr,
nullptr);
384 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,
static_cast<int>(iv.size()),
nullptr);
385 EVP_DecryptInit_ex(ctx,
nullptr,
nullptr,
386 reinterpret_cast<const unsigned char*
>(key.data()), iv.data());
387 EVP_DecryptUpdate(ctx, plaintext.data(), &len,
388 ciphertext.data(),
static_cast<int>(ciphertext.size()));
390 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
static_cast<int>(tag.size()),
391 const_cast<uint8_t*
>(tag.data()));
393 int ret = EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len);
394 EVP_CIPHER_CTX_free(ctx);
396 if (ret <= 0)
return {};
397 plaintext_len += len;
399 return std::string(plaintext.begin(), plaintext.begin() + plaintext_len);
404 std::string hex_data = encrypted_data;
405 if (hex_data.substr(0, 4) ==
"xor:")
407 hex_data = hex_data.substr(4);
411 decoded.reserve(hex_data.size() / 2);
412 for (
size_t i = 0; i + 1 < hex_data.size(); i += 2)
414 unsigned int byte = 0;
415 std::istringstream iss(hex_data.substr(i, 2));
416 iss >> std::hex >> byte;
417 decoded.push_back(
static_cast<char>(
byte));
421 for (
size_t i = 0; i < decoded.size(); ++i)
423 decoded[i] ^= key[i % key.size()];
434 const std::string& field_name)
const
449#ifdef DATABASE_HAS_OPENSSL
451 key.resize(32,
'\0');
453 constexpr int iv_len = 12;
454 constexpr int tag_len = 16;
456 std::vector<uint8_t> iv(iv_len);
457 RAND_bytes(iv.data(), iv_len);
459 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
462 std::vector<uint8_t> ciphertext(data.size() + EVP_MAX_BLOCK_LENGTH);
463 std::vector<uint8_t> tag(tag_len);
464 int len = 0, ct_len = 0;
466 EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(),
nullptr,
nullptr,
nullptr);
467 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len,
nullptr);
468 EVP_EncryptInit_ex(ctx,
nullptr,
nullptr,
469 reinterpret_cast<const unsigned char*
>(key.data()), iv.data());
470 EVP_EncryptUpdate(ctx, ciphertext.data(), &len,
471 reinterpret_cast<const unsigned char*
>(data.data()),
472 static_cast<int>(data.size()));
474 EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len);
476 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_len, tag.data());
477 EVP_CIPHER_CTX_free(ctx);
479 ciphertext.resize(ct_len);
480 return "aes:" + bytes_to_hex(iv) +
":" + bytes_to_hex(ciphertext) +
":" + bytes_to_hex(tag);
483 std::string result = data;
484 for (
size_t i = 0; i < result.size(); ++i)
486 result[i] ^= key[i % key.size()];
489 std::ostringstream oss;
490 for (
unsigned char c : result)
492 oss << std::hex << std::setfill(
'0') << std::setw(2)
493 <<
static_cast<int>(c);
495 return "xor:" + oss.str();
500 const std::string& field_name)
const
504 if (encrypted_data.empty())
515#ifdef DATABASE_HAS_OPENSSL
517 if (encrypted_data.substr(0, 4) ==
"aes:")
519 key.resize(32,
'\0');
521 auto first = encrypted_data.find(
':', 4);
522 auto second = encrypted_data.find(
':', first + 1);
523 if (first == std::string::npos || second == std::string::npos)
return {};
525 auto iv = hex_to_bytes(encrypted_data.substr(4, first - 4));
526 auto ciphertext = hex_to_bytes(encrypted_data.substr(first + 1, second - first - 1));
527 auto tag = hex_to_bytes(encrypted_data.substr(second + 1));
529 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
532 std::vector<uint8_t> plaintext(ciphertext.size() + EVP_MAX_BLOCK_LENGTH);
533 int len = 0, pt_len = 0;
535 EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(),
nullptr,
nullptr,
nullptr);
536 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN,
static_cast<int>(iv.size()),
nullptr);
537 EVP_DecryptInit_ex(ctx,
nullptr,
nullptr,
538 reinterpret_cast<const unsigned char*
>(key.data()), iv.data());
539 EVP_DecryptUpdate(ctx, plaintext.data(), &len,
540 ciphertext.data(),
static_cast<int>(ciphertext.size()));
542 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
static_cast<int>(tag.size()),
543 const_cast<uint8_t*
>(tag.data()));
545 int ret = EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len);
546 EVP_CIPHER_CTX_free(ctx);
548 if (ret <= 0)
return {};
550 return std::string(plaintext.begin(), plaintext.begin() + pt_len);
555 std::string hex_data = encrypted_data;
556 if (hex_data.substr(0, 4) ==
"xor:")
558 hex_data = hex_data.substr(4);
562 decoded.reserve(hex_data.size() / 2);
563 for (
size_t i = 0; i + 1 < hex_data.size(); i += 2)
565 unsigned int byte = 0;
566 std::istringstream iss(hex_data.substr(i, 2));
567 iss >> std::hex >> byte;
568 decoded.push_back(
static_cast<char>(
byte));
571 for (
size_t i = 0; i < decoded.size(); ++i)
573 decoded[i] ^= key[i % key.size()];
584 std::random_device rd;
585 std::mt19937 gen(rd());
586 std::uniform_int_distribution<> dis(0, 255);
588 std::string key(32,
'\0');
591 c =
static_cast<char>(dis(gen));
595 std::ostringstream oss;
596 for (
unsigned char c : key)
598 oss << std::hex << std::setfill(
'0') << std::setw(2)
599 <<
static_cast<int>(c);
616 std::random_device rd;
617 std::mt19937 gen(rd());
618 std::uniform_int_distribution<> dis(0, 255);
620 std::string key(32,
'\0');
623 c =
static_cast<char>(dis(gen));
626 std::ostringstream oss;
627 for (
unsigned char c : key)
629 oss << std::hex << std::setfill(
'0') << std::setw(2)
630 <<
static_cast<int>(c);
632 it->second = oss.str();
643 const std::string& column,
647 std::string key = table +
"." + column;
655 std::random_device rd;
656 std::mt19937 gen(rd());
657 std::uniform_int_distribution<> dis(0, 255);
659 std::string field_key(32,
'\0');
660 for (
auto& c : field_key)
662 c =
static_cast<char>(dis(gen));
665 std::ostringstream oss;
666 for (
unsigned char ch : field_key)
668 oss << std::hex << std::setfill(
'0') << std::setw(2)
669 <<
static_cast<int>(ch);
678 const std::string& column)
const
681 std::string key = table +
"." + column;
708 : log_file_path_(log_file_path)
725 auto time_t = std::chrono::system_clock::to_time_t(entry.
timestamp);
726 file << time_t <<
","
732 << (entry.
success ?
"true" :
"false") <<
","
740 const std::string& session_id,
741 const std::string& operation,
742 const std::string& table,
743 const std::string& query_hash,
745 const std::string& error_message)
748 entry.
timestamp = std::chrono::system_clock::now();
763 const std::string& client_ip,
765 const std::string& method)
768 entry.
timestamp = std::chrono::system_clock::now();
781 const std::string& operation,
782 const std::string& table,
783 const std::string& reason)
786 entry.
timestamp = std::chrono::system_clock::now();
788 entry.
operation =
"authorization_failure:" + operation;
799 std::chrono::hours window)
const
803 auto cutoff = std::chrono::system_clock::now() - window;
804 std::vector<audit_log_entry> result;
808 if (entry.timestamp >= cutoff)
810 result.push_back(entry);
818 const std::string& user_id, std::chrono::hours window)
const
822 auto cutoff = std::chrono::system_clock::now() - window;
823 std::vector<audit_log_entry> result;
827 if (entry.timestamp >= cutoff && entry.user_id == user_id)
829 result.push_back(entry);
837 std::chrono::hours window)
const
841 size_t total = logs.size();
843 size_t auth_events = 0;
845 for (
const auto& entry : logs)
851 if (entry.operation ==
"authentication")
857 std::ostringstream oss;
858 oss <<
"Security Report (last " << window.count() <<
" hours)\n"
859 <<
" Total events: " << total <<
"\n"
860 <<
" Failures: " << failures <<
"\n"
861 <<
" Auth events: " << auth_events <<
"\n"
863 << (total > 0 ? (100.0 * failures / total) : 0.0) <<
"%\n";
869 std::chrono::hours window)
const
872 std::vector<std::string> alerts;
875 std::unordered_map<std::string, size_t> user_failures;
876 for (
const auto& entry : logs)
880 ++user_failures[entry.user_id];
884 for (
const auto& [user_id, count] : user_failures)
888 alerts.push_back(
"User '" + user_id +
"' has " +
889 std::to_string(count) +
890 " failed operations in the last " +
891 std::to_string(window.count()) +
" hours");
912 return entry.timestamp < cutoff;
921 std::ofstream file(filename);
928 file <<
"timestamp,user_id,session_id,operation,table_name,"
929 "query_hash,success,error_message,client_ip,user_agent\n";
933 auto time_t = std::chrono::system_clock::to_time_t(entry.timestamp);
934 file << time_t <<
","
935 << entry.user_id <<
","
936 << entry.session_id <<
","
937 << entry.operation <<
","
938 << entry.table_name <<
","
939 << entry.query_hash <<
","
940 << (entry.success ?
"true" :
"false") <<
","
941 << entry.error_message <<
","
942 << entry.client_ip <<
","
943 << entry.user_agent <<
"\n";
void log_database_access(const std::string &user_id, const std::string &session_id, const std::string &operation, const std::string &table, const std::string &query_hash, bool success, const std::string &error_message="")
std::string generate_security_report(std::chrono::hours window) const
std::vector< audit_log_entry > get_user_audit_logs(const std::string &user_id, std::chrono::hours window) const
void set_log_retention_period(std::chrono::hours retention)
audit_logger()=default
Default constructor - used by database_context.
std::vector< audit_log_entry > audit_logs_
std::vector< std::string > detect_suspicious_activity(std::chrono::hours window) const
std::chrono::hours retention_period_
void persist_entry(const audit_log_entry &entry)
void log_authentication_event(const std::string &user_id, const std::string &client_ip, bool success, const std::string &method)
bool export_logs_to_file(const std::string &filename) const
void log_authorization_failure(const std::string &user_id, const std::string &operation, const std::string &table, const std::string &reason)
std::vector< audit_log_entry > get_audit_logs(std::chrono::hours window) const
std::string log_file_path_
bool rotate_encryption_keys()
std::optional< security_credentials > get_credentials(const std::string &connection_id) const
std::string encrypt_data(const std::string &data) const
void set_master_key(const std::string &key)
bool verify_password(const std::string &password, const std::string &hash) const
std::string hash_password(const std::string &password) const
bool remove_credentials(const std::string &connection_id)
std::unordered_map< std::string, std::string > encrypted_credentials_
bool store_credentials(const std::string &connection_id, const security_credentials &credentials)
std::string decrypt_data(const std::string &encrypted_data) const
std::mutex credentials_mutex_
std::unordered_map< std::string, std::string > field_keys_
bool configure_encrypted_column(const std::string &table, const std::string &column, encryption_type type)
bool generate_field_key(const std::string &field_name)
bool is_column_encrypted(const std::string &table, const std::string &column) const
bool rotate_field_key(const std::string &field_name)
std::unordered_map< std::string, encryption_type > encrypted_columns_
std::mutex encryption_mutex_
std::string derive_key(const std::string &field_name) const
void set_master_encryption_key(const std::string &key)
std::string decrypt_field_data(const std::string &encrypted_data, const std::string &field_name) const
std::string encrypt_field_data(const std::string &data, const std::string &field_name) const
authentication_method
Authentication methods supported.
encryption_type
Types of encryption supported.
Audit log entry for security events.
std::string error_message
std::chrono::system_clock::time_point timestamp
Secure credential storage.
std::string certificate_path
std::string private_key_path
std::string password_hash
authentication_method auth_method
encryption_type encryption