Skip to content

Commit 035564a

Browse files
committed
wire full winsecruntime config + document security
1 parent 2ed17f9 commit 035564a

3 files changed

Lines changed: 348 additions & 71 deletions

File tree

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,58 @@ However, we do **NOT** provide support for adding KeyAuth to your project. If yo
2222

2323
While our API ensures licenses validation, it's crucial to implement robust client-side protection like obfuscation and integrity checks to prevent software tampering, as vulnerabilities often stem from insufficient client security.
2424

25+
## **Secured example security**
26+
27+
This secured example ships with:
28+
- **WinSecRuntime**: runtime integrity checks and anti‑tamper signals
29+
- **NigelCrypt**: runtime string protection for sensitive literals
30+
31+
### **WinSecRuntime setup**
32+
33+
The example calls `run_runtime_security()` at startup (see `x64/main.cpp` and `x86/main.cpp`). It builds a `secure::runtime::Config cfg` and passes it into `WinSecRuntime::Initialize()` and `WinSecRuntime::RunAll()`.
34+
35+
By default the config values are set to the library defaults (all baselines `0`, allowlists `nullptr`, toggles `false`). This makes the checks **available** but **not enforced** until you set baselines or enable flags.
36+
37+
#### Enable or tune checks
38+
39+
Open `x64/main.cpp` (or `x86/main.cpp`) and edit the config block inside `run_runtime_security()`:
40+
41+
- `cfg.enforce_safe_dll_search = true;` enables safe DLL search enforcement
42+
- `cfg.disallow_unc = true;` blocks UNC execution
43+
- `cfg.disallow_motw = true;` blocks Mark‑of‑the‑Web files
44+
- `cfg.module_hashes` / `cfg.module_hash_count` lets you pin known module hashes
45+
- `cfg.iat_*` lets you harden the import table checks
46+
- `cfg.text_*` enables code section integrity checks (sha256/rolling crc/chunk)
47+
- `cfg.nop_sled_threshold` / `cfg.int3_sled_threshold` enables sled detection
48+
- `cfg.vm_min_cores` / `cfg.vm_min_ram_gb` can reduce VM abuse
49+
50+
If you want the checks to **block on failure**, keep:
51+
```cpp
52+
const auto report = WinSecRuntime::RunAll(policy);
53+
return report.ok();
54+
```
55+
and return `false` on failure (the example exits early when `run_runtime_security()` fails).
56+
57+
### **NigelCrypt string protection**
58+
59+
The example replaces UI/status strings and app config strings with NigelCrypt.
60+
See `nc()` and `nigel_string()` helpers in `x64/main.cpp` and `x86/main.cpp`.
61+
62+
#### How to change or add protected strings
63+
64+
```cpp
65+
std::string value = nc("My Text", "aad:label");
66+
```
67+
68+
- the first argument is the plaintext string
69+
- the second argument is an AAD tag (any stable label you want)
70+
71+
If you want to remove encryption for a string, replace `nc(...)` with a normal literal.
72+
73+
#### Important note about literals
74+
75+
NigelCrypt protects runtime storage. If you pass a literal directly, it still exists in the binary. For complete removal of plaintext literals, use the NigelCrypt packer to embed encrypted blobs.
76+
2577
## Copyright License
2678

2779
KeyAuth is licensed under **Elastic License 2.0**

x64/main.cpp

Lines changed: 152 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#include "auth_guard.hpp"
55
#include "lib/WinSecRuntime/WinSecRuntime.h"
66
#include "lib/nigelcrypt/nigelcrypt.hpp"
7-
#include "skStr.h"
87
#include "storage.hpp"
98
#include <chrono>
109
#include <ctime>
@@ -41,9 +40,122 @@ std::string nigel_string(const char* literal, std::string_view aad) {
4140
}
4241
}
4342

43+
std::string nc(const char* literal, std::string_view aad) {
44+
return nigel_string(literal, aad);
45+
}
46+
4447
bool run_runtime_security() {
4548
WinSecRuntime::Policy policy{};
4649
policy.mode = WinSecRuntime::Mode::Aggressive;
50+
secure::runtime::Config cfg{};
51+
52+
cfg.expected_parent_pid = 0;
53+
cfg.expected_image_path = L"...";
54+
cfg.require_same_session = false;
55+
cfg.expected_integrity_rid = 0;
56+
cfg.cmdline_hash_baseline = 0;
57+
cfg.cwd_hash_baseline = 0;
58+
cfg.disallow_unc = false;
59+
cfg.disallow_motw = false;
60+
cfg.cwd_allowlist_hashes = nullptr;
61+
cfg.cwd_allowlist_count = 0;
62+
cfg.image_path_allowlist_hashes = nullptr;
63+
cfg.image_path_allowlist_count = 0;
64+
cfg.enforce_safe_dll_search = false;
65+
cfg.known_dll_hashes = nullptr;
66+
cfg.known_dll_count = 0;
67+
68+
cfg.parent_chain_hashes = nullptr;
69+
cfg.parent_chain_hash_count = 0;
70+
cfg.parent_chain_max_depth = 4;
71+
72+
cfg.module_hashes = nullptr;
73+
cfg.module_hash_count = 0;
74+
75+
cfg.module_whitelist_hashes = nullptr;
76+
cfg.module_whitelist_count = 0;
77+
78+
cfg.module_list_hash_baseline = 0;
79+
cfg.module_count_baseline = 0;
80+
81+
cfg.driver_blacklist_hashes = nullptr;
82+
cfg.driver_blacklist_count = 0;
83+
84+
cfg.exec_private_max_regions = 0;
85+
cfg.enforce_module_path_policy = false;
86+
87+
cfg.process_hashes = nullptr;
88+
cfg.process_hash_count = 0;
89+
90+
cfg.window_hashes = nullptr;
91+
cfg.window_hash_count = 0;
92+
93+
cfg.vm_vendor_hashes = nullptr;
94+
cfg.vm_vendor_hash_count = 0;
95+
cfg.vm_min_cores = 0;
96+
cfg.vm_min_ram_gb = 0;
97+
98+
cfg.iat_baseline = 0;
99+
cfg.import_name_hash_baseline = 0;
100+
cfg.import_module_hash_baseline = 0;
101+
cfg.import_module_count_baseline = 0;
102+
cfg.import_func_count_baseline = 0;
103+
104+
cfg.iat_write_protect = false;
105+
cfg.iat_writable_check = false;
106+
cfg.iat_count_baseline = 0;
107+
cfg.iat_mirror = nullptr;
108+
cfg.iat_mirror_count = 0;
109+
cfg.iat_bounds_check = false;
110+
cfg.iat_require_executable = false;
111+
cfg.iat_disallow_self = false;
112+
113+
cfg.text_sha256_baseline = {};
114+
cfg.text_rolling_crc_baseline = 0;
115+
cfg.text_rolling_crc_window = 64;
116+
cfg.text_rolling_crc_stride = 16;
117+
118+
cfg.text_entropy_min = 0.0;
119+
cfg.text_entropy_max = 0.0;
120+
121+
cfg.text_chunk_seed = 0;
122+
cfg.text_chunk_size = 64;
123+
cfg.text_chunk_count = 32;
124+
cfg.text_chunk_baseline = 0;
125+
126+
cfg.nop_sled_threshold = 0;
127+
cfg.int3_sled_threshold = 0;
128+
129+
cfg.delay_import_name_hash_baseline = 0;
130+
131+
cfg.tls_callback_expected = 0;
132+
cfg.tls_callback_hash_baseline = 0;
133+
134+
cfg.entry_prologue_size = 16;
135+
cfg.entry_prologue_baseline = 0;
136+
137+
cfg.signature_required = false;
138+
139+
cfg.export_name_hash_baseline = 0;
140+
cfg.export_rva_hash_baseline = 0;
141+
cfg.export_name_table_hash_baseline = 0;
142+
cfg.export_ordinal_table_hash_baseline = 0;
143+
cfg.export_count_baseline = 0;
144+
145+
cfg.export_whitelist_hashes = nullptr;
146+
cfg.export_whitelist_count = 0;
147+
148+
cfg.export_blacklist_hashes = nullptr;
149+
cfg.export_blacklist_count = 0;
150+
151+
cfg.exec_private_whitelist = nullptr;
152+
cfg.exec_private_whitelist_count = 0;
153+
154+
cfg.prologue_guards = nullptr;
155+
cfg.prologue_guard_count = 0;
156+
cfg.prologue_jmp_forbidden = false;
157+
158+
policy.cfg = cfg;
47159

48160
if (!WinSecRuntime::Initialize(policy.mode, policy.cfg))
49161
return false;
@@ -115,28 +227,28 @@ void save_or_clear_creds(bool save, const std::string& username, const std::stri
115227
}
116228

117229
void print_user_data(const api& app) {
118-
std::cout << skCrypt("\n User data:");
119-
std::cout << skCrypt("\n Username: ") << app.user_data.username;
120-
std::cout << skCrypt("\n IP address: ") << app.user_data.ip;
121-
std::cout << skCrypt("\n Hardware-Id: ") << app.user_data.hwid;
122-
std::cout << skCrypt("\n Create date: ")
230+
std::cout << nc("\n User data:", "ui:user_data");
231+
std::cout << nc("\n Username: ", "ui:username") << app.user_data.username;
232+
std::cout << nc("\n IP address: ", "ui:ip") << app.user_data.ip;
233+
std::cout << nc("\n Hardware-Id: ", "ui:hwid") << app.user_data.hwid;
234+
std::cout << nc("\n Create date: ", "ui:createdate")
123235
<< tm_to_readable_time(utils::timet_to_tm(utils::string_to_timet(app.user_data.createdate)));
124-
std::cout << skCrypt("\n Last login: ")
236+
std::cout << nc("\n Last login: ", "ui:lastlogin")
125237
<< tm_to_readable_time(utils::timet_to_tm(utils::string_to_timet(app.user_data.lastlogin)));
126-
std::cout << skCrypt("\n Subscription(s): ");
238+
std::cout << nc("\n Subscription(s): ", "ui:subs");
127239

128240
for (size_t i = 0; i < app.user_data.subscriptions.size(); i++) {
129241
const auto& sub = app.user_data.subscriptions.at(i);
130-
std::cout << skCrypt("\n name: ") << sub.name;
131-
std::cout << skCrypt(" : expiry: ")
242+
std::cout << nc("\n name: ", "ui:sub_name") << sub.name;
243+
std::cout << nc(" : expiry: ", "ui:sub_expiry")
132244
<< tm_to_readable_time(utils::timet_to_tm(utils::string_to_timet(sub.expiry)));
133-
std::cout << skCrypt(" (") << remaining_until(sub.expiry) << skCrypt(")");
245+
std::cout << nc(" (", "ui:paren_open") << remaining_until(sub.expiry) << nc(")", "ui:paren_close");
134246
}
135247
}
136248
} // namespace
137249

138-
const std::string compilation_date = (std::string)skCrypt(__DATE__);
139-
const std::string compilation_time = (std::string)skCrypt(__TIME__);
250+
const std::string compilation_date = nigel_string(__DATE__, "build:date");
251+
const std::string compilation_time = nigel_string(__TIME__, "build:time");
140252
void sessionStatus();
141253

142254
static api* g_app = nullptr;
@@ -150,7 +262,7 @@ int main()
150262
nigelcrypt::set_strict_mode(strict);
151263

152264
if (!run_runtime_security()) {
153-
std::cout << skCrypt("\n\n Security checks failed.");
265+
std::cout << nc("\n\n Security checks failed.", "ui:sec_fail");
154266
Sleep(1500);
155267
return 1;
156268
}
@@ -166,14 +278,14 @@ int main()
166278
api app_instance(name, ownerid, version, url, path);
167279
g_app = &app_instance;
168280

169-
std::string consoleTitle = skCrypt("Loader - Built at: ").decrypt() + compilation_date + " " + compilation_time;
281+
std::string consoleTitle = nc("Loader - Built at: ", "ui:title") + compilation_date + " " + compilation_time;
170282
SetConsoleTitleA(consoleTitle.c_str());
171-
std::cout << skCrypt("\n\n Connecting..");
283+
std::cout << nc("\n\n Connecting..", "ui:connecting");
172284

173285
app_instance.init();
174286
if (!app_instance.response.success)
175287
{
176-
std::cout << skCrypt("\n Status: ") << app_instance.response.message;
288+
std::cout << nc("\n Status: ", "ui:status") << app_instance.response.message;
177289
app_instance.init_fail_delay();
178290
exit(1);
179291
}
@@ -186,8 +298,8 @@ int main()
186298
wipe_string(path);
187299

188300
if (api::lockout_active(login_guard)) {
189-
std::cout << skCrypt("\n Status: Too many attempts. Try again in ")
190-
<< api::lockout_remaining_ms(login_guard) << skCrypt(" ms.");
301+
std::cout << nc("\n Status: Too many attempts. Try again in ", "ui:lockout")
302+
<< api::lockout_remaining_ms(login_guard) << nc(" ms.", "ui:ms");
191303
app_instance.close_delay();
192304
return 0;
193305
}
@@ -201,48 +313,48 @@ int main()
201313

202314
if (!used_saved_creds)
203315
{
204-
std::cout << skCrypt("\n\n [1] Login\n [2] Register\n [3] Upgrade\n [4] License key only\n\n Choose option: ");
316+
std::cout << nc("\n\n [1] Login\n [2] Register\n [3] Upgrade\n [4] License key only\n\n Choose option: ", "ui:menu");
205317

206318
int option = 0;
207319
if (!read_int(option))
208320
{
209-
std::cout << skCrypt("\n\n Status: Failure: Invalid Selection");
321+
std::cout << nc("\n\n Status: Failure: Invalid Selection", "ui:bad_selection");
210322
app_instance.bad_input_delay();
211323
exit(1);
212324
}
213325

214326
switch (option)
215327
{
216328
case 1:
217-
std::cout << skCrypt("\n\n Enter username: ");
329+
std::cout << nc("\n\n Enter username: ", "ui:enter_user");
218330
std::cin >> username;
219-
std::cout << skCrypt("\n Enter password: ");
331+
std::cout << nc("\n Enter password: ", "ui:enter_pass");
220332
std::cin >> password;
221333
app_instance.login(username, password, "");
222334
break;
223335
case 2:
224-
std::cout << skCrypt("\n\n Enter username: ");
336+
std::cout << nc("\n\n Enter username: ", "ui:enter_user");
225337
std::cin >> username;
226-
std::cout << skCrypt("\n Enter password: ");
338+
std::cout << nc("\n Enter password: ", "ui:enter_pass");
227339
std::cin >> password;
228-
std::cout << skCrypt("\n Enter license: ");
340+
std::cout << nc("\n Enter license: ", "ui:enter_license");
229341
std::cin >> key;
230342
app_instance.regstr(username, password, key);
231343
break;
232344
case 3:
233-
std::cout << skCrypt("\n\n Enter username: ");
345+
std::cout << nc("\n\n Enter username: ", "ui:enter_user");
234346
std::cin >> username;
235-
std::cout << skCrypt("\n Enter license: ");
347+
std::cout << nc("\n Enter license: ", "ui:enter_license");
236348
std::cin >> key;
237349
app_instance.upgrade(username, key);
238350
break;
239351
case 4:
240-
std::cout << skCrypt("\n Enter license: ");
352+
std::cout << nc("\n Enter license: ", "ui:enter_license");
241353
std::cin >> key;
242354
app_instance.license(key, "");
243355
break;
244356
default:
245-
std::cout << skCrypt("\n\n Status: Failure: Invalid Selection");
357+
std::cout << nc("\n\n Status: Failure: Invalid Selection", "ui:bad_selection");
246358
app_instance.bad_input_delay();
247359
exit(1);
248360
}
@@ -253,30 +365,31 @@ int main()
253365

254366
if (!app_instance.response.success)
255367
{
256-
if (app_instance.response.message == "2FA code required.") {
368+
const std::string twofa_msg = nc("2FA code required.", "msg:2fa_required");
369+
if (app_instance.response.message == twofa_msg) {
257370
if (username.empty() || password.empty()) {
258-
std::cout << skCrypt("\n Your account has 2FA enabled, please enter 6-digit code:");
371+
std::cout << nc("\n Your account has 2FA enabled, please enter 6-digit code:", "ui:2fa_prompt");
259372
std::cin >> TfaCode;
260373
app_instance.license(key, TfaCode);
261374
}
262375
else {
263-
std::cout << skCrypt("\n Your account has 2FA enabled, please enter 6-digit code:");
376+
std::cout << nc("\n Your account has 2FA enabled, please enter 6-digit code:", "ui:2fa_prompt");
264377
std::cin >> TfaCode;
265378
app_instance.login(username, password, TfaCode);
266379
}
267380

268381
if (app_instance.response.message.empty())
269382
exit(11);
270383
if (!app_instance.response.success) {
271-
std::cout << skCrypt("\n Status: ") << app_instance.response.message;
384+
std::cout << nc("\n Status: ", "ui:status") << app_instance.response.message;
272385
std::remove(api::kSavePath);
273386
api::record_login_fail(login_guard);
274387
app_instance.init_fail_delay();
275388
exit(1);
276389
}
277390
}
278391
else {
279-
std::cout << skCrypt("\n Status: ") << app_instance.response.message;
392+
std::cout << nc("\n Status: ", "ui:status") << app_instance.response.message;
280393
std::remove(api::kSavePath);
281394
api::record_login_fail(login_guard);
282395
app_instance.init_fail_delay();
@@ -285,12 +398,12 @@ int main()
285398
}
286399
api::reset_lockout(login_guard);
287400

288-
std::cout << skCrypt("\n\n Save credentials to disk for auto-login? [y/N]: ");
401+
std::cout << nc("\n\n Save credentials to disk for auto-login? [y/N]: ", "ui:save_creds");
289402
const char save_choice = read_choice('n'); // read once to avoid double input. -nigel
290403
const bool save_creds = (save_choice == 'y' || save_choice == 'Y');
291404
save_or_clear_creds(save_creds, username, password, key);
292405
if (save_creds)
293-
std::cout << skCrypt("Successfully Created File For Auto Login");
406+
std::cout << nc("Successfully Created File For Auto Login", "ui:save_ok");
294407

295408
/*
296409
* Do NOT remove this checkAuthenticated() function.
@@ -315,8 +428,8 @@ int main()
315428

316429
print_user_data(app_instance);
317430

318-
std::cout << skCrypt("\n\n Status: ") << app_instance.response.message;
319-
std::cout << skCrypt("\n\n Closing in five seconds...");
431+
std::cout << nc("\n\n Status: ", "ui:status") << app_instance.response.message;
432+
std::cout << nc("\n\n Closing in five seconds...", "ui:closing");
320433
app_instance.close_delay();
321434

322435
return 0;

0 commit comments

Comments
 (0)