commit c94aba02ea3ee69bd963de650b14b39eb62602c7
parent ef262ddf1e104b58cb9427516035feffcd33ba1a
Author: erai <erai@omiltem.net>
Date: Mon, 3 Jun 2024 14:36:54 -0400
ssh publickey userauth
Diffstat:
M | chacha20.c | | | 27 | +++++---------------------- |
M | ed25519.c | | | 39 | +++++++++++++++++++++++++++++++-------- |
M | poly1305.c | | | 9 | ++++++--- |
M | sshd.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);