Files
m241Neelima/components/ip_net2-6.9/ipssh/src/ipssh_keyex_dh.c
T
2026-07-30 11:19:44 +05:30

1186 lines
36 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_keyex_dh.c,v $ $Revision: 1.62.10.5 $
* $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_keyex_dh.c,v $
* $Author: yli10 $ $Date: 2018-06-20 02:32:55 $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Roger Boden <roger@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#include "ipssh_h.h"
#include "ipssh_transport.h"
#include "ipssh_buffer.h"
#include "ipssh_keyex_dh.h"
#include <ipcom_clib.h>
#include <ipssh_config.h>
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_buffer.h>
#include <ipcom_sock.h>
#include <ipcom_syslog.h>
#include <ipcom_err.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
/*
****************************************************************************
* 4 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 5 DEFINES
****************************************************************************
*/
/*
* Define if you want debug printouts of the buffers that are hashed as part
* of the handshake.
*
* #define IPSSH_HASH_DBG
*/
/*
****************************************************************************
* 6 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_GLOBAL Ip_err ipssh_dss_sign(Ipssh_conn* ssh_conn, Ip_u8* data, Ip_u32 data_len, Ipcom_buffer* buf);
IP_GLOBAL Ip_err ipssh_send_kex_dh_reply(Ipssh_conn* ssh_conn);
IP_GLOBAL Ip_err ipssh_send_kex_dh_init(Ipssh_conn* ssh_conn);
IP_GLOBAL Ip_err ipssh_msg_kexdh_reply(Ipssh_conn* ssh_conn);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
IP_STATIC const char *ipssh_dh_g_gr1 = "2";
IP_STATIC const char *ipssh_dh_p_gr1 =
"FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
"29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
"EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
"E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
"EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
"FFFFFFFF" "FFFFFFFF";
IP_STATIC const char *ipssh_dh_g_gr14 = "2";
IP_STATIC const char *ipssh_dh_p_gr14 =
"FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
"29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
"EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
"E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
"EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
"C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
"83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
"670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
"E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
"DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
"15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF";
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipssh_create_dh_group
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL DH*
ipssh_create_dh_group(Ip_u8 dh_id)
{
DH* dh;
dh = DH_new();
if(!dh)
return IP_NULL;
switch (dh_id)
{
case IPSSH_KEYEX_DH_GR1_SHA1_ID:
if(BN_hex2bn(&(dh->p), ipssh_dh_p_gr1) == 0 ||
BN_hex2bn(&(dh->g), ipssh_dh_g_gr1) == 0)
{
IPCOM_LOG0(ERR, "ipssh_create_dh_group :: BN_hex2bn() failed for group 1");
DH_free(dh);
return IP_NULL;
}
break;
case IPSSH_KEYEX_DH_GR14_SHA1_ID:
if(BN_hex2bn(&(dh->p), ipssh_dh_p_gr14) == 0 ||
BN_hex2bn(&(dh->g), ipssh_dh_g_gr14) == 0)
{
IPCOM_LOG0(ERR, "ipssh_create_dh_group :: BN_hex2bn() failed for group 14");
DH_free(dh);
return IP_NULL;
}
break;
default:
IP_PANIC();
DH_free(dh);
return IP_NULL;
}
return dh;
}
/*
****************************************************************************
* 10 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipssh_dh_priv_key_set_length
*===========================================================================
* Description: This function sets the length of the DH private key.
* If not set, DH_generate_key will generate a private key
* of the same size as the DH group. Especially for large groups
* this is unnecessarily big, which will give long connection
* time, in particular on weak CPUs. According to key length
* recommendations, a DH private key length of 256 bits will be
* sufficient until the year 2040. To be on the safe side, we
* use twice that length, i.e. 512 bits.
* Parameters:
* Returns:
*/
IP_GLOBAL Ip_err ipssh_dh_priv_key_set_length(Ipssh_conn* ssh_conn)
{
if (BN_num_bits(ssh_conn->dh_data->dh->p) > 512)
{
ssh_conn->dh_data->dh->length = 512;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_calc_hash
*===========================================================================
* Description:
* Parameters:
* Returns:
* TODO: change to one argument: Ipssh_conn*?
*/
IP_GLOBAL Ip_err
ipssh_calc_hash(Ipssh_msg_list* msgs, const EVP_MD* (*hash_func)(void), int hash_comps,
Ip_u8** hash_value, Ip_u32* hash_len)
{
Ipcom_list *list, *node;
Ipssh_msg_list* msg;
Ip_s32 i, rc = IPCOM_SUCCESS;
Ipcom_buffer* buf;
EVP_MD_CTX* md_ctx;
Ip_size_t buf_len = 0;
/* Calculate needed buffer length */
list = (Ipcom_list*) msgs;
for(node = ipcom_list_first(list); node; node = ipcom_list_next(node))
{
msg = (Ipssh_msg_list*) node;
buf_len += msg->msg_len;
switch(msg->type)
{
case IPSSH_STR:
buf_len += 4;
break;
case IPSSH_MPINT:
buf_len += 5;
break;
}
}
buf = ipcom_buffer_new(buf_len+16);
md_ctx = EVP_MD_CTX_create();
if(!buf || !md_ctx)
{
IPCOM_LOG0(ERR, "ipssh_calc_hash :: out of memory");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
(void)EVP_DigestInit(md_ctx, hash_func());
list = (Ipcom_list*) msgs;
for(i=0; i<hash_comps; i++)
{
Ip_s32 found = 0;
for(node = ipcom_list_first(list); node; node = ipcom_list_next(node))
{
msg = (Ipssh_msg_list*) node;
if(msg->hash_pos == i)
{
if(msg->type == IPSSH_STR)
{
(void)ipssh_buffer_put_sshbuf(buf, msg->value, msg->msg_len);
}
else if(msg->type == IPSSH_MPINT)
{
(void)ipssh_buffer_put_ssh_mpint(buf, msg->value, msg->msg_len);
}
else if(msg->type == IPSSH_RAW || msg->type == IPSSH_INT)
{
(void)ipcom_buffer_put(buf, msg->value, msg->msg_len);
}
#if defined(IPSSH_DEBUG) && defined(IPSSH_HASH_DBG)
ipcom_debug_printf("dumping hash comp %d\n", i);
ipcom_buffer_dump(buf);
ipcom_debug_printf("\n");
#endif
(void)EVP_DigestUpdate(md_ctx, ipcom_buffer_start_ptr(buf),ipcom_buffer_len(buf));
found = 1;
ipcom_buffer_reset(buf);
break;
}
}
if(!found)
{
IPCOM_LOG1(ERR, "ipssh_calc_hash :: message no %d not found", i);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
*hash_len = md_ctx->digest->md_size;
*hash_value = ipcom_malloc(*hash_len);
if(!*hash_value)
{
IPCOM_LOG0(ERR, "ipssh_calc_hash :: out of memory");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
(void)EVP_DigestFinal(md_ctx, *hash_value, IP_NULL);
cleanup:
if(buf)
ipcom_buffer_free(buf);
if(md_ctx)
(void)EVP_MD_CTX_destroy(md_ctx);
return rc;
}
/*
*===========================================================================
* ipssh_verify_signature
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_verify_signature(EVP_PKEY* srv_key, Ip_u8* data, Ip_u32 data_len, const char* sig_type, Ip_u8* signature, Ip_u32 sig_len)
{
DSA_SIG* dsa_sig = IP_NULL;
EVP_MD_CTX md;
Ip_err rc = IPCOM_ERR_FAILED;
EVP_MD_CTX_init(&md);
switch(srv_key->type)
{
case NID_dsa:
if(ipcom_strcmp(sig_type, IPSSH_DSA_STR) != 0 || sig_len != 2*IPSSH_SIG_LEN)
return IPCOM_ERR_INVALID_ARG;
dsa_sig = DSA_SIG_new();
if(!dsa_sig)
return IPCOM_ERR_NO_MEMORY;
dsa_sig->r = BN_new();
dsa_sig->s = BN_new();
if(!dsa_sig->r || !dsa_sig->s)
{
rc = IPCOM_ERR_NO_MEMORY;
goto cleanup;
}
(void)BN_bin2bn(signature, IPSSH_SIG_LEN, dsa_sig->r);
(void)BN_bin2bn(signature+IPSSH_SIG_LEN, IPSSH_SIG_LEN, dsa_sig->s);
if (!EVP_VerifyInit(&md, EVP_dss1()) ||
!EVP_VerifyUpdate(&md, data, data_len) ||
!EVP_VerifyFinal(&md, signature, sig_len, srv_key))
{
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
rc = IPCOM_SUCCESS;
break;
#ifdef IPSSH_USE_RSA
case NID_rsaEncryption:
if(ipcom_strcmp(sig_type, IPSSH_RSA_STR) != 0)
return IPCOM_ERR_INVALID_ARG;
if (!EVP_VerifyInit(&md, EVP_sha1()) ||
!EVP_VerifyUpdate(&md, data, data_len) ||
!EVP_VerifyFinal(&md, signature, sig_len, srv_key))
{
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
rc = IPCOM_SUCCESS;
break;
#endif
default:
IP_PANIC();
}
cleanup:
if(dsa_sig)
DSA_SIG_free(dsa_sig);
EVP_MD_CTX_cleanup(&md);
return rc;
}
/*
*===========================================================================
* ipssh_dss_sign
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_dss_sign(Ipssh_conn* ssh_conn,
Ip_u8* data,
Ip_u32 data_len,
Ipcom_buffer* buf)
{
Ip_u8 *raw_sig;
Ip_u8 *tmp_raw_sig;
DSA_SIG *sig = IP_NULL;
const EVP_MD *evp_md = EVP_dss1();
EVP_MD_CTX md;
Ip_s32 r_len, s_len, rc=0;
Ip_u32 len;
Ip_size_t blob_len_pos;
int pad, i;
char* len_ptr;
EVP_PKEY* pkey = IP_NULL;
unsigned int sig_size;
IPCOM_LOG0(DEBUG2, "ipssh_dss_sign");
if(ipcom_buffer_capacity(buf) < 60)
{
IPCOM_LOG0(ERR, "ipssh_dss_sign :: too little buffer space");
return IPCOM_ERR_FAILED;
}
pkey = EVP_PKEY_new();
if (!pkey)
{
IPCOM_LOG0(ERR, "ipssh_dss_sign :: EVP_PKEY_new() failed");
return IPCOM_ERR_FAILED;
}
EVP_PKEY_set1_DSA(pkey, ssh_conn->srv_dsa_key);
sig_size = EVP_PKEY_size(pkey);
raw_sig = ipcom_malloc(sig_size);
if (!raw_sig)
{
IPCOM_LOG0(ERR, "ipssh_dss_sign :: out of memory");
return IPCOM_ERR_FAILED;
}
if (!EVP_SignInit(&md, evp_md)
|| !EVP_SignUpdate(&md, data, data_len)
|| !EVP_SignFinal(&md, raw_sig, &sig_size, pkey))
{
IPCOM_LOG0(ERR, "ipssh_dss_sign :: EVP_Sign*() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
tmp_raw_sig = raw_sig;
sig = d2i_DSA_SIG(IP_NULL, &tmp_raw_sig, sig_size);
if (sig == IP_NULL) {
IPCOM_LOG0(ERR, "ipssh_dss_sign :: d2i_DSA_SIG() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
r_len = BN_num_bytes(sig->r);
s_len = BN_num_bytes(sig->s);
if (r_len > IPSSH_MAX_INT_LEN || s_len > IPSSH_MAX_INT_LEN) {
IPCOM_LOG2(ERR, "ipssh_dss_sign :: bad sig size %d %d", r_len, s_len);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
len = (Ip_u32)ipcom_buffer_len(buf);
blob_len_pos = ipcom_buffer_len(buf);
(void)ipcom_buffer_put_int(buf, 0);
(void)ipssh_buffer_put_sshstr(buf, IPSSH_DSA_STR);
IPCOM_LOG2(DEBUG2, "ipssh_dss_sign :: r_len: %d, s_len: %d", r_len, s_len);
(void)ipcom_buffer_put_int(buf, ip_htonl(2*IPSSH_SIG_LEN));
for(i=0,pad=IPSSH_SIG_LEN-r_len; i<pad; i++)
(void)ipcom_buffer_put_char(buf, 0);
(void)BN_bn2bin(sig->r, ipcom_buffer_end_ptr(buf));
(void)ipcom_buffer_decr_cap(buf, r_len);
for(i=0,pad=IPSSH_SIG_LEN-s_len; i<pad; i++)
(void)ipcom_buffer_put_char(buf, 0);
(void)BN_bn2bin(sig->s, ipcom_buffer_end_ptr(buf));
(void)ipcom_buffer_decr_cap(buf, s_len);
len_ptr = ipcom_buffer_ptr2pos(buf, blob_len_pos);
IP_SET_32ON8(len_ptr, ip_htonl(((Ip_u32)ipcom_buffer_len(buf)-len-4))); /*lint !e666*/
cleanup:
EVP_PKEY_free(pkey);
(void)EVP_MD_CTX_cleanup(&md);
if(raw_sig)
ipcom_free(raw_sig);
if(sig)
DSA_SIG_free(sig);
return rc;
}
#ifdef IPSSH_USE_RSA
/*
*===========================================================================
* ipssh_rsa_sign
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_rsa_sign(Ipssh_conn* ssh_conn,
Ip_u8* data,
Ip_u32 data_len,
Ipcom_buffer* buf)
{
char* len_ptr;
Ip_u32 len;
Ip_size_t len2;
int rsa_blob_len;
unsigned int sig_len;
EVP_MD_CTX md;
EVP_PKEY* pkey = IP_NULL;
IPCOM_LOG0(DEBUG2, "ipssh_rsa_sign");
/* Length of whole signature */
len = (Ip_u32)ipcom_buffer_len(buf);
(void)ipcom_buffer_put_int(buf, 0);
(void)ipssh_buffer_put_sshstr(buf, IPSSH_RSA_STR);
/* Length of the RSA signature blob */
len2 = ipcom_buffer_len(buf);
(void)ipcom_buffer_put_int(buf, 0);
if((int)ipcom_buffer_capacity(buf) < RSA_size(ssh_conn->srv_rsa_key))
{
IPCOM_LOG0(ERR, "ipssh_rsa_sign :: too little buffer space");
return IPCOM_ERR_FAILED;
}
rsa_blob_len = RSA_size(ssh_conn->srv_rsa_key);
pkey = EVP_PKEY_new();
if (!pkey)
{
IPCOM_LOG0(ERR, "ipssh_rsa_sign :: EVP_PKEY_new() failed");
return IPCOM_ERR_FAILED;
}
EVP_PKEY_set1_RSA(pkey, ssh_conn->srv_rsa_key);
if (!EVP_SignInit(&md, EVP_sha1()) ||
!EVP_SignUpdate(&md, data, data_len) ||
!EVP_SignFinal(&md, ipcom_buffer_end_ptr(buf), &sig_len, pkey))
{
IPCOM_LOG0(ERR, "ipssh_rsa_sign :: EVP_Sign failed");
EVP_PKEY_free(pkey);
return IPCOM_ERR_FAILED;
}
EVP_PKEY_free(pkey);
EVP_MD_CTX_cleanup(&md);
(void)ipcom_buffer_decr_cap(buf, sig_len);
/* Set length of RSA signature blob */
len_ptr = ipcom_buffer_ptr2pos(buf, len2);
IP_SET_32ON8(len_ptr, ip_htonl(rsa_blob_len)); /*lint !e666*/
/* Set lenght of whole signature */
len_ptr = ipcom_buffer_ptr2pos(buf, len);
IP_SET_32ON8(len_ptr, ip_htonl(((Ip_u32)ipcom_buffer_len(buf)-len-4))); /*lint !e666*/
return IPCOM_SUCCESS;
}
#endif /* IPSSH_USE_RSA */
/*
*===========================================================================
* ipssh_sign_hash
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_sign_hash(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
{
/* Sign hash with server key */
switch (ssh_conn->conn_prop_pending.srv_host_key_alg[0])
{
case IPSSH_DSA_ID:
if(ipssh_dss_sign(ssh_conn, ssh_conn->hash, ssh_conn->hash_len, pkt))
{
IPCOM_LOG0(ERR, "ipssh_sign_hash :: ipssh_dss_sign() failed");
return IPCOM_ERR_FAILED;
}
break;
#ifdef IPSSH_USE_RSA
case IPSSH_RSA_ID:
if(ipssh_rsa_sign(ssh_conn, ssh_conn->hash, ssh_conn->hash_len, pkt))
{
IPCOM_LOG0(ERR, "ipssh_sign_hash :: ipssh_rsa_sign() failed");
return IPCOM_ERR_FAILED;
}
break;
#endif
default:
IP_PANIC();
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_send_kex_dh_reply
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_kex_dh_reply(Ipssh_conn* ssh_conn)
{
Ip_size_t len;
Ip_size_t f_pos;
Ip_size_t host_key_pos;
Ipcom_buffer* pkt;
Ip_err rc;
IPCOM_LOG0(DEBUG, "ipssh_send_kex_dh_reply()");
pkt = ipssh_buffer_new_v2_pkt(2048, IPSSH_MSG_KEXDH_REPLY);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: out of memory");
return IPCOM_ERR_FAILED;
}
/* Add server key */
len = ipcom_buffer_len(pkt);
host_key_pos = ipcom_buffer_len(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_kex_dh_reply :: ipssh_buffer_put_dsa_key() failed");
goto failed;
}
break;
#ifdef IPSSH_USE_RSA
case IPSSH_RSA_ID:
if(ipssh_buffer_put_rsa_key(pkt, ssh_conn->srv_rsa_key))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_buffer_put_rsa_key() failed");
goto failed;
}
break;
#endif
default:
IP_PANIC();
goto failed;
}
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs),
IPSSH_RAW,
(Ip_u8*)ipcom_buffer_ptr2pos(pkt, host_key_pos),
ipcom_buffer_len(pkt) - host_key_pos,
4))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_msg2list() failed for DSA key");
goto failed;
}
/* Add f */
f_pos = ipcom_buffer_len(pkt);
rc = ipssh_buffer_put_bn(pkt, ssh_conn->dh_data->dh->pub_key);
if (rc < 0)
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_buffer_put_bn() failed");
goto failed;
}
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs),
IPSSH_MPINT,
(Ip_u8*)IPSSH_GET_MPINT_VALUE(ipcom_buffer_ptr2pos(pkt, f_pos)),
ipcom_buffer_len(pkt) - f_pos - 4,
6))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_msg2list() failed for f");
goto failed;
}
/* Calculate hash */
if (ssh_conn->hash)
ipcom_free(ssh_conn->hash);
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_kex_dh_reply :: ipssh_calc_hash() failed");
goto failed;
}
if (ssh_conn->sess_id_len == 0)
{
/* The session ID is only updated in the inital key exchange */
ssh_conn->sess_id_len = ssh_conn->hash_len;
ssh_conn->sess_id = ipcom_malloc(ssh_conn->hash_len);
if(!ssh_conn->sess_id)
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: out of memory");
goto failed;
}
ipcom_memcpy(ssh_conn->sess_id, ssh_conn->hash, ssh_conn->sess_id_len);
}
/* Sign hash with server key */
switch (ssh_conn->conn_prop_pending.srv_host_key_alg[0])
{
case IPSSH_DSA_ID:
if(ipssh_dss_sign(ssh_conn, ssh_conn->hash, ssh_conn->hash_len, pkt))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_dss_sign() failed");
goto failed;
}
break;
#ifdef IPSSH_USE_RSA
case IPSSH_RSA_ID:
if(ipssh_rsa_sign(ssh_conn, ssh_conn->hash, ssh_conn->hash_len, pkt))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_rsa_sign() failed");
goto failed;
}
break;
#endif
default:
IP_PANIC();
goto failed;
}
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_send_pkt() failed");
return IPCOM_ERR_FAILED;
}
if(ipssh_compute_keys(ssh_conn))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_compute_keys() failed");
return IPCOM_ERR_FAILED;
}
if(ipssh_send_new_key_msg(ssh_conn))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_send_new_key() failed");
return IPCOM_ERR_FAILED;
}
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_NEW_KEY;
return IPCOM_SUCCESS;
failed:
ipcom_buffer_free(pkt);
return IPCOM_ERR_FAILED;
}
/*
*===========================================================================
* ipssh_send_kex_dh_init
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_kex_dh_init(Ipssh_conn* ssh_conn)
{
Ip_err rc = IPCOM_ERR_FAILED;
Ipcom_buffer* pkt;
Ip_size_t pos;
Ip_size_t len;
IPCOM_LOG1(DEBUG, "ipssh_send_kex_dh_init :: ssh_conn: %x", ssh_conn);
pkt = ipssh_buffer_new_v2_pkt(1024, IPSSH_MSG_KEXDH_INIT);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_init :: out of memory");
return IPCOM_ERR_FAILED;
}
ssh_conn->dh_data->dh = ipssh_create_dh_group(ssh_conn->conn_prop_pending.kex_alg[0]);
if(!ssh_conn->dh_data->dh)
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_init :: ipssh_create_dh_group() failed");
goto cleanup;
}
/* Reduce the size of the DH private key for large DH groups. It is unnecessarily big by
* default, which increases the connection time */
if (ipssh_dh_priv_key_set_length(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_init :: ipssh_dh_priv_key_set_length() failed");
goto cleanup;
}
/* Generate random number y and create e = g^x mod p */
if(!DH_generate_key(ssh_conn->dh_data->dh))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_init :: DH_generate_key() failed");
goto cleanup;
}
pos = ipcom_buffer_len(pkt);
if(ipssh_buffer_put_bn(pkt, ssh_conn->dh_data->dh->pub_key) < 0)
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_init :: ipssh_buffer_put_bn() failed");
goto cleanup;
}
pos += 4; /* proceed past length field */
len = ipcom_buffer_len(pkt) - pos ;
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs),
IPSSH_MPINT,
(Ip_u8*)ipcom_buffer_ptr2pos(pkt, pos),
len,
5))
{
IPCOM_LOG0(ERR, "ipssh_send_kex_dh_reply :: ipssh_msg2list() failed for f");
goto cleanup;
}
if(ipssh_send_pkt(ssh_conn, pkt, IP_FALSE) == IPCOM_SUCCESS)
{
rc = IPCOM_SUCCESS;
}
cleanup:
if(pkt)
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_msg_kex_dh_init
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_kex_dh_init(Ipssh_conn* ssh_conn)
{
Ip_u8* mp_int_value;
Ip_s32 len;
#ifdef IP_ASSERT
int secr_len;
#endif
IPCOM_LOG0(DEBUG, "ipssh_msg_kex_dh_init()");
if(ssh_conn->skip_guessed_kex_pkt)
{
IPCOM_LOG0(DEBUG, "ipssh_msg_kex_dh_init :: skipping guessed packet");
ssh_conn->skip_guessed_kex_pkt = 0;
(void)ipssh_consume_pkt(ssh_conn->recv_plain_buf);
return IPCOM_SUCCESS;
}
if (ssh_conn->dh_data->dh)
DH_free(ssh_conn->dh_data->dh);
ssh_conn->dh_data->dh = ipssh_create_dh_group(ssh_conn->conn_prop_pending.kex_alg[0]);
if(!ssh_conn->dh_data->dh)
{
IPCOM_LOG0(ERR, "ipssh_msg_kex_dh_init :: ipssh_create_dh_group() failed");
return IPCOM_ERR_FAILED;
}
/* Reduce the size of the DH private key for large DH groups. It is unnecessarily big by
* default, which increases the connection time */
if (ipssh_dh_priv_key_set_length(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_kex_dh_init :: ipssh_dh_priv_key_set_length() failed");
return IPCOM_ERR_FAILED;
}
/* Generate random number y and create f = g^y mod p */
if(!DH_generate_key(ssh_conn->dh_data->dh))
{
IPCOM_LOG0(ERR, "ipssh_msg_kex_dh_init :: DH_generate_key() failed");
return IPCOM_ERR_FAILED;
}
/* Get e from KEY_DH_INIT message */
if (ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6)
|| ipssh_buffer_get_int(ssh_conn->recv_plain_buf, &len))
{
IPCOM_LOG0(ERR, "ipssh_msg_kex_dh_init :: Failed to unpack e length");
return IPCOM_ERR_FAILED;
}
if (len == 0 || len != (Ip_s32)ipcom_buffer_len(ssh_conn->recv_plain_buf))
{
IPCOM_LOG1(ERR, "ipssh_msg_kex_dh_init :: Invalid length of e: %d", len);
return IPCOM_ERR_FAILED;
}
mp_int_value = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf);
if (ssh_conn->dh_data->e)
BN_free(ssh_conn->dh_data->e);
ssh_conn->dh_data->e = BN_bin2bn(mp_int_value, len, IP_NULL);
if(!ssh_conn->dh_data->e)
{
IPCOM_LOG0(ERR, "ipssh_msg_kex_dh_init :: BN_bin2bn() failed for e");
return IPCOM_ERR_FAILED;
}
(void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, len);
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), /* Store e for later hash calculation */
IPSSH_MPINT,
mp_int_value,
len,
5))
{
IPCOM_LOG0(ERR, "ipssh_msg_kex_dh_init :: ipssh_msg2list() failed");
return IPCOM_ERR_FAILED;
}
/* Calculate the shared secret */
ssh_conn->secr_len = DH_size(ssh_conn->dh_data->dh);
#ifdef IP_ASSERT
secr_len = ssh_conn->secr_len;
#endif
if (ssh_conn->shared_secret)
ipcom_free(ssh_conn->shared_secret); /* Free old shared secret, re-keying */
ssh_conn->shared_secret = ipcom_malloc(ssh_conn->secr_len+1);
if(!ssh_conn->shared_secret) {
IPCOM_LOG0(ERR, "ipssh_msg_kex_dh_init :: out of memory");
return IPCOM_ERR_FAILED;
}
ssh_conn->secr_len = DH_compute_key(ssh_conn->shared_secret, ssh_conn->dh_data->e, ssh_conn->dh_data->dh);
if(ssh_conn->secr_len < 0)
{
IPCOM_LOG0(ERR, "ipssh_msg_kex_dh_init :: DH_compute_key() failed");
return IPCOM_ERR_FAILED;
}
ip_assert(ssh_conn->secr_len <= secr_len);
/* If the shared secret is a negative number, we must add a zero byte */
if (ssh_conn->shared_secret[0] & 0x80)
{
ipcom_memmove(ssh_conn->shared_secret+1, ssh_conn->shared_secret, ssh_conn->secr_len);
ssh_conn->shared_secret[0] = 0;
ssh_conn->secr_len++;
}
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), /* Store shared secret for later hash calculation */
IPSSH_MPINT,
ssh_conn->shared_secret,
ssh_conn->secr_len,
7))
{
IPCOM_LOG0(ERR, "ipssh_msg_kex_dh_init :: ipssh_msg2list() failed");
return IPCOM_ERR_FAILED;
}
/* Send SSH_MSG_KEXDH_REPLY */
return ipssh_send_kex_dh_reply(ssh_conn);
}
/*
*===========================================================================
* ipssh_msg_kexdh_reply
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_kexdh_reply(Ipssh_conn* ssh_conn)
{
Ip_u8 pkt_type;
EVP_PKEY* srv_pub_key;
char* sig_type = IP_NULL;
Ip_u8* signature = IP_NULL;
Ip_u32 sign_len;
Ip_err rc = IPCOM_ERR_FAILED;
Ip_u8* ptr;
Ip_ptrdiff_t len;
union Ip_sockaddr_union peer_addr;
Ip_socklen_t peer_len;
pkt_type = *ipcom_buffer_ptr2pos(ssh_conn->recv_plain_buf, 5);
if(pkt_type != IPSSH_MSG_KEXDH_REPLY)
{
IPCOM_LOG1(ERR, "ipssh_msg_kexdh_reply :: unexpected pkt type: %d", pkt_type);
IP_PANIC();
return IPCOM_ERR_FAILED;
}
/* Unpack message */
(void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6);
ptr = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf);
if((srv_pub_key=ipssh_buffer_get_pub_key(ssh_conn->recv_plain_buf)) == IP_NULL)
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: failed to unpack srv pub key");
goto cleanup;
}
len = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf) - ptr;
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs),
IPSSH_RAW,
ptr,
len,
4))
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: ipssh_msg2list() failed");
goto cleanup;
}
ptr = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf);
if (ssh_conn->dh_data->f)
BN_free(ssh_conn->dh_data->f);
if(ipssh_buffer_get_ssh_mpint2(ssh_conn->recv_plain_buf, &(ssh_conn->dh_data->f)) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: failed to unpack f");
goto cleanup;
}
ptr += 4; /* Skip length field */
len = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf) - ptr;
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs),
IPSSH_MPINT,
ptr,
len,
6))
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: ipssh_msg2list() failed for f");
goto cleanup;
}
/* Calculate the shared secret */
ssh_conn->secr_len = DH_size(ssh_conn->dh_data->dh);
ssh_conn->shared_secret = ipcom_malloc(ssh_conn->secr_len+1);
if(!ssh_conn->shared_secret) {
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: out of memory");
return IPCOM_ERR_FAILED;
}
ssh_conn->secr_len = DH_compute_key(ssh_conn->shared_secret, ssh_conn->dh_data->f, ssh_conn->dh_data->dh);
if(ssh_conn->secr_len < 0)
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: DH_compute_key() failed");
return IPCOM_ERR_FAILED;
}
/* If the shared secret is a negative number, we must add a zero byte */
if (ssh_conn->shared_secret[0] & 0x80)
{
ipcom_memmove(ssh_conn->shared_secret+1, ssh_conn->shared_secret, ssh_conn->secr_len);
ssh_conn->shared_secret[0] = 0;
ssh_conn->secr_len++;
}
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), /* Store shared secret for later hash calculation */
IPSSH_MPINT,
ssh_conn->shared_secret,
ssh_conn->secr_len,
7))
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: ipssh_msg2list() failed");
return IPCOM_ERR_FAILED;
}
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 4)) /* Skip blob len field */
return IPCOM_ERR_FAILED;
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_kexdh_reply :: failed to unpack signature");
goto cleanup;
}
/* Calculate hash */
if (ssh_conn->hash)
ipcom_free(ssh_conn->hash);
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_kexdh_reply :: ipssh_calc_hash() failed");
goto cleanup;
}
/* Verify server key */
ipcom_memset(&peer_addr, 0, sizeof(peer_addr));
peer_len = sizeof(peer_addr);
if(ipcom_getpeername(ssh_conn->sock, &peer_addr.sa, &peer_len) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: ipcom_getpeername() failed");
goto cleanup;
}
if(ipssh_validate_srv_public_key_cb(srv_pub_key, &peer_addr) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: ipssh_validate_srv_public_key_cb() failed");
goto cleanup;
}
/* Verify signature */
if(ipssh_verify_signature(srv_pub_key, ssh_conn->hash, ssh_conn->hash_len, sig_type, signature, sign_len) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: 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_kexdh_reply :: 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);
}
if(ipssh_compute_keys(ssh_conn))
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: ipssh_compute_keys() failed");
goto cleanup;
}
if(ipssh_send_new_key_msg(ssh_conn))
{
IPCOM_LOG0(ERR, "ipssh_msg_kexdh_reply :: ipssh_send_new_key() failed");
goto cleanup;
}
rc = IPCOM_SUCCESS;
cleanup:
if(srv_pub_key)
EVP_PKEY_free(srv_pub_key);
if(sig_type)
ipcom_free(sig_type);
if(signature)
ipcom_free(signature);
return rc;
}
/*
*===========================================================================
* ipssh_do_keyex_dh_clt
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_do_keyex_dh_clt(Ipssh_conn* ssh_conn)
{
Ip_err rc = IPCOM_ERR_FAILED;
if(ipssh_send_kex_dh_init(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_do_keyex_dh_clt :: ipssh_send_kex_dh_init() failed");
}
if(ipssh_read_v2_pkt(ssh_conn) != IPCOM_SUCCESS)
goto cleanup;
if(ipssh_msg_kexdh_reply(ssh_conn) != IPCOM_SUCCESS)
goto cleanup;
if(ipssh_read_v2_pkt(ssh_conn) != IPCOM_SUCCESS)
goto cleanup;
if(ipssh_msg_new_key(ssh_conn) != IPCOM_SUCCESS)
goto cleanup;
rc = IPCOM_SUCCESS;
cleanup:
return rc;
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/