os

An operating system
git clone https://erai.gay/code/os/
Log | Files | Refs | README | LICENSE

commit c94aba02ea3ee69bd963de650b14b39eb62602c7
parent ef262ddf1e104b58cb9427516035feffcd33ba1a
Author: erai <erai@omiltem.net>
Date:   Mon,  3 Jun 2024 14:36:54 -0400

ssh publickey userauth

Diffstat:
Mchacha20.c | 27+++++----------------------
Med25519.c | 39+++++++++++++++++++++++++++++++--------
Mpoly1305.c | 9++++++---
Msshd.c | 622+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
4 files changed, 492 insertions(+), 205 deletions(-)

diff --git a/chacha20.c b/chacha20.c @@ -157,25 +157,9 @@ chacha20_stream(cipher: *byte, plain: *byte, len: int, index: *int, key: *byte, var i: int; var j: int; - j = 0; - i = index[0] & 63; - - if i { - chacha20_block(block, key, index[0] >> 6, nonce); - - loop { - if j == len || i == 64 { - break; - } - - cipher[j] = plain[j] ^ block[i]; - - index[0] = index[0] + 1; - i = i + 1; - j = j + 1; - } - } + block = (&_block):*byte; + j = 0; loop { if j == len { break; @@ -183,18 +167,17 @@ chacha20_stream(cipher: *byte, plain: *byte, len: int, index: *int, key: *byte, chacha20_block(block, key, index[0] >> 6, nonce); - i = 0; + i = index[0] & 63; loop { if j == len || i == 64 { break; } - cipher[j] = plain[j] ^ block[j]; + cipher[j] = plain[j] ^ block[i]; + index[0] = index[0] + 1; i = i + 1; j = j + 1; } - - index[0] = index[0] + 64; } } diff --git a/ed25519.c b/ed25519.c @@ -531,7 +531,7 @@ ed25519_sqrt(r: *int, x: *int): int { return i; } -// x**2 = (1 - y**2) / (d * y**2 - 1) mod p +// x**2 = (y**2 - 1) / (1 + d * y**2) mod p ed25519_decode(p: *int, y: *byte): int { var _xy: _ed25519_point; var xy: *int; @@ -557,8 +557,8 @@ ed25519_decode(p: *int, y: *byte): int { ed25519_d(b); ed25519_mul(b, b, a); ed25519_one(xy); - ed25519_sub(b, b, xy); - ed25519_sub(xy, xy, a); + ed25519_add(b, b, xy); + ed25519_sub(xy, a, xy); ed25519_inv(b, b); ed25519_mul(xy, xy, b); @@ -962,19 +962,42 @@ ed25519_eq(a: *int, b: *int): int { ed25519_verify(sig: *byte, pub: *byte, msg: *byte, len: int): int { var ctx: sha512_ctx; + var _a: _ed25519_point; var a: *int; + var _b: _ed25519_point; var b: *int; + var _r: _ed25519_point; var r: *int; + var _s: _ed25519_point; var s: *int; + var _k: _ed25519_point; var k: *int; + var _hk: _sha512_digest; var hk: *byte; + var _sk: _sha512_digest; + var sk: *byte; + + a = (&_a):*int; + b = (&_b):*int; + r = (&_r):*int; + s = (&_s):*int; + k = (&_k):*int; + hk = (&_hk):*byte; + sk = (&_sk):*byte; // A = public key - ed25519_decode(a, pub); + if !ed25519_decode(a, pub){ + return 0; + } // sig = R || S - ed25519_decode(r, sig); - ed25519_reduce_l(s, &sig[32]); + if !ed25519_decode(r, sig) { + return 0; + } + + bzero(sk, 64); + memcpy(sk, &sig[32], 32); + ed25519_reduce_l(s, sk); // k' = SHA-512(R || A || M) sha512_init(&ctx); @@ -1235,9 +1258,9 @@ ed25519_set_lsb(xy: *int, lsb: int) { a = &_a.x0; ed25519_zero(a); - ed25519_sub(a, a, &xy[8]); + ed25519_sub(a, a, xy); - ed25519_selectl(&xy[8], &xy[8], a, (xy[0] ^ lsb) & 1); + ed25519_selectl(xy, xy, a, (xy[0] ^ lsb) & 1); } ed25519_clamp(k: *int, b: *byte) { diff --git a/poly1305.c b/poly1305.c @@ -21,7 +21,7 @@ poly1305_reduce(a: *int) { c = (c + a[1]) >> 32; c = (c + a[2]) >> 32; c = (c + a[3]) >> 32; - c = (c + a[4] + (~3 & (-1 >> 32))) >> 32; + c = (c + a[4] + (-4 & (-1 >> 32))) >> 32; k = -c >> 32; @@ -29,7 +29,7 @@ poly1305_reduce(a: *int) { c = c + a[1]; a[1] = c & (-1 >> 32); c = c >> 32; c = c + a[2]; a[2] = c & (-1 >> 32); c = c >> 32; c = c + a[3]; a[3] = c & (-1 >> 32); c = c >> 32; - c = c + a[4] + (~3 & k); a[4] = c & (-1 >> 32); c = c >> 32; + c = c + a[4] + (-4 & (-1 >> 32) & k); a[4] = c & (-1 >> 32); c = c >> 32; } poly1305_load(dest: *int, src: *byte, n: int) { @@ -48,7 +48,6 @@ poly1305_load(dest: *int, src: *byte, n: int) { } poly1305_digest(dest: *byte, src: *int) { - poly1305_reduce(src); dest[0] = src[0]:byte; dest[1] = (src[0] >> 8):byte; dest[2] = (src[0] >> 16):byte; @@ -223,13 +222,17 @@ poly1305(mac: *byte, key: *byte, msg: *byte, len: int) { } poly1305_add(a, s); + poly1305_reduce(a); poly1305_mul(a, r); + poly1305_reduce(a); i = i + 16; } poly1305_load(s, &key[16], 4); + s[4] = 0; + poly1305_add(a, s); poly1305_digest(mac, a); diff --git a/sshd.c b/sshd.c @@ -1,40 +1,3 @@ -//-> -//byte SSH_MSG_USERAUTH_REQUEST -//string user name in ISO-10646 UTF-8 encoding [RFC3629] -//string service name in US-ASCII -//string "publickey" -//boolean FALSE -//string public key algorithm name -//string public key blob - -//<- -//byte SSH_MSG_USERAUTH_PK_OK -//string public key algorithm name from the request -//string public key blob from the request - -//-> -//byte SSH_MSG_USERAUTH_REQUEST -//string user name -//string service name -//string "publickey" -//boolean TRUE -//string public key algorithm name -//string public key to be used for authentication -//string signature - -//signature of -//string session identifier -//byte SSH_MSG_USERAUTH_REQUEST -//string user name -//string service name -//string "publickey" -//boolean TRUE -//string public key algorithm name -//string public key to be used for authentication - -//<- -//SSH_MSG_USERAUTH_PK_OK - read_line(fd: int, buf: *byte, max: int): int { var len: int; var c: int; @@ -110,62 +73,110 @@ read_rand(buf: *byte, n: int) { } } +struct _ssh_nonce { + x0: int; + x1: int; +} + read_frame(ctx: *sshd_ctx) { var len: int; var padlen: int; - var min_size: int; + var minlen: int; var align: int; - - min_size = ctx.blksize; - align = ctx.blksize; - - if min_size < 16 { - min_size = 16; + var maclen: int; + var tmp: int; + var _nonce: _ssh_nonce; + var nonce: *byte; + var seq: int; + var _mackey: _sha512_digest; + var mackey: *byte; + var _mac: _sha256_digest; + var mac: *byte; + + nonce = (&_nonce):*byte; + mackey = (&_mackey):*byte; + mac = (&_mac):*byte; + + if read_fill(ctx.fd, ctx.buf, 4) != 0 { + die("truncated"); } - if align < 8 { + if ctx.en_client_to_server { + nonce[0] = 0:byte; + nonce[1] = 0:byte; + nonce[2] = 0:byte; + nonce[3] = 0:byte; + nonce[4] = (ctx.seq_client_to_server >> 56):byte; + nonce[5] = (ctx.seq_client_to_server >> 48):byte; + nonce[6] = (ctx.seq_client_to_server >> 40):byte; + nonce[7] = (ctx.seq_client_to_server >> 32):byte; + nonce[8] = (ctx.seq_client_to_server >> 24):byte; + nonce[9] = (ctx.seq_client_to_server >> 16):byte; + nonce[10] = (ctx.seq_client_to_server >> 8):byte; + nonce[11] = ctx.seq_client_to_server:byte; + + seq = 0; + chacha20_stream((&tmp):*byte, ctx.buf, 4, &seq, &((&ctx.ek_client_to_server):*byte)[32], nonce); + + minlen = 16; align = 8; - } - - if read_fill(ctx.fd, ctx.buf, 5) != 0 { - exit(1); - } + maclen = 16; - // todo decrypt + len = ((&tmp):*byte)[3]:int + | (((&tmp):*byte)[2]:int << 8) + | (((&tmp):*byte)[1]:int << 16) + | (((&tmp):*byte)[0]:int << 24); - len = ctx.buf[3]:int - | (ctx.buf[2]:int << 8) - | (ctx.buf[1]:int << 16) - | (ctx.buf[0]:int << 24); + if len % align != 0 { + die("not aligned"); + } + } else { + minlen = 16; + align = 8; + maclen = 0; - padlen = ctx.buf[4]:int; + len = ctx.buf[3]:int + | (ctx.buf[2]:int << 8) + | (ctx.buf[1]:int << 16) + | (ctx.buf[0]:int << 24); - if len > ctx.bufsz - 4 { - exit(1); + if (len + 4) % align != 0 { + die("not aligned"); + } } - if len < 1 || padlen > len - 1 { - exit(1); + if len > ctx.bufsz - 4 - maclen { + die("too long"); } - if padlen < 4 { - exit(1); + if len < minlen - 4 { + die("too short"); } - if len < min_size - 4 { - exit(1); + if read_fill(ctx.fd, &ctx.buf[4], len + maclen) != 0 { + die("truncated"); } - if (len + 4) % align != 0 { - exit(1); + if ctx.en_client_to_server { + bzero(mackey, 64); + seq = 0; + chacha20_stream(mackey, mackey, 64, &seq, (&ctx.ek_client_to_server):*byte, nonce); + poly1305(mac, mackey, ctx.buf, len + 4); + if memcmp(mac, &ctx.buf[len + 4], 16) { + die("mac"); + } + memcpy(ctx.buf, (&tmp):*byte, 4); + seq = 64; + chacha20_stream(&ctx.buf[4], &ctx.buf[4], len, &seq, (&ctx.ek_client_to_server):*byte, nonce); } - if read_fill(ctx.fd, &ctx.buf[5], len - 1) != 0 { - exit(1); - } + padlen = ctx.buf[4]:int; - // todo decrypt + mac + if padlen < 4 || padlen > len - 1 { + die("bad padding"); + } + ctx.seq_client_to_server = (ctx.seq_client_to_server + 1) & (-1 >> 32); ctx.frame = &ctx.buf[5]; ctx.framelen = len - padlen - 1; ctx.index = 0; @@ -174,35 +185,57 @@ read_frame(ctx: *sshd_ctx) { write_frame(ctx: *sshd_ctx) { var len: int; var padlen: int; - var min_size: int; + var minlen: int; var align: int; - - align = ctx.blksize; - min_size = ctx.blksize; - - if align < 8 { + var maclen: int; + var _nonce: _ssh_nonce; + var nonce: *byte; + var seq: int; + var _mackey: _sha512_digest; + var mackey: *byte; + + nonce = (&_nonce):*byte; + mackey = (&_mackey):*byte; + + if ctx.en_server_to_client { + minlen = 16; align = 8; + maclen = 16; + padlen = align - ((ctx.framelen + 1) % align); + + nonce[0] = 0:byte; + nonce[1] = 0:byte; + nonce[2] = 0:byte; + nonce[3] = 0:byte; + nonce[4] = (ctx.seq_server_to_client >> 56):byte; + nonce[5] = (ctx.seq_server_to_client >> 48):byte; + nonce[6] = (ctx.seq_server_to_client >> 40):byte; + nonce[7] = (ctx.seq_server_to_client >> 32):byte; + nonce[8] = (ctx.seq_server_to_client >> 24):byte; + nonce[9] = (ctx.seq_server_to_client >> 16):byte; + nonce[10] = (ctx.seq_server_to_client >> 8):byte; + nonce[11] = ctx.seq_server_to_client:byte; + } else { + minlen = 16; + align = 8; + maclen = 0; + padlen = align - ((ctx.framelen + 5) % align); } - 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 { + if ctx.framelen + padlen + 5 < minlen { padlen = padlen + align; } - if padlen > ctx.bufsz - 5 - ctx.framelen { - exit(1); + if padlen + ctx.framelen + 5 + maclen > ctx.bufsz { + die("write too large"); } if padlen > 255 { - exit(1); + die("padding too large"); } len = padlen + ctx.framelen + 1; @@ -216,18 +249,34 @@ write_frame(ctx: *sshd_ctx) { read_rand(&ctx.frame[ctx.framelen + 5], padlen); ctx.framelen = ctx.framelen + 5 + padlen; - // todo encrypt + mac + if ctx.en_server_to_client { + seq = 0; + chacha20_stream(ctx.buf, ctx.buf, 4, &seq, &((&ctx.ek_server_to_client):*byte)[32], nonce); + seq = 0; + chacha20_stream(mackey, mackey, 64, &seq, (&ctx.ek_server_to_client):*byte, nonce); + seq = 64; + chacha20_stream(&ctx.buf[4], &ctx.buf[4], len, &seq, (&ctx.ek_server_to_client):*byte, nonce); + poly1305(&ctx.buf[len + 4], mackey, ctx.buf, len + 4); + } + + ctx.seq_server_to_client = (ctx.seq_server_to_client + 1) & (-1 >> 32); - if writeall(ctx.fd, ctx.frame, len + 4) != 0 { - exit(1); + if writeall(ctx.fd, ctx.frame, len + 4 + maclen) != 0 { + die("write truncated"); } } enum { + SSH_MSG_SERVICE_REQUEST = 0x05, + SSH_MSG_SERVICE_ACCEPT = 0x06, SSH_MSG_KEXINIT = 0x14, SSH_MSG_NEWKEYS = 0x15, SSH_MSG_ECDH_INIT = 0x1e, SSH_MSG_ECDH_REPLY = 0x1f, + SSH_MSG_USERAUTH_REQUEST = 0x32, + SSH_MSG_USERAUTH_FAILURE = 0x33, + SSH_MSG_USERAUTH_SUCCESS = 0x34, + SSH_MSG_USERAUTH_PK_OK = 0x3c, } doversion(ctx: *sshd_ctx) { @@ -237,7 +286,7 @@ doversion(ctx: *sshd_ctx) { //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); + die("bad version"); } ctx.cver = alloc(ctx.a, n + 1); @@ -245,11 +294,11 @@ doversion(ctx: *sshd_ctx) { ctx.cver[n] = 0:byte; if writeall(ctx.fd, ctx.sver, strlen(ctx.sver)) != 0 { - exit(1); + die("write truncated"); } if writeall(ctx.fd, "\r\n", 2) != 0 { - exit(1); + die("write truncated"); } } @@ -276,7 +325,7 @@ struct ssh_kexinit { decode_u8(s: *int, ctx: *sshd_ctx) { if ctx.framelen < 1 || ctx.index > ctx.framelen - 1 { - exit(1); + die("u8 truncated"); } *s = ctx.frame[ctx.index]:int; ctx.index = ctx.index + 1; @@ -284,7 +333,7 @@ decode_u8(s: *int, ctx: *sshd_ctx) { decode_u32(s: *int, ctx: *sshd_ctx) { if ctx.framelen < 4 || ctx.index > ctx.framelen - 4 { - exit(1); + die("u32 truncated"); } *s = (ctx.frame[ctx.index]:int << 24) | (ctx.frame[ctx.index + 1]: int << 16) @@ -296,7 +345,7 @@ decode_u32(s: *int, ctx: *sshd_ctx) { 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); + die("str truncated"); } s.s = &ctx.frame[ctx.index]; ctx.index = ctx.index + s.len; @@ -304,7 +353,7 @@ decode_str(s: *ssh_str, ctx: *sshd_ctx) { decode_cookie(s: *ssh_str, ctx: *sshd_ctx) { if ctx.framelen < 16 || ctx.index > ctx.framelen - 16 { - exit(1); + die("cookie truncated"); } s.s = &ctx.frame[ctx.index]; s.len = 16; @@ -320,7 +369,7 @@ decode_kexinit(kex: *ssh_kexinit, ctx: *sshd_ctx) { var tag: int; decode_u8(&tag, ctx); if tag != SSH_MSG_KEXINIT { - exit(1); + die("not a kexinit"); } decode_cookie(&kex.cookie, ctx); decode_str(&kex.kex_algorithms, ctx); @@ -336,7 +385,7 @@ decode_kexinit(kex: *ssh_kexinit, ctx: *sshd_ctx) { decode_bool(&kex.first_kex_packet_follows, ctx); decode_u32(&kex.reserved, ctx); if ctx.index != ctx.framelen { - exit(1); + die("trailing data"); } } @@ -344,7 +393,10 @@ decode_newkeys(kex: *ssh_newkeys, ctx: *sshd_ctx) { var tag: int; decode_u8(&tag, ctx); if tag != SSH_MSG_NEWKEYS { - exit(1); + die("not a newkeys"); + } + if ctx.index != ctx.framelen { + die("trailing data"); } } @@ -356,9 +408,72 @@ 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); + die("not a ecdh_init"); } decode_str(&dh.qc, ctx); + if ctx.index != ctx.framelen { + die("trailing data"); + } +} + +struct ssh_service_request { + name: ssh_str; +} + +decode_service_request(sr: *ssh_service_request, ctx: *sshd_ctx) { + var tag: int; + decode_u8(&tag, ctx); + if tag != SSH_MSG_SERVICE_REQUEST { + die("not a ecdh_init"); + } + decode_str(&sr.name, ctx); + if ctx.index != ctx.framelen { + die("trailing data"); + } +} + +struct ssh_userauth_request { + user: ssh_str; + service: ssh_str; + method: ssh_str; + has_sig: int; + alg: ssh_str; + pub: ssh_str; + sig: ssh_str; +} + +decode_userauth_request(ar: *ssh_userauth_request, ctx: *sshd_ctx) { + var tag: int; + decode_u8(&tag, ctx); + if tag != SSH_MSG_USERAUTH_REQUEST { + die("not a userauth_request"); + } + + decode_str(&ar.user, ctx); + decode_str(&ar.service, ctx); + decode_str(&ar.method, ctx); + + if ssh_streq(&ar.method, "none") { + ar.has_sig = 0; + set_str(&ar.alg, ""); + set_str(&ar.pub, ""); + set_str(&ar.sig, ""); + } else if ssh_streq(&ar.method, "publickey") { + decode_bool(&ar.has_sig, ctx); + decode_str(&ar.alg, ctx); + decode_str(&ar.pub, ctx); + if ar.has_sig { + decode_str(&ar.sig, ctx); + } else { + set_str(&ar.sig, ""); + } + } else { + die("unknown method"); + } + + if ctx.index != ctx.framelen { + die("trailing data"); + } } set_str(s: *ssh_str, v: *byte) { @@ -383,7 +498,7 @@ finish_frame(ctx: *sshd_ctx) { encode_u8(x: *int, ctx: *sshd_ctx) { if ctx.framelen < 1 || ctx.index > ctx.framelen - 1 { - exit(1); + die("u8 truncated"); } ctx.frame[ctx.index] = (*x):byte; ctx.index = ctx.index + 1; @@ -391,7 +506,7 @@ encode_u8(x: *int, ctx: *sshd_ctx) { encode_cookie(x: *ssh_str, ctx: *sshd_ctx) { if x.len != 16 || ctx.framelen < 16 || ctx.index > ctx.framelen - 16 { - exit(1); + die("cookie truncated"); } memcpy(&ctx.frame[ctx.index], x.s, 16); ctx.index = ctx.index + 16; @@ -405,7 +520,7 @@ encode_bool(x: *int, ctx: *sshd_ctx) { encode_u32(x: *int, ctx: *sshd_ctx) { if ctx.framelen < 4 || ctx.index > ctx.framelen - 4 { - exit(1); + die("u32 truncated"); } ctx.frame[ctx.index] = (*x >> 24): byte; ctx.frame[ctx.index + 1] = (*x >> 16): byte; @@ -425,7 +540,7 @@ encode_blob(x: *ssh_blob, ctx: *sshd_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); + die("str truncated"); } memcpy(&ctx.frame[ctx.index], x.s, x.len); ctx.index = ctx.index + x.len; @@ -463,6 +578,60 @@ encode_newkeys(kex: *ssh_newkeys, ctx: *sshd_ctx) { finish_frame(ctx); } +struct ssh_service_accept { + name: ssh_str; +} + +encode_service_accept(sa: *ssh_service_accept, ctx: *sshd_ctx) { + var tag: int; + clear_frame(ctx); + tag = SSH_MSG_SERVICE_ACCEPT; + encode_u8(&tag, ctx); + encode_str(&sa.name, ctx); + finish_frame(ctx); +} + +struct ssh_userauth_failure { + methods: ssh_str; + partial_success: int; +} + +encode_userauth_failure(uf: *ssh_userauth_failure, ctx: *sshd_ctx) { + var tag: int; + clear_frame(ctx); + tag = SSH_MSG_USERAUTH_FAILURE; + encode_u8(&tag, ctx); + encode_str(&uf.methods, ctx); + encode_bool(&uf.partial_success, ctx); + finish_frame(ctx); +} + +struct ssh_userauth_success { +} + +encode_userauth_success(us: *ssh_userauth_success, ctx: *sshd_ctx) { + var tag: int; + clear_frame(ctx); + tag = SSH_MSG_USERAUTH_SUCCESS; + encode_u8(&tag, ctx); + finish_frame(ctx); +} + +struct ssh_userauth_pk_ok { + alg: ssh_str; + pub: ssh_str; +} + +encode_userauth_pk_ok(ok: *ssh_userauth_pk_ok, ctx: *sshd_ctx) { + var tag: int; + clear_frame(ctx); + tag = SSH_MSG_USERAUTH_PK_OK; + encode_u8(&tag, ctx); + encode_str(&ok.alg, ctx); + encode_str(&ok.pub, ctx); + finish_frame(ctx); +} + struct ssh_blob { alg: ssh_str; blob: ssh_str; @@ -558,11 +727,11 @@ dokex(ctx: *sshd_ctx) { // <- SSH_MSG_KEXINIT read_frame(ctx); decode_kexinit(&ckex, ctx); - memcpy(ctx.ckex, ctx.buf, ctx.framelen); + memcpy(ctx.ckex, ctx.frame, ctx.framelen); ctx.ckexlen = ctx.framelen; if ckex.first_kex_packet_follows { - exit(1); + die("first_kex_packet_follows not implemented"); } // -> SSH_MSG_KEXINIT @@ -593,8 +762,7 @@ dokex(ctx: *sshd_ctx) { decode_ecdh_init(&cdh, ctx); if cdh.qc.len != 32 { - die("wtf"); - exit(1); + die("bad ecdh_init"); } read_rand((&rs):*byte, 32); @@ -602,13 +770,11 @@ dokex(ctx: *sshd_ctx) { x25519_base((&qs):*byte); if !x25519((&qs):*byte, (&qs):*byte, (&rs):*byte) { - die("wtf"); - exit(1); + die("bad x25519 server"); } if !x25519((&k):*byte, cdh.qc.s, (&rs):*byte) { - die("wtf"); - exit(1); + die("bad x25519 client"); } set_blob(&sdh.ks, "ssh-ed25519", (&ctx.pub):*byte, 32); @@ -633,77 +799,158 @@ dokex(ctx: *sshd_ctx) { 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); + memcpy((&ctx.k):*byte, (&k):*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); + kex_key((&ctx.iv_client_to_server):*byte, "A", ctx); + kex_key((&ctx.iv_server_to_client):*byte, "B", ctx); + kex_key((&ctx.ek_client_to_server):*byte, "C", ctx); + kex_key((&ctx.ek_server_to_client):*byte, "D", ctx); + kex_key((&ctx.ik_client_to_server):*byte, "E", ctx); + kex_key((&ctx.ik_server_to_client):*byte, "F", ctx); - 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); + // -> SSH_MSG_NEWKEYS + encode_newkeys(&newkeys, ctx); + write_frame(ctx); + ctx.en_server_to_client = 1; - 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); + // <- SSH_MSG_NEWKEYS + read_frame(ctx); + decode_newkeys(&newkeys, ctx); + ctx.en_client_to_server = 1; +} - 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); +kex_key(key: *byte, a: *byte, ctx: *sshd_ctx) { + var h: sha256_ctx; + + sha256_init(&h); + ssh_mpint_sha256_update(&h, (&ctx.k):*byte, 32); + sha256_update(&h, (&ctx.eh):*byte, 32); + sha256_update(&h, a, 1); + sha256_update(&h, (&ctx.session_id):*byte, 32); + sha256_final(key, &h); + + sha256_init(&h); + ssh_mpint_sha256_update(&h, (&ctx.k):*byte, 32); + sha256_update(&h, (&ctx.eh):*byte, 32); + sha256_update(&h, key, 32); + sha256_final(&key[32], &h); +} - 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); +ssh_streq(a: *ssh_str, b: *byte): int { + var len: int; + len = strlen(b); + return a.len == len && memcmp(a.s, b, len) == 0; } doauth(ctx: *sshd_ctx) { - var k: ed25519_pub; - unhex((&k):*byte, "5afb3032d86d60d700e1782bfe8083ff95504cecbda531bef5ba8cdd20f34c82"); - // todo auth - exit(1); + var csr: ssh_service_request; + var cua: ssh_userauth_request; + var ssa: ssh_service_accept; + var suf: ssh_userauth_failure; + var sus: ssh_userauth_success; + var sok: ssh_userauth_pk_ok; + var sig: _ed25519_sig; + var b: int; + var s: ssh_str; + + // <- SSH_MSG_SERVICE_REQUEST + read_frame(ctx); + decode_service_request(&csr, ctx); + if !ssh_streq(&csr.name, "ssh-userauth") { + die("not ssh-userauth"); + } + + // -> SSH_MSG_SERVICE_ACCEPT + set_str(&ssa.name, "ssh-userauth"); + encode_service_accept(&ssa, ctx); + write_frame(ctx); + + loop { + + // <- SSH_MSG_USERAUTH_REQUEST + read_frame(ctx); + decode_userauth_request(&cua, ctx); + if !ssh_streq(&cua.service, "ssh-connection") || !ssh_streq(&cua.user, "erai") { + die("bad auth"); + } + + if !ssh_streq(&cua.method, "publickey") || !ssh_streq(&cua.alg, "ssh-ed25519") { + // -> SSH_MSG_USERAUTH_FAILURE("publickey") + set_str(&suf.methods, "publickey"); + suf.partial_success = 0; + encode_userauth_failure(&suf, ctx); + write_frame(ctx); + continue; + } + + if cua.pub.len != ctx.userkeylen || memcmp(cua.pub.s, ctx.userkey, ctx.userkeylen) { + // -> SSH_MSG_USERAUTH_FAILURE("publickey") + set_str(&suf.methods, "publickey"); + suf.partial_success = 0; + encode_userauth_failure(&suf, ctx); + write_frame(ctx); + continue; + } + + if !cua.has_sig { + // -> SSH_MSG_USERAUTH_PK_OK + set_str(&sok.alg, "ssh-ed25519"); + sok.pub.s = ctx.userkey; + sok.pub.len = ctx.userkeylen; + encode_userauth_pk_ok(&sok, ctx); + write_frame(ctx); + continue; + } + + break; + } + + if cua.sig.len != 83 { + die("sig wrong length"); + } + + memcpy((&sig):*byte, &cua.sig.s[19], 64); + + clear_frame(ctx); + s.s = (&ctx.session_id):*byte; + s.len = 32; + encode_str(&s, ctx); + b = SSH_MSG_USERAUTH_REQUEST; + encode_u8(&b, ctx); + set_str(&s, "erai"); + encode_str(&s, ctx); + set_str(&s, "ssh-connection"); + encode_str(&s, ctx); + set_str(&s, "publickey"); + encode_str(&s, ctx); + b = 1; + encode_bool(&b, ctx); + set_str(&s, "ssh-ed25519"); + encode_str(&s, ctx); + s.s = ctx.userkey; + s.len = ctx.userkeylen; + encode_str(&s, ctx); + finish_frame(ctx); + + if !ed25519_verify((&sig):*byte, (&ctx.userpub):*byte, ctx.frame, ctx.framelen) { + die("bad signature"); + } + + encode_userauth_success(&sus, ctx); + write_frame(ctx); } cmain(ctx: *sshd_ctx) { doversion(ctx); dokex(ctx); doauth(ctx); - // todo main loop - exit(0); + die("shell not implemented"); } struct sockaddr { @@ -716,6 +963,11 @@ struct sshd_ctx { a: *alloc; priv: _ed25519_priv; pub: _ed25519_pub; + userpub: _ed25519_pub; + hostkey: *byte; + hostkeylen: int; + userkey: *byte; + userkeylen: int; buf: *byte; bufsz: int; frame: *byte; @@ -727,16 +979,36 @@ struct sshd_ctx { skexlen: int; ckex: *byte; ckexlen: int; - blksize: int; + macsize: 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; + k: _ed25519_point; + iv_client_to_server: _sha512_digest; + iv_server_to_client: _sha512_digest; + ek_client_to_server: _sha512_digest; + ek_server_to_client: _sha512_digest; + ik_client_to_server: _sha512_digest; + ik_server_to_client: _sha512_digest; + en_client_to_server: int; + en_server_to_client: int; + seq_client_to_server: int; + seq_server_to_client: int; +} + +format_key(d: **byte, dlen: *int, k: *byte, ctx: *sshd_ctx) { + var s: ssh_str; + clear_frame(ctx); + set_str(&s, "ssh-ed25519"); + encode_str(&s, ctx); + s.s = k; + s.len = 32; + encode_str(&s, ctx); + finish_frame(ctx); + *d = alloc(ctx.a, ctx.framelen); + memcpy(*d, ctx.frame, ctx.framelen); + *dlen = ctx.framelen; + clear_frame(ctx); } main(argc: int, argv: **byte, envp: **byte) { @@ -750,6 +1022,10 @@ main(argc: int, argv: **byte, envp: **byte) { bzero((&ctx):*byte, sizeof(ctx)); + if unhex((&ctx.userpub):*byte, "5afb3032d86d60d700e1782bfe8083ff95504cecbda531bef5ba8cdd20f34c82") != 32 { + die("invalid userpub"); + } + ed25519_pub((&ctx.pub):*byte, (&ctx.priv):*byte); ctx.a = &a; @@ -758,14 +1034,16 @@ main(argc: int, argv: **byte, envp: **byte) { ctx.skex = alloc(ctx.a, 4096); ctx.ckex = alloc(ctx.a, 4096); ctx.sver = "SSH-2.0-omiltem"; - ctx.blksize = 8; + + format_key(&ctx.hostkey, &ctx.hostkeylen, (&ctx.pub):*byte, &ctx); + format_key(&ctx.userkey, &ctx.userkeylen, (&ctx.userpub):*byte, &ctx); fd = socket(AF_INET, SOCK_STREAM, 0); if fd < 0 { die("failed to open socket"); } - port = 1234; + port = 4321; sa.fpa = AF_INET | ((port & 0xff) << 24) | (((port >> 8) & 0xff) << 16); sa.pad = 0; if bind(fd, (&sa):*byte, sizeof(sa)) != 0 { @@ -785,7 +1063,7 @@ main(argc: int, argv: **byte, envp: **byte) { if fork() == 0 { close(fd); cmain(&ctx); - exit(1); + die("cmain returned"); } close(ctx.fd);