0,0 → 1,422 |
; 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 |