code wiki / (root) / nx_ssh_client.nx

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}