From a70850fad6432b03be3010ebc13b364b4782941f Mon Sep 17 00:00:00 2001 From: hidnplayr Date: Wed, 10 Aug 2016 16:20:49 +0000 Subject: [PATCH] Working encryption and HMAC for transport, use libcrash for sha256 (and more to come), placed connection variables in separate struct, fixed bug in aes256_cbc_decode where IV was wrong when in- and output buffer were the same. git-svn-id: svn://kolibrios.org@6469 a494cfbc-eb01-0410-851d-a64ba20cac60 --- programs/network/ssh/aes256-cbc.inc | 63 ++- programs/network/ssh/aes256-ctr.inc | 14 +- programs/network/ssh/aes256.inc | 53 +-- programs/network/ssh/dh_gex.inc | 358 +++++++---------- programs/network/ssh/hmac_sha256.inc | 171 ++++++++ programs/network/ssh/mpint.inc | 6 +- programs/network/ssh/ssh.asm | 528 ++++++++++++++++++------- programs/network/ssh/ssh_transport.inc | 258 ++++++++---- 8 files changed, 934 insertions(+), 517 deletions(-) create mode 100644 programs/network/ssh/hmac_sha256.inc diff --git a/programs/network/ssh/aes256-cbc.inc b/programs/network/ssh/aes256-cbc.inc index cc1c5550f4..88532c9ca4 100644 --- a/programs/network/ssh/aes256-cbc.inc +++ b/programs/network/ssh/aes256-cbc.inc @@ -16,7 +16,7 @@ ; along with this program. If not, see . struct aes256_cbc_context aes256_context - vector rb 16 + vector rb AES256_BLOCKSIZE ends @@ -25,10 +25,10 @@ proc aes256_cbc_init _vector mcall 68, 12, sizeof.aes256_cbc_context ; handle errors - mov ecx, 16/4 + mov ecx, AES256_BLOCKSIZE/4 mov esi, [_vector] lea edi, [eax + aes256_cbc_context.vector] - rep movsd + rep movsd ; rep movsd is slow, but we don't care while init pop edi esi ebx @@ -39,24 +39,16 @@ proc aes256_cbc_encrypt _ctx, _in, _out push ebx esi edi DEBUGF 1,'plain : ' - stdcall dump_128bit_hex, [_in] - DEBUGF 1,'\n' + stdcall dump_hex, [_in], 4 mov edi, [_ctx] lea edi, [edi + aes256_cbc_context.vector] mov esi, [_in] +repeat AES256_BLOCKSIZE/4 lodsd xor eax, [edi] stosd - lodsd - xor eax, [edi] - stosd - lodsd - xor eax, [edi] - stosd - lodsd - xor eax, [edi] - stosd +end repeat mov esi, [_ctx] lea eax, [esi + aes256_cbc_context.key] @@ -66,25 +58,33 @@ proc aes256_cbc_encrypt _ctx, _in, _out mov esi, [_out] mov eax, [_ctx] lea edi, [eax + aes256_cbc_context.vector] +repeat AES256_BLOCKSIZE/4 movsd - movsd - movsd - movsd +end repeat DEBUGF 1,'cipher : ' - stdcall dump_128bit_hex, [_out] - DEBUGF 1,'\n\n' + stdcall dump_hex, [_out], 4 pop edi esi ebx ret endp proc aes256_cbc_decrypt _ctx, _in, _out + +locals + temp_iv rb AES256_BLOCKSIZE +endl + push ebx esi edi DEBUGF 1,'cipher : ' - stdcall dump_128bit_hex, [_in] - DEBUGF 1,'\n' + stdcall dump_hex, [_in], 4 + + mov esi, [_in] + lea edi, [temp_iv] +repeat AES256_BLOCKSIZE/4 + movsd +end repeat mov esi, [_ctx] lea eax, [esi + aes256_cbc_context.key] @@ -93,30 +93,21 @@ proc aes256_cbc_decrypt _ctx, _in, _out mov esi, [_ctx] lea esi, [esi + aes256_cbc_context.vector] mov edi, [_out] +repeat AES256_BLOCKSIZE/4 lodsd xor eax, [edi] stosd - lodsd - xor eax, [edi] - stosd - lodsd - xor eax, [edi] - stosd - lodsd - xor eax, [edi] - stosd +end repeat - mov esi, [_in] + lea esi, [temp_iv] mov edi, [_ctx] lea edi, [edi + aes256_cbc_context.vector] +repeat AES256_BLOCKSIZE/4 movsd - movsd - movsd - movsd +end repeat DEBUGF 1,'plain : ' - stdcall dump_128bit_hex, [_out] - DEBUGF 1,'\n\n' + stdcall dump_hex, [_out], 4 pop edi esi ebx ret diff --git a/programs/network/ssh/aes256-ctr.inc b/programs/network/ssh/aes256-ctr.inc index 8db1379a6d..17e79a5db0 100644 --- a/programs/network/ssh/aes256-ctr.inc +++ b/programs/network/ssh/aes256-ctr.inc @@ -16,8 +16,8 @@ ; along with this program. If not, see . struct aes256_ctr_context aes256_context - counter rb 16 - output rb 16 ; counter after aes_crypt + counter rb AES256_BLOCKSIZE + output rb AES256_BLOCKSIZE ; counter after aes_crypt ends @@ -26,10 +26,10 @@ proc aes256_ctr_init _counter mcall 68, 12, sizeof.aes256_ctr_context ; handle errors - mov ecx, 16/4 + mov ecx, AES256_BLOCKSIZE/4 mov esi, [_counter] lea edi, [eax + aes256_ctr_context.counter] - rep movsd + rep movsd ; rep movsd is slow, but we don't care while init pop edi esi ebx @@ -42,8 +42,7 @@ proc aes256_ctr_crypt _ctx, _in, _out push ebx esi edi DEBUGF 1,'plain : ' - stdcall dump_128bit_hex, [_in] - DEBUGF 1,'\n' + stdcall dump_hex, [_in], 4 mov esi, [_ctx] lea eax, [esi + aes256_ctr_context.key] @@ -101,8 +100,7 @@ proc aes256_ctr_crypt _ctx, _in, _out mov dword[esi + aes256_ctr_context.counter + 4*3], edx DEBUGF 1,'cipher : ' - stdcall dump_128bit_hex, [_out] - DEBUGF 1,'\n\n' + stdcall dump_hex, [_out], 4 pop edi esi ebx ret diff --git a/programs/network/ssh/aes256.inc b/programs/network/ssh/aes256.inc index 85aa747897..3d949fb8ac 100644 --- a/programs/network/ssh/aes256.inc +++ b/programs/network/ssh/aes256.inc @@ -16,26 +16,13 @@ ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . -AES256_ROUNDS = 14 +AES256_ROUNDS = 14 +AES256_BLOCKSIZE = 16 struct aes256_context key rd 4*(AES256_ROUNDS+1) ends -proc dump_128bit_hex _ptr - pushad - - mov esi, [_ptr] - mov ecx, 4 - .next_dword: - lodsd - bswap eax - DEBUGF 1,'%x',eax - loop .next_dword - - popad - ret -endp proc aes256_set_encrypt_key _ctx, _userkey locals @@ -48,30 +35,11 @@ endl mov esi, [_userkey] lea edi, [ebx + aes256_context.key] +repeat 8 lodsd bswap eax stosd - lodsd - bswap eax - stosd - lodsd - bswap eax - stosd - lodsd - bswap eax - stosd - lodsd - bswap eax - stosd - lodsd - bswap eax - stosd - lodsd - bswap eax - stosd - lodsd - bswap eax - stosd +end repeat lea esi, [ebx + aes256_context.key] @@ -176,7 +144,6 @@ endl jmp .while .done: - DEBUGF 1,' \n' pop edi esi ebx ret endp @@ -266,8 +233,7 @@ endl push ebx esi edi DEBUGF 1,'input : ' - stdcall dump_128bit_hex, [_in] - DEBUGF 1,'\n' + stdcall dump_hex, [_in], 4 mov ebx, [_key] mov esi, [_in] @@ -663,8 +629,7 @@ endl stosd DEBUGF 1,'output : ' - stdcall dump_128bit_hex, [_out] - DEBUGF 1,'\n' + stdcall dump_hex, [_out], 4 pop edi esi ebx ret @@ -679,8 +644,7 @@ endl push ebx esi edi DEBUGF 1,'input : ' - stdcall dump_128bit_hex, [_in] - DEBUGF 1,'\n' + stdcall dump_hex, [_in], 4 mov ebx, [_key] mov esi, [_in] @@ -1073,8 +1037,7 @@ endl stosd DEBUGF 1,'output : ' - stdcall dump_128bit_hex, [_out] - DEBUGF 1,'\n' + stdcall dump_hex, [_out], 4 pop edi esi ebx ret diff --git a/programs/network/ssh/dh_gex.inc b/programs/network/ssh/dh_gex.inc index 434bc90714..35db9154d1 100644 --- a/programs/network/ssh/dh_gex.inc +++ b/programs/network/ssh/dh_gex.inc @@ -25,39 +25,39 @@ proc dh_gex ;---------------------------------------------- ; >> Send Diffie-Hellman Group Exchange Request - DEBUGF 1, "Sending GEX\n" - stdcall ssh_send_packet, [socketnum], ssh_gex_req, ssh_gex_req.length, 0 + DEBUGF 2, "Sending GEX\n" + stdcall ssh_send_packet, con, ssh_gex_req, ssh_gex_req.length, 0 cmp eax, -1 je .socket_err ;--------------------------------------------- ; << Parse Diffie-Hellman Group Exchange Group - stdcall ssh_recv_packet, [socketnum], rx_buffer, BUFFERSIZE, 0 + stdcall ssh_recv_packet, con, 0 cmp eax, -1 je .socket_err - cmp [rx_buffer+ssh_header.message_code], SSH_MSG_KEX_DH_GEX_GROUP + cmp [con.rx_buffer.message_code], SSH_MSG_KEX_DH_GEX_GROUP jne proto_err - DEBUGF 1, "Received GEX group\n" + DEBUGF 2, "Received GEX group\n" - mov esi, rx_buffer+sizeof.ssh_header - mov edi, dh_p + mov esi, con.rx_buffer+sizeof.ssh_packet_header + mov edi, con.dh_p DEBUGF 1, "DH modulus (p): " call mpint_to_little_endian - stdcall mpint_print, dh_p + stdcall mpint_print, con.dh_p DEBUGF 1, "DH base (g): " - mov edi, dh_g + mov edi, con.dh_g call mpint_to_little_endian - stdcall mpint_print, dh_g + stdcall mpint_print, con.dh_g ;------------------------------------------- ; >> Send Diffie-Hellman Group Exchange Init ; generate a random number x, where 1 < x < (p-1)/2 - mov edi, dh_x+4 - mov [dh_x], DH_PRIVATE_KEY_SIZE/8 + mov edi, con.dh_x+4 + mov [con.dh_x], DH_PRIVATE_KEY_SIZE/8 mov ecx, DH_PRIVATE_KEY_SIZE/8/4 @@: push ecx @@ -71,352 +71,300 @@ proc dh_gex shl eax, 1 jnc @f mov byte[edi], 0 - inc dword[dh_x] + inc dword[con.dh_x] @@: ; Fill remaining bytes with zeros ; TO BE REMOVED ? if ((MAX_BITS-DH_PRIVATE_KEY_SIZE) > 0) mov ecx, (MAX_BITS-DH_PRIVATE_KEY_SIZE)/8/4 xor eax, eax - rep stosd + rep stosd end if DEBUGF 1, "DH x: " - stdcall mpint_length, dh_x;;;;;;;;;;;;; - stdcall mpint_print, dh_x + stdcall mpint_length, con.dh_x;;;;;;;;;;;;; + stdcall mpint_print, con.dh_x ; Compute e = g^x mod p - stdcall mpint_modexp, dh_e, dh_g, dh_x, dh_p - stdcall mpint_length, dh_e + stdcall mpint_modexp, con.dh_e, con.dh_g, con.dh_x, con.dh_p + stdcall mpint_length, con.dh_e DEBUGF 1, "DH e: " - stdcall mpint_print, dh_e + stdcall mpint_print, con.dh_e ; Create group exchange init packet - mov edi, tx_buffer+ssh_header.message_code + mov edi, con.tx_buffer.message_code mov al, SSH_MSG_KEX_DH_GEX_INIT stosb - mov esi, dh_e + mov esi, con.dh_e call mpint_to_big_endian - DEBUGF 1, "Sending GEX init\n" - mov ecx, dword[tx_buffer+ssh_header.message_code+1] + DEBUGF 2, "Sending GEX init\n" + mov ecx, dword[con.tx_buffer.message_code+1] bswap ecx add ecx, 5 - stdcall ssh_send_packet, [socketnum], tx_buffer+ssh_header.message_code, ecx, 0 + stdcall ssh_send_packet, con, con.tx_buffer.message_code, ecx, 0 cmp eax, -1 je .socket_err ;--------------------------------------------- ; << Parse Diffie-Hellman Group Exchange Reply - stdcall ssh_recv_packet, [socketnum], rx_buffer, BUFFERSIZE, 0 + stdcall ssh_recv_packet, con, 0 cmp eax, -1 je .socket_err - cmp [rx_buffer+ssh_header.message_code], SSH_MSG_KEX_DH_GEX_REPLY + cmp [con.rx_buffer.message_code], SSH_MSG_KEX_DH_GEX_REPLY jne .proto_err - DEBUGF 1, "Received GEX Reply\n" + DEBUGF 2, "Received GEX Reply\n" ;-------------------------------- -; HASH: string K_S, the host key - mov esi, rx_buffer+sizeof.ssh_header +; HASH: string K_S, the host key + mov esi, con.rx_buffer+sizeof.ssh_packet_header mov edx, [esi] bswap edx add edx, 4 lea ebx, [esi+edx] push ebx - call sha256_update + invoke sha256_update, con.temp_ctx, esi, edx ;-------------------------------------------------------------------------- -; HASH: uint32 min, minimal size in bits of an acceptable group -; uint32 n, preferred size in bits of the group the server will send -; uint32 max, maximal size in bits of an acceptable group - mov esi, ssh_gex_req+sizeof.ssh_header-ssh_header.message_code - mov edx, 12 - call sha256_update +; HASH: uint32 min, minimal size in bits of an acceptable group +; uint32 n, preferred size in bits of the group the server will send +; uint32 max, maximal size in bits of an acceptable group + invoke sha256_update, con.temp_ctx, ssh_gex_req+sizeof.ssh_packet_header-ssh_packet_header.message_code, 12 ;---------------------------- -; HASH: mpint p, safe prime - mov esi, dh_p +; HASH: mpint p, safe prime + mov esi, con.dh_p mov edi, mpint_tmp call mpint_to_big_endian lea edx, [eax+4] - mov esi, mpint_tmp - call sha256_update + invoke sha256_update, con.temp_ctx, mpint_tmp, edx ;---------------------------------------- -; HASH: mpint g, generator for subgroup - mov esi, dh_g +; HASH: mpint g, generator for subgroup + mov esi, con.dh_g mov edi, mpint_tmp call mpint_to_big_endian lea edx, [eax+4] - mov esi, mpint_tmp - call sha256_update + invoke sha256_update, con.temp_ctx, mpint_tmp, edx ;--------------------------------------------------- -; HASH: mpint e, exchange value sent by the client - mov esi, tx_buffer+sizeof.ssh_header +; HASH: mpint e, exchange value sent by the client + mov esi, con.tx_buffer+sizeof.ssh_packet_header mov edx, [esi] bswap edx add edx, 4 - call sha256_update + invoke sha256_update, con.temp_ctx, esi, edx ;--------------------------------------------------- -; HASH: mpint f, exchange value sent by the server +; HASH: mpint f, exchange value sent by the server mov esi, [esp] mov edx, [esi] bswap edx add edx, 4 - call sha256_update + invoke sha256_update, con.temp_ctx, esi, edx pop esi - mov edi, dh_f + mov edi, con.dh_f call mpint_to_little_endian DEBUGF 1, "DH f: " - stdcall mpint_print, dh_f + stdcall mpint_print, con.dh_f - mov edi, dh_signature + mov edi, con.dh_signature call mpint_to_little_endian DEBUGF 1, "DH signature: " - stdcall mpint_print, dh_signature + stdcall mpint_print, con.dh_signature ;-------------------------------------- ; Calculate shared secret K = f^x mod p - stdcall mpint_modexp, rx_buffer, dh_f, dh_x, dh_p - stdcall mpint_length, rx_buffer + stdcall mpint_modexp, con.rx_buffer, con.dh_f, con.dh_x, con.dh_p + stdcall mpint_length, con.rx_buffer DEBUGF 1, "DH K: " - stdcall mpint_print, rx_buffer + stdcall mpint_print, con.rx_buffer ; We always need it in big endian order, so store it as such. - mov edi, dh_K - mov esi, rx_buffer + mov edi, con.dh_K + mov esi, con.rx_buffer call mpint_to_big_endian - mov [dh_K.length], eax + mov [con.dh_K_length], eax ;----------------------------------- -; HASH: mpint K, the shared secret - mov edx, [dh_K.length] +; HASH: mpint K, the shared secret + mov edx, [con.dh_K_length] add edx, 4 - mov esi, dh_K - call sha256_update + invoke sha256_update, con.temp_ctx, con.dh_K, edx ;------------------------------- ; Finalize the exchange hash (H) - mov edi, dh_H - call sha256_final + invoke sha256_final, con.temp_ctx + mov esi, con.temp_ctx.hash + mov edi, con.dh_H + mov ecx, SHA256_HASH_SIZE/4 + rep movsd DEBUGF 1, "Exchange hash H: " - stdcall dump_256bit_hex, dh_H + stdcall dump_hex, con.dh_H, 8 ; TODO: skip this block when re-keying - mov esi, dh_H - mov edi, session_id - mov ecx, 32/4 + mov esi, con.dh_H + mov edi, con.session_id + mov ecx, SHA256_HASH_SIZE/4 rep movsd ;--------------- ; Calculate keys -; TODO: re-use partial hash of K and H +; First, calculate partial hash of K and H so we can re-use it for every key. + + invoke sha256_init, con.k_h_ctx + + mov edx, [con.dh_K_length] + add edx, 4 + invoke sha256_update, con.k_h_ctx, con.dh_K, edx + invoke sha256_update, con.k_h_ctx, con.dh_H, 32 ;--------------------------------------------------------------- ; Initial IV client to server: HASH(K || H || "A" || session_id) - call sha256_init - mov edx, [dh_K.length] - add edx, 4 - mov esi, dh_K - call sha256_update - mov edx, 32 - mov esi, dh_H - call sha256_update - mov edx, 1 - mov esi, str_A - call sha256_update - mov edx, 32 - mov esi, session_id - call sha256_update - mov edi, tx_iv - call sha256_final + mov esi, con.k_h_ctx + mov edi, con.temp_ctx + mov ecx, sizeof.ctx_sha224256/4 + rep movsd + mov [con.session_id_prefix], 'A' + invoke sha256_update, con.temp_ctx, con.session_id_prefix, 32+1 + invoke sha256_final, con.temp_ctx.hash + mov edi, con.tx_iv + mov esi, con.temp_ctx + mov ecx, SHA256_HASH_SIZE/4 + rep movsd DEBUGF 1, "Remote IV: " - stdcall dump_256bit_hex, tx_iv + stdcall dump_hex, con.tx_iv, 8 ;--------------------------------------------------------------- ; Initial IV server to client: HASH(K || H || "B" || session_id) - call sha256_init - mov edx, [dh_K.length] - add edx, 4 - mov esi, dh_K - call sha256_update - mov edx, 32 - mov esi, dh_H - call sha256_update - mov edx, 1 - mov esi, str_B - call sha256_update - mov edx, 32 - mov esi, session_id - call sha256_update - mov edi, rx_iv - call sha256_final + mov esi, con.k_h_ctx + mov edi, con.temp_ctx + mov ecx, sizeof.ctx_sha224256/4 + rep movsd + inc [con.session_id_prefix] + invoke sha256_update, con.temp_ctx, con.session_id_prefix, 32+1 + invoke sha256_final, con.temp_ctx + mov edi, con.rx_iv + mov esi, con.temp_ctx + mov ecx, SHA256_HASH_SIZE/4 + rep movsd DEBUGF 1, "Local IV: " - stdcall dump_256bit_hex, rx_iv + stdcall dump_hex, con.rx_iv, 8 ;------------------------------------------------------------------- ; Encryption key client to server: HASH(K || H || "C" || session_id) - call sha256_init - mov edx, [dh_K.length] - add edx, 4 - mov esi, dh_K - call sha256_update - mov edx, 32 - mov esi, dh_H - call sha256_update - mov edx, 1 - mov esi, str_C - call sha256_update - mov edx, 32 - mov esi, session_id - call sha256_update - mov edi, tx_enc_key - call sha256_final + mov esi, con.k_h_ctx + mov edi, con.temp_ctx + mov ecx, sizeof.ctx_sha224256/4 + rep movsd + inc [con.session_id_prefix] + invoke sha256_update, con.temp_ctx, con.session_id_prefix, 32+1 + invoke sha256_final, con.temp_ctx + mov edi, con.tx_enc_key + mov esi, con.temp_ctx + mov ecx, SHA256_HASH_SIZE/4 + rep movsd DEBUGF 1, "Remote key: " - stdcall dump_256bit_hex, tx_enc_key + stdcall dump_hex, con.tx_enc_key, 8 ;------------------------------------------------------------------- ; Encryption key server to client: HASH(K || H || "D" || session_id) - call sha256_init - mov edx, [dh_K.length] - add edx, 4 - mov esi, dh_K - call sha256_update - mov edx, 32 - mov esi, dh_H - call sha256_update - mov edx, 1 - mov esi, str_D - call sha256_update - mov edx, 32 - mov esi, session_id - call sha256_update - mov edi, rx_enc_key - call sha256_final + mov esi, con.k_h_ctx + mov edi, con.temp_ctx + mov ecx, sizeof.ctx_sha224256/4 + rep movsd + inc [con.session_id_prefix] + invoke sha256_update, con.temp_ctx, con.session_id_prefix, 32+1 + invoke sha256_final, con.temp_ctx + mov edi, con.rx_enc_key + mov esi, con.temp_ctx + mov ecx, SHA256_HASH_SIZE/4 + rep movsd DEBUGF 1, "Local key: " - stdcall dump_256bit_hex, rx_enc_key + stdcall dump_hex, con.rx_enc_key, 8 ;------------------------------------------------------------------ ; Integrity key client to server: HASH(K || H || "E" || session_id) - call sha256_init - mov edx, [dh_K.length] - add edx, 4 - mov esi, dh_K - call sha256_update - mov edx, 32 - mov esi, dh_H - call sha256_update - mov edx, 1 - mov esi, str_E - call sha256_update - mov edx, 32 - mov esi, session_id - call sha256_update - mov edi, tx_int_key - call sha256_final + mov esi, con.k_h_ctx + mov edi, con.temp_ctx + mov ecx, sizeof.ctx_sha224256/4 + rep movsd + inc [con.session_id_prefix] + invoke sha256_update, con.temp_ctx, con.session_id_prefix, 32+1 + invoke sha256_final, con.temp_ctx + mov edi, con.tx_int_key + mov esi, con.temp_ctx + mov ecx, SHA256_HASH_SIZE/4 + rep movsd DEBUGF 1, "Remote Integrity key: " - stdcall dump_256bit_hex, tx_int_key + stdcall dump_hex, con.tx_int_key, 8 ;------------------------------------------------------------------ ; Integrity key server to client: HASH(K || H || "F" || session_id) - call sha256_init - mov edx, [dh_K.length] - add edx, 4 - mov esi, dh_K - call sha256_update - mov edx, 32 - mov esi, dh_H - call sha256_update - mov edx, 1 - mov esi, str_F - call sha256_update - mov edx, 32 - mov esi, session_id - call sha256_update - mov edi, rx_int_key - call sha256_final + mov esi, con.k_h_ctx + mov edi, con.temp_ctx + mov ecx, sizeof.ctx_sha224256/4 + rep movsd + inc [con.session_id_prefix] + invoke sha256_update, con.temp_ctx, con.session_id_prefix, 32+1 + invoke sha256_final, con.temp_ctx + mov edi, con.rx_int_key + mov esi, con.temp_ctx + mov ecx, SHA256_HASH_SIZE/4 + rep movsd DEBUGF 1, "Local Integrity key: " - stdcall dump_256bit_hex, rx_int_key + stdcall dump_hex, con.rx_int_key, 8 ;------------------------------------- ; << Parse Diffie-Hellman New Keys MSG - stdcall ssh_recv_packet, [socketnum], rx_buffer, BUFFERSIZE, 0 + stdcall ssh_recv_packet, con, 0 cmp eax, -1 je .socket_err - cmp [rx_buffer+ssh_header.message_code], SSH_MSG_NEWKEYS + cmp [con.rx_buffer.message_code], SSH_MSG_NEWKEYS jne .proto_err - DEBUGF 1, "Received New Keys\n" + DEBUGF 2, "Received New Keys\n" ;------------------------------- ; >> Reply with New Keys message - stdcall ssh_send_packet, [socketnum], ssh_new_keys, ssh_new_keys.length, 0 + stdcall ssh_send_packet, con, ssh_new_keys, ssh_new_keys.length, 0 xor eax, eax ret .socket_err: - DEBUGF 2, "Socket error during key exchange!\n" + DEBUGF 3, "Socket error during key exchange!\n" mov eax, 1 ret .proto_err: - DEBUGF 2, "Protocol error during key exchange!\n" + DEBUGF 3, "Protocol error during key exchange!\n" mov eax, 2 ret endp - -proc dump_256bit_hex _ptr - pushad - - mov esi, [_ptr] - mov ecx, 8 - .next_dword: - lodsd - bswap eax - DEBUGF 1,'%x',eax - loop .next_dword - DEBUGF 1,'\n' - - popad - ret -endp - -iglobal - - str_A db 'A' - str_B db 'B' - str_C db 'C' - str_D db 'D' - str_E db 'E' - str_F db 'F' - -endg \ No newline at end of file diff --git a/programs/network/ssh/hmac_sha256.inc b/programs/network/ssh/hmac_sha256.inc new file mode 100644 index 0000000000..61c2d0ffb4 --- /dev/null +++ b/programs/network/ssh/hmac_sha256.inc @@ -0,0 +1,171 @@ +; hmac.inc - HMAC: Keyed-Hashing for Message Authentication +; +; Copyright (C) 2016 Denis Karpenko +; Copyright (C) 2016 Jeffrey Amelynck +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + +; Main concept: +; To compute HMAC over the data `text' we perform +; H(K XOR opad, H(K XOR ipad, text)) + +struct hmac_sha256_context + hash rb SHA256_HASH_SIZE + ipad_ctx ctx_sha224256 + opad_ctx ctx_sha224256 +ends + +; We will precompute partial hashes of K XOR ipad and K XOR opad, +; and store them in the context structure. + +proc hmac_sha256_setkey ctx, key, key_length + +locals + k_temp rb SHA224256_BLOCK_SIZE +endl + + pusha + +; input esi = key, ecx=key_length + mov ecx, [key_length] + cmp ecx, SHA224256_BLOCK_SIZE + ja .hash_it +; Key is smaller then or equal to blocksize, +; copy key to ipad + mov esi, [key] + lea edi, [k_temp] + rep movsb + mov ecx, SHA224256_BLOCK_SIZE + sub ecx, [key_length] + jz .finish +; append zeros to the key + xor al, al + rep stosb + jmp .finish + +; Given key is larger then key size, hash it + .hash_it: + invoke sha256_init, [ctx] + invoke sha256_update, [ctx], [key], [key_length] + invoke sha256_final, [ctx] + mov esi, [ctx] + lea edi, [k_temp] + mov ecx, SHA256_HASH_SIZE/4 + rep movsd + xor eax, eax + mov ecx, (SHA224256_BLOCK_SIZE-SHA256_HASH_SIZE)/4 + rep stosd + + .finish: +; xor ipad buffer with 0x36363... + lea esi, [k_temp] + mov ecx, SHA224256_BLOCK_SIZE/4 + @@: + xor dword[esi], 0x36363636 ; ipad constant + add esi, 4 + dec ecx + jnz @r + +; Init our hash with k_xor_ipad + mov ebx, [ctx] + lea edi, [ebx+hmac_sha256_context.ipad_ctx] + invoke sha256_init, edi + + lea esi, [k_temp] + DEBUGF 1, "HASH: " + stdcall dump_hex, esi, SHA224256_BLOCK_SIZE/4 + + mov ebx, [ctx] + lea edi, [ebx+hmac_sha256_context.ipad_ctx] + invoke sha256_update, edi, esi, SHA224256_BLOCK_SIZE + +; xor opad buffer with 0x5c5c5... + lea esi, [k_temp] + mov ecx, SHA224256_BLOCK_SIZE/4 + @@: + xor dword[esi], 0x36363636 xor 0x5c5c5c5c ; opad constant + add esi, 4 + dec ecx + jnz @r + +; Init our hash with k_xor_opad + mov ebx, [ctx] + lea edi, [ebx+hmac_sha256_context.opad_ctx] + invoke sha256_init, edi + + lea esi, [k_temp] + DEBUGF 1, "HASH: " + stdcall dump_hex, esi, SHA224256_BLOCK_SIZE/4 + + mov ebx, [ctx] + lea edi, [ebx+hmac_sha256_context.opad_ctx] + invoke sha256_update, edi, esi, SHA224256_BLOCK_SIZE + + popa + ret + +endp + +; Copy our pre-computed partial hashes to the stack, complete and finalize them. +; TODO: prevent unnescessary copying of output hash +; TODO: remove unnescessary pushing/popping + +proc hmac_sha256 ctx, _data, _length + +locals + inner_ctx ctx_sha224256 + outer_ctx ctx_sha224256 +endl + + pusha + DEBUGF 1, "HMAC: " + mov ebx, [_length] + shr ebx, 2 + stdcall dump_hex, [_data], ebx + +; Copy partial hashes of ipad and opad to our temporary buffers + mov esi, [ctx] + lea esi, [esi+hmac_sha256_context.ipad_ctx] + lea edi, [inner_ctx] +repeat (sizeof.ctx_sha224256)/4*2 + movsd +end repeat + +; Append provided data to inner hash and finalize + lea ebx, [inner_ctx] + invoke sha256_update, ebx, [_data], [_length] + lea ebx, [inner_ctx] + invoke sha256_final, ebx + + DEBUGF 1, "Inner Hash: " + lea esi, [inner_ctx.hash] + stdcall dump_hex, esi, SHA256_HASH_SIZE/4 + +; Calculate outer hash + lea ebx, [outer_ctx] + lea esi, [inner_ctx.hash] + invoke sha256_update, ebx, esi, SHA256_HASH_SIZE + lea ebx, [outer_ctx] + invoke sha256_final, ebx +; Copy output hash to ctx structure ; FIXME + lea esi, [outer_ctx.hash] + mov edi, [ctx] +repeat SHA256_HASH_SIZE/4 + movsd +end repeat + + popa + ret + +endp diff --git a/programs/network/ssh/mpint.inc b/programs/network/ssh/mpint.inc index 4ae0aaaeb6..3b729581f6 100644 --- a/programs/network/ssh/mpint.inc +++ b/programs/network/ssh/mpint.inc @@ -541,13 +541,13 @@ proc mpint_modexp uses edi eax ebx ecx, dst, base, exp, mod ret .mod_zero: - DEBUGF 1, "modexp with modulo 0\n" + DEBUGF 3, "modexp with modulo 0\n" ; if mod is zero, result = 0 stdcall mpint_zero, [dst] ret .exp_zero: - DEBUGF 1, "modexp with exponent 0\n" + DEBUGF 3, "modexp with exponent 0\n" ; if exponent is zero, result = 1 stdcall mpint_zero, [dst] mov eax, [dst] @@ -556,7 +556,7 @@ proc mpint_modexp uses edi eax ebx ecx, dst, base, exp, mod ret .invalid: - DEBUGF 1, "modexp: Invalid input!\n" + DEBUGF 3, "modexp: Invalid input!\n" ret endp \ No newline at end of file diff --git a/programs/network/ssh/ssh.asm b/programs/network/ssh/ssh.asm index 70f39fbcb9..c8b3ddf1ff 100644 --- a/programs/network/ssh/ssh.asm +++ b/programs/network/ssh/ssh.asm @@ -18,7 +18,7 @@ format binary as "" __DEBUG__ = 1 -__DEBUG_LEVEL__ = 1 +__DEBUG_LEVEL__ = 2 ; 1: Extreme debugging, 2: Debugging, 3: Errors only BUFFERSIZE = 4096 MAX_BITS = 8192 @@ -33,16 +33,17 @@ use32 dd i_end ; initialized size dd mem+4096 ; required memory dd mem+4096 ; stack pointer - dd hostname ; parameters + dd params ; parameters dd 0 ; path include '../../macros.inc' +;include '../../struct.inc' purge mov,add,sub include '../../proc32.inc' include '../../dll.inc' include '../../debug-fdo.inc' include '../../network.inc' -;include '../../develop/libraries/libcrash/trunk/libcrash.inc' +include '../../develop/libraries/libcrash/trunk/libcrash.inc' include 'mcodes.inc' include 'ssh_transport.inc' @@ -53,7 +54,7 @@ include 'random.inc' include 'aes256.inc' include 'aes256-ctr.inc' include 'aes256-cbc.inc' -include '../../fs/kfar/trunk/kfar_arc/sha256.inc' +include 'hmac_sha256.inc' ; macros for network byte order macro dd_n op { @@ -68,24 +69,120 @@ macro dw_n op { (((op) and 000FFh) shl 8) } +proc dump_hex _ptr, _length +if __DEBUG_LEVEL__ <= 1 + pushad + + mov esi, [_ptr] + mov ecx, [_length] + .next_dword: + lodsd + bswap eax + DEBUGF 1,'%x',eax + loop .next_dword + DEBUGF 1,'\n' + + popad + ret +end if +endp + +struct ssh_connection + +; Connection + + hostname rb 1024 + + socketnum dd ? + + sockaddr dw ? ; Address family + port dw ? + ip dd ? + rb 10 + +; Encryption/Decryption + + rx_crypt_proc dd ? + tx_crypt_proc dd ? + rx_crypt_ctx_ptr dd ? + tx_crypt_ctx_ptr dd ? + rx_crypt_blocksize dd ? + tx_crypt_blocksize dd ? + +; Message authentication + + rx_mac_proc dd ? + tx_mac_proc dd ? + rx_mac_ctx hmac_sha256_context + tx_mac_ctx hmac_sha256_context + rx_mac_length dd ? + tx_mac_length dd ? + +; Buffers + + rx_seq dd ? ; Packet sequence number for MAC + rx_buffer ssh_packet_header + rb BUFFERSIZE-sizeof.ssh_packet_header + + tx_seq dd ? ; Packet sequence number for MAC + tx_buffer ssh_packet_header + rb BUFFERSIZE-sizeof.ssh_packet_header + + send_data dw ? + +; Output from key exchange + dh_K dd ? ; Shared Secret (Big endian) + rb MAX_BITS/8 + dh_K_length dd ? ; Length in little endian + + dh_H rb 32 ; Exchange Hash + session_id_prefix db ? + session_id rb 32 + rx_iv rb 32 ; Rx initialisation vector + tx_iv rb 32 ; Tx initialisation vector + rx_enc_key rb 32 ; Rx encryption key + tx_enc_key rb 32 ; Tx encryption key + rx_int_key rb 32 ; Rx integrity key + tx_int_key rb 32 ; Tx integrity key + +; Diffie Hellman + dh_p dd ? + rb MAX_BITS/8 + dh_g dd ? + rb MAX_BITS/8 + dh_x dd ? + rb MAX_BITS/8 + dh_e dd ? + rb MAX_BITS/8 + dh_f dd ? + rb MAX_BITS/8 + + dh_signature dd ? + rb MAX_BITS/8 + + temp_ctx ctx_sha224256 + k_h_ctx ctx_sha224256 + +ends + start: mcall 68, 11 ; Init heap - DEBUGF 1, "SSH: Loading libraries\n" + DEBUGF 2, "SSH: Loading libraries\n" stdcall dll.Load, @IMPORT test eax, eax jnz exit - DEBUGF 1, "SSH: Init PRNG\n" + DEBUGF 2, "SSH: Init PRNG\n" call init_random - DEBUGF 1, "SSH: Init Console\n" + DEBUGF 2, "SSH: Init Console\n" invoke con_start, 1 invoke con_init, 80, 25, 80, 25, title -; Check for parameters - cmp byte[hostname], 0 - jne resolve +; Check for parameters TODO +; cmp byte[params], 0 +; jne resolve main: invoke con_cls @@ -96,7 +193,7 @@ prompt: ; write prompt invoke con_write_asciiz, str2 ; read string - mov esi, hostname + mov esi, con.hostname invoke con_gets, esi, 256 ; check for exit test eax, eax @@ -105,10 +202,11 @@ prompt: jz done resolve: - mov [sockaddr1.port], 22 shl 8 + mov [con.sockaddr], AF_INET4 + mov [con.port], 22 shl 8 ; delete terminating '\n' - mov esi, hostname + mov esi, con.hostname @@: lodsb cmp al, ':' @@ -130,21 +228,21 @@ resolve: jb hostname_error cmp al, 9 ja hostname_error - lea ebx, [ebx*4 + ebx] + lea ebx, [ebx*4+ebx] shl ebx, 1 add ebx, eax jmp .portloop .port_done: xchg bl, bh - mov [sockaddr1.port], bx + mov [con.port], bx .done: ; resolve name push esp ; reserve stack place push esp - invoke getaddrinfo, hostname, 0, 0 + invoke getaddrinfo, con.hostname, 0, 0 pop esi ; test for error test eax, eax @@ -152,7 +250,7 @@ resolve: invoke con_cls invoke con_write_asciiz, str3 - invoke con_write_asciiz, hostname + invoke con_write_asciiz, con.hostname ; write results invoke con_write_asciiz, str8 @@ -160,7 +258,7 @@ resolve: ; convert IP address to decimal notation mov eax, [esi+addrinfo.ai_addr] mov eax, [eax+sockaddr_in.sin_addr] - mov [sockaddr1.ip], eax + mov [con.ip], eax invoke inet_ntoa, eax ; write result invoke con_write_asciiz, eax @@ -176,47 +274,56 @@ resolve: mcall socket, AF_INET4, SOCK_STREAM, 0 cmp eax, -1 jz socket_err - mov [socketnum], eax + mov [con.socketnum], eax ; Connect - mcall connect, [socketnum], sockaddr1, 18 + DEBUGF 2, "Connecting to server\n" + mcall connect, [con.socketnum], con.sockaddr, 18 test eax, eax jnz socket_err -; Start calculating hash meanwhile - call sha256_init +; Start calculating hash + invoke sha256_init, con.temp_ctx ; HASH: string V_C, the client's version string (CR and NL excluded) - mov esi, ssh_ident_ha - mov edx, ssh_ident.length+4-2 - call sha256_update + invoke sha256_update, con.temp_ctx, ssh_ident_ha, ssh_ident.length+4-2 -; Send our identification string - DEBUGF 1, "Sending ID string\n" - mcall send, [socketnum], ssh_ident, ssh_ident.length, 0 +; >> Send our identification string + DEBUGF 2, "Sending ID string\n" + mcall send, [con.socketnum], ssh_ident, ssh_ident.length, 0 cmp eax, -1 je socket_err -; Check protocol version of server - mcall recv, [socketnum], rx_buffer, BUFFERSIZE, 0 +; << Check protocol version of server + mcall recv, [con.socketnum], con.rx_buffer, BUFFERSIZE, 0 cmp eax, -1 je socket_err - DEBUGF 1, "Received ID string\n" - cmp dword[rx_buffer], "SSH-" + DEBUGF 2, "Received ID string\n" + cmp dword[con.rx_buffer], "SSH-" jne proto_err - cmp dword[rx_buffer+4], "2.0-" + cmp dword[con.rx_buffer+4], "2.0-" jne proto_err ; HASH: string V_S, the server's version string (CR and NL excluded) lea edx, [eax+2] sub eax, 2 bswap eax - mov [rx_buffer-4], eax - mov esi, rx_buffer-4 - call sha256_update + mov dword[con.rx_buffer-4], eax + invoke sha256_update, con.temp_ctx, con.rx_buffer-4, edx -; Key Exchange init - DEBUGF 1, "Sending KEX init\n" +; >> Key Exchange init + mov [con.rx_seq], 0 + mov [con.tx_seq], 0 + mov [con.rx_crypt_blocksize], 4 ; minimum blocksize + mov [con.tx_crypt_blocksize], 4 + mov [con.rx_crypt_proc], 0 + mov [con.tx_crypt_proc], 0 + mov [con.rx_mac_proc], 0 + mov [con.tx_mac_proc], 0 + mov [con.rx_mac_length], 0 + mov [con.tx_mac_length], 0 + + DEBUGF 2, "Sending KEX init\n" mov edi, ssh_kex.cookie call MBRandom stosd @@ -226,32 +333,31 @@ resolve: stosd call MBRandom stosd - stdcall ssh_send_packet, [socketnum], ssh_kex, ssh_kex.length, 0 + stdcall ssh_send_packet, con, ssh_kex, ssh_kex.length, 0 cmp eax, -1 je socket_err ; HASH: string I_C, the payload of the client's SSH_MSG_KEXINIT - mov eax, [tx_buffer+ssh_header.length] + mov eax, dword[con.tx_buffer+ssh_packet_header.packet_length] bswap eax - movzx ebx, [tx_buffer+ssh_header.padding] + movzx ebx, [con.tx_buffer+ssh_packet_header.padding_length] sub eax, ebx dec eax lea edx, [eax+4] bswap eax - mov [tx_buffer+1], eax - mov esi, tx_buffer+1 - call sha256_update + mov dword[con.tx_buffer+1], eax + invoke sha256_update, con.temp_ctx, con.tx_buffer+1, edx -; Check key exchange init of server - stdcall ssh_recv_packet, [socketnum], rx_buffer, BUFFERSIZE, 0 +; << Check key exchange init of server + stdcall ssh_recv_packet, con, 0 cmp eax, -1 je socket_err - cmp [rx_buffer+ssh_header.message_code], SSH_MSG_KEXINIT + cmp [con.rx_buffer.message_code], SSH_MSG_KEXINIT jne proto_err - DEBUGF 1, "Received KEX init\n" + DEBUGF 2, "Received KEX init\n" - lea esi, [rx_buffer+sizeof.ssh_header+16] + lea esi, [con.rx_buffer+sizeof.ssh_packet_header+16] lodsd bswap eax DEBUGF 1, "kex_algorithms: %s\n", esi @@ -295,38 +401,144 @@ resolve: lodsb DEBUGF 1, "KEX First Packet Follows: %u\n", al - ; TODO + ; TODO: parse this structure and init procedures accordingly ; HASH: string I_S, the payload of the servers's SSH_MSG_KEXINIT - mov eax, [rx_buffer+ssh_header.length] - movzx ebx, [rx_buffer+ssh_header.padding] + mov eax, dword[con.rx_buffer+ssh_packet_header.packet_length] + movzx ebx, [con.rx_buffer+ssh_packet_header.padding_length] sub eax, ebx dec eax lea edx, [eax+4] bswap eax - mov [rx_buffer+sizeof.ssh_header-5], eax - mov esi, rx_buffer+sizeof.ssh_header-5 - call sha256_update + mov dword[con.rx_buffer+sizeof.ssh_packet_header-5], eax + invoke sha256_update, con.temp_ctx, con.rx_buffer+sizeof.ssh_packet_header-5, edx ; Exchange keys with the server + stdcall dh_gex test eax, eax jnz exit ; Set keys - DEBUGF 1, "SSH: Init encryption\n" - stdcall aes256_cbc_init, rx_iv - mov [rx_context], eax - stdcall aes256_set_encrypt_key, [rx_context], rx_enc_key - mov [decrypt_proc], aes256_cbc_decrypt - mov [rx_blocksize], 32 - DEBUGF 1, "SSH: Init decryption\n" - stdcall aes256_cbc_init, tx_iv - mov [tx_context], eax - stdcall aes256_set_decrypt_key, [tx_context], tx_enc_key - mov [encrypt_proc], aes256_cbc_encrypt - mov [tx_blocksize], 32 + DEBUGF 2, "SSH: Setting encryption keys\n" + + stdcall aes256_cbc_init, con.rx_iv + mov [con.rx_crypt_ctx_ptr], eax + + stdcall aes256_set_decrypt_key, eax, con.rx_enc_key + mov [con.rx_crypt_proc], aes256_cbc_decrypt + mov [con.rx_crypt_blocksize], AES256_BLOCKSIZE + + stdcall aes256_cbc_init, con.tx_iv + mov [con.tx_crypt_ctx_ptr], eax + + stdcall aes256_set_encrypt_key, eax, con.tx_enc_key + mov [con.tx_crypt_proc], aes256_cbc_encrypt + mov [con.tx_crypt_blocksize], AES256_BLOCKSIZE + + stdcall hmac_sha256_setkey, con.rx_mac_ctx, con.rx_int_key, SHA256_HASH_SIZE + mov [con.rx_mac_proc], hmac_sha256 + mov [con.rx_mac_length], SHA256_HASH_SIZE + + stdcall hmac_sha256_setkey, con.tx_mac_ctx, con.tx_int_key, SHA256_HASH_SIZE + mov [con.tx_mac_proc], hmac_sha256 + mov [con.tx_mac_length], SHA256_HASH_SIZE + +; TODO: erase all keys from memory and free the memory + +; >> Request service (user-auth) + + DEBUGF 2, "SSH: Requesting service\n" + + stdcall ssh_send_packet, con, ssh_request_service, ssh_request_service.length, 0 + cmp eax, -1 + je socket_err + +; << Check for service acceptance + + stdcall ssh_recv_packet, con, 0 + cmp eax, -1 + je socket_err + + cmp [con.rx_buffer.message_code], SSH_MSG_SERVICE_ACCEPT + jne proto_err + +; >> Request user authentication + +; TODO: Request username from the user +; invoke con_write_asciiz, str12 +; invoke con_gets, username, 256 +; test eax, eax +; jz done + +; TODO: implement password authentication + + DEBUGF 2, "SSH: User authentication\n" + + stdcall ssh_send_packet, con, ssh_request_userauth, ssh_request_userauth.length, 0 + cmp eax, -1 + je socket_err + +; << Check for userauth acceptance + + stdcall ssh_recv_packet, con, 0 + cmp eax, -1 + je socket_err + + cmp [con.rx_buffer.message_code], SSH_MSG_USERAUTH_SUCCESS + jne proto_err + +; >> Open channel + + DEBUGF 2, "SSH: Open channel\n" + + stdcall ssh_send_packet, con, ssh_channel_open, ssh_channel_open.length, 0 + cmp eax, -1 + je socket_err + +; << Check for channel open confirmation + + stdcall ssh_recv_packet, con, 0 + cmp eax, -1 + je socket_err + + cmp [con.rx_buffer.message_code], SSH_MSG_CHANNEL_OPEN_CONFIRMATION + jne proto_err + +; >> Channel request: pty + + DEBUGF 2, "SSH: Request pty\n" + + stdcall ssh_send_packet, con, ssh_channel_request, ssh_channel_request.length, 0 + cmp eax, -1 + je socket_err + +; << Check for channel request confirmation + + stdcall ssh_recv_packet, con, 0 + cmp eax, -1 + je socket_err + + cmp [con.rx_buffer.message_code], SSH_MSG_CHANNEL_SUCCESS + jne proto_err + +; >> Channel request: shell + + DEBUGF 2, "SSH: Request shell\n" + + stdcall ssh_send_packet, con, ssh_shell_request, ssh_shell_request.length, 0 + cmp eax, -1 + je socket_err + +; << Check for channel request confirmation (FIXME: this may not be first packet!) + +; stdcall ssh_recv_packet, con, 0 +; cmp eax, -1 +; je socket_err + +; cmp [con.rx_buffer.message_code], SSH_MSG_CHANNEL_SUCCESS +; jne proto_err ; Launch network thread mcall 18, 7 @@ -340,13 +552,26 @@ mainloop: test eax, 0x200 ; con window closed? jnz exit - stdcall ssh_recv_packet, [socketnum], rx_buffer, BUFFERSIZE, 0 - cmp eax, -1 - je closed + stdcall ssh_recv_packet, con, 0 + cmp eax, 0 + jbe closed + cmp [con.rx_buffer.message_code], SSH_MSG_CHANNEL_DATA + jne .dump + + mov eax, dword[con.rx_buffer.message_code+5] + bswap eax DEBUGF 1, 'SSH: got %u bytes of data !\n', eax - mov esi, rx_buffer + lea esi, [con.rx_buffer.message_code+5+4] + mov ecx, eax + lea edi, [esi + eax] + mov byte [edi], 0 + invoke con_write_asciiz, esi + jmp mainloop + + .dump: + lea esi, [con.rx_buffer] mov ecx, eax pusha @@: @@ -355,23 +580,22 @@ mainloop: dec ecx jnz @r popa - lea edi, [esi + eax] - mov byte [edi], 0 - invoke con_write_asciiz, esi + DEBUGF 1, "\n" jmp mainloop + proto_err: - DEBUGF 1, "SSH: protocol error\n" + DEBUGF 3, "SSH: protocol error\n" invoke con_write_asciiz, str7 jmp prompt socket_err: - DEBUGF 1, "SSH: socket error %d\n", ebx + DEBUGF 3, "SSH: socket error %d\n", ebx invoke con_write_asciiz, str6 jmp prompt dns_error: - DEBUGF 1, "SSH: DNS error %d\n", eax + DEBUGF 3, "SSH: DNS error %d\n", eax invoke con_write_asciiz, str5 jmp prompt @@ -386,8 +610,8 @@ closed: done: invoke con_exit, 1 exit: - DEBUGF 1, "SSH: Exiting\n" - mcall close, [socketnum] + DEBUGF 3, "SSH: Exiting\n" + mcall close, [con.socketnum] mcall -1 @@ -395,14 +619,8 @@ thread: mcall 40, 0 .loop: invoke con_getch2 - mov [send_data], ax - xor esi, esi - inc esi - test al, al - jnz @f - inc esi - @@: - stdcall ssh_send_packet, [socketnum], send_data, 0 + mov [ssh_channel_data+9], al + stdcall ssh_send_packet, con, ssh_channel_data, ssh_channel_data.length, 0 invoke con_get_flags test eax, 0x200 ; con window closed? @@ -423,17 +641,12 @@ str8 db ' (',0 str9 db ')',10,0 str10 db 'Invalid hostname.',10,10,0 str11 db 10,'Remote host closed the connection.',10,10,0 - -sockaddr1: - dw AF_INET4 - .port dw 0 - .ip dd 0 - rb 10 +str12 db 'Enter username: ',0 ssh_ident_ha: dd_n (ssh_ident.length-2) ssh_ident: - db "SSH-2.0-KolibriOS_SSH_0.01",13,10 + db "SSH-2.0-KolibriOS_SSH_0.02",13,10 .length = $ - ssh_ident ssh_kex: @@ -479,9 +692,9 @@ ssh_kex: ssh_gex_req: db SSH_MSG_KEX_DH_GEX_REQUEST - dd_n 128 ; DH GEX min - dd_n 256 ; DH GEX number of bits - dd_n 512 ; DH GEX Max + dd_n 128 ; DH GEX min + dd_n 256 ; DH GEX number of bits + dd_n 512 ; DH GEX Max .length = $ - ssh_gex_req @@ -490,16 +703,75 @@ ssh_new_keys: .length = $ - ssh_new_keys +ssh_request_service: + db SSH_MSG_SERVICE_REQUEST + dd_n 12 ; String length + db "ssh-userauth" ; Service name + .length = $ - ssh_request_service + + +ssh_request_userauth: + db SSH_MSG_USERAUTH_REQUEST + dd_n 12 + dd_n 8 + db "username" ; user name in ISO-10646 UTF-8 encoding [RFC3629] + dd_n 14 + db "ssh-connection" ; service name in US-ASCII + dd_n 4 + db "none" ; method name in US-ASCII +; Other options: publickey, password, hostbased + .length = $ - ssh_request_userauth + + +ssh_channel_open: + db SSH_MSG_CHANNEL_OPEN + dd_n 7 + db "session" + dd_n 0 ; Sender channel + dd_n 1024 ; Initial window size + dd_n 1024 ; maximum packet size + .length = $ - ssh_channel_open + +ssh_channel_request: + db SSH_MSG_CHANNEL_REQUEST + dd_n 0 ; Recipient channel + dd_n 7 + db "pty-req" + db 1 ; Bool: want reply + dd_n 5 + db "xterm" + dd_n 80 ; terminal width (rows) + dd_n 25 ; terminal height (rows) + dd_n 0 ; terminal width (pixels) + dd_n 0 ; terminal height (pixels) + + dd_n 0 ; list of supported opcodes + .length = $ - ssh_channel_request + +ssh_shell_request: + db SSH_MSG_CHANNEL_REQUEST + dd_n 0 ; Recipient channel + dd_n 5 + db "shell" + db 1 ; Bool: want reply + .length = $ - ssh_shell_request + +ssh_channel_data: + db SSH_MSG_CHANNEL_DATA + dd_n 0 ; Sender channel + dd_n 1 + db ? + .length = $ - ssh_channel_data + + include_debug_strings - -; import align 4 @IMPORT: library network, 'network.obj', \ - console, 'console.obj';, \ -; libcrash, 'libcrash.obj' + console, 'console.obj', \ + libcrash, 'libcrash.obj' import network, \ getaddrinfo, 'getaddrinfo', \ @@ -518,61 +790,23 @@ import console, \ con_write_string, 'con_write_string', \ con_get_flags, 'con_get_flags' -;import libcrash, \ -; crash.hash, 'crash_hash' +import libcrash, \ + sha256_init, 'sha256_init', \ + sha256_update, 'sha256_update', \ + sha256_final, 'sha256_final' IncludeIGlobals i_end: -decrypt_proc dd dummy_encrypt -encrypt_proc dd dummy_encrypt -rx_blocksize dd 4 -tx_blocksize dd 4 -rx_context dd ? -tx_context dd ? - IncludeUGlobals -socketnum dd ? -rx_packet_length dd ? ;;;;; -rx_buffer: rb BUFFERSIZE+1 -tx_buffer: rb BUFFERSIZE+1 +params rb 1024 -send_data dw ? +con ssh_connection -hostname rb 1024 - -; Diffie Hellman variables -dh_p dd ? - rb MAX_BITS/8 -dh_g dd ? - rb MAX_BITS/8 -dh_x dd ? - rb MAX_BITS/8 -dh_e dd ? - rb MAX_BITS/8 -dh_f dd ? - rb MAX_BITS/8 - -dh_signature dd ? - rb MAX_BITS/8 - -; Output from key exchange -dh_K dd ? ; Shared Secret (Big endian) - rb MAX_BITS/8 - .length dd ? ; Length in little endian - -dh_H rb 32 ; Exchange Hash -session_id rb 32 -rx_iv rb 32 ; Rx initialisation vector -tx_iv rb 32 ; Tx initialisation vector -rx_enc_key rb 32 ; Rx encryption key -tx_enc_key rb 32 ; Tx encryption key -rx_int_key rb 32 ; Rx integrity key -tx_int_key rb 32 ; Tx integrity key - -; Temporary values ; To be removed +; Temporary values ; To be removed FIXME mpint_tmp rb MPINT_MAX_LEN+4 + mem: diff --git a/programs/network/ssh/ssh_transport.inc b/programs/network/ssh/ssh_transport.inc index a6bd7995ed..6c1b8cf3dd 100644 --- a/programs/network/ssh/ssh_transport.inc +++ b/programs/network/ssh/ssh_transport.inc @@ -15,120 +15,181 @@ ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . -struct ssh_header - length dd ? - padding db ? - message_code db ? + +struct ssh_packet_header + packet_length dd ? ; The length of the packet in bytes, not including 'mac' or the + ; 'packet_length' field itself. + padding_length db ? ; Length of 'random padding' (bytes). + + message_code db ? ; First byte of payload ends -proc dummy_encrypt _key, _in, _out - ret -endp - -proc ssh_recv_packet sock, buf, size, flags +proc ssh_recv_packet connection, flags locals - bufferptr dd ? - remaining dd ? - padding dd ? + data_length dd ? ; Total length of packet without MAC endl - DEBUGF 1, "ssh_recv_packet\n" + DEBUGF 2, "> " ; Receive first block (Read length, padding length, message code) - mcall recv, [sock], [buf], [rx_blocksize], [flags] - DEBUGF 1, "chunk = %u\n", eax - cmp eax, [rx_blocksize] - jne .fail ;;;; + mov ebx, [connection] + mov ecx, [ebx+ssh_connection.socketnum] + mov esi, [ebx+ssh_connection.rx_crypt_blocksize] + lea edx, [ebx+ssh_connection.rx_buffer] + mov edi, [flags] + mcall recv + DEBUGF 1, "chunk = %u ", eax + mov ebx, [connection] + cmp eax, [ebx+ssh_connection.rx_crypt_blocksize] + jne .fail -; stdcall [decrypt_proc], [rx_context], [buf], [buf] +; Decrypt first block + cmp [ebx+ssh_connection.rx_crypt_proc], 0 + je @f + pusha + lea esi, [ebx+ssh_connection.rx_buffer] + stdcall [ebx+ssh_connection.rx_crypt_proc], [ebx+ssh_connection.rx_crypt_ctx_ptr], esi, esi + popa + @@: - mov ebx, [buf] - movzx eax, [ebx+ssh_header.padding] - mov [padding], eax - mov eax, [ebx+ssh_header.length] - bswap eax ; length to little endian - mov [ebx+ssh_header.length], eax - DEBUGF 1, "ssh_recv_packet length = %u\n", eax +; Check data length + mov esi, [ebx+ssh_connection.rx_buffer.packet_length] + bswap esi ; convert length to little endian + mov [ebx+ssh_connection.rx_buffer.packet_length], esi + DEBUGF 1, "packet length=%u ", esi + cmp esi, BUFFERSIZE + ja .fail ; packet is too large - cmp eax, [size] - ja .fail ;;;; +; Calculate amount of remaining data + add esi, 4 ; Packet length field itself is not included in the count + sub esi, [ebx+ssh_connection.rx_crypt_blocksize] ; Already received this amount of data + add esi, [ebx+ssh_connection.rx_mac_length] + jz .got_all_data - sub eax, [rx_blocksize] - add eax, 4 - mov [remaining], eax - add ebx, [rx_blocksize] - mov [bufferptr], ebx +; Receive remaining data + lea edx, [ebx+ssh_connection.rx_buffer] + add edx, [ebx+ssh_connection.rx_crypt_blocksize] + mov ecx, [ebx+ssh_connection.socketnum] + mov edi, [flags] .receive_loop: - mcall recv, [sock], [bufferptr], [remaining], 0 - DEBUGF 1, "chunk = %u\n", eax + mcall recv + DEBUGF 1, "chunk = %u ", eax cmp eax, 0 jbe .fail - add [bufferptr], eax - sub [remaining], eax - ja .receive_loop + add edx, eax + sub esi, eax + jnz .receive_loop -; .decrypt_loop: -; stdcall [decrypt_proc], [rx_context], [buf], [buf] -; ja .decrypt_loop +; Decrypt data + mov ebx, [connection] + cmp [ebx+ssh_connection.rx_crypt_proc], 0 + je .decrypt_complete + mov ecx, [ebx+ssh_connection.rx_buffer.packet_length] + add ecx, 4 ; Packet_length field itself + sub ecx, [ebx+ssh_connection.rx_crypt_blocksize] ; Already decrypted this amount of data + jz .decrypt_complete -; .hmac_loop: -; TODO -; ja .hmac_loop + lea esi, [ebx+ssh_connection.rx_buffer] + add esi, [ebx+ssh_connection.rx_crypt_blocksize] + .decrypt_loop: + pusha + stdcall [ebx+ssh_connection.rx_crypt_proc], [ebx+ssh_connection.rx_crypt_ctx_ptr], esi, esi + popa + add esi, [ebx+ssh_connection.rx_crypt_blocksize] + sub ecx, [ebx+ssh_connection.rx_crypt_blocksize] + jnz .decrypt_loop + .decrypt_complete: -; Return usefull data length in eax - mov eax, [buf] - movzx ebx, [eax+ssh_header.padding] - mov eax, [eax+ssh_header.length] +; Authenticate message + cmp [ebx+ssh_connection.rx_mac_proc], 0 + je .mac_complete + lea esi, [ebx+ssh_connection.rx_seq] + mov ecx, [ebx+ssh_connection.rx_buffer.packet_length] + add ecx, 8 ; packet_length field itself + sequence number + lea eax, [ebx+ssh_connection.rx_mac_ctx] + mov edx, [ebx+ssh_connection.rx_buffer.packet_length] + bswap edx ; convert length to big endian + mov [ebx+ssh_connection.rx_buffer.packet_length], edx + stdcall [ebx+ssh_connection.rx_mac_proc], eax, esi, ecx + mov edx, [ebx+ssh_connection.rx_buffer.packet_length] + bswap edx ; convert length to little endian + mov [ebx+ssh_connection.rx_buffer.packet_length], edx + + lea esi, [ebx+ssh_connection.rx_mac_ctx] + lea edi, [ebx+ssh_connection.rx_buffer] + add edi, [ebx+ssh_connection.rx_buffer.packet_length] + add edi, 4 + mov ecx, [ebx+ssh_connection.rx_mac_length] + shr ecx, 2 + repe cmpsd + jne .mac_failed + .mac_complete: + inc byte[ebx+ssh_connection.rx_seq+3] ; Update sequence counter + jnc @f + inc byte[ebx+ssh_connection.rx_seq+2] + jnc @f + inc byte[ebx+ssh_connection.rx_seq+1] + jnc @f + inc byte[ebx+ssh_connection.rx_seq+0] + @@: + +; Return useful data length to the caller via eax register + .got_all_data: + mov eax, [ebx+ssh_connection.rx_buffer.packet_length] + movzx ebx, [ebx+ssh_connection.rx_buffer.padding_length] sub eax, ebx - DEBUGF 1, "ssh_recv_packet complete, usefull data length=%u\n", eax + DEBUGF 1, "useful data length=%u\n", eax ret .fail: - DEBUGF 1, "ssh_recv_packet failed!\n" + DEBUGF 3, "ssh_recv_packet failed!\n" + mov eax, -1 + ret + + .mac_failed: + DEBUGF 3, "ssh_recv_packet MAC failed!\n" mov eax, -1 ret endp -proc ssh_send_packet sock, buf, payloadsize, flags +proc ssh_send_packet connection, buf, payload_size, flags locals - size dd ? + packet_size dd ? endl - DEBUGF 1, "ssh_send_packet: size=%u\n", [payloadsize] + DEBUGF 2, "< " - mov eax, [payloadsize] - inc eax ; padding length byte - - lea edx, [eax+4] ; total packet size (without padding) - mov [size], edx - mov ebx, [tx_blocksize] +; Pad the packet with random data + mov eax, [payload_size] + inc eax ; padding length byte + lea edx, [eax+4] ; total packet size (without padding and MAC) + mov [packet_size], edx + mov ecx, [connection] + mov ebx, [ecx+ssh_connection.tx_crypt_blocksize] dec ebx and edx, ebx neg edx - add edx, [tx_blocksize] - cmp edx, 4 ; minimum padding size + add edx, [ecx+ssh_connection.tx_crypt_blocksize] + cmp edx, 4 ; minimum padding size jae @f - add edx, [tx_blocksize] + add edx, [ecx+ssh_connection.tx_crypt_blocksize] @@: - DEBUGF 1, "Padding %u bytes\n", edx - add [size], edx + DEBUGF 1, "padding %u bytes ", edx + add [packet_size], edx add eax, edx - DEBUGF 1, "Total size: %u\n", eax + DEBUGF 1, "total size: %u ", eax bswap eax - mov edi, tx_buffer - stosd + lea edi, [ecx+ssh_connection.tx_buffer] + stosd ; packet_length mov al, dl - stosb + stosb ; padding_length mov esi, [buf] -; cmp esi, edi -; je @f - mov ecx, [payloadsize] + mov ecx, [payload_size] rep movsb -; @@: mov ebx, edx mov esi, edx @@ -146,8 +207,59 @@ endl dec esi jnz @r - mcall send, [sock], tx_buffer, [size], [flags] +; Message authentication + mov edx, [connection] + cmp [edx+ssh_connection.tx_mac_proc], 0 + je .mac_complete +; DEBUGF 1, "MAC sequence number: 0x%x\n", [edx+ssh_connection.tx_seq] + lea esi, [edx+ssh_connection.tx_seq] + mov ecx, [packet_size] + add ecx, 4 ; Sequence number length + lea eax, [edx+ssh_connection.tx_mac_ctx] + stdcall [edx+ssh_connection.tx_mac_proc], eax, esi, ecx + + lea esi, [edx+ssh_connection.tx_mac_ctx] + lea edi, [edx+ssh_connection.tx_buffer] + add edi, [packet_size] + mov ecx, [edx+ssh_connection.tx_mac_length] + shr ecx, 2 + rep movsd + .mac_complete: + inc byte[edx+ssh_connection.tx_seq+3] ; Update sequence counter + jnc @f + inc byte[edx+ssh_connection.tx_seq+2] + jnc @f + inc byte[edx+ssh_connection.tx_seq+1] + jnc @f + inc byte[edx+ssh_connection.tx_seq+0] + @@: + +; Encrypt data + cmp [edx+ssh_connection.tx_crypt_proc], 0 + je .encrypt_complete + lea esi, [edx+ssh_connection.tx_buffer] + mov ecx, [packet_size] + .encrypt_loop: + pusha + stdcall [edx+ssh_connection.tx_crypt_proc], [edx+ssh_connection.tx_crypt_ctx_ptr], esi, esi + popa + add esi, [edx+ssh_connection.tx_crypt_blocksize] + sub ecx, [edx+ssh_connection.tx_crypt_blocksize] + jnz .encrypt_loop + .encrypt_complete: + +; Send the packet + mov ebx, [connection] + mov ecx, [ebx+ssh_connection.socketnum] + lea edx, [ebx+ssh_connection.tx_buffer] + mov esi, [packet_size] + add esi, [ebx+ssh_connection.tx_mac_length] + mov edi, [flags] + mcall send + + DEBUGF 1, "\n" ret -endp \ No newline at end of file +endp +