commit ef262ddf1e104b58cb9427516035feffcd33ba1a
parent c1445fabb4514cbd12295018fea538bb588f6c86
Author: erai <erai@omiltem.net>
Date: Mon, 3 Jun 2024 00:41:57 -0400
ssh key exchange
Diffstat:
M | build.sh | | | 3 | ++- |
M | sshd.c | | | 817 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
2 files changed, 755 insertions(+), 65 deletions(-)
diff --git a/build.sh b/build.sh
@@ -1,6 +1,7 @@
#!/sh
LIBS="bufio.c lib.c alloc.c syscall.c"
+CRYPTO="ed25519.c sha512.c sha256.c chacha20.c poly1305.c"
CC="cc1.c type.c parse1.c lex1.c as.c"
GENLEX="genlex.c"
BOOT="pxe.asm"
@@ -25,7 +26,7 @@ ALL="${LIBS} ${CC} ${GENLEX} ${BOOT} ${SSHD} ${KERNEL} ${SHELL} ${BIN}"
./cc2 ${LIBS} xxd.c -o xxd
./cc2 ${LIBS} cpio.c -o cpio
./cc2 ${LIBS} sh.c -o sh
-./cc2 ${LIBS} sshd.c -o sshd
+./cc2 ${LIBS} ${CRYPTO} sshd.c -o sshd
./cc2 ${LIBS} vi.c -o vi
for name in ${ALL}; do echo ${name}; done | ./cpio -o > initramfs
diff --git a/sshd.c b/sshd.c
@@ -1,66 +1,3 @@
-// authentication -> ssh-ed25519
-// key exchange -> curve25519-sha256
-// cipher -> chacha20-poly1305@openssh.com
-// mac -> none
-
-//<-
-//SSH-2.0-omiltem<CR><LF>
-
-//->
-//SSH-2.0-VERSION<CR><LF>
-
-//->
-//byte SSH_MSG_KEXINIT
-//byte[16] cookie (random bytes)
-//name-list kex_algorithms
-//name-list server_host_key_algorithms
-//name-list encryption_algorithms_client_to_server
-//name-list encryption_algorithms_server_to_client
-//name-list mac_algorithms_client_to_server
-//name-list mac_algorithms_server_to_client
-//name-list compression_algorithms_client_to_server
-//name-list compression_algorithms_server_to_client
-//name-list languages_client_to_server
-//name-list languages_server_to_client
-//boolean first_kex_packet_follows
-//uint32 0 (reserved for future extension)
-
-//<-
-//byte SSH_MSG_KEXINIT
-//byte[16] cookie (random bytes)
-//name-list kex_algorithms
-//name-list server_host_key_algorithms
-//name-list encryption_algorithms_client_to_server
-//name-list encryption_algorithms_server_to_client
-//name-list mac_algorithms_client_to_server
-//name-list mac_algorithms_server_to_client
-//name-list compression_algorithms_client_to_server
-//name-list compression_algorithms_server_to_client
-//name-list languages_client_to_server
-//name-list languages_server_to_client
-//boolean first_kex_packet_follows
-//uint32 0 (reserved for future extension)
-
-//->
-//byte SSH_MSG_KEX_ECDH_INIT
-//string Q_C, client's ephemeral public key octet string
-
-//<-
-//byte SSH_MSG_KEX_ECDH_REPLY
-//string K_S, server's public host key
-//string Q_S, server's ephemeral public key octet string
-//string the signature on the exchange hash
-
-//signature of
-//string V_C, client's identification string (CR and LF excluded)
-//string V_S, server's identification string (CR and LF excluded)
-//string I_C, payload of the client's SSH_MSG_KEXINIT
-//string I_S, payload of the server's SSH_MSG_KEXINIT
-//string K_S, server's public host key
-//string Q_C, client's ephemeral public key octet string
-//string Q_S, server's ephemeral public key octet string
-//mpint K, shared secret
-
//->
//byte SSH_MSG_USERAUTH_REQUEST
//string user name in ISO-10646 UTF-8 encoding [RFC3629]
@@ -98,7 +35,759 @@
//<-
//SSH_MSG_USERAUTH_PK_OK
+read_line(fd: int, buf: *byte, max: int): int {
+ var len: int;
+ var c: int;
+ len = 0;
+ loop {
+ if len == max {
+ return 0;
+ }
+
+ c = fdgetc(fd);
+ if c == '\n' {
+ buf[len] = 0:byte;
+ return len;
+ }
+
+ if c == -1 {
+ return 0;
+ }
+
+ if c == '\r' {
+ continue;
+ }
+
+ buf[len] = c:byte;
+ len = len + 1;
+ }
+}
+
+writeall(fd: int, buf: *byte, n: int): int {
+ var ret: int;
+ loop {
+ if n == 0 {
+ return 0;
+ }
+
+ ret = write(fd, buf, n);
+ if ret < 0 {
+ return ret;
+ }
+
+ buf = &buf[ret];
+ n = n - ret;
+ }
+}
+
+read_fill(fd: int, buf: *byte, n: int): int {
+ var ret: int;
+ loop {
+ if n == 0 {
+ return 0;
+ }
+
+ ret = read(fd, buf, n);
+ if ret <= 0 {
+ return -1;
+ }
+
+ buf = &buf[ret];
+ n = n - ret;
+ }
+}
+
_rdrand(): int;
+read_rand(buf: *byte, n: int) {
+ var i: int;
+ i = 0;
+ loop {
+ if i == n {
+ break;
+ }
+ buf[i] = _rdrand():byte;
+ i = i + 1;
+ }
+}
+
+read_frame(ctx: *sshd_ctx) {
+ var len: int;
+ var padlen: int;
+ var min_size: int;
+ var align: int;
+
+ min_size = ctx.blksize;
+ align = ctx.blksize;
+
+ if min_size < 16 {
+ min_size = 16;
+ }
+
+ if align < 8 {
+ align = 8;
+ }
+
+ if read_fill(ctx.fd, ctx.buf, 5) != 0 {
+ exit(1);
+ }
+
+ // todo decrypt
+
+ len = ctx.buf[3]:int
+ | (ctx.buf[2]:int << 8)
+ | (ctx.buf[1]:int << 16)
+ | (ctx.buf[0]:int << 24);
+
+ padlen = ctx.buf[4]:int;
+
+ if len > ctx.bufsz - 4 {
+ exit(1);
+ }
+
+ if len < 1 || padlen > len - 1 {
+ exit(1);
+ }
+
+ if padlen < 4 {
+ exit(1);
+ }
+
+ if len < min_size - 4 {
+ exit(1);
+ }
+
+ if (len + 4) % align != 0 {
+ exit(1);
+ }
+
+ if read_fill(ctx.fd, &ctx.buf[5], len - 1) != 0 {
+ exit(1);
+ }
+
+ // todo decrypt + mac
+
+ ctx.frame = &ctx.buf[5];
+ ctx.framelen = len - padlen - 1;
+ ctx.index = 0;
+}
+
+write_frame(ctx: *sshd_ctx) {
+ var len: int;
+ var padlen: int;
+ var min_size: int;
+ var align: int;
+
+ align = ctx.blksize;
+ min_size = ctx.blksize;
+
+ if align < 8 {
+ align = 8;
+ }
+
+ if min_size < 16 {
+ min_size = 16;
+ }
+
+ padlen = align - ((ctx.framelen + 5) % align);
+ if padlen < 4 {
+ padlen = padlen + align;
+ }
+
+ if ctx.framelen + padlen + 5 < min_size {
+ padlen = padlen + align;
+ }
+
+ if padlen > ctx.bufsz - 5 - ctx.framelen {
+ exit(1);
+ }
+
+ if padlen > 255 {
+ exit(1);
+ }
+
+ len = padlen + ctx.framelen + 1;
+
+ ctx.frame = &ctx.frame[-5];
+ ctx.frame[0] = (len >> 24):byte;
+ ctx.frame[1] = (len >> 16):byte;
+ ctx.frame[2] = (len >> 8):byte;
+ ctx.frame[3] = len:byte;
+ ctx.frame[4] = padlen:byte;
+ read_rand(&ctx.frame[ctx.framelen + 5], padlen);
+ ctx.framelen = ctx.framelen + 5 + padlen;
+
+ // todo encrypt + mac
+
+ if writeall(ctx.fd, ctx.frame, len + 4) != 0 {
+ exit(1);
+ }
+}
+
+enum {
+ SSH_MSG_KEXINIT = 0x14,
+ SSH_MSG_NEWKEYS = 0x15,
+ SSH_MSG_ECDH_INIT = 0x1e,
+ SSH_MSG_ECDH_REPLY = 0x1f,
+}
+
+doversion(ctx: *sshd_ctx) {
+ var ident: *byte;
+ var n: int;
+
+ //SSH-2.0-VERSION<CR><LF>
+ n = read_line(ctx.fd, ctx.buf, ctx.bufsz);
+ if n < 8 || memcmp(ctx.buf, "SSH-2.0-", 8) != 0 {
+ exit(1);
+ }
+
+ ctx.cver = alloc(ctx.a, n + 1);
+ memcpy(ctx.cver, ctx.buf, n);
+ ctx.cver[n] = 0:byte;
+
+ if writeall(ctx.fd, ctx.sver, strlen(ctx.sver)) != 0 {
+ exit(1);
+ }
+
+ if writeall(ctx.fd, "\r\n", 2) != 0 {
+ exit(1);
+ }
+}
+
+struct ssh_str {
+ len: int;
+ s: *byte;
+}
+
+struct ssh_kexinit {
+ cookie: ssh_str;
+ kex_algorithms: ssh_str;
+ server_host_key_algorithms: ssh_str;
+ encryption_algorithms_client_to_server: ssh_str;
+ encryption_algorithms_server_to_client: ssh_str;
+ mac_algorithms_client_to_server: ssh_str;
+ mac_algorithms_server_to_client: ssh_str;
+ compression_algorithms_client_to_server: ssh_str;
+ compression_algorithms_server_to_client: ssh_str;
+ languages_client_to_server: ssh_str;
+ languages_server_to_client: ssh_str;
+ first_kex_packet_follows: int;
+ reserved: int;
+}
+
+decode_u8(s: *int, ctx: *sshd_ctx) {
+ if ctx.framelen < 1 || ctx.index > ctx.framelen - 1 {
+ exit(1);
+ }
+ *s = ctx.frame[ctx.index]:int;
+ ctx.index = ctx.index + 1;
+}
+
+decode_u32(s: *int, ctx: *sshd_ctx) {
+ if ctx.framelen < 4 || ctx.index > ctx.framelen - 4 {
+ exit(1);
+ }
+ *s = (ctx.frame[ctx.index]:int << 24)
+ | (ctx.frame[ctx.index + 1]: int << 16)
+ | (ctx.frame[ctx.index + 2]: int << 8)
+ | ctx.frame[ctx.index + 3]: int;
+ ctx.index = ctx.index + 4;
+}
+
+decode_str(s: *ssh_str, ctx: *sshd_ctx) {
+ decode_u32(&s.len, ctx);
+ if s.len < 0 || ctx.framelen < s.len || ctx.index > ctx.framelen - s.len {
+ exit(1);
+ }
+ s.s = &ctx.frame[ctx.index];
+ ctx.index = ctx.index + s.len;
+}
+
+decode_cookie(s: *ssh_str, ctx: *sshd_ctx) {
+ if ctx.framelen < 16 || ctx.index > ctx.framelen - 16 {
+ exit(1);
+ }
+ s.s = &ctx.frame[ctx.index];
+ s.len = 16;
+ ctx.index = ctx.index + 16;
+}
+
+decode_bool(s: *int, ctx: *sshd_ctx) {
+ decode_u8(s, ctx);
+ *s = (*s != 0);
+}
+
+decode_kexinit(kex: *ssh_kexinit, ctx: *sshd_ctx) {
+ var tag: int;
+ decode_u8(&tag, ctx);
+ if tag != SSH_MSG_KEXINIT {
+ exit(1);
+ }
+ decode_cookie(&kex.cookie, ctx);
+ decode_str(&kex.kex_algorithms, ctx);
+ decode_str(&kex.server_host_key_algorithms, ctx);
+ decode_str(&kex.encryption_algorithms_client_to_server, ctx);
+ decode_str(&kex.encryption_algorithms_server_to_client, ctx);
+ decode_str(&kex.mac_algorithms_client_to_server, ctx);
+ decode_str(&kex.mac_algorithms_server_to_client, ctx);
+ decode_str(&kex.compression_algorithms_client_to_server, ctx);
+ decode_str(&kex.compression_algorithms_server_to_client, ctx);
+ decode_str(&kex.languages_client_to_server, ctx);
+ decode_str(&kex.languages_server_to_client, ctx);
+ decode_bool(&kex.first_kex_packet_follows, ctx);
+ decode_u32(&kex.reserved, ctx);
+ if ctx.index != ctx.framelen {
+ exit(1);
+ }
+}
+
+decode_newkeys(kex: *ssh_newkeys, ctx: *sshd_ctx) {
+ var tag: int;
+ decode_u8(&tag, ctx);
+ if tag != SSH_MSG_NEWKEYS {
+ exit(1);
+ }
+}
+
+struct ssh_ecdh_init {
+ qc: ssh_str;
+}
+
+decode_ecdh_init(dh: *ssh_ecdh_init, ctx: *sshd_ctx) {
+ var tag: int;
+ decode_u8(&tag, ctx);
+ if tag != SSH_MSG_ECDH_INIT {
+ exit(1);
+ }
+ decode_str(&dh.qc, ctx);
+}
+
+set_str(s: *ssh_str, v: *byte) {
+ s.s = v;
+ s.len = strlen(v);
+}
+
+struct _ssh_cookie {
+ x0: int;
+ x1: int;
+}
+
+clear_frame(ctx: *sshd_ctx) {
+ ctx.frame = &ctx.buf[5];
+ ctx.framelen = ctx.bufsz - 5;
+ ctx.index = 0;
+}
+
+finish_frame(ctx: *sshd_ctx) {
+ ctx.framelen = ctx.index;
+}
+
+encode_u8(x: *int, ctx: *sshd_ctx) {
+ if ctx.framelen < 1 || ctx.index > ctx.framelen - 1 {
+ exit(1);
+ }
+ ctx.frame[ctx.index] = (*x):byte;
+ ctx.index = ctx.index + 1;
+}
+
+encode_cookie(x: *ssh_str, ctx: *sshd_ctx) {
+ if x.len != 16 || ctx.framelen < 16 || ctx.index > ctx.framelen - 16 {
+ exit(1);
+ }
+ memcpy(&ctx.frame[ctx.index], x.s, 16);
+ ctx.index = ctx.index + 16;
+}
+
+encode_bool(x: *int, ctx: *sshd_ctx) {
+ var y: int;
+ y = *x != 0;
+ encode_u8(&y, ctx);
+}
+
+encode_u32(x: *int, ctx: *sshd_ctx) {
+ if ctx.framelen < 4 || ctx.index > ctx.framelen - 4 {
+ exit(1);
+ }
+ ctx.frame[ctx.index] = (*x >> 24): byte;
+ ctx.frame[ctx.index + 1] = (*x >> 16): byte;
+ ctx.frame[ctx.index + 2] = (*x >> 8): byte;
+ ctx.frame[ctx.index + 3] = (*x): byte;
+ ctx.index = ctx.index + 4;
+}
+
+encode_blob(x: *ssh_blob, ctx: *sshd_ctx) {
+ var len: int;
+ len = x.alg.len + x.blob.len + 8;
+ encode_u32(&len, ctx);
+ encode_str(&x.alg, ctx);
+ encode_str(&x.blob, ctx);
+}
+
+encode_str(x: *ssh_str, ctx: *sshd_ctx) {
+ encode_u32(&x.len, ctx);
+ if ctx.framelen < x.len || ctx.index > ctx.framelen - x.len {
+ exit(1);
+ }
+ memcpy(&ctx.frame[ctx.index], x.s, x.len);
+ ctx.index = ctx.index + x.len;
+}
+
+encode_kexinit(kex: *ssh_kexinit, ctx: *sshd_ctx) {
+ var tag: int;
+ clear_frame(ctx);
+ tag = SSH_MSG_KEXINIT;
+ encode_u8(&tag, ctx);
+ encode_cookie(&kex.cookie, ctx);
+ encode_str(&kex.kex_algorithms, ctx);
+ encode_str(&kex.server_host_key_algorithms, ctx);
+ encode_str(&kex.encryption_algorithms_client_to_server, ctx);
+ encode_str(&kex.encryption_algorithms_server_to_client, ctx);
+ encode_str(&kex.mac_algorithms_client_to_server, ctx);
+ encode_str(&kex.mac_algorithms_server_to_client, ctx);
+ encode_str(&kex.compression_algorithms_client_to_server, ctx);
+ encode_str(&kex.compression_algorithms_server_to_client, ctx);
+ encode_str(&kex.languages_client_to_server, ctx);
+ encode_str(&kex.languages_server_to_client, ctx);
+ encode_bool(&kex.first_kex_packet_follows, ctx);
+ encode_u32(&kex.reserved, ctx);
+ finish_frame(ctx);
+}
+
+struct ssh_newkeys {
+}
+
+encode_newkeys(kex: *ssh_newkeys, ctx: *sshd_ctx) {
+ var tag: int;
+ clear_frame(ctx);
+ tag = SSH_MSG_NEWKEYS;
+ encode_u8(&tag, ctx);
+ finish_frame(ctx);
+}
+
+struct ssh_blob {
+ alg: ssh_str;
+ blob: ssh_str;
+}
+
+struct ssh_ecdh_reply {
+ ks: ssh_blob;
+ qs: ssh_str;
+ sig: ssh_blob;
+}
+
+set_blob(d: *ssh_blob, alg: *byte, b: *byte, n: int) {
+ set_str(&d.alg, alg);
+ d.blob.s = b;
+ d.blob.len = n;
+}
+
+encode_ecdh_reply(dh: *ssh_ecdh_reply, ctx: *sshd_ctx) {
+ var tag: int;
+ clear_frame(ctx);
+ tag = SSH_MSG_ECDH_REPLY;
+ encode_u8(&tag, ctx);
+ encode_blob(&dh.ks, ctx);
+ encode_str(&dh.qs, ctx);
+ encode_blob(&dh.sig, ctx);
+ finish_frame(ctx);
+}
+
+ssh_u32_sha256_update(ctx: *sha256_ctx, n: int) {
+ var a: byte;
+ a = (n >> 24):byte;
+ sha256_update(ctx, &a, 1);
+ a = (n >> 16):byte;
+ sha256_update(ctx, &a, 1);
+ a = (n >> 8):byte;
+ sha256_update(ctx, &a, 1);
+ a = n:byte;
+ sha256_update(ctx, &a, 1);
+}
+
+ssh_blob_sha256_update(ctx: *sha256_ctx, b: *ssh_blob) {
+ ssh_u32_sha256_update(ctx, b.alg.len + b.blob.len + 8);
+ ssh_str_sha256_update(ctx, b.alg.s, b.alg.len);
+ ssh_str_sha256_update(ctx, b.blob.s, b.blob.len);
+}
+
+ssh_str_sha256_update(ctx: *sha256_ctx, b: *byte, n: int) {
+ ssh_u32_sha256_update(ctx, n);
+ sha256_update(ctx, b, n);
+}
+
+ssh_mpint_sha256_update(ctx: *sha256_ctx, b: *byte, n: int) {
+ var a: byte;
+
+ loop {
+ if n == 1 {
+ break;
+ }
+
+ if b[0]:int != 0 {
+ break;
+ }
+
+ b = &b[1];
+ n = n - 1;
+ }
+
+ if b[0]:int & 128 {
+ ssh_u32_sha256_update(ctx, n + 1);
+ a = 0:byte;
+ sha256_update(ctx, &a, 1);
+ sha256_update(ctx, b, n);
+ } else {
+ ssh_u32_sha256_update(ctx, n);
+ sha256_update(ctx, b, n);
+ }
+}
+
+dokex(ctx: *sshd_ctx) {
+ var cookie: _ssh_cookie;
+ var ckex: ssh_kexinit;
+ var skex: ssh_kexinit;
+ var newkeys: ssh_newkeys;
+ var cdh: ssh_ecdh_init;
+ var sdh: ssh_ecdh_reply;
+ var rs: _ed25519_point;
+ var qs: _ed25519_point;
+ var k: _ed25519_point;
+ var ehctx: sha256_ctx;
+ var eh: _sha256_digest;
+ var sig: _ed25519_sig;
+
+ // <- SSH_MSG_KEXINIT
+ read_frame(ctx);
+ decode_kexinit(&ckex, ctx);
+ memcpy(ctx.ckex, ctx.buf, ctx.framelen);
+ ctx.ckexlen = ctx.framelen;
+
+ if ckex.first_kex_packet_follows {
+ exit(1);
+ }
+
+ // -> SSH_MSG_KEXINIT
+ read_rand((&cookie): *byte, 16);
+ skex.cookie.len = 16;
+ skex.cookie.s = (&cookie): *byte;
+ set_str(&skex.kex_algorithms, "curve25519-sha256");
+ set_str(&skex.server_host_key_algorithms, "ssh-ed25519");
+ set_str(&skex.encryption_algorithms_client_to_server, "chacha20-poly1305@openssh.com");
+ set_str(&skex.encryption_algorithms_server_to_client, "chacha20-poly1305@openssh.com");
+ set_str(&skex.mac_algorithms_client_to_server, "none");
+ set_str(&skex.mac_algorithms_server_to_client, "none");
+ set_str(&skex.compression_algorithms_client_to_server, "none");
+ set_str(&skex.compression_algorithms_server_to_client, "none");
+ set_str(&skex.languages_client_to_server, "");
+ set_str(&skex.languages_server_to_client, "");
+ skex.first_kex_packet_follows = 0;
+ skex.reserved = 0;
+
+ encode_kexinit(&skex, ctx);
+ memcpy(ctx.skex, ctx.frame, ctx.framelen);
+ ctx.skexlen = ctx.index;
+
+ write_frame(ctx);
+
+ // <- SSH_MSG_ECDH_INIT
+ read_frame(ctx);
+ decode_ecdh_init(&cdh, ctx);
+
+ if cdh.qc.len != 32 {
+ die("wtf");
+ exit(1);
+ }
+
+ read_rand((&rs):*byte, 32);
+
+ x25519_base((&qs):*byte);
+
+ if !x25519((&qs):*byte, (&qs):*byte, (&rs):*byte) {
+ die("wtf");
+ exit(1);
+ }
+
+ if !x25519((&k):*byte, cdh.qc.s, (&rs):*byte) {
+ die("wtf");
+ exit(1);
+ }
+
+ set_blob(&sdh.ks, "ssh-ed25519", (&ctx.pub):*byte, 32);
+ sdh.qs.s = (&qs):*byte;
+ sdh.qs.len = 32;
+ set_blob(&sdh.sig, "ssh-ed25519", (&sig):*byte, 64);
+
+ sha256_init(&ehctx);
+ ssh_str_sha256_update(&ehctx, ctx.cver, strlen(ctx.cver));
+ ssh_str_sha256_update(&ehctx, ctx.sver, strlen(ctx.sver));
+ ssh_str_sha256_update(&ehctx, ctx.ckex, ctx.ckexlen);
+ ssh_str_sha256_update(&ehctx, ctx.skex, ctx.skexlen);
+ ssh_blob_sha256_update(&ehctx, &sdh.ks);
+ ssh_str_sha256_update(&ehctx, cdh.qc.s, 32);
+ ssh_str_sha256_update(&ehctx, (&qs):*byte, 32);
+ ssh_mpint_sha256_update(&ehctx, (&k):*byte, 32);
+ sha256_final((&eh):*byte, &ehctx);
+
+ ed25519_sign((&sig):*byte, (&ctx.priv):*byte, (&eh):*byte, 32);
+
+ // -> SSH_MSG_ECDH_REPLY
+ encode_ecdh_reply(&sdh, ctx);
+ write_frame(ctx);
+
+ // <- SSH_MSG_NEWKEYS
+ read_frame(ctx);
+ decode_newkeys(&newkeys, ctx);
+
+ // -> SSH_MSG_NEWKEYS
+ encode_newkeys(&newkeys, ctx);
+ write_frame(ctx);
+
+ if !ctx.has_session_id {
+ memcpy((&ctx.session_id):*byte, (&eh):*byte, 32);
+ ctx.has_session_id = 1;
+ }
+
+ memcpy((&ctx.eh):*byte, (&eh):*byte, 32);
+
+ sha256_init(&ehctx);
+ ssh_mpint_sha256_update(&ehctx, (&k):*byte, 32);
+ sha256_update(&ehctx, (&ctx.eh):*byte, 32);
+ sha256_update(&ehctx, "A", 1);
+ sha256_update(&ehctx, (&ctx.session_id):*byte, 32);
+ sha256_final((&ctx.iv_client_to_server):*byte, &ehctx);
+
+ sha256_init(&ehctx);
+ ssh_mpint_sha256_update(&ehctx, (&k):*byte, 32);
+ sha256_update(&ehctx, (&ctx.eh):*byte, 32);
+ sha256_update(&ehctx, "B", 1);
+ sha256_update(&ehctx, (&ctx.session_id):*byte, 32);
+ sha256_final((&ctx.iv_server_to_client):*byte, &ehctx);
+
+ sha256_init(&ehctx);
+ ssh_mpint_sha256_update(&ehctx, (&k):*byte, 32);
+ sha256_update(&ehctx, (&ctx.eh):*byte, 32);
+ sha256_update(&ehctx, "C", 1);
+ sha256_update(&ehctx, (&ctx.session_id):*byte, 32);
+ sha256_final((&ctx.ek_client_to_server):*byte, &ehctx);
+
+ sha256_init(&ehctx);
+ ssh_mpint_sha256_update(&ehctx, (&k):*byte, 32);
+ sha256_update(&ehctx, (&ctx.eh):*byte, 32);
+ sha256_update(&ehctx, "D", 1);
+ sha256_update(&ehctx, (&ctx.session_id):*byte, 32);
+ sha256_final((&ctx.ek_server_to_client):*byte, &ehctx);
+
+ sha256_init(&ehctx);
+ ssh_mpint_sha256_update(&ehctx, (&k):*byte, 32);
+ sha256_update(&ehctx, (&ctx.eh):*byte, 32);
+ sha256_update(&ehctx, "E", 1);
+ sha256_update(&ehctx, (&ctx.session_id):*byte, 32);
+ sha256_final((&ctx.ik_client_to_server):*byte, &ehctx);
+
+ sha256_init(&ehctx);
+ ssh_mpint_sha256_update(&ehctx, (&k):*byte, 32);
+ sha256_update(&ehctx, (&ctx.eh):*byte, 32);
+ sha256_update(&ehctx, "F", 1);
+ sha256_update(&ehctx, (&ctx.session_id):*byte, 32);
+ sha256_final((&ctx.ik_server_to_client):*byte, &ehctx);
+}
+
+doauth(ctx: *sshd_ctx) {
+ var k: ed25519_pub;
+ unhex((&k):*byte, "5afb3032d86d60d700e1782bfe8083ff95504cecbda531bef5ba8cdd20f34c82");
+ // todo auth
+ exit(1);
+}
+
+cmain(ctx: *sshd_ctx) {
+ doversion(ctx);
+ dokex(ctx);
+ doauth(ctx);
+ // todo main loop
+ exit(0);
+}
+
+struct sockaddr {
+ fpa: int;
+ pad: int;
+}
+
+struct sshd_ctx {
+ fd: int;
+ a: *alloc;
+ priv: _ed25519_priv;
+ pub: _ed25519_pub;
+ buf: *byte;
+ bufsz: int;
+ frame: *byte;
+ framelen: int;
+ index: int;
+ sver: *byte;
+ cver: *byte;
+ skex: *byte;
+ skexlen: int;
+ ckex: *byte;
+ ckexlen: int;
+ blksize: int;
+ has_session_id: int;
+ session_id: _sha256_digest;
+ eh: _sha256_digest;
+ iv_client_to_server: _sha256_digest;
+ iv_server_to_client: _sha256_digest;
+ ek_client_to_server: _sha256_digest;
+ ek_server_to_client: _sha256_digest;
+ ik_client_to_server: _sha256_digest;
+ ik_server_to_client: _sha256_digest;
+}
+
main(argc: int, argv: **byte, envp: **byte) {
- exit(_rdrand());
+ var fd: int;
+ var cfd: int;
+ var port: int;
+ var sa: sockaddr;
+ var ctx: sshd_ctx;
+ var a: alloc;
+ setup_alloc(&a);
+
+ bzero((&ctx):*byte, sizeof(ctx));
+
+ ed25519_pub((&ctx.pub):*byte, (&ctx.priv):*byte);
+
+ ctx.a = &a;
+ ctx.bufsz = 64 * 1024;
+ ctx.buf = alloc(ctx.a, ctx.bufsz);
+ ctx.skex = alloc(ctx.a, 4096);
+ ctx.ckex = alloc(ctx.a, 4096);
+ ctx.sver = "SSH-2.0-omiltem";
+ ctx.blksize = 8;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if fd < 0 {
+ die("failed to open socket");
+ }
+
+ port = 1234;
+ sa.fpa = AF_INET | ((port & 0xff) << 24) | (((port >> 8) & 0xff) << 16);
+ sa.pad = 0;
+ if bind(fd, (&sa):*byte, sizeof(sa)) != 0 {
+ die("failed to bind");
+ }
+
+ if listen(fd, 128) != 0 {
+ die("failed to listen");
+ }
+
+ loop {
+ ctx.fd = accept(fd, 0:*byte, 0:*int);
+ if ctx.fd < 0 {
+ continue;
+ }
+
+ if fork() == 0 {
+ close(fd);
+ cmain(&ctx);
+ exit(1);
+ }
+
+ close(ctx.fd);
+ }
}