openswan协商流程之(五):main_inR2_outI3()
主模式第五包:main_inR2_outI3
文章目录
1. 序言
main_inR2_outI3()函数是ISAKMP协商过程中第五包的核心处理函数的入口,发起端通过此包收到相应端的KE值和Nonce值收到,可以生成需要的密钥信息。这里我们主要说明main_inR2_outI3的函数调用关系、处理流程以及对源码的注释分析,关于main_inR2_outI3的上下文环境暂不叙述,留给后面的文章进行更新。
ISAKMP协商报文的处理流程都比较复杂,此函数在协商的报文处理函数中比较复杂的,因此个人学习期间难免有遗漏和理解错误的地方,请大家多多批评指正。
对于源码的学习,我并没有把每一行进行备注,而是将自己认为的关键点做了注释或者标注。
目前主要是整理源码中的处理里流程和实现逻辑,尚未深入比较细节的处理;后续在我整理完毕使用主模式协商的9个报文后,我再次结合代码整理每一个报文的详细流程,到时把每一个报文的注意事项、作用,处理方式做一个整体上的把握。同时结合书本上的描述来解释代码层的实现。
2.函数调用关系
这里暂时给出xmind整理的思维导图。
 
3. 第五个报文流程图
第五个报文的处理流程主要分为三个大功能:
- 解析收到的对端报文
- 密钥交换载荷KE
- Nonce载荷
 
- 使用DH算法制作加密密钥、认证密钥、哈希密钥等
- 构造应答报文(第五个报文)
- 签名
- 报文加密
 

4. main_inR2_outI3()源码学习
该函数主要的功能是:
- 解析对端的KE载荷和Nonce,因为这是制作密钥的基础材料。
- 解析完毕后,开始制作密钥。
stf_status
main_inR2_outI3(struct msg_digest *md)
{
    struct dh_continuation *dh;
	/*获取受到的第四个报文中的KE载荷*/
    pb_stream *const keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs;
    struct state *const st = md->st;
    /* if we are already processing a packet on this st, we will be unable
     * to start another crypto operation below */
    if (is_suspended(st)) {
        openswan_log("%s: already processing a suspended cyrpto operation "
                     "on this SA, duplicate will be dropped.", __func__);
	return STF_TOOMUCHCRYPTO;
    }
    /* KE in *//*解析KE载荷并存储在st->st_gr上*/
    RETURN_STF_FAILURE(accept_KE(&st->st_gr, "Gr"
				 , st->st_oakley.group, keyex_pbs));
    /* Nr in *//*解析Nonce载荷,并存储在st->st_nr*/
    RETURN_STF_FAILURE(accept_v1_nonce(md, &st->st_nr, "Nr"));
    dh = alloc_thing(struct dh_continuation, "aggr outR1 DH");
    if(!dh) { return STF_FATAL; }
    dh->md = md;
    set_suspended(st, md);/*挂起当前报文,防止由于耗时操作重新收到对方的重发报文*/
    pcrc_init(&dh->dh_pcrc);
    dh->dh_pcrc.pcrc_func = main_inR2_outI3_cryptotail;
    return start_dh_secretiv(&dh->dh_pcrc, st
			     , st->st_import
			     , INITIATOR
			     , st->st_oakley.group->group);
}
5. start_dh_secretiv()源码学习
该函数的主要功能:
- 获取协商的认证算法、哈希算法、oakley群信息
- 准备Nonce、KE、cookie、st_sec_chunk(密钥信息?)
- 制作密钥(该流程中会通过回调函数组装报文并发送)
- 发送完毕报文的后续处理
/*
 * invoke helper to do DH work.
 */
stf_status start_dh_secretiv(struct pluto_crypto_req_cont *cn
			     , struct state *st
			     , enum crypto_importance importance
			     , enum phase1_role init       /* TRUE=g_init,FALSE=g_r */
			     , u_int16_t oakley_group2)
{
    struct pluto_crypto_req r;
    struct pcr_skeyid_q *dhq;
    const chunk_t *pss = get_preshared_secret(st->st_connection);/*获取预共享秘钥*/
    err_t e;
    bool toomuch = FALSE;
    pcr_init(&r, pcr_compute_dh_iv, importance);/*使用DH算法生成三把秘钥*/
    dhq = &r.pcr_d.dhq;
    passert(st->st_sec_in_use);
    /* convert appropriate data to dhq */
    dhq->auth = st->st_oakley.auth;
    dhq->prf_hash = st->st_oakley.prf_hash;
    dhq->oakley_group = oakley_group2;
    dhq->init = init;
    dhq->keysize = st->st_oakley.enckeylen/BITS_PER_BYTE;
    passert(r.pcr_d.dhq.oakley_group != 0);
    DBG(DBG_CONTROL | DBG_CRYPT,
       DBG_log("parent1 type: %d group: %d len: %d\n", r.pcr_type,
	    r.pcr_d.dhq.oakley_group, (int)r.pcr_len));
	/*将*pss的内容拷贝到dhq->space中,起始位置是thespace;同时将dhq->pss指向spce对应的空间。然后更新thespace*/
    if(pss) {
	pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->pss, *pss);
    }
    pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->ni,  st->st_ni);
    pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->nr,  st->st_nr);
    pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->gi,  st->st_gi);
    pluto_crypto_copychunk(&dhq->thespace, dhq->space, &dhq->gr,  st->st_gr);
    pluto_crypto_copychunk(&dhq->thespace, dhq->space
			   , &dhq->secret, st->st_sec_chunk);
/*最后发起端和相应者Nonce、KE等信息全部拷贝到了space中*/
#ifdef HAVE_LIBNSS
    /*copying required encryption algo*/
    /*dhq->encrypt_algo = st->st_oakley.encrypt;*/
    dhq->encrypter = st->st_oakley.encrypter;
    DBG(DBG_CRYPT, DBG_log("Copying DH pub key pointer to be sent to a thread helper"));
    pluto_crypto_copychunk(&dhq->thespace, dhq->space , &dhq->pubk, st->pubk);
#endif
/*将发起者、相应者cookie也拷贝到dhq->space中*/
    pluto_crypto_allocchunk(&dhq->thespace, &dhq->icookie, COOKIE_SIZE);
    memcpy(wire_chunk_ptr(dhq, &dhq->icookie), st->st_icookie, COOKIE_SIZE);
    pluto_crypto_allocchunk(&dhq->thespace, &dhq->rcookie, COOKIE_SIZE);
    memcpy(wire_chunk_ptr(dhq, &dhq->rcookie)
	   , st->st_rcookie, COOKIE_SIZE);
/*至此,发起端和相应者Nonce、KE、cookie等信息全部拷贝到了space中*/
    passert(dhq->oakley_group != 0);
    e = send_crypto_helper_request(&r, cn, &toomuch);/*r 中包含上述信息,用来生成三把秘钥*/
    if(e != NULL) {
	loglog(RC_LOG_SERIOUS, "can not start crypto helper: %s", e);
	if(toomuch) {
	    return STF_TOOMUCHCRYPTO;
	} else {
	    return STF_FAIL;
	}
    } else if(!toomuch) {
	st->st_calculating = TRUE;
	delete_event(st);
	event_schedule(EVENT_CRYPTO_FAILED, EVENT_CRYPTO_FAILED_DELAY, st);
	return STF_SUSPEND;
    } else {
	/* we must have run the continuation directly, so
	 * complete_state_transition already got called.
	 */
	return STF_INLINE;
    }
}
6. main_inR2_outI3_cryptotail()源码学习
static void
main_inR2_outI3_cryptotail(struct pluto_crypto_req_cont *pcrc
			   , struct pluto_crypto_req *r
			   , err_t ugh)
{
  struct dh_continuation *dh = (struct dh_continuation *)pcrc;
  struct msg_digest *md = dh->md;
  struct state *const st = md->st;
  stf_status e;
  DBG(DBG_CONTROLMORE
      , DBG_log("main inR2_outI3: calculated DH, sending R1"));
  if (st == NULL) {
      loglog(RC_LOG_SERIOUS, "%s: Request was disconnected from state",
	      __FUNCTION__);
      if (dh->md)
          release_md(dh->md);
      return;
  }
  passert(cur_state == NULL);
  passert(st != NULL);
  passert(st->st_suspended_md == dh->md);
  set_suspended(st, NULL);	/* no longer connected or suspended */
  set_cur_state(st);
  st->st_calculating = FALSE;
  if(ugh) {
      loglog(RC_LOG_SERIOUS, "failed in DH exponentiation: %s", ugh);
      e = STF_FATAL;
  } else {
      e = main_inR2_outI3_continue(md, r);
  }
  if(dh->md != NULL) {
      complete_v1_state_transition(&dh->md, e);
      if(dh->md) release_md(dh->md);
  }
  reset_cur_state();
}
7. main_inR2_outI3_cryptotail()源码学习
该函数的主要功能是:
- 将制作的密钥存储在状态上
- 解析对方报文中的证书,确定是否需要发送证书载荷
- 构造ID标识载荷、并计算哈希
- 如果使用哈希认证则填充哈希载荷;如果使用签名认证则进行数字签名
- 报文加密
/* STATE_MAIN_I2:
 * SMF_PSK_AUTH: HDR, KE, Nr --> HDR*, IDi1, HASH_I
 * SMF_DS_AUTH: HDR, KE, Nr --> HDR*, IDi1, [ CERT, ] SIG_I
 *
 * The following are not yet implemented.
 * SMF_PKE_AUTH: HDR, KE, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
 *	    --> HDR*, HASH_I
 * SMF_RPKE_AUTH: HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>Ke_r
 *	    --> HDR*, HASH_I
 */
static stf_status
main_inR2_outI3_continue(struct msg_digest *md
			 , struct pluto_crypto_req *r)
{
    struct state *const st = md->st;
	/*选择认证的载荷类型:哈希\签名*/
    int auth_payload = st->st_oakley.auth == OAKLEY_PRESHARED_KEY
	? ISAKMP_NEXT_HASH : ISAKMP_NEXT_SIG;
    pb_stream id_pbs;	/* ID Payload; also used for hash calculation */
    bool send_cert = FALSE;
    bool send_cr = FALSE;
    generalName_t *requested_ca = NULL;
    cert_t mycert = st->st_connection->spd.this.cert;/*获取自己的证书*/
    finish_dh_secretiv(st, r);/*将DH产生的三把秘钥存储在结构体上*/
    if(!r->pcr_success) {
        return STF_FAIL + INVALID_KEY_INFORMATION;
    }
    /* decode certificate requests */
    decode_cr(md, &requested_ca);/*解析报文中的证书载荷,并使用requested_ca链起来*/
    if(requested_ca != NULL)
    {
	st->hidden_variables.st_got_certrequest = TRUE;
    }
    /*
     * send certificate if we have one and auth is RSA, and we were
     * told we can send one if asked, and we were asked, or we were told
     * to always send one.
     * 同时满足下面的条件再发送证书载荷:
     *		1. 使用RSA签名
     *		2. 本端导入了数字证书
     *		3. 配置要求发送证书或强制发送证书
     *		4. 从对端收到了证书
     */
    send_cert = st->st_oakley.auth == OAKLEY_RSA_SIG
	&& mycert.type != CERT_NONE
	&& ((st->st_connection->spd.this.sendcert == cert_sendifasked
	     && st->hidden_variables.st_got_certrequest)
	    || st->st_connection->spd.this.sendcert==cert_alwayssend
	    || st->st_connection->spd.this.sendcert==cert_forcedtype);
    doi_log_cert_thinking(md
			  , st->st_oakley.auth
			  , mycert.type
			  , st->st_connection->spd.this.sendcert
			  , st->hidden_variables.st_got_certrequest
			  , send_cert);
    /* send certificate request, if we don't have a preloaded RSA public key */
    send_cr = !no_cr_send && send_cert && !has_preloaded_public_key(st);/*是否可以发送证书*/
    DBG(DBG_CONTROL
	, DBG_log(" I am %ssending a certificate request"
		  , send_cr ? "" : "not "));
    /*
     * free collected certificate requests since as initiator
     * we don't heed them anyway
     */
    free_generalNames(requested_ca, TRUE);
    /* done parsing; initialize crypto  */
#ifdef NAT_TRAVERSAL
    if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATD) {
      nat_traversal_natd_lookup(md);/*检测隧道两端是否经过NAT穿越*/
    }
    if (st->hidden_variables.st_nat_traversal) {
      nat_traversal_show_result(st->hidden_variables.st_nat_traversal
				, md->sender_port);
    }
    if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_KA) {
      nat_traversal_new_ka_event();
    }
#endif
    /*************** build output packet HDR*;IDii;HASH/SIG_I ***************/
    /* ??? NOTE: this is almost the same as main_inI3_outR3's code */
    /* HDR* out done */
	/*HDR在哪里完成填充的呢???*/
    /* IDii out */
    {
	struct isakmp_ipsec_id id_hd;
	chunk_t id_b;
	/*从状态/连接上获取本端标识信息*/
	build_id_payload(&id_hd, &id_b, &st->st_connection->spd.this);
	id_hd.isaiid_np = (send_cert)? ISAKMP_NEXT_CERT : auth_payload;
	if (!out_struct(&id_hd
			, &isakmp_ipsec_identification_desc
			, &md->rbody
			, &id_pbs)
	    || !out_chunk(id_b, &id_pbs, "my identity"))
	    return STF_INTERNAL_ERROR;
	close_output_pbs(&id_pbs);
    }
    /* CERT out */
    if (send_cert)
    {
	pb_stream cert_pbs;
	struct isakmp_cert cert_hd;
	cert_hd.isacert_np = (send_cr)? ISAKMP_NEXT_CR : ISAKMP_NEXT_SIG;
	cert_hd.isacert_type = mycert.type;
	openswan_log("I am sending my cert");
	if (!out_struct(&cert_hd
			, &isakmp_ipsec_certificate_desc
			, &md->rbody
			, &cert_pbs))
	    return STF_INTERNAL_ERROR;
	if(mycert.forced) {
	  if (!out_chunk(mycert.u.blob, &cert_pbs, "forced CERT"))
	    return STF_INTERNAL_ERROR;
	} else {
	  if (!out_chunk(get_mycert(mycert), &cert_pbs, "CERT"))
	    return STF_INTERNAL_ERROR;
	}
	close_output_pbs(&cert_pbs);
    }
    /* CR out *//*添加证书请求载荷,即要求对对端进行发送证书以求认证...*/
    if (send_cr)
    {
	openswan_log("I am sending a certificate request");
	if (!build_and_ship_CR(mycert.type
			       , st->st_connection->spd.that.ca
			       , &md->rbody, ISAKMP_NEXT_SIG))
	    return STF_INTERNAL_ERROR;
    }
#ifdef TPM
    {
	pb_stream *pbs = &md->rbody;
	size_t enc_len = pbs_offset(pbs) - sizeof(struct isakmp_hdr);
	TCLCALLOUT_crypt("preHash", st,pbs,sizeof(struct isakmp_hdr),enc_len);
	/* find location of ID PBS */
	tpm_findID(pbs, &id_pbs);
    }
#endif
    /* HASH_I or SIG_I out */
    {
	u_char hash_val[MAX_DIGEST_LEN];
	size_t hash_len = main_mode_hash(st, hash_val, TRUE, &id_pbs);
	if (auth_payload == ISAKMP_NEXT_HASH)
	{
	    /* HASH_I out */
	    if (!out_generic_raw(ISAKMP_NEXT_NONE
				 , &isakmp_hash_desc
				 , &md->rbody
				 , hash_val, hash_len, "HASH_I"))
		return STF_INTERNAL_ERROR;
	}
	else
	{
	    /* SIG_I out */
	    u_char sig_val[RSA_MAX_OCTETS];
	    size_t sig_len = RSA_sign_hash(st->st_connection
		, sig_val, hash_val, hash_len);
	    if (sig_len == 0)
	    {
		loglog(RC_LOG_SERIOUS, "unable to locate my private key for RSA Signature");
		return STF_FAIL + AUTHENTICATION_FAILED;
	    }
	    if (!out_generic_raw(ISAKMP_NEXT_NONE
				 , &isakmp_signature_desc
				 , &md->rbody
				 , sig_val
				 , sig_len
				 , "SIG_I"))
		return STF_INTERNAL_ERROR;
	}
    }
    /* encrypt message, except for fixed part of header */
    /* st_new_iv was computed by generate_skeyids_iv */
    if (!encrypt_message(&md->rbody, st))
	return STF_INTERNAL_ERROR;	/* ??? we may be partly committed */
    return STF_OK;
}
openswan协商流程之(五):main_inR2_outI3()的更多相关文章
- openswan协商流程之(七):main_inR3
		主模式第六包(收包):main_inR3 1. 序言 main_inR3()函数是ISAKMP协商过程中第一阶段的最后一个报文的接收处理函数,它的作用同main_inI3_outR3()部分功能相同: ... 
- openswan协商流程之(一):main_outI1()
		主模式第一包:main_outI1() 1. 序言 main_outI1()作为主模式主动发起连接请求的核心处理函数,我们可以通过学习该函数的处理流程来探究openswan中报文封装的基本思想.如果之 ... 
- openswan协商流程之(六):main_inI3_outR3()
		主模式第六包:main_inI3_outR3 1. 序言 main_inI3_outR3()函数是ISAKMP协商过程中第六包的核心处理函数的入口,第五六包主要用来验证对方的身份信息,同时此报文也是加 ... 
- openswan协商流程之(四):main_inI2_outR2()
		主模式第四包:main_inI2_outR2 1. 序言 main_inI2_outR2()函数是ISAKMP协商过程中第四包的核心处理函数的入口,同时在此处理流程中已经获取到足够的隧道信息,可以生成 ... 
- openswan协商流程之(三):main_inR1_outI2
		主模式第三包:main_inR1_outI2 1. 序言 main_inR1_outI2()函数是ISAKMP协商过程中第三包的核心处理函数的入口.这里我们主要说明main_inR1_outI2的函数 ... 
- openswan协商流程之(二):main_inI1_outR1()
		主模式第二包:main_inI1_outR1() 文章目录 主模式第二包:main_inI1_outR1() 1. 序言 2. `main_inI1_outR1()`处理流程图 3. `main_in ... 
- IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包
		IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 文章目录 IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 1. IKEv2 协商总体框架 2. 第二包流程图 3. ... 
- 以太网驱动的流程浅析(五)-mii_bus初始化以及phy id的获取【原创】
		以太网驱动的流程浅析(五)-mii_bus初始化以及phy id的获取 Author:张昺华 Email:920052390@qq.com Time:2019年3月23日星期六 此文也在我的个人公众号 ... 
- Raid 10配置流程(五块磁盘)
		Raid 10配置流程(五块磁盘) 1.在虚拟机中再添加5块硬盘. 2.使用mdadm命令创建raid10,名称为“/dev/md0” -C代表创建操作,-v显示创建过程,-a yes检查RAID名 ... 
随机推荐
- [源码解析] 机器学习参数服务器ps-lite 之(3) ----- 代理人Customer
			[源码解析] 机器学习参数服务器ps-lite 之(3) ----- 代理人Customer 目录 [源码解析] 机器学习参数服务器ps-lite 之(3) ----- 代理人Customer 0x0 ... 
- JavaEE精英进阶课学习笔记《博学谷》
			JavaEE精英进阶课学习笔记<博学谷> 第1章 亿可控系统分析与设计 学习目标 了解物联网应用领域及发展现状 能够说出亿可控的核心功能 能够画出亿可控的系统架构图 能够完成亿可控环境的准 ... 
- JUC学习笔记(六)
			JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html JUC学习笔记(二)https://www.cnblogs.com/lm66/p/1511 ... 
- Nginx 文件名逻辑漏洞(CVE-2013-4547)
			影响版本 Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7 漏洞成因 这个漏洞其实和代码执行没有太大关系,其主要原因是错误地解析了请求的URI,错误地获取到用户请求的文件名,导 ... 
- Android音视频开发(1):H264 基本原理
			前言 H264 视频压缩算法现在无疑是所有视频压缩技术中使用最广泛,最流行的.随着 x264/openh264 以及 ffmpeg 等开源库的推出,大多数使用者无需再对H264的细节做过多的研究,这大 ... 
- MapReduce框架-Join的使用
			引言 首先先明白在关系型数据库中Join的用法. Join在MapReduce中的用法也是用于两个文件之间的连接. 使用MR程序解决两张表的join问题,有两种解决方案 à MR程序的join应用 1 ... 
- OpenCV 之 特征匹配
			OpenCV 中有两种特征匹配方法:暴力匹配 (Brute force matching) 和 最近邻匹配 (Nearest Neighbors matching) 它们都继承自 Descriptor ... 
- linux服务器下TCP抓包
			1.首先ifconfig查看当前服务器的网卡信息 2.执行tcpdump -i ens160[网卡信息] -s 0 port 8080[监听的端口号] -w ./fileName.pcapng 3.可 ... 
- CVE-2021-21972 vSphere Client RCE复现,附POC & EXP
			漏洞简介 vSphere 是 VMware 推出的虚拟化平台套件,包含 ESXi.vCenter Server 等一系列的软件.其中 vCenter Server 为 ESXi 的控制中心,可从单一控 ... 
- NOIP 模拟 $36\; \rm Cicada 拿衣服$
			题解 \(by\;zj\varphi\) 发现右端点固定时,左端点的 \(min-max\) 单调递减,且对于 \(or\) 和 \(and\) 相减,最多有 \(\rm2logn\)个不同的值,且相 ... 
