nx_ssh_client.nx source
↩ module page · 89 lines · 4471 B
1// nx_ssh_client.nx -- sovereign SSH-2 client, end to end: KEX -> key
2// derivation -> chacha20-poly1305@openssh.com transport -> userauth
3// (publickey, Ed25519, our own identity) -> session channel + exec. All
4// bits-up on our own crypto; proven phase by phase against the real NAS
5// OpenSSH server. This is the remote-access substrate for managing the
6// fleet (and the path to deploying the sites TCP_NODELAY fix).
7//
8// Identity: a persistent Ed25519 seed at ~/.nishi/nx_ssh_id (generated once).
9// Its authorized_keys line is printed so it can be authorized on the NAS.
10//
11// license_tier: ORIGINAL
12
13import "nx_syscalls.nx"
14import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
15import "nx_ssh_lib.nx"
16
17func main() -> i64 {
18 // ---- sovereign identity (persistent Ed25519 seed) ----
19 let seed: *u8 = sys_mmap(32); let pub: *u8 = sys_mmap(32)
20 let idpath: *u8 = "/mnt/c/Users/elder/.nishi/nx_ssh_id" as *u8
21 let lenbox: *i64 = sys_mmap(16) as *i64
22 let data: *u8 = sys_read_file(idpath, lenbox)
23 var have_id: i64 = 0
24 if (data as i64) != 0 { if lenbox[0] == 32 { ssh_put_bytes(seed, 0, data, 32); have_id = 1 } }
25 if have_id == 0 {
26 rand_bytes(seed, 32)
27 let wfd: i64 = sys_openat_wr(idpath, 384) // 0600
28 if wfd >= 0 { sys_write(wfd, seed, 32); sys_close(wfd) }
29 }
30 ed25519_pub_from_priv(seed, pub)
31 ssh_puts("Nishi SSH identity -- authorize on the NAS (~/.ssh/authorized_keys):\n" as *u8)
32 ssh_print_authkeys(pub)
33 ssh_puts("\n" as *u8)
34
35 // ---- connect + KEX ----
36 let st: *SshState = sys_mmap(SSH_STATE_BYTES) as *SshState
37 let fd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
38 if fd < 0 { ssh_puts("socket fail\n" as *u8); return 1 }
39 let sa: *u8 = sys_mmap(16); ssh_sockaddr(sa, (192 << 24) | (168 << 16) | (8 << 8) | 240, 22)
40 if nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS) != 0 { ssh_puts("connect fail (.8.240:22)\n" as *u8); return 2 }
41 st.fd = fd
42 if ssh_kex(st) != 0 { ssh_puts("KEX failed\n" as *u8); return 3 }
43 if st.verified != 1 { ssh_puts("host signature NOT verified\n" as *u8); return 4 }
44 ssh_puts("[1] KEX ok, host Ed25519 sig verified.\n" as *u8)
45
46 // ---- NEWKEYS + encrypted transport ----
47 let nk: *u8 = sys_mmap(64)
48 if ssh_read_packet(fd, nk) < 0 { ssh_puts("no server NEWKEYS\n" as *u8); return 5 }
49 if nk[0] != 21 as u8 { ssh_puts("expected NEWKEYS\n" as *u8); return 6 }
50 let mynk: *u8 = sys_mmap(8); mynk[0] = 21 as u8
51 ssh_send_packet(fd, mynk, 1)
52 ssh_derive_session(st)
53 st.seq_c2s = 3; st.seq_s2c = 3
54 ssh_puts("[2] NEWKEYS + session keys -- encrypted transport up.\n" as *u8)
55
56 // ---- SERVICE_REQUEST(ssh-userauth) ----
57 let sr: *u8 = sys_mmap(64); var m: i64 = 0
58 m = ssh_put_byte(sr, m, 5); m = ssh_put_str(sr, m, "ssh-userauth" as *u8, 12)
59 ssh_enc_send(st, sr, m)
60 let rep: *u8 = sys_mmap(512)
61 if ssh_enc_recv(st, rep) < 0 { ssh_puts("SERVICE recv error\n" as *u8); return 7 }
62 if rep[0] != 6 as u8 { ssh_puts("no SERVICE_ACCEPT\n" as *u8); return 8 }
63 ssh_puts("[3] SERVICE_ACCEPT (ssh-userauth).\n" as *u8)
64
65 // ---- userauth "none" probe (shows accepted methods) ----
66 let ua: *u8 = sys_mmap(128); var n: i64 = 0
67 n = ssh_put_byte(ua, n, 50); n = ssh_put_str(ua, n, "elderwesto" as *u8, 10)
68 n = ssh_put_str(ua, n, "ssh-connection" as *u8, 14); n = ssh_put_str(ua, n, "none" as *u8, 4)
69 ssh_enc_send(st, ua, n)
70 let ur: *u8 = sys_mmap(512)
71 if ssh_enc_recv(st, ur) < 0 { ssh_puts("userauth recv error\n" as *u8); return 9 }
72 if ur[0] == 51 as u8 {
73 let mlen: i64 = ssh_u32be(ur, 1)
74 ssh_puts("[4] server accepts: " as *u8); sys_write(1, (ur + 5) as *u8, mlen); ssh_puts("\n" as *u8)
75 }
76
77 // ---- userauth publickey (Ed25519, our identity) ----
78 let ok: i64 = ssh_userauth_publickey(st, "elderwesto" as *u8, 10, seed, pub)
79 if ok == 1 {
80 ssh_puts("[5] USERAUTH_SUCCESS (publickey/ed25519) -- authenticated 1:1, sovereign.\n" as *u8)
81 ssh_puts("[6] exec on NAS:\n--------\n" as *u8)
82 ssh_exec(st, "uname -a; id; echo NISHI_SSH_OK" as *u8, 31)
83 ssh_puts("--------\n" as *u8)
84 sys_close(fd); return 0
85 }
86 ssh_puts("[5] publickey auth FAILED -- our key is not yet in the NAS authorized_keys.\n" as *u8)
87 ssh_puts(" Add the 'ssh-ed25519 ... nishi-ssh' line above to the NAS, then re-run.\n" as *u8)
88 sys_close(fd); return 0
89}