nx_iot_local_kasa_test.nx source
↩ module page · 147 lines · 6735 B
1// nx_iot_local_kasa_test.nx -- gate for the TP-Link Kasa LAN driver.
2//
3// SOVEREIGN proof (this is a NON-NOVEL protocol; the matching 3rd-party
4// python-kasa KAT cross-check runs in the harness later, never here):
5// - autokey hand-derived KAT: encrypt "{}" -> [0xD0, 0xAD]
6// (0x7B^0xAB=0xD0, then 0x7D^0xD0=0xAD -- derivable from the spec)
7// - autokey round-trip identity over a real command
8// - TCP frame: 4-byte BE length round-trip + decrypt recovers payload
9// - capability builders carry hue/saturation/brightness/color_temp
10// - reply classifier marker detection
11// - negative controls: BUF_TOO_SMALL + BAD_LEN
12//
13// expect_exit: 0
14//
15// license_tier: ORIGINAL
16
17import "nx_syscalls_x86_64.nx"
18import "nx_iot_local_kasa.nx"
19
20func main() -> i64 {
21 // ---- verdict validity gate -------------------------------------
22 if nx_iot_kasa_verdict_is_valid(NX_IOT_KASA_VERDICT_OK) != 1 { return 1 }
23 if nx_iot_kasa_verdict_is_valid(NX_IOT_KASA_VERDICT_BAD_LEN) != 1 { return 2 }
24 if nx_iot_kasa_verdict_is_valid(NX_IOT_KASA_VERDICT_N) != 0 { return 3 }
25 if nx_iot_kasa_verdict_is_valid(-1) != 0 { return 4 }
26
27 let a: *u8 = sys_mmap(256)
28 let b: *u8 = sys_mmap(256)
29 let c: *u8 = sys_mmap(256)
30 let qv: *i64 = sys_mmap(8) as *i64
31
32 // ---- autokey KAT: encrypt "{}" -> [0xD0, 0xAD] -----------------
33 nx_iot_kasa_encrypt("{}", 2, a)
34 if (a[0] as i64) != 0xD0 { return 10 }
35 if (a[1] as i64) != 0xAD { return 11 }
36 nx_iot_kasa_decrypt(a, 2, b)
37 if (b[0] as i64) != 0x7B { return 12 }
38 if (b[1] as i64) != 0x7D { return 13 }
39 nx_iot_kasa_encrypt("{", 1, a)
40 if (a[0] as i64) != 0xD0 { return 14 }
41
42 // ---- relay builder: {"system":{"set_relay_state":{"state":1}}} -
43 let rl: i64 = nx_iot_kasa_pt_relay(a, 256, 1)
44 if rl <= 0 { return 20 }
45 if (a[0] as i64) != 0x7B { return 21 }
46 if (a[rl - 1] as i64) != 0x7D { return 22 }
47 if (a[rl - 2] as i64) != 0x7D { return 23 }
48 if (a[rl - 3] as i64) != 0x7D { return 24 }
49 if (a[rl - 4] as i64) != 0x31 { return 25 }
50 if nx_iot_kasa_find(a, rl, "set_relay_state", 15) < 0 { return 26 }
51 if nx_iot_kasa_find(a, rl, "system", 6) < 0 { return 27 }
52 let rl0: i64 = nx_iot_kasa_pt_relay(b, 256, 0)
53 if (b[rl0 - 4] as i64) != 0x30 { return 28 }
54
55 // ---- relay round-trip through autokey --------------------------
56 nx_iot_kasa_encrypt(a, rl, c)
57 nx_iot_kasa_decrypt(c, rl, b)
58 var i: i64 = 0
59 while i < rl {
60 if (b[i] as i64) != (a[i] as i64) { return 30 }
61 i = i + 1
62 }
63
64 // ---- TCP frame round-trip --------------------------------------
65 let fl: i64 = nx_iot_kasa_frame_tcp(a, rl, c, 256, qv)
66 if qv[0] != NX_IOT_KASA_VERDICT_OK { return 40 }
67 if fl != rl + 4 { return 41 }
68 if nx_iot_kasa_get_u32_be(c, 0) != rl { return 42 }
69 let dl: i64 = nx_iot_kasa_deframe_tcp(c, fl, b, 256, qv)
70 if qv[0] != NX_IOT_KASA_VERDICT_OK { return 43 }
71 if dl != rl { return 44 }
72 var k: i64 = 0
73 while k < rl {
74 if (b[k] as i64) != (a[k] as i64) { return 45 }
75 k = k + 1
76 }
77
78 // ---- sysinfo probe builder -------------------------------------
79 let sl: i64 = nx_iot_kasa_pt_sysinfo(a, 256)
80 if sl <= 0 { return 50 }
81 if (a[0] as i64) != 0x7B { return 51 }
82 if nx_iot_kasa_find(a, sl, "get_sysinfo", 11) < 0 { return 52 }
83
84 // ---- colour-bulb capability builder ----------------------------
85 let ll: i64 = nx_iot_kasa_pt_light(a, 256, 1, 120, 100, 75, 0)
86 if ll <= 0 { return 60 }
87 if (a[0] as i64) != 0x7B { return 61 }
88 if (a[ll - 1] as i64) != 0x7D { return 62 }
89 if nx_iot_kasa_find(a, ll, "hue", 3) < 0 { return 63 }
90 if nx_iot_kasa_find(a, ll, "saturation", 10) < 0 { return 64 }
91 if nx_iot_kasa_find(a, ll, "brightness", 10) < 0 { return 65 }
92 if nx_iot_kasa_find(a, ll, "color_temp", 10) < 0 { return 66 }
93 if nx_iot_kasa_find(a, ll, "transition_light_state", 22) < 0 { return 67 }
94 let fl2: i64 = nx_iot_kasa_frame_tcp(a, ll, c, 256, qv)
95 if qv[0] != NX_IOT_KASA_VERDICT_OK { return 68 }
96 let dl2: i64 = nx_iot_kasa_deframe_tcp(c, fl2, b, 256, qv)
97 if dl2 != ll { return 69 }
98
99 // ---- reply classifier ------------------------------------------
100 if nx_iot_kasa_looks_like_sysinfo("xx sw_ver yy", 12) != 1 { return 70 }
101 if nx_iot_kasa_looks_like_sysinfo("hello world!", 12) != 0 { return 71 }
102
103 // ---- negative controls -----------------------------------------
104 if nx_iot_kasa_frame_tcp(a, rl, c, rl + 2, qv) != -1 { return 80 }
105 if qv[0] != NX_IOT_KASA_VERDICT_BUF_TOO_SMALL { return 81 }
106 if nx_iot_kasa_deframe_tcp(c, 3, b, 256, qv) != -1 { return 82 }
107 if qv[0] != NX_IOT_KASA_VERDICT_BAD_LEN { return 83 }
108 nx_iot_kasa_put_u32_be(c, 0, 9999)
109 if nx_iot_kasa_deframe_tcp(c, 8, b, 256, qv) != -1 { return 84 }
110 if qv[0] != NX_IOT_KASA_VERDICT_BAD_LEN { return 85 }
111
112 // ---- SoftAP creds-push builder (the provisioning PUSH_CREDS payload) -
113 let pl: i64 = nx_iot_kasa_pt_set_stainfo(a, 256, "HomeNet", 7, "s3cr3t", 6, 3)
114 if pl <= 0 { return 90 }
115 if (a[0] as i64) != 0x7B { return 91 } // {
116 if (a[pl - 1] as i64) != 0x7D { return 92 } // }
117 if nx_iot_kasa_find(a, pl, "set_stainfo", 11) < 0 { return 93 }
118 if nx_iot_kasa_find(a, pl, "netif", 5) < 0 { return 94 }
119 if nx_iot_kasa_find(a, pl, "HomeNet", 7) < 0 { return 95 }
120 if nx_iot_kasa_find(a, pl, "s3cr3t", 6) < 0 { return 96 }
121 if nx_iot_kasa_find(a, pl, "key_type", 8) < 0 { return 97 }
122 // round-trips through autokey + TCP frame like every other command
123 let fl3: i64 = nx_iot_kasa_frame_tcp(a, pl, c, 256, qv)
124 if qv[0] != NX_IOT_KASA_VERDICT_OK { return 98 }
125 let dl3: i64 = nx_iot_kasa_deframe_tcp(c, fl3, b, 256, qv)
126 if dl3 != pl { return 99 }
127 var z: i64 = 0
128 while z < pl {
129 if (b[z] as i64) != (a[z] as i64) { return 100 }
130 z = z + 1
131 }
132
133 // ---- JSON-escaping closes the injection hazard -----------------
134 // ssid carries a quote, password a backslash -> both must be escaped.
135 let pe: i64 = nx_iot_kasa_pt_set_stainfo(a, 256, "ab\"cd", 5, "p\\q", 3, 3)
136 if pe <= 0 { return 101 }
137 if nx_iot_kasa_find(a, pe, "ab\\\"cd", 6) < 0 { return 102 } // a b \ " c d
138 if nx_iot_kasa_find(a, pe, "p\\\\q", 4) < 0 { return 103 } // p \ \ q
139 let fl4: i64 = nx_iot_kasa_frame_tcp(a, pe, c, 256, qv) // still framable
140 if qv[0] != NX_IOT_KASA_VERDICT_OK { return 104 }
141
142 // ---- buffer-too-small + bad-length guards ----------------------
143 if nx_iot_kasa_pt_set_stainfo(a, 8, "HomeNet", 7, "s3cr3t", 6, 3) != -1 { return 105 }
144 if nx_iot_kasa_pt_set_stainfo(a, 256, "x", -1, "y", 1, 3) != -1 { return 106 }
145
146 return 0
147}