/** @internal * @brief Starts diffie-hellman-group1 key exchange */ int ssh_client_dh_init(ssh_session session){ ssh_string e = NULL; int rc; if (dh_generate_x(session) < 0) { goto error; } if (dh_generate_e(session) < 0) { goto error; } e = dh_get_e(session); if (e == NULL) { goto error; } rc = ssh_buffer_pack(session->out_buffer, "bS", SSH2_MSG_KEXDH_INIT, e); if (rc != SSH_OK) { goto error; } ssh_string_burn(e); ssh_string_free(e); e=NULL; rc = packet_send(session); return rc; error: if(e != NULL){ ssh_string_burn(e); ssh_string_free(e); } return SSH_ERROR; }
static int dh_handshake(SSH_SESSION *session){ STRING *e,*f,*pubkey,*signature; int ret; switch(session->dh_handshake_state){ case DH_STATE_INIT: packet_clear_out(session); buffer_add_u8(session->out_buffer,SSH2_MSG_KEXDH_INIT); dh_generate_x(session); dh_generate_e(session); e=dh_get_e(session); buffer_add_ssh_string(session->out_buffer,e); ret=packet_send(session); free(e); session->dh_handshake_state=DH_STATE_INIT_TO_SEND; if(ret==SSH_ERROR) return ret; case DH_STATE_INIT_TO_SEND: ret=packet_flush(session,0); if(ret!=SSH_OK) return ret; // SSH_ERROR or SSH_AGAIN session->dh_handshake_state=DH_STATE_INIT_SENT; case DH_STATE_INIT_SENT: ret=packet_wait(session,SSH2_MSG_KEXDH_REPLY,1); if(ret != SSH_OK) return ret; pubkey=buffer_get_ssh_string(session->in_buffer); if(!pubkey){ ssh_set_error(session,SSH_FATAL,"No public key in packet"); return SSH_ERROR; } dh_import_pubkey(session,pubkey); f=buffer_get_ssh_string(session->in_buffer); if(!f){ ssh_set_error(session,SSH_FATAL,"No F number in packet"); return SSH_ERROR; } dh_import_f(session,f); free(f); if(!(signature=buffer_get_ssh_string(session->in_buffer))){ ssh_set_error(session,SSH_FATAL,"No signature in packet"); return SSH_ERROR; } session->dh_server_signature=signature; dh_build_k(session); // send the MSG_NEWKEYS packet_clear_out(session); buffer_add_u8(session->out_buffer,SSH2_MSG_NEWKEYS); packet_send(session); session->dh_handshake_state=DH_STATE_NEWKEYS_TO_SEND; case DH_STATE_NEWKEYS_TO_SEND: ret=packet_flush(session,0); if(ret != SSH_OK) return ret; ssh_say(2,"SSH_MSG_NEWKEYS sent\n"); session->dh_handshake_state=DH_STATE_NEWKEYS_SENT; case DH_STATE_NEWKEYS_SENT: ret=packet_wait(session,SSH2_MSG_NEWKEYS,1); if(ret != SSH_OK) return ret; ssh_say(2,"Got SSH_MSG_NEWKEYS\n"); make_sessionid(session); /* set the cryptographic functions for the next crypto */ /* (it is needed for generate_session_keys for key lenghts) */ if(crypt_set_algorithms(session)) return SSH_ERROR; generate_session_keys(session); /* verify the host's signature. XXX do it sooner */ signature=session->dh_server_signature; session->dh_server_signature=NULL; if(signature_verify(session,signature)){ free(signature); return SSH_ERROR; } free(signature); /* forget it for now ... */ /* once we got SSH2_MSG_NEWKEYS we can switch next_crypto and current_crypto */ if(session->current_crypto) crypto_free(session->current_crypto); /* XXX later, include a function to change keys */ session->current_crypto=session->next_crypto; session->next_crypto=crypto_new(); session->dh_handshake_state=DH_STATE_FINISHED; return SSH_OK; default: ssh_set_error(session,SSH_FATAL,"Invalid state in dh_handshake():%d",session->dh_handshake_state); return SSH_ERROR; } /* not reached */ return SSH_ERROR; }
/** @internal * @brief launches the DH handshake state machine * @param session session handle * @returns SSH_OK or SSH_ERROR * @warning this function returning is no proof that DH handshake is * completed */ static int dh_handshake(ssh_session session) { ssh_string e = NULL; ssh_string f = NULL; ssh_string signature = NULL; int rc = SSH_ERROR; enter_function(); switch (session->dh_handshake_state) { case DH_STATE_INIT: if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_INIT) < 0) { goto error; } if (dh_generate_x(session) < 0) { goto error; } if (dh_generate_e(session) < 0) { goto error; } e = dh_get_e(session); if (e == NULL) { goto error; } if (buffer_add_ssh_string(session->out_buffer, e) < 0) { goto error; } ssh_string_burn(e); ssh_string_free(e); e=NULL; rc = packet_send(session); if (rc == SSH_ERROR) { goto error; } session->dh_handshake_state = DH_STATE_INIT_SENT; case DH_STATE_INIT_SENT: /* wait until ssh_packet_dh_reply is called */ break; case DH_STATE_NEWKEYS_SENT: /* wait until ssh_packet_newkeys is called */ break; case DH_STATE_FINISHED: leave_function(); return SSH_OK; default: ssh_set_error(session, SSH_FATAL, "Invalid state in dh_handshake(): %d", session->dh_handshake_state); leave_function(); return SSH_ERROR; } leave_function(); return SSH_AGAIN; error: if(e != NULL){ ssh_string_burn(e); ssh_string_free(e); } if(f != NULL){ ssh_string_burn(f); ssh_string_free(f); } if(signature != NULL){ ssh_string_burn(signature); ssh_string_free(signature); } leave_function(); return rc; }