PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::security::audit_key_manager Class Reference

Holds the active encryption key and any retired keys for decryption of historical records. More...

#include <audit_log_cipher.h>

Collaboration diagram for kcenon::pacs::security::audit_key_manager:
Collaboration graph

Public Member Functions

 audit_key_manager ()=default
 
void register_key (audit_cipher_key key)
 Register a key (idempotent by key_id; overwrites existing entry)
 
VoidResult set_active (const std::string &key_id)
 Mark a previously-registered key as the active write key.
 
const audit_cipher_key * find (const std::string &key_id) const
 Look up a key by identifier (returns nullptr if unknown)
 
Result< audit_cipher_key > active () const
 Retrieve the active write key (error if none configured)
 
void forget (const std::string &key_id)
 Drop a key by id (does nothing if it was not registered)
 
std::size_t size () const
 Number of registered keys.
 
VoidResult load_from_env ()
 Load keys from environment variables.
 

Static Public Member Functions

static VoidResult decode_key (const std::string &encoded, std::uint8_t *out, std::size_t out_size)
 Decode a hex or base64 key string into a fixed-size buffer.
 

Private Attributes

std::mutex mutex_
 
std::unordered_map< std::string, audit_cipher_key > keys_
 
std::string active_id_
 

Detailed Description

Holds the active encryption key and any retired keys for decryption of historical records.

A single "active" key is used for all new writes. Any number of additional keys (including the former active key) may be registered so that previously-written records remain verifiable after rotation.

Thread-safety: all public operations are internally synchronised.

Key sourcing: in production, populate via load_from_env() which reads PACS_AUDIT_LOG_ACTIVE_KEY_ID plus PACS_AUDIT_LOG_KEY_<id> and PACS_AUDIT_LOG_MAC_<id> (both hex- or base64-encoded). NEVER hard-code keys in source. Rotation procedure: provision a new <id>, update PACS_AUDIT_LOG_ACTIVE_KEY_ID, keep the previous key registered for as long as historical records are retained.

Definition at line 123 of file audit_log_cipher.h.

Constructor & Destructor Documentation

◆ audit_key_manager()

kcenon::pacs::security::audit_key_manager::audit_key_manager ( )
default

Member Function Documentation

◆ active()

Result< audit_cipher_key > kcenon::pacs::security::audit_key_manager::active ( ) const
nodiscard

Retrieve the active write key (error if none configured)

Definition at line 202 of file audit_log_cipher.cpp.

202 {
203 std::lock_guard<std::mutex> lock(mutex_);
204 if (active_id_.empty()) {
206 kcenon::common::error::codes::common_errors::not_found,
207 "no active audit encryption key configured");
208 }
209 auto it = keys_.find(active_id_);
210 if (it == keys_.end()) {
212 kcenon::common::error::codes::common_errors::internal_error,
213 "active audit key missing from key store: " + active_id_);
214 }
216}
std::unordered_map< std::string, audit_cipher_key > keys_
Result< T > pacs_error(int code, const std::string &message, const std::string &details="")
Create a PACS error result with module context.
Definition result.h:234

References active_id_, keys_, mutex_, and kcenon::pacs::pacs_error().

Referenced by load_from_env().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ decode_key()

VoidResult kcenon::pacs::security::audit_key_manager::decode_key ( const std::string & encoded,
std::uint8_t * out,
std::size_t out_size )
staticnodiscard

Decode a hex or base64 key string into a fixed-size buffer.

Accepts:

  • lowercase/uppercase hexadecimal of exactly 2*N characters, or
  • base64 (standard alphabet, padding required) of N bytes
Parameters
encodedthe encoded string
outdestination buffer; must be exactly out_size bytes
out_sizesize of the destination buffer; must equal the key length N
Returns
VoidResult success or decode error

Definition at line 229 of file audit_log_cipher.cpp.

231 {
232 if (encoded.empty() || out == nullptr || out_size == 0) {
233 return pacs_void_error(
234 kcenon::common::error::codes::common_errors::invalid_argument,
235 "empty key encoding or invalid buffer");
236 }
237
238 // Hex first: encoded length must be exactly 2 * out_size
239 if (encoded.size() == out_size * 2) {
240 if (detail::hex_decode(encoded, out, out_size)) {
241 return kcenon::common::ok();
242 }
243 // fall through — may still be base64 of the same length
244 }
245
246 std::vector<std::uint8_t> decoded;
247 if (!detail::base64_decode(encoded, decoded)) {
248 return pacs_void_error(
249 kcenon::common::error::codes::common_errors::invalid_argument,
250 "audit key is neither valid hex nor base64");
251 }
252 if (decoded.size() != out_size) {
253 return pacs_void_error(
254 kcenon::common::error::codes::common_errors::invalid_argument,
255 "audit key has wrong length after decoding");
256 }
257 std::memcpy(out, decoded.data(), out_size);
258 // wipe temporary copy
259 volatile std::uint8_t* p = decoded.data();
260 for (std::size_t i = 0; i < decoded.size(); ++i) p[i] = 0;
261 return kcenon::common::ok();
262}
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.
VoidResult pacs_void_error(int code, const std::string &message, const std::string &details="")
Create a PACS void error result.
Definition result.h:249
std::string out

References kcenon::pacs::security::detail::base64_decode(), kcenon::pacs::security::detail::hex_decode(), out, and kcenon::pacs::pacs_void_error().

Referenced by load_from_env().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ find()

const audit_cipher_key * kcenon::pacs::security::audit_key_manager::find ( const std::string & key_id) const
nodiscard

Look up a key by identifier (returns nullptr if unknown)

Definition at line 195 of file audit_log_cipher.cpp.

196 {
197 std::lock_guard<std::mutex> lock(mutex_);
198 auto it = keys_.find(key_id);
199 return it == keys_.end() ? nullptr : &it->second;
200}

References keys_, and mutex_.

Referenced by load_from_env().

Here is the caller graph for this function:

◆ forget()

void kcenon::pacs::security::audit_key_manager::forget ( const std::string & key_id)

Drop a key by id (does nothing if it was not registered)

Definition at line 218 of file audit_log_cipher.cpp.

218 {
219 std::lock_guard<std::mutex> lock(mutex_);
220 keys_.erase(key_id);
221 if (active_id_ == key_id) active_id_.clear();
222}

References active_id_, keys_, and mutex_.

◆ load_from_env()

VoidResult kcenon::pacs::security::audit_key_manager::load_from_env ( )
nodiscard

Load keys from environment variables.

Reads:

  • PACS_AUDIT_LOG_ACTIVE_KEY_ID — the active write key id
  • PACS_AUDIT_LOG_KEYS — comma-separated list of ids to load (optional; defaults to the active id only)
  • PACS_AUDIT_LOG_KEY_<id> — 32-byte data key, hex or base64
  • PACS_AUDIT_LOG_MAC_<id> — 32-byte MAC key, hex or base64
Returns
VoidResult reporting the first configuration error, if any

Definition at line 290 of file audit_log_cipher.cpp.

290 {
291 const std::string active = getenv_str("PACS_AUDIT_LOG_ACTIVE_KEY_ID");
292 if (active.empty()) {
293 return pacs_void_error(
294 kcenon::common::error::codes::common_errors::invalid_argument,
295 "PACS_AUDIT_LOG_ACTIVE_KEY_ID is not set");
296 }
297
298 std::vector<std::string> ids;
299 const std::string list = getenv_str("PACS_AUDIT_LOG_KEYS");
300 if (list.empty()) {
301 ids.push_back(active);
302 } else {
303 ids = split_csv(list);
304 if (std::find(ids.begin(), ids.end(), active) == ids.end()) {
305 ids.push_back(active);
306 }
307 }
308
309 for (const auto& id : ids) {
310 if (id.empty() || id.find('|') != std::string::npos) {
311 return pacs_void_error(
312 kcenon::common::error::codes::common_errors::invalid_argument,
313 "invalid audit key id: '" + id + "'");
314 }
315 const std::string key_env = "PACS_AUDIT_LOG_KEY_" + id;
316 const std::string mac_env = "PACS_AUDIT_LOG_MAC_" + id;
317 const std::string key_val = getenv_str(key_env.c_str());
318 const std::string mac_val = getenv_str(mac_env.c_str());
319 if (key_val.empty() || mac_val.empty()) {
320 return pacs_void_error(
321 kcenon::common::error::codes::common_errors::invalid_argument,
322 "missing env var for audit key '" + id + "'");
323 }
324
325 audit_cipher_key k;
326 k.key_id = id;
327 auto r1 = decode_key(key_val, k.data_key.data(), k.data_key.size());
328 if (!r1.is_ok()) {
329 return pacs_void_error(
330 kcenon::common::error::codes::common_errors::invalid_argument,
331 "failed to decode " + key_env);
332 }
333 auto r2 = decode_key(mac_val, k.mac_key.data(), k.mac_key.size());
334 if (!r2.is_ok()) {
335 return pacs_void_error(
336 kcenon::common::error::codes::common_errors::invalid_argument,
337 "failed to decode " + mac_env);
338 }
339 register_key(std::move(k));
340 }
341
342 return set_active(active);
343}
VoidResult set_active(const std::string &key_id)
Mark a previously-registered key as the active write key.
static VoidResult decode_key(const std::string &encoded, std::uint8_t *out, std::size_t out_size)
Decode a hex or base64 key string into a fixed-size buffer.
Result< audit_cipher_key > active() const
Retrieve the active write key (error if none configured)
void register_key(audit_cipher_key key)
Register a key (idempotent by key_id; overwrites existing entry)
const audit_cipher_key * find(const std::string &key_id) const
Look up a key by identifier (returns nullptr if unknown)
@ empty
Z - Replace with zero-length value.
@ id
Implant Displaced (alternate code)

References active(), kcenon::pacs::security::audit_cipher_key::data_key, decode_key(), kcenon::pacs::security::empty, find(), kcenon::pacs::security::audit_cipher_key::key_id, kcenon::pacs::security::list, kcenon::pacs::security::audit_cipher_key::mac_key, kcenon::pacs::pacs_void_error(), register_key(), and set_active().

Here is the call graph for this function:

◆ register_key()

void kcenon::pacs::security::audit_key_manager::register_key ( audit_cipher_key key)

Register a key (idempotent by key_id; overwrites existing entry)

Definition at line 177 of file audit_log_cipher.cpp.

177 {
178 std::lock_guard<std::mutex> lock(mutex_);
179 const std::string id = key.key_id;
180 keys_[id] = std::move(key);
181 if (active_id_.empty()) active_id_ = id;
182}

References active_id_, kcenon::pacs::security::audit_cipher_key::key_id, keys_, and mutex_.

Referenced by load_from_env().

Here is the caller graph for this function:

◆ set_active()

VoidResult kcenon::pacs::security::audit_key_manager::set_active ( const std::string & key_id)
nodiscard

Mark a previously-registered key as the active write key.

Definition at line 184 of file audit_log_cipher.cpp.

184 {
185 std::lock_guard<std::mutex> lock(mutex_);
186 if (keys_.find(key_id) == keys_.end()) {
187 return pacs_void_error(
188 kcenon::common::error::codes::common_errors::not_found,
189 "audit key not registered: " + key_id);
190 }
191 active_id_ = key_id;
192 return kcenon::common::ok();
193}

References active_id_, keys_, mutex_, and kcenon::pacs::pacs_void_error().

Referenced by load_from_env().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ size()

std::size_t kcenon::pacs::security::audit_key_manager::size ( ) const
nodiscard

Number of registered keys.

Definition at line 224 of file audit_log_cipher.cpp.

224 {
225 std::lock_guard<std::mutex> lock(mutex_);
226 return keys_.size();
227}

References keys_, and mutex_.

Member Data Documentation

◆ active_id_

std::string kcenon::pacs::security::audit_key_manager::active_id_
private

Definition at line 180 of file audit_log_cipher.h.

Referenced by active(), forget(), register_key(), and set_active().

◆ keys_

std::unordered_map<std::string, audit_cipher_key> kcenon::pacs::security::audit_key_manager::keys_
private

Definition at line 179 of file audit_log_cipher.h.

Referenced by active(), find(), forget(), register_key(), set_active(), and size().

◆ mutex_

std::mutex kcenon::pacs::security::audit_key_manager::mutex_
mutableprivate

Definition at line 178 of file audit_log_cipher.h.

Referenced by active(), find(), forget(), register_key(), set_active(), and size().


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