422 lines
12 KiB
PHP
422 lines
12 KiB
PHP
|
; dh_gex.inc - Diffie Hellman Group exchange
|
||
|
;
|
||
|
; Copyright (C) 2015-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 <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
; https://www.ietf.org/rfc/rfc4419.txt
|
||
|
|
||
|
; TODO: dont convert mpints to little endian immediately.
|
||
|
; Or maybe even better, not at all.
|
||
|
|
||
|
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
|
||
|
cmp eax, -1
|
||
|
je .socket_err
|
||
|
|
||
|
;---------------------------------------------
|
||
|
; << Parse Diffie-Hellman Group Exchange Group
|
||
|
|
||
|
stdcall ssh_recv_packet, [socketnum], rx_buffer, BUFFERSIZE, 0
|
||
|
cmp eax, -1
|
||
|
je .socket_err
|
||
|
|
||
|
cmp [rx_buffer+ssh_header.message_code], SSH_MSG_KEX_DH_GEX_GROUP
|
||
|
jne proto_err
|
||
|
DEBUGF 1, "Received GEX group\n"
|
||
|
|
||
|
mov esi, rx_buffer+sizeof.ssh_header
|
||
|
mov edi, dh_p
|
||
|
DEBUGF 1, "DH modulus (p): "
|
||
|
call mpint_to_little_endian
|
||
|
stdcall mpint_print, dh_p
|
||
|
|
||
|
DEBUGF 1, "DH base (g): "
|
||
|
mov edi, dh_g
|
||
|
call mpint_to_little_endian
|
||
|
stdcall mpint_print, 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 ecx, DH_PRIVATE_KEY_SIZE/8/4
|
||
|
@@:
|
||
|
push ecx
|
||
|
call MBRandom
|
||
|
pop ecx
|
||
|
stosd
|
||
|
dec ecx
|
||
|
jnz @r
|
||
|
|
||
|
; If the highest bit is set, add a zero byte
|
||
|
shl eax, 1
|
||
|
jnc @f
|
||
|
mov byte[edi], 0
|
||
|
inc dword[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
|
||
|
end if
|
||
|
|
||
|
DEBUGF 1, "DH x: "
|
||
|
stdcall mpint_length, dh_x;;;;;;;;;;;;;
|
||
|
stdcall mpint_print, dh_x
|
||
|
|
||
|
; Compute e = g^x mod p
|
||
|
stdcall mpint_modexp, dh_e, dh_g, dh_x, dh_p
|
||
|
stdcall mpint_length, dh_e
|
||
|
|
||
|
DEBUGF 1, "DH e: "
|
||
|
stdcall mpint_print, dh_e
|
||
|
|
||
|
; Create group exchange init packet
|
||
|
mov edi, tx_buffer+ssh_header.message_code
|
||
|
mov al, SSH_MSG_KEX_DH_GEX_INIT
|
||
|
stosb
|
||
|
mov esi, dh_e
|
||
|
call mpint_to_big_endian
|
||
|
|
||
|
DEBUGF 1, "Sending GEX init\n"
|
||
|
mov ecx, dword[tx_buffer+ssh_header.message_code+1]
|
||
|
bswap ecx
|
||
|
add ecx, 5
|
||
|
stdcall ssh_send_packet, [socketnum], tx_buffer+ssh_header.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
|
||
|
cmp eax, -1
|
||
|
je .socket_err
|
||
|
|
||
|
cmp [rx_buffer+ssh_header.message_code], SSH_MSG_KEX_DH_GEX_REPLY
|
||
|
jne .proto_err
|
||
|
|
||
|
DEBUGF 1, "Received GEX Reply\n"
|
||
|
|
||
|
;--------------------------------
|
||
|
; HASH: string K_S, the host key
|
||
|
mov esi, rx_buffer+sizeof.ssh_header
|
||
|
mov edx, [esi]
|
||
|
bswap edx
|
||
|
add edx, 4
|
||
|
lea ebx, [esi+edx]
|
||
|
push ebx
|
||
|
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
|
||
|
mov esi, ssh_gex_req+sizeof.ssh_header-ssh_header.message_code
|
||
|
mov edx, 12
|
||
|
call sha256_update
|
||
|
|
||
|
;----------------------------
|
||
|
; HASH: mpint p, safe prime
|
||
|
mov esi, dh_p
|
||
|
mov edi, mpint_tmp
|
||
|
call mpint_to_big_endian
|
||
|
lea edx, [eax+4]
|
||
|
mov esi, mpint_tmp
|
||
|
call sha256_update
|
||
|
|
||
|
;----------------------------------------
|
||
|
; HASH: mpint g, generator for subgroup
|
||
|
mov esi, dh_g
|
||
|
mov edi, mpint_tmp
|
||
|
call mpint_to_big_endian
|
||
|
lea edx, [eax+4]
|
||
|
mov esi, mpint_tmp
|
||
|
call sha256_update
|
||
|
|
||
|
;---------------------------------------------------
|
||
|
; HASH: mpint e, exchange value sent by the client
|
||
|
mov esi, tx_buffer+sizeof.ssh_header
|
||
|
mov edx, [esi]
|
||
|
bswap edx
|
||
|
add edx, 4
|
||
|
call sha256_update
|
||
|
|
||
|
;---------------------------------------------------
|
||
|
; HASH: mpint f, exchange value sent by the server
|
||
|
mov esi, [esp]
|
||
|
mov edx, [esi]
|
||
|
bswap edx
|
||
|
add edx, 4
|
||
|
call sha256_update
|
||
|
pop esi
|
||
|
|
||
|
mov edi, dh_f
|
||
|
call mpint_to_little_endian
|
||
|
|
||
|
DEBUGF 1, "DH f: "
|
||
|
stdcall mpint_print, dh_f
|
||
|
|
||
|
mov edi, dh_signature
|
||
|
call mpint_to_little_endian
|
||
|
|
||
|
DEBUGF 1, "DH signature: "
|
||
|
stdcall mpint_print, 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
|
||
|
|
||
|
DEBUGF 1, "DH K: "
|
||
|
stdcall mpint_print, rx_buffer
|
||
|
|
||
|
; We always need it in big endian order, so store it as such.
|
||
|
mov edi, dh_K
|
||
|
mov esi, rx_buffer
|
||
|
call mpint_to_big_endian
|
||
|
mov [dh_K.length], eax
|
||
|
|
||
|
;-----------------------------------
|
||
|
; HASH: mpint K, the shared secret
|
||
|
mov edx, [dh_K.length]
|
||
|
add edx, 4
|
||
|
mov esi, dh_K
|
||
|
call sha256_update
|
||
|
|
||
|
;-------------------------------
|
||
|
; Finalize the exchange hash (H)
|
||
|
mov edi, dh_H
|
||
|
call sha256_final
|
||
|
|
||
|
DEBUGF 1, "Exchange hash H: "
|
||
|
stdcall dump_256bit_hex, dh_H
|
||
|
|
||
|
; TODO: skip this block when re-keying
|
||
|
mov esi, dh_H
|
||
|
mov edi, session_id
|
||
|
mov ecx, 32/4
|
||
|
rep movsd
|
||
|
|
||
|
;---------------
|
||
|
; Calculate keys
|
||
|
|
||
|
; TODO: re-use partial hash of K and H
|
||
|
|
||
|
;---------------------------------------------------------------
|
||
|
; 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
|
||
|
|
||
|
DEBUGF 1, "Remote IV: "
|
||
|
stdcall dump_256bit_hex, tx_iv
|
||
|
|
||
|
;---------------------------------------------------------------
|
||
|
; 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
|
||
|
|
||
|
DEBUGF 1, "Local IV: "
|
||
|
stdcall dump_256bit_hex, rx_iv
|
||
|
|
||
|
;-------------------------------------------------------------------
|
||
|
; 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
|
||
|
|
||
|
DEBUGF 1, "Remote key: "
|
||
|
stdcall dump_256bit_hex, tx_enc_key
|
||
|
|
||
|
;-------------------------------------------------------------------
|
||
|
; 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
|
||
|
|
||
|
DEBUGF 1, "Local key: "
|
||
|
stdcall dump_256bit_hex, rx_enc_key
|
||
|
|
||
|
;------------------------------------------------------------------
|
||
|
; 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
|
||
|
|
||
|
DEBUGF 1, "Remote Integrity key: "
|
||
|
stdcall dump_256bit_hex, tx_int_key
|
||
|
|
||
|
;------------------------------------------------------------------
|
||
|
; 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
|
||
|
|
||
|
DEBUGF 1, "Local Integrity key: "
|
||
|
stdcall dump_256bit_hex, rx_int_key
|
||
|
|
||
|
;-------------------------------------
|
||
|
; << Parse Diffie-Hellman New Keys MSG
|
||
|
|
||
|
stdcall ssh_recv_packet, [socketnum], rx_buffer, BUFFERSIZE, 0
|
||
|
cmp eax, -1
|
||
|
je .socket_err
|
||
|
|
||
|
cmp [rx_buffer+ssh_header.message_code], SSH_MSG_NEWKEYS
|
||
|
jne .proto_err
|
||
|
|
||
|
DEBUGF 1, "Received New Keys\n"
|
||
|
|
||
|
;-------------------------------
|
||
|
; >> Reply with New Keys message
|
||
|
|
||
|
stdcall ssh_send_packet, [socketnum], ssh_new_keys, ssh_new_keys.length, 0
|
||
|
|
||
|
xor eax, eax
|
||
|
ret
|
||
|
|
||
|
.socket_err:
|
||
|
DEBUGF 2, "Socket error during key exchange!\n"
|
||
|
mov eax, 1
|
||
|
ret
|
||
|
|
||
|
.proto_err:
|
||
|
DEBUGF 2, "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
|