Logger System 0.1.3
High-performance C++20 thread-safe logging system with asynchronous capabilities
Loading...
Searching...
No Matches
secure_key_storage.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
11#pragma once
12
14#include <vector>
15#include <cstdint>
16#include <filesystem>
17#include <fstream>
18#include <cstring>
19#include <random>
20
21// OpenSSL 3.x+ for secure memory cleanup (optional, fallback to manual zeroing)
22#if __has_include(<openssl/rand.h>)
23#include <openssl/rand.h>
24#include <openssl/crypto.h>
25#include <openssl/opensslv.h>
26#define HAS_OPENSSL 1
27#endif
28
30
41public:
46 explicit secure_key(size_t size)
47 : data_(size, 0) {
48 }
49
54 explicit secure_key(std::vector<uint8_t> data)
55 : data_(std::move(data)) {
56 }
57
63 }
64
65 // Prevent copying
66 secure_key(const secure_key&) = delete;
67 secure_key& operator=(const secure_key&) = delete;
68
69 // Allow move only
70 secure_key(secure_key&& other) noexcept
71 : data_(std::move(other.data_)) {
72 // Clear the moved-from object
73 other.secure_clear();
74 }
75
76 secure_key& operator=(secure_key&& other) noexcept {
77 if (this != &other) {
79 data_ = std::move(other.data_);
80 other.secure_clear();
81 }
82 return *this;
83 }
84
88 const std::vector<uint8_t>& data() const {
89 return data_;
90 }
91
95 std::vector<uint8_t>& mutable_data() {
96 return data_;
97 }
98
102 size_t size() const {
103 return data_.size();
104 }
105
106private:
111 if (data_.empty()) {
112 return;
113 }
114
115#ifdef HAS_OPENSSL
116 // Use OpenSSL's secure memory clearing
117 OPENSSL_cleanse(data_.data(), data_.size());
118#else
119 // Fallback: manual zeroing with volatile to prevent optimization
120 volatile uint8_t* ptr = data_.data();
121 for (size_t i = 0; i < data_.size(); ++i) {
122 ptr[i] = 0;
123 }
124#endif
125 data_.clear();
126 }
127
128 std::vector<uint8_t> data_;
129};
130
142public:
149 secure_key key(size);
150
151#ifdef HAS_OPENSSL
152 // Use OpenSSL's cryptographically secure random generator
153 if (RAND_bytes(key.mutable_data().data(), size) != 1) {
154 return result<secure_key>{
156 "Failed to generate secure random key"};
157 }
158#else
159 // Fallback: use std::random_device (less secure on some platforms)
160 std::random_device rd;
161 for (size_t i = 0; i < size; ++i) {
162 key.mutable_data()[i] = static_cast<uint8_t>(rd() & 0xFF);
163 }
164#endif
165
166 return result<secure_key>(std::move(key));
167 }
168
177 const secure_key& key,
178 const std::filesystem::path& path,
179 const std::filesystem::path& allowed_base = "/var/log/keys"
180 ) {
181 // 1. Validate path
182 auto validation = validate_key_path(path, allowed_base);
183 if (validation.is_err()) {
184 return validation;
185 }
186
187 // 2. Ensure parent directory exists
188 try {
189 auto parent = path.parent_path();
190 if (!parent.empty() && !std::filesystem::exists(parent)) {
191 std::filesystem::create_directories(parent);
192 }
193 } catch (const std::filesystem::filesystem_error& e) {
196 std::string("Failed to create key directory: ") + e.what()
197 );
198 }
199
200 // 3. Write key to file
201 std::ofstream file(path, std::ios::binary | std::ios::trunc);
202 if (!file) {
205 "Failed to open key file for writing"
206 );
207 }
208
209 file.write(
210 reinterpret_cast<const char*>(key.data().data()),
211 key.size()
212 );
213
214 if (file.fail()) {
217 "Failed to write key data"
218 );
219 }
220
221 file.close();
222
223 // 4. Set secure file permissions (0600 = owner read/write only)
224 try {
225 std::filesystem::permissions(
226 path,
227 std::filesystem::perms::owner_read |
228 std::filesystem::perms::owner_write,
229 std::filesystem::perm_options::replace
230 );
231 } catch (const std::filesystem::filesystem_error& e) {
232 // Permission setting failed, delete the insecure file
233 std::filesystem::remove(path);
236 std::string("Failed to set secure permissions: ") + e.what()
237 );
238 }
239
240 return common::ok();
241 }
242
251 const std::filesystem::path& path,
252 size_t expected_size = 32,
253 const std::filesystem::path& allowed_base = "/var/log/keys"
254 ) {
255 // 1. Validate path
256 auto validation = validate_key_path(path, allowed_base);
257 if (validation.is_err()) {
258 return result<secure_key>{
259 get_logger_error_code(validation),
260 get_logger_error_message(validation)};
261 }
262
263 // 2. Check if file exists
264 if (!std::filesystem::exists(path)) {
265 return result<secure_key>{
267 "Key file does not exist"};
268 }
269
270 // 3. Verify file permissions (must not be readable by group/others)
271 try {
272 auto status = std::filesystem::status(path);
273 auto perms = status.permissions();
274
275 if ((perms & std::filesystem::perms::group_read) != std::filesystem::perms::none ||
276 (perms & std::filesystem::perms::others_read) != std::filesystem::perms::none) {
277 return result<secure_key>{
279 "Key file permissions are too permissive (must be 0600 or stricter)"};
280 }
281 } catch (const std::filesystem::filesystem_error& e) {
282 return result<secure_key>{
284 std::string("Failed to check file permissions: ") + e.what()};
285 }
286
287 // 4. Verify file size
288 size_t file_size = 0;
289 try {
290 file_size = std::filesystem::file_size(path);
291 } catch (const std::filesystem::filesystem_error& e) {
292 return result<secure_key>{
294 std::string("Failed to get file size: ") + e.what()};
295 }
296
297 if (file_size != expected_size) {
298 return result<secure_key>{
300 "Invalid key file size (expected " + std::to_string(expected_size) +
301 " bytes, got " + std::to_string(file_size) + ")"};
302 }
303
304 // 5. Read key from file
305 std::ifstream file(path, std::ios::binary);
306 if (!file) {
307 return result<secure_key>{
309 "Failed to open key file for reading"};
310 }
311
312 secure_key key(expected_size);
313 file.read(
314 reinterpret_cast<char*>(key.mutable_data().data()),
315 expected_size
316 );
317
318 if (file.fail()) {
319 return result<secure_key>{
321 "Failed to read key data"};
322 }
323
324 return result<secure_key>(std::move(key));
325 }
326
327private:
335 const std::filesystem::path& path,
336 const std::filesystem::path& allowed_base
337 ) {
338 try {
339 // Convert both paths to absolute canonical form
340 auto canonical_path = std::filesystem::weakly_canonical(path);
341 auto canonical_base = std::filesystem::weakly_canonical(allowed_base);
342
343 // Check if path starts with allowed_base
344 auto [mismatch_iter, _] = std::mismatch(
345 canonical_base.begin(), canonical_base.end(),
346 canonical_path.begin(), canonical_path.end()
347 );
348
349 if (mismatch_iter != canonical_base.end()) {
352 "Key path must be within allowed directory: " +
353 canonical_base.string()
354 );
355 }
356
357 return common::ok();
358 } catch (const std::filesystem::filesystem_error& e) {
361 std::string("Path validation failed: ") + e.what()
362 );
363 }
364 }
365};
366
367} // namespace kcenon::logger::security
Secure storage and retrieval of encryption keys.
static result< secure_key > load_key(const std::filesystem::path &path, size_t expected_size=32, const std::filesystem::path &allowed_base="/var/log/keys")
Load key from file with permission verification.
static common::VoidResult save_key(const secure_key &key, const std::filesystem::path &path, const std::filesystem::path &allowed_base="/var/log/keys")
Save key to file with secure permissions.
static common::VoidResult validate_key_path(const std::filesystem::path &path, const std::filesystem::path &allowed_base)
Validate key file path (prevent path traversal)
static result< secure_key > generate_key(size_t size=32)
Generate a secure random key.
RAII wrapper for encryption keys with secure memory management.
secure_key(std::vector< uint8_t > data)
Construct with data.
size_t size() const
Get key size in bytes.
void secure_clear()
Securely clear key data from memory.
secure_key(size_t size)
Construct with specified size.
secure_key(const secure_key &)=delete
const std::vector< uint8_t > & data() const
Get const reference to key data.
secure_key & operator=(secure_key &&other) noexcept
~secure_key()
Destructor - securely clears key from memory.
std::vector< uint8_t > & mutable_data()
Get mutable reference to key data (use with caution)
secure_key & operator=(const secure_key &)=delete
secure_key(secure_key &&other) noexcept
Error codes specific to the logger system.
VoidResult ok()
common::VoidResult make_logger_void_result(logger_error_code code, const std::string &message="")
std::string get_logger_error_message(const common::VoidResult &result)
Get error message from a VoidResult.
@ size
Rotate based on file size only.
logger_error_code get_logger_error_code(const common::VoidResult &result)