Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
database::security::credential_manager Class Reference

Manages encrypted credential storage and retrieval. More...

#include <secure_connection.h>

Collaboration diagram for database::security::credential_manager:
Collaboration graph

Public Member Functions

 credential_manager ()=default
 Default constructor - used by database_context.
 
bool store_credentials (const std::string &connection_id, const security_credentials &credentials)
 
std::optional< security_credentialsget_credentials (const std::string &connection_id) const
 
bool remove_credentials (const std::string &connection_id)
 
void set_master_key (const std::string &key)
 
bool rotate_encryption_keys ()
 
std::string hash_password (const std::string &password) const
 
bool verify_password (const std::string &password, const std::string &hash) const
 

Private Member Functions

std::string encrypt_data (const std::string &data) const
 
std::string decrypt_data (const std::string &encrypted_data) const
 

Private Attributes

std::mutex credentials_mutex_
 
std::unordered_map< std::string, std::string > encrypted_credentials_
 
std::string master_key_
 

Detailed Description

Manages encrypted credential storage and retrieval.

Note
This class uses dependency injection pattern. Access via database_context::get_credential_manager() (Sprint 3, Task 3.3).

Example:

auto context = std::make_shared<database_context>();
auto cred_mgr = context->get_credential_manager();

Definition at line 99 of file secure_connection.h.

Constructor & Destructor Documentation

◆ credential_manager()

database::security::credential_manager::credential_manager ( )
default

Default constructor - used by database_context.

Member Function Documentation

◆ decrypt_data()

std::string database::security::credential_manager::decrypt_data ( const std::string & encrypted_data) const
private

Definition at line 352 of file secure_connection.cpp.

353 {
354 if (encrypted_data.empty())
355 {
356 return {};
357 }
358
359#ifdef DATABASE_HAS_OPENSSL
360 // AES-256-GCM decryption: parse "aes:<iv_hex>:<ciphertext_hex>:<tag_hex>"
361 if (encrypted_data.substr(0, 4) == "aes:")
362 {
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)
366 {
367 return {};
368 }
369
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));
373
374 std::string key = master_key_.empty() ? "default_key_placeholder!!" : master_key_;
375 key.resize(32, '\0');
376
377 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
378 if (!ctx) return {};
379
380 std::vector<uint8_t> plaintext(ciphertext.size() + EVP_MAX_BLOCK_LENGTH);
381 int len = 0, plaintext_len = 0;
382
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()));
389 plaintext_len = len;
390 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, static_cast<int>(tag.size()),
391 const_cast<uint8_t*>(tag.data()));
392
393 int ret = EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len);
394 EVP_CIPHER_CTX_free(ctx);
395
396 if (ret <= 0) return {}; // Authentication failed
397 plaintext_len += len;
398
399 return std::string(plaintext.begin(), plaintext.begin() + plaintext_len);
400 }
401#endif
402
403 // Legacy XOR format (with or without "xor:" prefix)
404 std::string hex_data = encrypted_data;
405 if (hex_data.substr(0, 4) == "xor:")
406 {
407 hex_data = hex_data.substr(4);
408 }
409
410 std::string decoded;
411 decoded.reserve(hex_data.size() / 2);
412 for (size_t i = 0; i + 1 < hex_data.size(); i += 2)
413 {
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));
418 }
419
420 std::string key = master_key_.empty() ? "default_key" : master_key_;
421 for (size_t i = 0; i < decoded.size(); ++i)
422 {
423 decoded[i] ^= key[i % key.size()];
424 }
425
426 return decoded;
427 }

References master_key_.

Referenced by get_credentials(), and rotate_encryption_keys().

Here is the caller graph for this function:

◆ encrypt_data()

std::string database::security::credential_manager::encrypt_data ( const std::string & data) const
private

Definition at line 282 of file secure_connection.cpp.

283 {
284 if (data.empty())
285 {
286 return {};
287 }
288
289#ifdef DATABASE_HAS_OPENSSL
290 // AES-256-GCM encryption
291 std::string key = master_key_.empty() ? "default_key_placeholder!!" : master_key_;
292 // Pad or truncate key to 32 bytes for AES-256
293 key.resize(32, '\0');
294
295 constexpr int iv_len = 12;
296 constexpr int tag_len = 16;
297
298 std::vector<uint8_t> iv(iv_len);
299 RAND_bytes(iv.data(), iv_len);
300
301 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
302 if (!ctx) return {};
303
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;
307
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);
320
321 ciphertext.resize(ciphertext_len);
322
323 // Format: "aes:<iv_hex>:<ciphertext_hex>:<tag_hex>"
324 return "aes:" + bytes_to_hex(iv) + ":" + bytes_to_hex(ciphertext) + ":" + bytes_to_hex(tag);
325#else
326 // WARNING: XOR obfuscation is NOT cryptographically secure.
327 static bool warned = false;
328 if (!warned)
329 {
330 std::cerr << "[database_system] WARNING: Using XOR placeholder for data encryption. "
331 << "Build with OpenSSL for AES-256-GCM support.\n";
332 warned = true;
333 }
334
335 std::string key = master_key_.empty() ? "default_key" : master_key_;
336 std::string result = data;
337 for (size_t i = 0; i < result.size(); ++i)
338 {
339 result[i] ^= key[i % key.size()];
340 }
341
342 std::ostringstream oss;
343 for (unsigned char c : result)
344 {
345 oss << std::hex << std::setfill('0') << std::setw(2)
346 << static_cast<int>(c);
347 }
348 return "xor:" + oss.str();
349#endif
350 }

References master_key_.

Referenced by rotate_encryption_keys(), and store_credentials().

Here is the caller graph for this function:

◆ get_credentials()

std::optional< security_credentials > database::security::credential_manager::get_credentials ( const std::string & connection_id) const

Definition at line 51 of file secure_connection.cpp.

53 {
54 std::lock_guard<std::mutex> lock(credentials_mutex_);
55
56 auto it = encrypted_credentials_.find(connection_id);
57 if (it == encrypted_credentials_.end())
58 {
59 return std::nullopt;
60 }
61
62 std::string decrypted = decrypt_data(it->second);
63 if (decrypted.empty())
64 {
65 return std::nullopt;
66 }
67
68 // Deserialize credentials
69 std::istringstream iss(decrypted);
70 security_credentials creds;
71 std::getline(iss, creds.username);
72 std::getline(iss, creds.password_hash);
73 std::getline(iss, creds.certificate_path);
74 std::getline(iss, creds.private_key_path);
75 std::getline(iss, creds.ca_cert_path);
76
77 int auth_method_int = 0;
78 int encryption_int = 0;
79 iss >> auth_method_int >> encryption_int;
80 creds.auth_method = static_cast<authentication_method>(auth_method_int);
81 creds.encryption = static_cast<encryption_type>(encryption_int);
82
83 return creds;
84 }
std::unordered_map< std::string, std::string > encrypted_credentials_
std::string decrypt_data(const std::string &encrypted_data) const
authentication_method
Authentication methods supported.
encryption_type
Types of encryption supported.

References database::security::security_credentials::auth_method, database::security::security_credentials::ca_cert_path, database::security::security_credentials::certificate_path, credentials_mutex_, decrypt_data(), encrypted_credentials_, database::security::security_credentials::encryption, database::security::security_credentials::password_hash, database::security::security_credentials::private_key_path, and database::security::security_credentials::username.

Here is the call graph for this function:

◆ hash_password()

std::string database::security::credential_manager::hash_password ( const std::string & password) const

Definition at line 175 of file secure_connection.cpp.

176 {
177 if (password.empty())
178 {
179 return {};
180 }
181
182#ifdef DATABASE_HAS_OPENSSL
183 // PBKDF2-HMAC-SHA256: cryptographically secure password hashing
184 constexpr int iterations = 100000;
185 constexpr int salt_len = 16;
186 constexpr int hash_len = 32;
187
188 std::vector<uint8_t> salt(salt_len);
189 RAND_bytes(salt.data(), salt_len);
190
191 std::vector<uint8_t> hash(hash_len);
192 PKCS5_PBKDF2_HMAC(
193 password.c_str(), static_cast<int>(password.size()),
194 salt.data(), salt_len,
195 iterations,
196 EVP_sha256(),
197 hash_len, hash.data()
198 );
199
200 // Format: "pbkdf2:<iterations>:<salt_hex>:<hash_hex>"
201 std::ostringstream oss;
202 oss << "pbkdf2:" << iterations << ":" << bytes_to_hex(salt) << ":" << bytes_to_hex(hash);
203 return oss.str();
204#else
205 // WARNING: FNV1a is NOT cryptographically secure.
206 // Build with OpenSSL to enable PBKDF2-HMAC-SHA256 password hashing.
207 static bool warned = false;
208 if (!warned)
209 {
210 std::cerr << "[database_system] WARNING: Using FNV1a placeholder for password hashing. "
211 << "Build with OpenSSL for PBKDF2-HMAC-SHA256 support.\n";
212 warned = true;
213 }
214
215 uint64_t hash = 0xcbf29ce484222325ULL;
216 for (char c : password)
217 {
218 hash ^= static_cast<uint64_t>(c);
219 hash *= 0x100000001b3ULL;
220 }
221
222 std::ostringstream oss;
223 oss << std::hex << std::setfill('0') << std::setw(16) << hash;
224 return "fnv1a:" + oss.str();
225#endif
226 }

References database::security::password.

Referenced by verify_password().

Here is the caller graph for this function:

◆ remove_credentials()

bool database::security::credential_manager::remove_credentials ( const std::string & connection_id)

Definition at line 86 of file secure_connection.cpp.

87 {
88 std::lock_guard<std::mutex> lock(credentials_mutex_);
89 return encrypted_credentials_.erase(connection_id) > 0;
90 }

References credentials_mutex_, and encrypted_credentials_.

◆ rotate_encryption_keys()

bool database::security::credential_manager::rotate_encryption_keys ( )

Definition at line 98 of file secure_connection.cpp.

99 {
100 std::lock_guard<std::mutex> lock(credentials_mutex_);
101
102 if (master_key_.empty())
103 {
104 return false;
105 }
106
107 // Generate new key
108 std::string old_key = master_key_;
109 std::random_device rd;
110 std::mt19937 gen(rd());
111 std::uniform_int_distribution<int> dist(33, 126); // printable ASCII
112 std::string new_key;
113 new_key.reserve(32);
114 for (int i = 0; i < 32; ++i)
115 {
116 new_key.push_back(static_cast<char>(dist(gen)));
117 }
118
119 // Re-encrypt all stored credentials with new key
120 std::unordered_map<std::string, std::string> rotated;
121 for (const auto& [id, encrypted] : encrypted_credentials_)
122 {
123 // Decrypt with old key
124 std::string decrypted = decrypt_data(encrypted);
125 if (decrypted.empty())
126 {
127 return false; // Rotation failed — abort to preserve data
128 }
129
130 // Re-encrypt with new key (temporarily set new key)
131 master_key_ = new_key;
132 std::string re_encrypted = encrypt_data(decrypted);
133 master_key_ = old_key; // Restore old key in case of further failures
134
135 if (re_encrypted.empty())
136 {
137 return false;
138 }
139 rotated[id] = re_encrypted;
140 }
141
142 // Commit: swap all credentials and update key
143 encrypted_credentials_ = std::move(rotated);
144 master_key_ = new_key;
145 return true;
146 }
std::string encrypt_data(const std::string &data) const

References credentials_mutex_, decrypt_data(), encrypt_data(), encrypted_credentials_, and master_key_.

Here is the call graph for this function:

◆ set_master_key()

void database::security::credential_manager::set_master_key ( const std::string & key)

Definition at line 92 of file secure_connection.cpp.

93 {
94 std::lock_guard<std::mutex> lock(credentials_mutex_);
95 master_key_ = key;
96 }

References credentials_mutex_, and master_key_.

◆ store_credentials()

bool database::security::credential_manager::store_credentials ( const std::string & connection_id,
const security_credentials & credentials )

Definition at line 30 of file secure_connection.cpp.

32 {
33 std::lock_guard<std::mutex> lock(credentials_mutex_);
34
35 // Serialize credentials to a simple format and encrypt
36 std::ostringstream oss;
37 oss << credentials.username << "\n"
38 << credentials.password_hash << "\n"
39 << credentials.certificate_path << "\n"
40 << credentials.private_key_path << "\n"
41 << credentials.ca_cert_path << "\n"
42 << static_cast<int>(credentials.auth_method) << "\n"
43 << static_cast<int>(credentials.encryption);
44
45 std::string serialized = oss.str();
46 std::string encrypted = encrypt_data(serialized);
47 encrypted_credentials_[connection_id] = encrypted;
48 return true;
49 }

References database::security::security_credentials::auth_method, database::security::security_credentials::ca_cert_path, database::security::security_credentials::certificate_path, credentials_mutex_, encrypt_data(), encrypted_credentials_, database::security::security_credentials::encryption, database::security::security_credentials::password_hash, database::security::security_credentials::private_key_path, and database::security::security_credentials::username.

Here is the call graph for this function:

◆ verify_password()

bool database::security::credential_manager::verify_password ( const std::string & password,
const std::string & hash ) const

Definition at line 228 of file secure_connection.cpp.

230 {
231 if (password.empty() || hash.empty())
232 {
233 return false;
234 }
235
236#ifdef DATABASE_HAS_OPENSSL
237 // Parse format: "pbkdf2:<iterations>:<salt_hex>:<hash_hex>"
238 if (hash.substr(0, 7) == "pbkdf2:")
239 {
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)
243 {
244 return false;
245 }
246
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));
250
251 std::vector<uint8_t> computed(expected_hash.size());
252 PKCS5_PBKDF2_HMAC(
253 password.c_str(), static_cast<int>(password.size()),
254 salt.data(), static_cast<int>(salt.size()),
255 iterations,
256 EVP_sha256(),
257 static_cast<int>(computed.size()), computed.data()
258 );
259
260 return computed == expected_hash;
261 }
262
263 // Legacy FNV1a format migration: verify with old algorithm
264 if (hash.substr(0, 6) == "fnv1a:")
265 {
266 uint64_t h = 0xcbf29ce484222325ULL;
267 for (char c : password)
268 {
269 h ^= static_cast<uint64_t>(c);
270 h *= 0x100000001b3ULL;
271 }
272 std::ostringstream oss;
273 oss << std::hex << std::setfill('0') << std::setw(16) << h;
274 return hash == ("fnv1a:" + oss.str());
275 }
276#endif
277
278 // Fallback: direct comparison (covers fnv1a format without OpenSSL)
279 return hash_password(password) == hash;
280 }
std::string hash_password(const std::string &password) const

References hash_password(), and database::security::password.

Here is the call graph for this function:

Member Data Documentation

◆ credentials_mutex_

std::mutex database::security::credential_manager::credentials_mutex_
mutableprivate

◆ encrypted_credentials_

std::unordered_map<std::string, std::string> database::security::credential_manager::encrypted_credentials_
private

◆ master_key_

std::string database::security::credential_manager::master_key_
private

The documentation for this class was generated from the following files: