1. 写在前面
最近,需要协助硬件IC的同事验证SM2的加密、解密、签名、验签、秘钥交换等功能。为此我们以GMSSL的软算法为基准,验证IC的硬件实现,但是在验证SM2加密时,发现了i2o_SM2CiphertextValue中的bug,如下:
2. 问题描述
下面是我自己写的一个调用GMSSL接口实现SM2加密的函数接口,测试中发现指针*c中输出的数据并非我们想要的。经过打印c
的地址,发现在调用函数i2o_SM2CiphertextValue
前后指针c的地址竟然发生了变化。
uint32_t Simu_Sm2_Encrypt(uint8_t *m, uint32_t mLen,
uint8_t *rand_key, uint8_t *pubkey,
uint8_t *c, uint32_t *cLen)
{
......
printf("Before c = %p\n", c); /* 测试,打印c的地址 */
if ((*cLen = i2o_SM2CiphertextValue(group, cv, &c)) <= 0)
{
printf("ERROR: %s %d\n", __func__, __LINE__);
goto ERR;
}
printf("After c = %p\n", c); /* 测试,打印c的地址 */
#if SIMU_DPI_SM2_DEBUG == 1
printf("Ciphertext[%d bytes] -->\n", *cLen);
BIO_dump_fp(stdout, (const char *)(c - *cLen), *cLen);
#endif
......
}
运行后打印的指针c的地址如下:
Before c = 0x7ffdb2603520
After c = 0x7ffdb26035a1
那么可以肯定的是函数i2o_SM2CiphertextValue
修改了指针c的地址。经过排查发现了是如下代码的问题:
int i2o_SM2CiphertextValue(const EC_GROUP *group, const SM2CiphertextValue *cv,
unsigned char **pout)
{
int ret = 0, outlen = 0, nbytes;
EC_POINT *point = NULL;
BN_CTX *bn_ctx = NULL;
unsigned char *buf;
unsigned char *p;
size_t siz;
......
if (*pout) {
p = *pout; // 若*pout非空,则使用外部传递的地址,存储数据,而我们传递的是c,所以非空
} else {
......
}
......
p += siz; // p指向的地址一直在增加
outlen += siz;
/* encode ciphertext */
memcpy(p, ASN1_STRING_get0_data(cv->ciphertext),
ASN1_STRING_length(cv->ciphertext));
p += ASN1_STRING_length(cv->ciphertext);
outlen += ASN1_STRING_length(cv->ciphertext);
/* encode hash */
memcpy(p, ASN1_STRING_get0_data(cv->hash),
ASN1_STRING_length(cv->hash));
p += ASN1_STRING_length(cv->hash); // p指向的地址一直在增加
outlen += ASN1_STRING_length(cv->hash);
/* output */
if (*pout) {
*pout = p; // 这里又将p的地址重新赋给了*pout,这是问题的关键所在
} else {
*pout = buf;
buf = NULL;
}
ret = outlen;
}
经过上面代码的分析,是该函数对指针c的地址进行了修改,导致了c指向的内容并非我们想要的。
3. 解决办法
将i2o_SM2CiphertextValue
的如下代码屏蔽掉即可:
/* output */
if (*pout) {
//*pout = p;
} else {
*pout = buf;
buf = NULL;
}