/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_keyex_rsa.c,v $ $Revision: 1.4.12.3 $ * $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_keyex_rsa.c,v $ * $Author: jli7 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Roger Boden ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include "ipssh_config.h" #ifdef IPSSH_USE_KEYEX_RSA /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include #include #include #include #include #include #include #include #include "ipssh.h" #include "ipssh_h.h" #include "ipssh_buffer.h" #include "ipssh_transport.h" #include "ipssh_keyex_dh.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_GLOBAL Ip_err ipssh_send_msg_kexrsa_pubkey(Ipssh_conn* ssh_conn); IP_GLOBAL Ip_err ipssh_send_msg_kexrsa_done(Ipssh_conn* ssh_conn); /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ IP_STATIC int ipssh_kexrsa_get_hash_size(Ipssh_conn* ssh_conn) { switch(ssh_conn->conn_prop_pending.kex_alg[0]) { case IPSSH_KEYEX_RSA1024_SHA1_ID: return SHA_DIGEST_LENGTH; case IPSSH_KEYEX_RSA2048_SHA256_ID: return SHA256_DIGEST_LENGTH; default: IP_PANIC(); return 0; } } /* *=========================================================================== * ipssh_send_msg_kexrsa_secret *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_send_msg_kexrsa_secret(Ipssh_conn* ssh_conn, RSA* transient_key) { Ip_err rc = IPCOM_ERR_FAILED; int encr_k_len; int len; Ip_u8* encr_k = IP_NULL; Ip_u8* oaep_padded_shared_secret = IP_NULL; Ip_u8* mpint_shared_secret = IP_NULL; Ipcom_buffer* pkt = IP_NULL; /* Generate random integer, K in rfc 4432, also known as shared secret */ ssh_conn->secr_len = RSA_size(transient_key) - 2*ipssh_kexrsa_get_hash_size(ssh_conn) - 7; ssh_conn->shared_secret = ipcom_malloc(RSA_size(transient_key)); if (!ssh_conn->shared_secret) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_secret :: out of memory"); return IPCOM_ERR_NO_MEMORY; } if (!RAND_bytes(ssh_conn->shared_secret, ssh_conn->secr_len)) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_secret :: failed to get random bytes"); goto cleanup; } if (ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_MPINT, ssh_conn->shared_secret, ssh_conn->secr_len, 7)) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_secret :: failed to add K to hash list"); goto cleanup; } encr_k_len = RSA_size(transient_key); encr_k = ipcom_malloc(encr_k_len); oaep_padded_shared_secret = ipcom_malloc(encr_k_len); mpint_shared_secret = ipcom_malloc(ssh_conn->secr_len+4); if (!encr_k || !oaep_padded_shared_secret || !mpint_shared_secret) goto cleanup; IP_SET_HTONL(mpint_shared_secret, ssh_conn->secr_len); ipcom_memcpy(mpint_shared_secret+4, ssh_conn->shared_secret, ssh_conn->secr_len); ipcrypto_rsa_oaep_encode(ssh_conn->hash_func(), mpint_shared_secret, ssh_conn->secr_len+4, oaep_padded_shared_secret, encr_k_len); len = RSA_public_encrypt(encr_k_len, oaep_padded_shared_secret, encr_k, transient_key, RSA_NO_PADDING); if (len < 0) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_secret :: failed to encrypt K"); goto cleanup; } if (ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_STR, encr_k, len, 6)) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_secret :: failed to add encrypted K to hash list"); goto cleanup; } pkt = ipssh_buffer_new_v2_pkt(encr_k_len+64, IPSSH_MSG_KEX_RSA_SECRET); if (!pkt) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_secret :: failed to allocate pkt"); goto cleanup; } (void)ipssh_buffer_put_sshbuf(pkt, encr_k, encr_k_len); rc = ipssh_send_pkt(ssh_conn, pkt, IP_FALSE); cleanup: if (encr_k) ipcom_free(encr_k); if (oaep_padded_shared_secret) ipcom_free(oaep_padded_shared_secret); if (mpint_shared_secret) ipcom_free(mpint_shared_secret); if(pkt) ipcom_buffer_free(pkt); return rc; } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ IP_GLOBAL Ip_err ipssh_msg_kexrsa_done(Ipssh_conn* ssh_conn) { char* sig_type = IP_NULL; Ip_u8* signature = IP_NULL; Ip_u32 sign_len; Ip_err rc = IPCOM_ERR_FAILED; if(ipssh_calc_hash((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), ssh_conn->hash_func, 8, &(ssh_conn->hash), &(ssh_conn->hash_len))) { IPCOM_LOG0(ERR, "ipssh_msg_kexrsa_done :: ipssh_calc_hash() failed"); goto cleanup; } if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6)) /* Skip headers */ goto cleanup; /* Process signature */ if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 4)) /* Skip blob len field */ goto cleanup; sig_type=ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf); signature=ipssh_buffer_get_sshbuf(ssh_conn->recv_plain_buf, &sign_len); if(!sig_type || !signature) { IPCOM_LOG0(ERR, "ipssh_msg_kexrsa_done :: failed to unpack signature"); goto cleanup; } /* Verify signature */ if(ipssh_verify_signature(ssh_conn->data.clt.srv_pub_key, ssh_conn->hash, ssh_conn->hash_len, sig_type, signature, sign_len) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_msg_kexrsa_done :: ipssh_verify_signature() failed"); goto cleanup; } if(ssh_conn->sess_id_len == 0) { ssh_conn->sess_id = ipcom_malloc(ssh_conn->hash_len); if(!ssh_conn->sess_id) { IPCOM_LOG0(ERR, "ipssh_msg_kexrsa_done :: out of memory"); goto cleanup; } ssh_conn->sess_id_len = ssh_conn->hash_len; ipcom_memcpy(ssh_conn->sess_id, ssh_conn->hash, ssh_conn->sess_id_len); } rc = IPCOM_SUCCESS; cleanup: if (sig_type) ipcom_free(sig_type); if (signature) ipcom_free(signature); return rc; } /* *=========================================================================== * ipssh_msg_kexrsa_pubkey *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_kexrsa_pubkey(Ipssh_conn* ssh_conn) { Ip_err rc = IPCOM_ERR_FAILED; EVP_PKEY* transient_key = IP_NULL; Ip_u8* srv_key_ptr; Ip_u8* transient_key_ptr; (void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6); srv_key_ptr = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf); ssh_conn->data.clt.srv_pub_key = ipssh_buffer_get_pub_key(ssh_conn->recv_plain_buf); if (!ssh_conn->data.clt.srv_pub_key) { IPCOM_LOG0(ERR, "ipssh_msg_kexrsa_pubkey :: failed to unpack srv key"); return IPCOM_ERR_FAILED; } if (ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_RAW, srv_key_ptr, ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf) - srv_key_ptr, 4)) { IPCOM_LOG0(ERR, "ipssh_msg_kexrsa_pubkey :: failed to add host key to hash list"); goto cleanup; } transient_key_ptr = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf); transient_key = ipssh_buffer_get_pub_key(ssh_conn->recv_plain_buf); if (!transient_key) { IPCOM_LOG0(ERR, "ipssh_msg_kexrsa_pubkey :: failed to unpack transient key"); goto cleanup; } if (transient_key->type != NID_rsaEncryption) { IPCOM_LOG1(ERR, "ipssh_msg_kexrsa_pubkey :: transient key not an RSA key: key type=%d", transient_key->type); goto cleanup; } if (ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_RAW, transient_key_ptr, ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf) - transient_key_ptr, 5)) { IPCOM_LOG0(ERR, "ipssh_msg_kexrsa_pubkey :: failed to add transient key to hash list"); goto cleanup; } rc = ipssh_send_msg_kexrsa_secret(ssh_conn, transient_key->pkey.rsa); cleanup: if (transient_key) EVP_PKEY_free(transient_key); return rc; } /* *=========================================================================== * ipssh_send_msg_kexrsa_pubkey *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_send_msg_kexrsa_pubkey(Ipssh_conn* ssh_conn) { Ipcom_buffer* pkt; Ip_err rc = IPCOM_ERR_FAILED; Ip_size_t srv_key_pos; Ip_size_t transient_key_pos; pkt = ipssh_buffer_new_v2_pkt(1024, IPSSH_MSG_KEX_RSA_PUBKEY); if (!pkt) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_pubkey :: out of memory"); return IPCOM_ERR_FAILED; } srv_key_pos = ipcom_buffer_end_pos(pkt); switch(ssh_conn->conn_prop_pending.srv_host_key_alg[0]) { case IPSSH_DSA_ID: if (ipssh_buffer_put_dsa_key(pkt, ssh_conn->srv_dsa_key)) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_pubkey :: failed to add srv dsa key to pkt"); goto cleanup; } break; case IPSSH_RSA_ID: if (ipssh_buffer_put_rsa_key(pkt, ssh_conn->srv_rsa_key)) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_pubkey :: failed to add srv rsa key to pkt"); goto cleanup; } break; default: IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_pubkey :: unknown srv host key"); IP_PANIC(); goto cleanup; } if (ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_RAW, (Ip_u8*)ipcom_buffer_ptr2pos(pkt, srv_key_pos), ipcom_buffer_end_pos(pkt) - srv_key_pos, 4)) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_pubkey :: failed to add host key to hash list"); goto cleanup; } transient_key_pos = ipcom_buffer_end_pos(pkt); if (ipssh_buffer_put_rsa_key(pkt, ssh_conn->srv_rsa_keyex_key)) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_pubkey :: out of memory"); goto cleanup; } if (ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_RAW, (Ip_u8*)ipcom_buffer_ptr2pos(pkt, transient_key_pos), ipcom_buffer_end_pos(pkt) - transient_key_pos, 5)) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_pubkey :: failed to add transient key to hash list"); goto cleanup; } rc = ipssh_send_pkt(ssh_conn, pkt, IP_FALSE); ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_KEX_RSA; cleanup: ipcom_buffer_free(pkt); return rc; } /* *=========================================================================== * ipssh_send_msg_kexrsa_done *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_send_msg_kexrsa_done(Ipssh_conn* ssh_conn) { Ip_err rc = IPCOM_ERR_FAILED; Ipcom_buffer* pkt = IP_NULL; if(ipssh_calc_hash((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), ssh_conn->hash_func, 8, &(ssh_conn->hash), &(ssh_conn->hash_len))) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_done :: ipssh_calc_hash() failed"); goto failed; } if(ssh_conn->sess_id_len == 0) { ssh_conn->sess_id = ipcom_malloc(ssh_conn->hash_len); if(!ssh_conn->sess_id) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_done :: out of memory"); goto failed; } ssh_conn->sess_id_len = ssh_conn->hash_len; ipcom_memcpy(ssh_conn->sess_id, ssh_conn->hash, ssh_conn->sess_id_len); } pkt = ipssh_buffer_new_v2_pkt(2048, IPSSH_MSG_KEX_RSA_DONE); if (!pkt) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_done :: failed to allocate pkt"); goto failed; } if (ipssh_sign_hash(ssh_conn, pkt) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_done :: ipssh_sign_hash() failed"); goto failed; } if (ipssh_send_pkt(ssh_conn, pkt, IP_FALSE) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_done :: failed to send SSH_MSG_NEW_KEY"); goto failed; } if(ipssh_compute_keys(ssh_conn)) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_done :: ipssh_compute_keys() failed"); goto failed; } if (ipssh_send_new_key_msg(ssh_conn) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_send_msg_kexrsa_done :: failed to send SSH_MSG_NEW_KEY"); goto failed; } rc = IPCOM_SUCCESS; failed: if (pkt) ipcom_buffer_free(pkt); return rc; } /* *=========================================================================== * *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_msg_kex_rsa_secret(Ipssh_conn* ssh_conn) { Ip_u8* encr_shared_secret; /* K in rfc 4432 */ int len; Ip_u8* shared_secret = IP_NULL; Ip_u8* oaep_padded_shared_secret = IP_NULL; Ip_err rc = IPCOM_ERR_FAILED; (void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6); encr_shared_secret = ipssh_buffer_get_sshbuf(ssh_conn->recv_plain_buf, (Ip_u32*)&len); if (!encr_shared_secret) { IPCOM_LOG0(ERR, "ipssh_msg_kex_rsa_secret :: failed to unpack secret"); return IPCOM_ERR_FAILED; } if (ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_STR, encr_shared_secret, len, 6)) { IPCOM_LOG0(ERR, "ipssh_msg_kex_rsa_secret :: failed to add encrypted K to hash list"); goto cleanup; } shared_secret = ipcom_malloc(RSA_size(ssh_conn->srv_rsa_keyex_key)+64); oaep_padded_shared_secret = ipcom_malloc(RSA_size(ssh_conn->srv_rsa_keyex_key)+64); if (!shared_secret || !oaep_padded_shared_secret) { IPCOM_LOG0(ERR, "ipssh_msg_kex_rsa_secret :: out of memory"); goto cleanup; } len = RSA_private_decrypt(len, encr_shared_secret, oaep_padded_shared_secret, ssh_conn->srv_rsa_keyex_key, RSA_NO_PADDING); if (len < 0) { IPCOM_LOG0(ERR, "ipssh_msg_kex_rsa_secret :: RSA_private_decrypt failed"); goto cleanup; } if (ipcrypto_rsa_oaep_decode(ssh_conn->hash_func(), oaep_padded_shared_secret, len, shared_secret, (int*)&len) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_msg_kex_rsa_secret :: failed to oaep decode shared secret"); goto cleanup; } if (len < 4) { IPCOM_LOG1(ERR, "ipssh_msg_kex_rsa_secret :: invalid K lenght: %d", len); goto cleanup; } ipcom_memmove(shared_secret, &shared_secret[4], len -4); /* Remove the length field */ len -= 4; if (ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_MPINT, shared_secret, len, 7)) { IPCOM_LOG0(ERR, "ipssh_msg_kex_rsa_secret :: failed to add shared secret K to hash list"); goto cleanup; } ssh_conn->shared_secret = shared_secret; shared_secret = IP_NULL; ssh_conn->secr_len = len; rc = ipssh_send_msg_kexrsa_done(ssh_conn); ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_NEW_KEY; cleanup: ipcom_free(encr_shared_secret); if (shared_secret) ipcom_free(shared_secret); if (oaep_padded_shared_secret) ipcom_free(oaep_padded_shared_secret); return rc; } /* *=========================================================================== * ipssh_do_keyex_rsa_clt *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_do_keyex_rsa_clt(Ipssh_conn* ssh_conn) { if (ipssh_read_v2_pkt2(ssh_conn, IPSSH_MSG_KEX_RSA_PUBKEY) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_do_keyex_rsa_clt :: failed to read SSH_MSG_KEY_RSA_PUBKEY"); return IPCOM_ERR_FAILED; } if (ipssh_msg_kexrsa_pubkey(ssh_conn) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_do_keyex_rsa_clt :: failed to process SSH_MSG_KEY_RSA_PUBKEY"); return IPCOM_ERR_FAILED; } if (ipssh_read_v2_pkt2(ssh_conn, IPSSH_MSG_KEX_RSA_DONE) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_do_keyex_rsa_clt :: failed to read SSH_MSG_KEY_RSA_DONE"); return IPCOM_ERR_FAILED; } if (ipssh_msg_kexrsa_done(ssh_conn) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_do_keyex_rsa_clt :: failed to process SSH_MSG_KEY_RSA_DONE"); return IPCOM_ERR_FAILED; } if(ipssh_compute_keys(ssh_conn)) { IPCOM_LOG0(ERR, "ipssh_do_keyex_rsa_clt :: ipssh_compute_keys() failed"); return IPCOM_ERR_FAILED; } if (ipssh_send_new_key_msg(ssh_conn) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_do_keyex_rsa_clt :: failed to send SSH_MSG_NEW_KEY"); return IPCOM_ERR_FAILED; } if(ipssh_read_v2_pkt2(ssh_conn, IPSSH_MSG_NEWKEYS) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_do_keyex_rsa_clt :: failed to read SSH_MSG_NEW_KEY"); return IPCOM_ERR_FAILED; } if(ipssh_msg_new_key(ssh_conn) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_do_keyex_rsa_clt :: failed to process SSH_MSG_NEW_KEY"); return IPCOM_ERR_FAILED; } return IPCOM_SUCCESS; } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ #else int ipssh_keyex_rsa_empty_file; #endif /* IPSSH_USE_RSA_KEYEX */ /* **************************************************************************** * END OF FILE **************************************************************************** */