PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
audit_log_cipher.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, kcenon
3// See the LICENSE file in the project root for full license information.
4
35#ifndef PACS_SECURITY_AUDIT_LOG_CIPHER_HPP
36#define PACS_SECURITY_AUDIT_LOG_CIPHER_HPP
37
39
40#include <array>
41#include <cstddef>
42#include <cstdint>
43#include <mutex>
44#include <string>
45#include <unordered_map>
46#include <vector>
47
48namespace kcenon::pacs::security {
49
50// =============================================================================
51// Constants (ISO 27799 ยง7.9 mapped values)
52// =============================================================================
53
55inline constexpr const char* audit_cipher_format_tag = "PACSAUDIT";
56inline constexpr const char* audit_cipher_format_version = "v1";
57
59inline constexpr std::size_t audit_cipher_key_bytes = 32;
60
62inline constexpr std::size_t audit_cipher_mac_key_bytes = 32;
63
65inline constexpr std::size_t audit_cipher_iv_bytes = 12;
66
68inline constexpr std::size_t audit_cipher_tag_bytes = 16;
69
70// =============================================================================
71// Key Material
72// =============================================================================
73
83 std::string key_id;
84
86 std::array<std::uint8_t, audit_cipher_key_bytes> data_key{};
87
89 std::array<std::uint8_t, audit_cipher_mac_key_bytes> mac_key{};
90
92 void wipe() noexcept;
93
95 audit_cipher_key() = default;
97 audit_cipher_key(audit_cipher_key&&) noexcept = default;
98 audit_cipher_key& operator=(const audit_cipher_key&) = default;
99 audit_cipher_key& operator=(audit_cipher_key&&) noexcept = default;
100};
101
102// =============================================================================
103// Key Manager
104// =============================================================================
105
124public:
125 audit_key_manager() = default;
126
128 void register_key(audit_cipher_key key);
129
131 [[nodiscard]] VoidResult set_active(const std::string& key_id);
132
134 [[nodiscard]] const audit_cipher_key* find(const std::string& key_id) const;
135
137 [[nodiscard]] Result<audit_cipher_key> active() const;
138
140 void forget(const std::string& key_id);
141
143 [[nodiscard]] std::size_t size() const;
144
158 [[nodiscard]] VoidResult load_from_env();
159
172 [[nodiscard]] static VoidResult decode_key(
173 const std::string& encoded,
174 std::uint8_t* out,
175 std::size_t out_size);
176
177private:
178 mutable std::mutex mutex_;
179 std::unordered_map<std::string, audit_cipher_key> keys_;
180 std::string active_id_;
181};
182
183// =============================================================================
184// Cipher
185// =============================================================================
186
197public:
202 explicit audit_log_cipher(audit_key_manager& keys);
203
216 [[nodiscard]] Result<std::string> encrypt_record(
217 const std::string& plaintext) const;
218
230 [[nodiscard]] Result<std::string> decrypt_record(
231 const std::string& encoded) const;
232
237 [[nodiscard]] VoidResult verify_record(const std::string& encoded) const;
238
240 [[nodiscard]] static bool looks_encrypted(const std::string& line) noexcept;
241
242private:
244};
245
246// =============================================================================
247// Small Encoding Helpers (exposed for tests)
248// =============================================================================
249
250namespace detail {
251
253[[nodiscard]] std::string base64_encode(const std::uint8_t* data,
254 std::size_t size);
255
257[[nodiscard]] bool base64_decode(const std::string& in,
258 std::vector<std::uint8_t>& out);
259
261[[nodiscard]] bool hex_decode(const std::string& in,
262 std::uint8_t* out,
263 std::size_t out_size);
264
266[[nodiscard]] bool constant_time_equal(const std::uint8_t* a,
267 const std::uint8_t* b,
268 std::size_t n) noexcept;
269
270} // namespace detail
271
272} // namespace kcenon::pacs::security
273
274#endif // PACS_SECURITY_AUDIT_LOG_CIPHER_HPP
std::string base64_encode(std::string_view input)
Encode data to Base64.
Holds the active encryption key and any retired keys for decryption of historical records.
std::unordered_map< std::string, audit_cipher_key > keys_
Encrypts and decrypts single audit log records.
bool constant_time_equal(const std::uint8_t *a, const std::uint8_t *b, std::size_t n) noexcept
Constant-time equality on byte buffers of equal length.
bool base64_decode(const std::string &in, std::vector< std::uint8_t > &out)
Standard base64 decode; returns false on malformed input.
bool hex_decode(const std::string &in, std::uint8_t *out, std::size_t out_size)
Hex decode (lowercase or uppercase); returns false on malformed input.
constexpr std::size_t audit_cipher_mac_key_bytes
HMAC-SHA256 key length in bytes (equal to block size for full strength)
constexpr std::size_t audit_cipher_iv_bytes
GCM IV length in bytes (NIST SP 800-38D recommended 96 bits)
constexpr std::size_t audit_cipher_key_bytes
AES-256 key length in bytes.
constexpr const char * audit_cipher_format_version
constexpr std::size_t audit_cipher_tag_bytes
GCM authentication tag length in bytes.
constexpr const char * audit_cipher_format_tag
Format version string embedded in every encrypted record.
Result<T> type aliases and helpers for PACS system.
std::string out
A versioned symmetric key pair (data encryption + MAC)
std::array< std::uint8_t, audit_cipher_key_bytes > data_key
256-bit AES data encryption key
void wipe() noexcept
Secure zero helper (wipes key material on destruction)
std::string key_id
Stable identifier (ASCII, non-empty, no '|' character)
std::array< std::uint8_t, audit_cipher_mac_key_bytes > mac_key
256-bit HMAC-SHA256 key