eSNACC对BIT STRING的编码和解码

eSNACC对BIT STRING的编码和解码

本文剖析asn-bits.h/c,从源代码来学习eSNACC对BIT STRING的编码和解码。

比特字符串的编码和解码比较复杂,我们来仔细分析一下代码吧。

 

eSNACC用一个结构体来表示BIT STRING,定义如下:

typedef  struct  AsnBits
{
  
int    bitLen;//bit位总长度
  char    *bits;
}
 AsnBits;

这两个参数分别是:

bitlen代表这个比特串的bit位的总长度,注意是bit位,不是字节数!

bits用来存放比特串;要注意这是一个bit串,不是字符串,也就是说中间可以有0.这不是一个要求以null结尾的字符串!

 

在头文件中其他的就是BER、DER的编码解码的声明,还有一些帮助函数的声明和宏定义。

 

来研究一下主要实现:

先看最外层编码和解码函数:

/**/ /*
 * encodes universal TAG LENGTH and Contents of and ASN.1 BIT STRING
 
*/

AsnLen
BEncAsnBits PARAMS ((b, data),
    GenBuf 
* b _AND_
    AsnBits 
* data)
{
    AsnLen len;

    len 
=  BEncAsnBitsContent (b, data);
    len 
+= BEncDefLen (b, len);
    len 
+= BEncTag1 (b, UNIV, PRIM, BITSTRING_TAG_CODE);
    
return len;
}
   /**/ /* BEncAsnInt */


/**/ /*
 * decodes universal TAG LENGTH and Contents of and ASN.1 BIT STRING
 
*/

void
BDecAsnBits PARAMS ((b, result, bytesDecoded, env),
    GenBuf 
* b _AND_
    AsnBits    
* result _AND_
    AsnLen 
* bytesDecoded _AND_
    jmp_buf env)
{
    AsnTag tag;
    AsnLen elmtLen;

    
if (((tag =BDecTag (b, bytesDecoded, env)) !=
        MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE)) 
&&
        (tag 
!= MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE)))
    
{
         Asn1Error (
"BDecAsnBits: ERROR - wrong tag on BIT STRING.\n");
         longjmp (env, 
-40);
    }


    elmtLen 
= BDecLen (b, bytesDecoded, env);
    BDecAsnBitsContent (b, tag, elmtLen, result, bytesDecoded, env);

}
   /**/ /* BDecAsnBits */

 我们发现在BEncAsnBits中编码时对比特串的标签只可能是UNIV-PRIM-BITSTRING_TAG_CODE,但是解码时却支持两种标签:UNIV-PRIM-BITSTRING_TAG_CODE和UNIV-CONS-BITSTRING_TAG_CODE。

第一个UNIV-PRIM-BITSTRING_TAG_CODE就是原生的比特串,而第二个UNIV-CONS-BITSTRING_TAG_CODE是对应多个原生或者连接型比特串构造而成的比特串(嵌套)。这种数据是在什么时候编码形成的就留到以后的文章来研究了。反正在当前这对文件的编码中肯定不会产生。

 

我们看一下真正编码比特串内容的函数:

/**/ /*
 * Encodes the BIT STRING value (including the unused bits
 * byte) to the given buffer.
 
*/

AsnLen
BEncAsnBitsContent PARAMS ((b, bits),
    GenBuf 
* b _AND_
    AsnBits 
* bits)
{
    unsigned 
long unusedBits;
    unsigned 
long byteLen;
    
int i = 0;
    
/**//* Check for a dumb special case */
    
for (i=0; i <bits->bitLen/8 + 1; i++)
    
{
        
if (bits->bits[i] != 0)
            
break;
    }

    
if (i == bits->bitLen/8 + 1)
    
{
        bits
->bitLen = 1;
        unusedBits 
= 7;
    }


    
/**//* Work out number of unused bits */
    unusedBits 
= (bits->bitLen % 8);
    
if (unusedBits != 0)
        unusedBits 
= 8 - unusedBits;

    
/**//* Work out number of bytes */
    
if (bits->bitLen == 0{
        byteLen 
= 0;
    }

    
else {
      byteLen 
= ((bits->bitLen-1/ 8+ 1;
      
      
/**//* Ensure last byte is zero padded */
      
if (unusedBits) {
          
//此处为什么只在字节长度为1时才做这个处理呢?
          if ((byteLen == 1&& (bits->bits[0!= 0))
          
{
            bits
->bits[byteLen-1= (char)(bits->bits[byteLen-1& 
            (
0xff << unusedBits));
          }

      }

    }


    BufPutSegRvs (b, bits
->bits, byteLen);
   
    
/**//* check for special DER encoding rules to return 03 01 00 not
       03 02 07 00    RWC 
*/

    
if ( ((bits->bits[0!= 0|| (byteLen > 1)) 
        
&& (unusedBits != 7) )
    
{
       BufPutByteRvs (b, (unsigned 
char)unusedBits);
       
return byteLen + 1;
    }

    
else
       
return byteLen;//如果未用的位的数目为7,并且长度大于1,或者第一字节不为0,就不在填充了,这是为什么呢?这不就和解码时相冲突了吗?

}
  /**/ /* BEncAsnBitsContent */

我对这个函数还存在几个问题,就如同在上面注释中写的。

首先我们看到他判断要编码的串是不是就是一个空串,如果是空串,就把长度设为1,未使用字节数设为7。然后计算了一下将位长度转为字节(8位)时会产生的未使用的字节数。

接着就是实战了,根据bit长度来取得要保存这些bit需要的字节数:byteLen = ((bits->bitLen-1) / 8) + 1;然后如果根据前面计算的如果有未使用的字节数,就要用0填充。但是这里不知道他为什么要把这步操作放到if中:只在字节长度为1时才做这个处理!?除非就是外部传进来的bits->bits本来就是用0填充好了的,所以不需要修正。但是如果是填充好了的,那对1个字节长度的也不需要做这个操作了。

另外就是,在填充无效位数时,为什么是这样一个条件?因为在解码函数(下面分析)中,都始终会减去代表这个未用位字节。

 

我们来看看解码函数

/**/ /*
 * Decodes the content of a BIT STRING (including the unused bits octet)
 * Always returns a single contiguous bit string
 
*/

void
BDecAsnBitsContent PARAMS ((b, tagId, len, result, bytesDecoded, env),
    GenBuf 
* b _AND_
    AsnTag tagId _AND_
    AsnLen len _AND_
    AsnBits 
* result _AND_
    AsnLen 
* bytesDecoded _AND_
    jmp_buf env)
{
    
/**//*
     * tagId is encoded tag shifted into long int.
     * if CONS bit is set then constructed bit string
     
*/

    
if (TAG_IS_CONS (tagId))
        BDecConsAsnBits (b, len, result, bytesDecoded, env);
    
else /**//* primitive octet string */
    
{
        
if (len == INDEFINITE_LEN)
        
{
             Asn1Error (
"BDecAsnBitsContent: ERROR - indefinite length on primitive\n");
             longjmp (env, 
-65);
        }

        (
*bytesDecoded) += len;
        len
--;//减去代表未用位的那个字节
        result->bitLen = (len * 8- (unsigned int)BufGetByte (b);//得到有效位。
        result->bits =  Asn1Alloc (len);
        CheckAsn1Alloc (result
->bits, env);
        BufCopy (result
->bits, b, len);
        
if (BufReadError (b))
        
{
            Asn1Error (
"BDecAsnBitsContent: ERROR - decoded past end of data\n");
            longjmp (env, 
-4);
        }

    }

}
   /**/ /* BDecAsnBitsContent */

他通过标签走了两条分支,对应原生比特串,解码过程如下:

必须定义确定的长度,否则报错。然后数据指针先加上指定长度。

注意此时先将len--!为什么呢?这是对应这个len还包含了存放那个说明未使用位的值的1个字节,所以len先减去这个。而下一句就是用BufGetByte获取那个字节,从而得到未使用的bit位的数目。所以用字节数len*8减去这个值就是全部有用的bit位的数了。而后面的逻辑就很清楚了:分配一个len个字节的空间并且把bit串拷贝进去。

  

eSNACC对应ConsAsnBits解码的两个函数就不深入分析了,其原理构造了一个结构体,里面包含一些长度信息等,和一张指针表,表中有128个指针,用于指向比特串碎片,所以先对分散的比特串分别解析,最后分配一整块大内存,再把指针指向的碎片的中内容拷贝过来。详细代码剖析可以参见eSNACC对OCTET STRING 的编码和解码。

 

文件中其他的一些帮助函数理解起来都比较简单,但是似乎发现AsnBitsEquiv中存在一个bug:

/**/ /*
 * Returns TRUE if the given BIT STRINGs are identical.
 * Otherwise returns FALSE.
 
*/

int
AsnBitsEquiv PARAMS ((b1, b2),
    AsnBits 
* b1 _AND_
    AsnBits 
* b2)
{
    
int octetsLessOne;
    
int unusedBits;

    
if ((b1->bitLen == 0&& (b2->bitLen == 0))
        
return TRUE;

    octetsLessOne 
= (b1->bitLen-1)/8;//字节长度减一
    unusedBits = b1->bitLen % 8;
    
if (unusedBits != 0)
       unusedBits 
= 8 - unusedBits;

    
/**//* trailing bits may not be significant  */
    
//此处应该是一个bug。。
    /**//*return b1->bitLen == b2->bitLen && !memcmpeq (b1->bits, b2->bits, octetsLessOne) && 
    ((b1->bits[octetsLessOne] & (0xFF << unusedBits)) == (b1->bits[octetsLessOne] & (0xFF << unusedBits)));
*/

    
return b1->bitLen == b2->bitLen && !memcmpeq (b1->bits, b2->bits, octetsLessOne) && 
        ((b1
->bits[octetsLessOne] & (0xFF << unusedBits)) == (b2->bits[octetsLessOne] & (0xFF << unusedBits)));

}
  /**/ /* AsnBitsEquiv */

bug就是函数最后这句话,原来那句我就是被注释的那句,他目的是比较:1、两个比特串长度要相等;2、有效bit位长度字节数减1的串要相同(也就是不考虑后面可能需要补齐的字节);3、检查可能需要被补齐的那个字节的对应有效位是否相等。很明显,应该是b1和b2的,所以修正在注释下面。

 

分析到此,发现比特串的编码解码模块存在bug,似乎还有上面说的设计问题,比较困惑:是不是作者在写这个模块时处在XXX时期?呵呵~ 

你可能感兴趣的:(eSNACC对BIT STRING的编码和解码)