Skip to content
API Documentation
BastionGuard™
BastionGuard::Activation::anonymous_namespace{activation.cpp} Namespace Reference

Functions

uint32_t rotr (uint32_t value, unsigned int count)
std::string sha256_hex (const std::string &input)
std::string uppercase (std::string value)
std::string lowercase (std::string value)
std::string trim (const std::string &value)
std::string normalize_engine_id (const std::string &value)
std::string compact_key (const std::string &raw)
std::string extract_activation_key (const std::string &raw)
std::string compact_hex_machine_id (const std::string &raw)
std::string collapse_spaces (const std::string &value)
std::string canonical_serial (std::string value)
bool all_zero_like (const std::string &value)
bool is_placeholder_serial (const std::string &value)
std::string read_first_valid_serial (const std::vector< std::string > &paths)
std::string read_first_existing_file (const std::vector< std::string > &paths)
std::string legacy_application_machine_id ()
int b64url_value (char c)
std::optional< std::string > base64url_decode (const std::string &input)
std::string percent_decode (const std::string &value)
std::map< std::string, std::string > parse_payload (const std::string &payload)
std::string today_ymd ()
bool is_valid_expiry (const std::string &expires)
bool has_expired (const std::string &expires)
bool is_running_as_root ()
std::string user_config_dir ()
std::string system_config_dir ()
std::string user_activation_key_path ()
std::string system_activation_key_path ()
std::string preferred_activation_key_path_for_save ()
std::string read_file_all (const std::string &path)
std::string sign_payload_v1 (const std::string &payload)
std::string sign_payload_v2 (const std::string &canonical_payload)
uint64_t fnv1a64 (const std::string &input)
std::string normalize_activation_code (std::string value)
std::string group_activation_code (const std::string &compact)
std::string field_or_empty (const std::map< std::string, std::string > &fields, const std::string &name)
std::string code_payload_v3 (const std::map< std::string, std::string > &fields)
std::string generate_activation_code_v3 (const std::string &canonical_payload)
std::map< std::string, std::string > parse_key_block (const std::string &key)
std::string canonical_v2_payload (const std::map< std::string, std::string > &fields)

Variables

constexpr std::string_view kActivationSecret
constexpr std::string_view kProductId = "BastionGuardRecoveryData"
constexpr std::string_view kActivationEngineId = "supportterdonation"
constexpr std::string_view kKeyPrefixV1 = "BGRD1"
constexpr std::string_view kKeyPrefixV2 = "BGRD2"
constexpr std::string_view kKeyPrefixV3 = "BGRD3"
constexpr std::string_view kKeyBeginMarker = "-----BEGIN BASTIONGUARD ACTIVATION KEY-----"
constexpr std::string_view kKeyEndMarker = "-----END BASTIONGUARD ACTIVATION KEY-----"

Function Documentation

◆ all_zero_like()

bool BastionGuard::Activation::anonymous_namespace{activation.cpp}::all_zero_like ( const std::string & value)

Definition at line 296 of file activation.cpp.

296 {
297 bool has_alnum = false;
298 for (unsigned char c : value) {
299 if (std::isalnum(c) == 0) continue;
300 has_alnum = true;
301 if (c != '0') return false;
302 }
303 return has_alnum;
304}

References all_zero_like().

Referenced by all_zero_like(), and is_placeholder_serial().

◆ b64url_value()

int BastionGuard::Activation::anonymous_namespace{activation.cpp}::b64url_value ( char c)

Definition at line 375 of file activation.cpp.

375 {
376 if (c >= 'A' && c <= 'Z') return c - 'A';
377 if (c >= 'a' && c <= 'z') return c - 'a' + 26;
378 if (c >= '0' && c <= '9') return c - '0' + 52;
379 if (c == '-' || c == '+') return 62;
380 if (c == '_' || c == '/') return 63;
381 return -1;
382}

References b64url_value().

Referenced by b64url_value(), and base64url_decode().

◆ base64url_decode()

std::optional< std::string > BastionGuard::Activation::anonymous_namespace{activation.cpp}::base64url_decode ( const std::string & input)

Definition at line 384 of file activation.cpp.

384 {
385 std::string output;
386 int val = 0;
387 int valb = -8;
388
389 for (char c : input) {
390 if (c == '=') break;
391 const int decoded = b64url_value(c);
392 if (decoded < 0) return std::nullopt;
393 val = (val << 6) + decoded;
394 valb += 6;
395 if (valb >= 0) {
396 output.push_back(static_cast<char>((val >> valb) & 0xff));
397 valb -= 8;
398 }
399 }
400
401 return output;
402}

References b64url_value(), and base64url_decode().

Referenced by base64url_decode().

◆ canonical_serial()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::canonical_serial ( std::string value)

Definition at line 290 of file activation.cpp.

290 {
291 value = trim(value);
292 value = collapse_spaces(value);
293 return uppercase(value);
294}
std::string collapse_spaces(const std::string &value)

References canonical_serial(), collapse_spaces(), trim(), and uppercase().

Referenced by canonical_serial(), canonical_v2_payload(), code_payload_v3(), is_placeholder_serial(), and read_first_valid_serial().

◆ canonical_v2_payload()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::canonical_v2_payload ( const std::map< std::string, std::string > & fields)

Definition at line 650 of file activation.cpp.

650 {
651 const std::string product = trim(field_or_empty(fields, "PRODUCT"));
652 const std::string engine = normalize_engine_id(field_or_empty(fields, "ENGINE"));
653 const std::string machine = compact_hex_machine_id(field_or_empty(fields, "MACHINE"));
654 const std::string motherboard = canonical_serial(field_or_empty(fields, "MOTHERBOARD"));
655 const std::string owner = trim(field_or_empty(fields, "OWNER"));
656 std::string edition = trim(field_or_empty(fields, "EDITION"));
657 std::string expires = lowercase(trim(field_or_empty(fields, "EXPIRES")));
658
659 if (edition.empty()) edition = "Donation";
660 if (expires.empty()) expires = "never";
661
662 std::ostringstream out;
663 out << "product=" << product << "\n"
664 << "engine=" << engine << "\n"
665 << "machine=" << machine << "\n"
666 << "motherboard=" << motherboard << "\n"
667 << "owner=" << owner << "\n"
668 << "edition=" << edition << "\n"
669 << "expires=" << expires;
670 return out.str();
671}
std::string normalize_engine_id(const std::string &value)
std::string field_or_empty(const std::map< std::string, std::string > &fields, const std::string &name)
std::string compact_hex_machine_id(const std::string &raw)

References canonical_serial(), canonical_v2_payload(), compact_hex_machine_id(), field_or_empty(), lowercase(), normalize_engine_id(), and trim().

Referenced by canonical_v2_payload().

◆ code_payload_v3()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::code_payload_v3 ( const std::map< std::string, std::string > & fields)

Definition at line 578 of file activation.cpp.

578 {
579 const std::string product = trim(field_or_empty(fields, "PRODUCT"));
580 const std::string engine = normalize_engine_id(field_or_empty(fields, "ENGINE"));
581 const std::string machine = compact_hex_machine_id(field_or_empty(fields, "MACHINE"));
582 const std::string motherboard = canonical_serial(field_or_empty(fields, "MOTHERBOARD"));
583 const std::string key_id = trim(field_or_empty(fields, "KEYID"));
584 std::string edition = trim(field_or_empty(fields, "EDITION"));
585 std::string expires = lowercase(trim(field_or_empty(fields, "EXPIRES")));
586
587 if (edition.empty()) edition = "Donation";
588 if (expires.empty()) expires = "never";
589
590 std::ostringstream out;
591 out << "product=" << product << "\n"
592 << "engine=" << engine << "\n"
593 << "machine=" << machine << "\n"
594 << "motherboard=" << motherboard << "\n"
595 << "edition=" << edition << "\n"
596 << "expires=" << expires;
597
598 // BGRD3.1: optional random KEYID. When present, it is part of the
599 // activation code calculation so the same hardware can receive different
600 // valid keys without making the readable BGRD3 block fragile.
601 if (!key_id.empty()) {
602 out << "\nkeyid=" << key_id;
603 }
604
605 return out.str();
606}

References canonical_serial(), code_payload_v3(), compact_hex_machine_id(), field_or_empty(), lowercase(), normalize_engine_id(), and trim().

Referenced by code_payload_v3().

◆ collapse_spaces()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::collapse_spaces ( const std::string & value)

Definition at line 266 of file activation.cpp.

266 {
267 std::string out;
268 out.reserve(value.size());
269 bool last_space = false;
270
271 for (unsigned char c : value) {
272 if (c == '\0') continue;
273 if (std::isspace(c) != 0) {
274 if (!last_space && !out.empty()) {
275 out.push_back(' ');
276 }
277 last_space = true;
278 } else {
279 out.push_back(static_cast<char>(c));
280 last_space = false;
281 }
282 }
283
284 while (!out.empty() && out.back() == ' ') {
285 out.pop_back();
286 }
287 return out;
288}

References collapse_spaces().

Referenced by canonical_serial(), and collapse_spaces().

◆ compact_hex_machine_id()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::compact_hex_machine_id ( const std::string & raw)

Definition at line 255 of file activation.cpp.

255 {
256 std::string out;
257 out.reserve(raw.size());
258 for (unsigned char c : raw) {
259 if (std::isxdigit(c)) {
260 out.push_back(static_cast<char>(std::toupper(c)));
261 }
262 }
263 return out;
264}

References compact_hex_machine_id().

Referenced by canonical_v2_payload(), code_payload_v3(), and compact_hex_machine_id().

◆ compact_key()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::compact_key ( const std::string & raw)

Definition at line 184 of file activation.cpp.

184 {
185 std::string out;
186 out.reserve(raw.size());
187 for (unsigned char c : raw) {
188 if (!std::isspace(c)) out.push_back(static_cast<char>(c));
189 }
190 return out;
191}

References compact_key().

Referenced by compact_key(), and extract_activation_key().

◆ extract_activation_key()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::extract_activation_key ( const std::string & raw)

Definition at line 193 of file activation.cpp.

193 {
194 const std::string text = trim(raw);
195 if (text.empty()) return {};
196
197 const std::string begin_marker(kKeyBeginMarker);
198 const std::string end_marker(kKeyEndMarker);
199 const auto begin = text.find(begin_marker);
200 if (begin != std::string::npos) {
201 const auto end = text.find(end_marker, begin + begin_marker.size());
202 if (end != std::string::npos) {
203 return trim(text.substr(begin, end + end_marker.size() - begin));
204 }
205 return trim(text.substr(begin));
206 }
207
208 // BGRD2/BGRD3 can also be pasted without BEGIN/END markers. Keep the
209 // multiline field block intact because validation is calculated from
210 // field values, not from a compact one-line token.
211 if (((text.find(std::string(kKeyPrefixV2)) != std::string::npos &&
212 text.find("SIGNATURE=") != std::string::npos) ||
213 (text.find(std::string(kKeyPrefixV3)) != std::string::npos &&
214 text.find("CODE=") != std::string::npos))) {
215 return text;
216 }
217
218 // Legacy BGRD1 support. Paste operations from GUI toolkits can include
219 // invisible Unicode bytes or labels around the key. Keep only characters
220 // valid in the BGRD1 token and extract exactly one
221 // BGRD1.<payload>.<40-hex-signature> token.
222 std::string filtered;
223 filtered.reserve(raw.size());
224 for (unsigned char c : raw) {
225 if (std::isalnum(c) != 0 || c == '.' || c == '-' || c == '_') {
226 filtered.push_back(static_cast<char>(c));
227 }
228 }
229
230 const std::string prefix = std::string(kKeyPrefixV1) + ".";
231 const auto start = filtered.find(prefix);
232 if (start == std::string::npos) {
233 return compact_key(raw);
234 }
235
236 const auto payload_dot = filtered.find('.', start + prefix.size());
237 if (payload_dot == std::string::npos) {
238 return filtered.substr(start);
239 }
240
241 const size_t sig_start = payload_dot + 1;
242 size_t sig_end = sig_start;
243 while (sig_end < filtered.size() && sig_end - sig_start < 40 &&
244 std::isxdigit(static_cast<unsigned char>(filtered[sig_end])) != 0) {
245 ++sig_end;
246 }
247
248 if (sig_end - sig_start == 40) {
249 return filtered.substr(start, sig_end - start);
250 }
251
252 return filtered.substr(start);
253}

References compact_key(), extract_activation_key(), kKeyBeginMarker, kKeyEndMarker, kKeyPrefixV1, kKeyPrefixV2, kKeyPrefixV3, and trim().

Referenced by extract_activation_key().

◆ field_or_empty()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::field_or_empty ( const std::map< std::string, std::string > & fields,
const std::string & name )

Definition at line 573 of file activation.cpp.

573 {
574 const auto it = fields.find(name);
575 return it == fields.end() ? std::string{} : it->second;
576}

References field_or_empty().

Referenced by canonical_v2_payload(), code_payload_v3(), and field_or_empty().

◆ fnv1a64()

uint64_t BastionGuard::Activation::anonymous_namespace{activation.cpp}::fnv1a64 ( const std::string & input)

Definition at line 543 of file activation.cpp.

543 {
544 uint64_t hash = 14695981039346656037ULL;
545 for (unsigned char c : input) {
546 hash ^= static_cast<uint64_t>(c);
547 hash *= 1099511628211ULL;
548 }
549 return hash;
550}

References fnv1a64().

Referenced by fnv1a64(), and generate_activation_code_v3().

◆ generate_activation_code_v3()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::generate_activation_code_v3 ( const std::string & canonical_payload)

Definition at line 608 of file activation.cpp.

608 {
609 static constexpr std::string_view alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
610 const std::string material = std::string(kKeyPrefixV3) + "\n" + canonical_payload + "\n" + std::string(kActivationSecret);
611
612 std::string compact;
613 compact.reserve(25);
614 for (int block = 0; block < 5; ++block) {
615 uint64_t h = fnv1a64(material + "\nblock=" + std::to_string(block));
616 for (int i = 0; i < 5; ++i) {
617 const uint64_t mixed = h ^ (h >> 29U) ^ (h >> 47U);
618 compact.push_back(alphabet[static_cast<size_t>(mixed & 31ULL)]);
619 h = (h >> 5U) ^ (h << 17U) ^ (0x9E3779B97F4A7C15ULL + static_cast<uint64_t>(block * 131 + i));
620 }
621 }
622
623 return group_activation_code(compact);
624}
std::string group_activation_code(const std::string &compact)

References fnv1a64(), generate_activation_code_v3(), group_activation_code(), kActivationSecret, and kKeyPrefixV3.

Referenced by generate_activation_code_v3().

◆ group_activation_code()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::group_activation_code ( const std::string & compact)

Definition at line 564 of file activation.cpp.

564 {
565 std::string out;
566 for (size_t i = 0; i < compact.size(); ++i) {
567 if (i > 0 && i % 5 == 0) out.push_back('-');
568 out.push_back(compact[i]);
569 }
570 return out;
571}

References group_activation_code().

Referenced by generate_activation_code_v3(), and group_activation_code().

◆ has_expired()

bool BastionGuard::Activation::anonymous_namespace{activation.cpp}::has_expired ( const std::string & expires)

Definition at line 484 of file activation.cpp.

484 {
485 if (expires == "never") return false;
486 if (!is_valid_expiry(expires)) return true;
487 return expires < today_ymd();
488}

References has_expired(), is_valid_expiry(), and today_ymd().

Referenced by has_expired().

◆ is_placeholder_serial()

bool BastionGuard::Activation::anonymous_namespace{activation.cpp}::is_placeholder_serial ( const std::string & value)

Definition at line 306 of file activation.cpp.

306 {
307 const std::string serial = canonical_serial(value);
308 if (serial.empty()) return true;
309 if (all_zero_like(serial)) return true;
310
311 static constexpr std::string_view placeholders[] = {
312 "0",
313 "00",
314 "00000000",
315 "0000000000000000",
316 "00000000000000000000000000000000",
317 "NONE",
318 "UNKNOWN",
319 "UNKNOWN SERIAL",
320 "UNKNOWN SERIAL NUMBER",
321 "NOT SPECIFIED",
322 "NOT APPLICABLE",
323 "N/A",
324 "NA",
325 "DEFAULT STRING",
326 "SYSTEM SERIAL NUMBER",
327 "BASE BOARD SERIAL NUMBER",
328 "TO BE FILLED BY O.E.M.",
329 "TO BE FILLED BY OEM"
330 };
331
332 for (std::string_view placeholder : placeholders) {
333 if (serial == placeholder) return true;
334 }
335 return false;
336}

References all_zero_like(), canonical_serial(), and is_placeholder_serial().

Referenced by is_placeholder_serial(), and read_first_valid_serial().

◆ is_running_as_root()

bool BastionGuard::Activation::anonymous_namespace{activation.cpp}::is_running_as_root ( )

Definition at line 490 of file activation.cpp.

490 {
491 return ::geteuid() == 0;
492}

References is_running_as_root().

Referenced by is_running_as_root(), and preferred_activation_key_path_for_save().

◆ is_valid_expiry()

bool BastionGuard::Activation::anonymous_namespace{activation.cpp}::is_valid_expiry ( const std::string & expires)

Definition at line 469 of file activation.cpp.

469 {
470 if (expires == "never") return true;
471 if (expires.size() != 10) return false;
472 return std::isdigit(static_cast<unsigned char>(expires[0])) &&
473 std::isdigit(static_cast<unsigned char>(expires[1])) &&
474 std::isdigit(static_cast<unsigned char>(expires[2])) &&
475 std::isdigit(static_cast<unsigned char>(expires[3])) &&
476 expires[4] == '-' &&
477 std::isdigit(static_cast<unsigned char>(expires[5])) &&
478 std::isdigit(static_cast<unsigned char>(expires[6])) &&
479 expires[7] == '-' &&
480 std::isdigit(static_cast<unsigned char>(expires[8])) &&
481 std::isdigit(static_cast<unsigned char>(expires[9]));
482}

References is_valid_expiry().

Referenced by has_expired(), and is_valid_expiry().

◆ legacy_application_machine_id()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::legacy_application_machine_id ( )

Definition at line 352 of file activation.cpp.

352 {
353 std::string seed = read_first_existing_file({
354 "/etc/machine-id",
355 "/var/lib/dbus/machine-id"
356 });
357
358 if (seed.empty()) {
359 char hostname[256] = {};
360 if (gethostname(hostname, sizeof(hostname) - 1) == 0) {
361 seed = hostname;
362 }
363 }
364
365 if (seed.empty()) {
366 seed = "unknown-machine";
367 }
368
369 seed += "|";
370 seed += kProductId;
371
372 return uppercase(sha256_hex(seed)).substr(0, 16);
373}
std::string read_first_existing_file(const std::vector< std::string > &paths)

References kProductId, legacy_application_machine_id(), read_first_existing_file(), sha256_hex(), and uppercase().

Referenced by legacy_application_machine_id().

◆ lowercase()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::lowercase ( std::string value)

Definition at line 160 of file activation.cpp.

160 {
161 std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) {
162 return static_cast<char>(std::tolower(c));
163 });
164 return value;
165}

References lowercase().

Referenced by canonical_v2_payload(), code_payload_v3(), lowercase(), and normalize_engine_id().

◆ normalize_activation_code()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::normalize_activation_code ( std::string value)

Definition at line 552 of file activation.cpp.

552 {
553 value = uppercase(value);
554 std::string out;
555 out.reserve(value.size());
556 for (unsigned char c : value) {
557 if (std::isalnum(c) != 0) {
558 out.push_back(static_cast<char>(c));
559 }
560 }
561 return out;
562}

References normalize_activation_code(), and uppercase().

Referenced by normalize_activation_code().

◆ normalize_engine_id()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::normalize_engine_id ( const std::string & value)

Definition at line 180 of file activation.cpp.

180 {
181 return lowercase(trim(value));
182}

References lowercase(), normalize_engine_id(), and trim().

Referenced by canonical_v2_payload(), code_payload_v3(), and normalize_engine_id().

◆ parse_key_block()

std::map< std::string, std::string > BastionGuard::Activation::anonymous_namespace{activation.cpp}::parse_key_block ( const std::string & key)

Definition at line 626 of file activation.cpp.

626 {
627 std::map<std::string, std::string> fields;
628 std::stringstream ss(key);
629 std::string line;
630
631 while (std::getline(ss, line)) {
632 line = trim(line);
633 if (line.empty()) continue;
634 if (line == kKeyBeginMarker || line == kKeyEndMarker) continue;
635 if (line == kKeyPrefixV2 || line == kKeyPrefixV3) {
636 fields["FORMAT"] = line;
637 continue;
638 }
639
640 const auto eq = line.find('=');
641 if (eq == std::string::npos) continue;
642 std::string name = uppercase(trim(line.substr(0, eq)));
643 std::string value = trim(line.substr(eq + 1));
644 fields[name] = value;
645 }
646
647 return fields;
648}

References kKeyBeginMarker, kKeyEndMarker, kKeyPrefixV2, kKeyPrefixV3, parse_key_block(), trim(), and uppercase().

Referenced by parse_key_block().

◆ parse_payload()

std::map< std::string, std::string > BastionGuard::Activation::anonymous_namespace{activation.cpp}::parse_payload ( const std::string & payload)

Definition at line 428 of file activation.cpp.

428 {
429 std::map<std::string, std::string> fields;
430 std::stringstream ss(payload);
431 std::string item;
432
433 while (std::getline(ss, item, ';')) {
434 const auto eq = item.find('=');
435 if (eq == std::string::npos) continue;
436 const std::string key = item.substr(0, eq);
437 const std::string value = item.substr(eq + 1);
438 fields[key] = percent_decode(value);
439 }
440
441 return fields;
442}

References parse_payload(), and percent_decode().

Referenced by parse_payload().

◆ percent_decode()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::percent_decode ( const std::string & value)

Definition at line 404 of file activation.cpp.

404 {
405 std::string out;
406 out.reserve(value.size());
407
408 for (size_t i = 0; i < value.size(); ++i) {
409 if (value[i] == '%' && i + 2 < value.size()) {
410 const char h1 = value[i + 1];
411 const char h2 = value[i + 2];
412 if (std::isxdigit(static_cast<unsigned char>(h1)) &&
413 std::isxdigit(static_cast<unsigned char>(h2))) {
414 const std::string hex{h1, h2};
415 const char ch = static_cast<char>(std::strtol(hex.c_str(), nullptr, 16));
416 out.push_back(ch);
417 i += 2;
418 continue;
419 }
420 }
421 if (value[i] == '+') out.push_back(' ');
422 else out.push_back(value[i]);
423 }
424
425 return out;
426}

References percent_decode().

Referenced by parse_payload(), and percent_decode().

◆ preferred_activation_key_path_for_save()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::preferred_activation_key_path_for_save ( )

Definition at line 516 of file activation.cpp.

516 {
517 // When the GUI is launched through pkexec/root, save a system-wide key so
518 // the activation is not tied to /root/.config and survives future root runs.
519 if (is_running_as_root()) {
521 }
523}

References is_running_as_root(), preferred_activation_key_path_for_save(), system_activation_key_path(), and user_activation_key_path().

Referenced by preferred_activation_key_path_for_save().

◆ read_file_all()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::read_file_all ( const std::string & path)

Definition at line 525 of file activation.cpp.

525 {
526 std::ifstream input(path, std::ios::binary);
527 if (!input.is_open()) return {};
528 std::ostringstream content;
529 content << input.rdbuf();
530 return content.str();
531}

References read_file_all().

Referenced by read_file_all().

◆ read_first_existing_file()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::read_first_existing_file ( const std::vector< std::string > & paths)

Definition at line 444 of file activation.cpp.

444 {
445 for (const auto& path : paths) {
446 std::ifstream input(path);
447 if (!input.is_open()) continue;
448 std::string line;
449 std::getline(input, line);
450 line = trim(line);
451 if (!line.empty()) return line;
452 }
453 return {};
454}

References read_first_existing_file(), and trim().

Referenced by legacy_application_machine_id(), and read_first_existing_file().

◆ read_first_valid_serial()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::read_first_valid_serial ( const std::vector< std::string > & paths)

Definition at line 338 of file activation.cpp.

338 {
339 for (const auto& path : paths) {
340 std::ifstream input(path);
341 if (!input.is_open()) continue;
342 std::string line;
343 std::getline(input, line);
344 line = canonical_serial(line);
345 if (!is_placeholder_serial(line)) return line;
346 }
347 return {};
348}

References canonical_serial(), is_placeholder_serial(), and read_first_valid_serial().

Referenced by read_first_valid_serial().

◆ rotr()

uint32_t BastionGuard::Activation::anonymous_namespace{activation.cpp}::rotr ( uint32_t value,
unsigned int count )

Definition at line 51 of file activation.cpp.

51 {
52 return (value >> count) | (value << (32U - count));
53}

References rotr().

Referenced by rotr(), and sha256_hex().

◆ sha256_hex()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::sha256_hex ( const std::string & input)

Definition at line 55 of file activation.cpp.

55 {
56 static constexpr std::array<uint32_t, 64> k = {
57 0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U,
58 0x3956c25bU, 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U,
59 0xd807aa98U, 0x12835b01U, 0x243185beU, 0x550c7dc3U,
60 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U, 0xc19bf174U,
61 0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU,
62 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU,
63 0x983e5152U, 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U,
64 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U,
65 0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU, 0x53380d13U,
66 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U,
67 0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U,
68 0xd192e819U, 0xd6990624U, 0xf40e3585U, 0x106aa070U,
69 0x19a4c116U, 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U,
70 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU, 0x682e6ff3U,
71 0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U,
72 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xc67178f2U
73 };
74
75 std::array<uint32_t, 8> h = {
76 0x6a09e667U, 0xbb67ae85U, 0x3c6ef372U, 0xa54ff53aU,
77 0x510e527fU, 0x9b05688cU, 0x1f83d9abU, 0x5be0cd19U
78 };
79
80 std::vector<uint8_t> msg(input.begin(), input.end());
81 const uint64_t bit_len = static_cast<uint64_t>(msg.size()) * 8ULL;
82
83 msg.push_back(0x80U);
84 while ((msg.size() % 64U) != 56U) {
85 msg.push_back(0x00U);
86 }
87
88 for (int i = 7; i >= 0; --i) {
89 msg.push_back(static_cast<uint8_t>((bit_len >> (i * 8)) & 0xffU));
90 }
91
92 for (size_t offset = 0; offset < msg.size(); offset += 64) {
93 std::array<uint32_t, 64> w{};
94 for (size_t i = 0; i < 16; ++i) {
95 const size_t j = offset + i * 4;
96 w[i] = (static_cast<uint32_t>(msg[j]) << 24U) |
97 (static_cast<uint32_t>(msg[j + 1]) << 16U) |
98 (static_cast<uint32_t>(msg[j + 2]) << 8U) |
99 static_cast<uint32_t>(msg[j + 3]);
100 }
101
102 for (size_t i = 16; i < 64; ++i) {
103 const uint32_t s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >> 3U);
104 const uint32_t s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >> 10U);
105 w[i] = w[i - 16] + s0 + w[i - 7] + s1;
106 }
107
108 uint32_t a = h[0];
109 uint32_t b = h[1];
110 uint32_t c = h[2];
111 uint32_t d = h[3];
112 uint32_t e = h[4];
113 uint32_t f = h[5];
114 uint32_t g = h[6];
115 uint32_t hh = h[7];
116
117 for (size_t i = 0; i < 64; ++i) {
118 const uint32_t s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
119 const uint32_t ch = (e & f) ^ ((~e) & g);
120 const uint32_t temp1 = hh + s1 + ch + k[i] + w[i];
121 const uint32_t s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
122 const uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
123 const uint32_t temp2 = s0 + maj;
124
125 hh = g;
126 g = f;
127 f = e;
128 e = d + temp1;
129 d = c;
130 c = b;
131 b = a;
132 a = temp1 + temp2;
133 }
134
135 h[0] += a;
136 h[1] += b;
137 h[2] += c;
138 h[3] += d;
139 h[4] += e;
140 h[5] += f;
141 h[6] += g;
142 h[7] += hh;
143 }
144
145 std::ostringstream out;
146 out << std::hex << std::setfill('0');
147 for (uint32_t part : h) {
148 out << std::setw(8) << part;
149 }
150 return out.str();
151}
uint32_t rotr(uint32_t value, unsigned int count)

References rotr(), and sha256_hex().

Referenced by legacy_application_machine_id(), sha256_hex(), sign_payload_v1(), and sign_payload_v2().

◆ sign_payload_v1()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::sign_payload_v1 ( const std::string & payload)

Definition at line 533 of file activation.cpp.

533 {
534 const std::string input = std::string(kKeyPrefixV1) + "|" + payload + "|" + std::string(kActivationSecret);
535 return uppercase(sha256_hex(input)).substr(0, 40);
536}

References kActivationSecret, kKeyPrefixV1, sha256_hex(), sign_payload_v1(), and uppercase().

Referenced by sign_payload_v1().

◆ sign_payload_v2()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::sign_payload_v2 ( const std::string & canonical_payload)

Definition at line 538 of file activation.cpp.

538 {
539 const std::string input = std::string(kKeyPrefixV2) + "\n" + canonical_payload + "\n" + std::string(kActivationSecret);
540 return uppercase(sha256_hex(input)).substr(0, 40);
541}

References kActivationSecret, kKeyPrefixV2, sha256_hex(), sign_payload_v2(), and uppercase().

Referenced by sign_payload_v2().

◆ system_activation_key_path()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::system_activation_key_path ( )

Definition at line 512 of file activation.cpp.

512 {
513 return (std::filesystem::path(system_config_dir()) / "activation.key").string();
514}

References system_activation_key_path(), and system_config_dir().

Referenced by preferred_activation_key_path_for_save(), and system_activation_key_path().

◆ system_config_dir()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::system_config_dir ( )

Definition at line 504 of file activation.cpp.

504 {
505 return "/etc/bastionguard-recovery-data";
506}

References system_config_dir().

Referenced by system_activation_key_path(), and system_config_dir().

◆ today_ymd()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::today_ymd ( )

Definition at line 456 of file activation.cpp.

456 {
457 const std::time_t now = std::time(nullptr);
458 std::tm tm{};
459#if defined(_POSIX_VERSION)
460 localtime_r(&now, &tm);
461#else
462 tm = *std::localtime(&now);
463#endif
464 char buffer[16] = {};
465 std::strftime(buffer, sizeof(buffer), "%Y-%m-%d", &tm);
466 return buffer;
467}

References today_ymd().

Referenced by has_expired(), and today_ymd().

◆ trim()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::trim ( const std::string & value)

Definition at line 167 of file activation.cpp.

167 {
168 const auto begin = std::find_if_not(value.begin(), value.end(), [](unsigned char c) {
169 return std::isspace(c) != 0;
170 });
171 if (begin == value.end()) return {};
172
173 const auto end = std::find_if_not(value.rbegin(), value.rend(), [](unsigned char c) {
174 return std::isspace(c) != 0;
175 }).base();
176
177 return std::string(begin, end);
178}

References trim().

Referenced by canonical_serial(), canonical_v2_payload(), code_payload_v3(), extract_activation_key(), normalize_engine_id(), parse_key_block(), read_first_existing_file(), and trim().

◆ uppercase()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::uppercase ( std::string value)

Definition at line 153 of file activation.cpp.

153 {
154 std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) {
155 return static_cast<char>(std::toupper(c));
156 });
157 return value;
158}

References uppercase().

Referenced by canonical_serial(), legacy_application_machine_id(), normalize_activation_code(), parse_key_block(), sign_payload_v1(), sign_payload_v2(), and uppercase().

◆ user_activation_key_path()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::user_activation_key_path ( )

Definition at line 508 of file activation.cpp.

508 {
509 return (std::filesystem::path(user_config_dir()) / "activation.key").string();
510}

References user_activation_key_path(), and user_config_dir().

Referenced by preferred_activation_key_path_for_save(), and user_activation_key_path().

◆ user_config_dir()

std::string BastionGuard::Activation::anonymous_namespace{activation.cpp}::user_config_dir ( )

Definition at line 494 of file activation.cpp.

494 {
495 if (const char* xdg = std::getenv("XDG_CONFIG_HOME"); xdg && *xdg) {
496 return (std::filesystem::path(xdg) / "bastionguard-recovery-data").string();
497 }
498 if (const char* home = std::getenv("HOME"); home && *home) {
499 return (std::filesystem::path(home) / ".config" / "bastionguard-recovery-data").string();
500 }
501 return ".";
502}

References user_config_dir().

Referenced by user_activation_key_path(), and user_config_dir().

Variable Documentation

◆ kActivationEngineId

std::string_view BastionGuard::Activation::anonymous_namespace{activation.cpp}::kActivationEngineId = "supportterdonation"
constexpr

Definition at line 44 of file activation.cpp.

◆ kActivationSecret

std::string_view BastionGuard::Activation::anonymous_namespace{activation.cpp}::kActivationSecret
constexpr
Initial value:
=
"BastionGuard-Recovery-Data-2026-Calogero-Scarna-Donation-Activation-v1"

Definition at line 37 of file activation.cpp.

Referenced by generate_activation_code_v3(), sign_payload_v1(), and sign_payload_v2().

◆ kKeyBeginMarker

std::string_view BastionGuard::Activation::anonymous_namespace{activation.cpp}::kKeyBeginMarker = "-----BEGIN BASTIONGUARD ACTIVATION KEY-----"
constexpr

Definition at line 48 of file activation.cpp.

Referenced by extract_activation_key(), and parse_key_block().

◆ kKeyEndMarker

std::string_view BastionGuard::Activation::anonymous_namespace{activation.cpp}::kKeyEndMarker = "-----END BASTIONGUARD ACTIVATION KEY-----"
constexpr

Definition at line 49 of file activation.cpp.

Referenced by extract_activation_key(), and parse_key_block().

◆ kKeyPrefixV1

std::string_view BastionGuard::Activation::anonymous_namespace{activation.cpp}::kKeyPrefixV1 = "BGRD1"
constexpr

Definition at line 45 of file activation.cpp.

Referenced by extract_activation_key(), and sign_payload_v1().

◆ kKeyPrefixV2

std::string_view BastionGuard::Activation::anonymous_namespace{activation.cpp}::kKeyPrefixV2 = "BGRD2"
constexpr

Definition at line 46 of file activation.cpp.

Referenced by extract_activation_key(), parse_key_block(), and sign_payload_v2().

◆ kKeyPrefixV3

std::string_view BastionGuard::Activation::anonymous_namespace{activation.cpp}::kKeyPrefixV3 = "BGRD3"
constexpr

◆ kProductId

std::string_view BastionGuard::Activation::anonymous_namespace{activation.cpp}::kProductId = "BastionGuardRecoveryData"
constexpr

Definition at line 40 of file activation.cpp.

Referenced by legacy_application_machine_id().