基于openswan klips的IPsec实现分析(十)认证算法维护
转载请注明出处:http://blog.csdn.net/rosetta
这里指的认证算法是ESP使用的,对通信过程中的信息做哈希,用来校验信息的完整性的;协商时的认证算法仅用来做哈希,哈希结果再用来做签名和验签。
相对于加密算法,认证算法会比较简单些,它也不需要像加密算法一样去构造一个结构体(当然如果需要也是可以这么做的)。
发送方处理比较简单,主要是由ipsec_xmit_encap_once()对加密后的部分数据做哈希,然后一起发给对方;接收方稍微复杂点,先把发送方已经计算好的那份哈希弄出来,再对密文用同样的哈希算法做哈希,最后比较两个结果。
详细过程如下分析。
发送方:
发送方就一个函数ipsec_xmit_encap_once()。
ipsec_xmit_encap_once()函数
enum ipsec_xmit_value
ipsec_xmit_encap_once(structipsec_xmit_state *ixs)
{
printk("infunc:%s\n", "ipsec_xmit_encap_once");
#ifdef CONFIG_KLIPS_ESP
structesphdr *espp;
unsignedchar *idat, *pad;
intauthlen = 0, padlen = 0, i;
#endif /* !CONFIG_KLIPS_ESP */
#ifdef CONFIG_KLIPS_AH
structiphdr ipo;
structahhdr *ahp;
#endif /* CONFIG_KLIPS_AH */
#if defined(CONFIG_KLIPS_AUTH_HMAC_MD5) ||defined(CONFIG_KLIPS_AUTH_HMAC_SHA1)
union{
#ifdef CONFIG_KLIPS_AUTH_HMAC_MD5
MD5_CTXmd5;
#endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */
#ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1
SHA1_CTXsha1;
#endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */
}tctx;
__u8hash[AH_AMAX];
#endif /*defined(CONFIG_KLIPS_AUTH_HMAC_MD5) || defined(CONFIG_KLIPS_AUTH_HMACn_SHA1) */
intheadroom = 0, tailroom = 0, ilen = 0, len = 0;
unsignedchar *dat;
intblocksize = 8; /* XXX: should be inside ixs --jjo */
structipsec_alg_enc *ixt_e = NULL;
structipsec_alg_auth *ixt_a = NULL;
ixs->iphlen= ixs->iph->ihl << 2;
ixs->pyldsz= ntohs(ixs->iph->tot_len) - ixs->iphlen;
ixs->sa_len= satot(&ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOT_BUF);
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:ipsec_xmit_encap_once:"
"calling output for <%s%s%s>,SA:%s\n",
IPS_XFORM_NAME(ixs->ipsp),
ixs->sa_len ? ixs->sa_txt : "(error)");
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"ixs->ipsp->ips_said.proto:%d\n",
ixs->ipsp->ips_said.proto);
//因为此函数(ipsec_xmit_encap_once)的外面是一个while(ixs->ipsp)循环,所以会循环sp链表,
//最常见的情况是隧道模式(IPIP)+ESP,但这两个的协议号都是放在这个proto中的。
//所以会进这个函数两次。
switch(ixs->ipsp->ips_said.proto){
#ifdef CONFIG_KLIPS_AH
caseIPPROTO_AH:
headroom+= sizeof(struct ahhdr);
break;
#endif /* CONFIG_KLIPS_AH */
#ifdef CONFIG_KLIPS_ESP
caseIPPROTO_ESP://esp协议号50.假如只esp无ah,走这里。
//如果还有AH的话,会进这个函数三次。
{
//printk("in func:%s,%s\n", __func__, "IPPROTO_ESP");
ixt_e=ixs->ipsp->ips_alg_enc;//包含加密算法函数指针等结构体。--esp时指定的加密算法。
if(ixt_e) {
blocksize= ixt_e->ixt_common.ixt_blocksize;
//比如sm1算法的ixt_blocksize为16,ocs算法为8。
headroom+= ESP_HEADER_LEN + ixt_e->ixt_common.ixt_support.ias_ivlen/8;
//ESP_HEADER_LEN为SPI(4字节)+序列号(4字节)=8字节,最后一个是数据,一般是IV。
//可看pdf版本rfc2406第二节图.
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"ias_ivlen:%d\n",
ixt_e->ixt_common.ixt_support.ias_ivlen
);
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"ias_id:%d,ias_name:%s\n",
ixt_e->ixt_common.ixt_support.ias_id,
ixt_e->ixt_common.ixt_support.ias_name
);
//以esp 3des-md5为例.
//ias_ivlen为64
//isa_id为3(正是3des算法的id)//whack--status 可以查看
//000algorithm ESP encrypt: id=3, name=ESP_3DES, ivlen=64, keysizemin=192,keysizemax=192
//ias_name为NULL,不知道为什么?可能没加到那个名字宏里。
}else {
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_BADALG;
}
ixt_a=ixs->ipsp->ips_alg_auth;
if(ixt_a) {
tailroom+= AHHMAC_HASHLEN;
}else
{
//printk("21111111.\n");//走这
switch(ixs->ipsp->ips_authalg){
#ifdef CONFIG_KLIPS_AUTH_HMAC_MD5
caseAH_MD5:
authlen= AHHMAC_HASHLEN;
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_MD5 */
#ifdef CONFIG_KLIPS_AUTH_HMAC_SHA1
caseAH_SHA:
authlen= AHHMAC_HASHLEN;
break;
#endif /* CONFIG_KLIPS_AUTH_HMAC_SHA1 */
caseAH_NONE:
break;
default:
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_BADALG;
}
}
//认证长度12,为什么MD5,SHA1,甚至是SM3的认证长度都为12?
//这个12的依据是?
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"1,authlen:%d, tailroom:%d\n",
authlen,tailroom);
tailroom+= blocksize != 1 ?
((blocksize- ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 :
((4- ((ixs->pyldsz + 2) % 4)) % 4) + 2;
tailroom+= authlen;
//这个应该根据rfc2406进行填充?
KLIPS_PRINT(debug_tunnel& DB_TN_OXFS,
"klips_debug:plmdebug:"
"2,authlen:%d, tailroom:%d\n",
authlen,tailroom);
}
break;
#endif /* CONFIG_KLIPS_ESP */
#ifdef CONFIG_KLIPS_IPIP
caseIPPROTO_IPIP://什么时候用IPIP?隧道模式,这里只是分配了下空间。
headroom+= sizeof(struct iphdr);
ixs->iphlen= sizeof(struct iphdr);
break;
#endif /* !CONFIG_KLIPS_IPIP */
#ifdef CONFIG_KLIPS_IPCOMP
caseIPPROTO_COMP://压缩?
break;
#endif /* CONFIG_KLIPS_IPCOMP */
default:
ixs->stats->tx_errors++;
returnIPSEC_XMIT_BADPROTO;
}
KLIPS_PRINT(debug_tunnel& DB_TN_CROUT,
"klips_debug:ipsec_xmit_encap_once:"
"pushing %d bytes, putting %d, proto%d.\n",
headroom, tailroom,ixs->ipsp->ips_said.proto);
if(skb_headroom(ixs->skb)< headroom) {
printk(KERN_WARNING
"klips_error:ipsec_xmit_encap_once:"
"tried to skb_push headroom=%d, %davailable. This should never happen,please report.\n",
headroom, skb_headroom(ixs->skb));
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_PUSHPULLERR;
}
//skb头部预留headroom字节空间。
dat= skb_push(ixs->skb, headroom);
ilen= ixs->skb->len - tailroom;
if(skb_tailroom(ixs->skb)< tailroom) {
printk(KERN_WARNING
"klips_error:ipsec_xmit_encap_once:"
"tried to skb_put %d, %davailable. This should never happen,please report.\n",
tailroom, skb_tailroom(ixs->skb));
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_PUSHPULLERR;
}
//skb尾部预留tailroom字节空间。
skb_put(ixs->skb,tailroom);
KLIPS_PRINT(debug_tunnel& DB_TN_CROUT,
"klips_debug:ipsec_xmit_encap_once:"
"head,tailroom: %d,%d beforexform.\n",
skb_headroom(ixs->skb),skb_tailroom(ixs->skb));
len= ixs->skb->len;
if(len> 0xfff0) {
printk(KERN_WARNING"klips_error:ipsec_xmit_encap_once: "
"tot_len (%d) > 65520. This should never happen, pleasereport.\n",
len);
ixs->stats->tx_errors++;
returnIPSEC_XMIT_BADLEN;
}
memmove((void*)dat, (void *)(dat + headroom), ixs->iphlen);
ixs->iph= (struct iphdr *)dat;
ixs->iph->tot_len= htons(ixs->skb->len);
//不懂?//虽然没怎么看明白,在IPIP模式下,即隧道模式,这里的目的猜是在ESP包前面加一个原始IP头。
//这里仅仅是加了空间,具体的赋值在下面操作。
switch(ixs->ipsp->ips_said.proto){
#ifdef CONFIG_KLIPS_ESP
caseIPPROTO_ESP:
espp= (struct esphdr *)(dat + ixs->iphlen);//dat是之前IPIP时加的IP原始头指针。
//此时再加一个IP长度,那么这个位置espp就是放ESP头的位置。
espp->esp_spi= ixs->ipsp->ips_said.spi;
espp->esp_rpl= htonl(++(ixs->ipsp->ips_replaywin_lastseq));
if(!ixt_e) {
ixs->stats->tx_errors++;
returnIPSEC_XMIT_ESP_BADALG;
}
idat= dat + ixs->iphlen + headroom;
ilen= len - (ixs->iphlen + headroom + authlen);
/*Self-describing padding */
pad= &dat[len - tailroom];
padlen= tailroom - 2 - authlen;
for(i = 0; i < padlen; i++) {
pad[i]= i + 1;
}
dat[len- authlen - 2] = padlen;
dat[len- authlen - 1] = ixs->iph->protocol;
ixs->iph->protocol= IPPROTO_ESP;
//以上这段操作没怎么看懂。对需要加密/认证的数据进行数据补齐。
#ifdef CONFIG_KLIPS_DEBUG
if(debug_tunnel& DB_TN_ENCAP) {
dmp("pre-encrypt", dat,len);//最终需要加密的数据在dat里。
}
#endif
/*
* Do all operations here:
* copy IV->ESP, encrypt, update ips IV
*
*/
{
intret;
memcpy(espp->esp_iv,
ixs->ipsp->ips_iv,
ixs->ipsp->ips_iv_size);