code wiki / _hdl_build / nx_cms_mfa_gate.nx
nx_cms_mfa_gate.nx source
↩ module page · 45 lines · 2548 B
1// nx_cms_mfa_gate.nx -- INTEGRATION gate for the #12 MFA wiring in nx_cms_admin. Drives the REAL ca_mfa_ok
2// (the function the /admin/login handler calls after the argon2id password check) over a constructed POST
3// body + base32 secret: not-enrolled=pass-through, enrolled+valid=accept, enrolled+wrong/missing=reject,
4// enrolled+adjacent-step=accept (skew). exit 0 = pass, N = assertion N failed.
5import "nx_cms_admin.nx" // ca_mfa_ok, ca_cat + (transitively) nx_mfa: base32_encode/hotp_sha1_value/totp_sha1_render
6import "nx_assert.nx"
7
8// build "code=<6 ascii digits>" into buf -> returns length.
9func g_codebody(buf: *u8, code6: *u8) -> i64 {
10 var n: i64 = ca_cat(buf, 0, "code=" as *u8)
11 var i: i64 = 0; while i < 6 { buf[n] = code6[i]; n = n + 1; i = i + 1 }
12 return n
13}
14
15func main() -> i64 {
16 let seed: *u8 = "12345678901234567890" as *u8
17 let b32: *u8 = sys_mmap(64); let b32len: i64 = base32_encode(seed, 20, b32)
18 let now: i64 = 1111111111
19 let step: i64 = now / 30
20
21 let cur: *u8 = sys_mmap(16); totp_sha1_render(hotp_sha1_value(seed, 20, step, 6), 6, cur)
22 let m1: *u8 = sys_mmap(16); totp_sha1_render(hotp_sha1_value(seed, 20, step - 1, 6), 6, m1)
23
24 let okbody: *u8 = sys_mmap(64); let okn: i64 = g_codebody(okbody, cur)
25 let m1body: *u8 = sys_mmap(64); let m1n: i64 = g_codebody(m1body, m1)
26 let badbody: *u8 = sys_mmap(64); let badn: i64 = ca_cat(badbody, 0, "code=000000" as *u8)
27 let nobody: *u8 = sys_mmap(64); let non: i64 = ca_cat(nobody, 0, "other=x" as *u8)
28
29 nx_puts_err("login code="); sys_write(2, cur, 6); nx_puts_err("\n" as *u8)
30
31 // not enrolled (seclen 0) -> pass-through regardless of body (password-only, graceful)
32 if ca_mfa_ok(okbody, okn, b32, 0, now) != 1 { return 1 }
33 // enrolled + valid current code -> accept
34 if ca_mfa_ok(okbody, okn, b32, b32len, now) != 1 { return 2 }
35 // enrolled + adjacent-step code -> accept (clock skew)
36 if ca_mfa_ok(m1body, m1n, b32, b32len, now) != 1 { return 3 }
37 // enrolled + wrong code -> reject
38 if ca_mfa_ok(badbody, badn, b32, b32len, now) != 0 { return 4 }
39 // enrolled + NO code field -> reject (password alone is not enough)
40 if ca_mfa_ok(nobody, non, b32, b32len, now) != 0 { return 5 }
41
42 nx_puts_err("MFA wiring: not-enrolled=pass, valid=accept, skew=accept, wrong=reject, missing=reject\n" as *u8)
43 nx_puts_err("nx_cms_mfa_gate verdict=GREEN pass=5 (TOTP second factor LIVE in /admin/login; password alone insufficient when enrolled)\n" as *u8)
44 return 0
45}