Fix CMS bug

This commit is contained in:
Zhi Guan
2026-06-17 18:45:32 +08:00
parent a3dc07db74
commit c5468b4c1b
5 changed files with 214 additions and 52 deletions

View File

@@ -55,7 +55,7 @@ option(ENABLE_SM4_CBC_MAC "Enable SM4-CBC-MAC" OFF)
option(ENABLE_SM2_EXTS "Enable SM2 Extensions" OFF)
option(ENABLE_SM9 "Enable SM9" ON)
option(ENABLE_CMS "Enable CMS" OFF)
option(ENABLE_CMS "Enable CMS" ON)
option(ENABLE_SECP256R1 "Enable ECDH/ECDSA on curve secp256r1" ON)
@@ -117,7 +117,6 @@ set(src
src/pem.c
src/x509_alg.c
src/x509_cer.c
src/x509_vfy.c
src/x509_ext.c
src/x509_req.c
src/x509_crl.c
@@ -819,7 +818,7 @@ endif()
#
set(CPACK_PACKAGE_NAME "GmSSL")
set(CPACK_PACKAGE_VENDOR "GmSSL develop team")
set(CPACK_PACKAGE_VERSION "3.2.0-dev.1086")
set(CPACK_PACKAGE_VERSION "3.2.0-dev.1087")
set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md)
set(CPACK_NSIS_MODIFY_PATH ON)
include(CPack)

View File

@@ -362,6 +362,8 @@ int cms_enveloped_data_encrypt_to_der(
const uint8_t *shared_info1, size_t shared_info1_len,
const uint8_t *shared_info2, size_t shared_info2_len,
uint8_t **out, size_t *outlen);
// issuer/serial must be extracted from cert via x509_cert_get_issuer_and_serial_number()
// to get canonical DER encoding; raw bytes may differ after ASN.1 INTEGER normalization.
int cms_enveloped_data_decrypt_from_der(
const X509_KEY *sm2_key,
const uint8_t *issuer, size_t issuer_len,
@@ -415,6 +417,7 @@ int cms_signed_and_enveloped_data_encipher_to_der(
const uint8_t *shared_info1, size_t shared_info1_len,
const uint8_t *shared_info2, size_t shared_info2_len,
uint8_t **out, size_t *outlen);
// rcpt_issuer/rcpt_serial must be from x509_cert_get_issuer_and_serial_number() (see above).
int cms_signed_and_enveloped_data_decipher_from_der(
const X509_KEY *rcpt_key,
const uint8_t *rcpt_issuer, size_t rcpt_issuer_len,

View File

@@ -18,7 +18,7 @@ extern "C" {
#define GMSSL_VERSION_NUM 30200
#define GMSSL_VERSION_STR "GmSSL 3.2.0-dev.1086"
#define GMSSL_VERSION_STR "GmSSL 3.2.0-dev.1087"
int gmssl_version_num(void);
const char *gmssl_version_str(void);

View File

@@ -101,31 +101,10 @@ int cms_content_type_from_der(int *oid, const uint8_t **in, size_t *inlen)
return 1;
}
/*
static int cms_content_info_data_header_to_der(size_t dlen, uint8_t **out, size_t *outlen)
{
uint8_t d[1];
size_t len = 0;
size_t content_len = 0;
if (asn1_octet_string_to_der(p, dlen, NULL, &content_len) != 1
|| cms_content_type_to_der(OID_cms_data, out, outlen) != 1
|| asn1_explicit_header_to_der(0, content_len, out, outlen) < 0
|| asn1_octet_string_to_der(dlen, out, outlen) < 0) {
error_print();
return -1;
}
return 1;
}
*/
int cms_content_info_header_to_der(int content_type, size_t content_len, uint8_t **out, size_t *outlen)
{
size_t len = content_len; // 注意由于header_to_der没有输出数据因此需要加上数据的长度header length 才是正确的值
/*
if (content_type == OID_cms_data) {
return cms_content_info_data_header_to_der(content_len, out, outlen);
}
*/
if (cms_content_type_to_der(content_type, NULL, &len) != 1
|| asn1_explicit_header_to_der(0, content_len, NULL, &len) < 0
@@ -1112,7 +1091,7 @@ int cms_signed_data_sign_to_der(
&issuer, &issuer_len, &serial, &serial_len) != 1
|| cms_signer_infos_add_signer_info(
signer_infos, &signer_infos_len, sizeof(signer_infos),
&sm3_ctx, signers->sign_key,
&sm3_ctx, signers[i].sign_key,
issuer, issuer_len, serial, serial_len,
NULL, 0, NULL, 0) != 1) {
error_print();
@@ -1152,7 +1131,7 @@ int cms_signed_data_verify_from_der(
int digest_algors[4];
size_t digest_algors_cnt;
SM3_CTX sm3_ctx;
uint8_t content_info_header[128];
uint8_t content_info_header[256];
size_t content_info_header_len;
uint8_t *p = content_info_header;
const uint8_t *signer_infos;
@@ -1265,7 +1244,7 @@ int cms_recipient_info_from_der(
serial_number, serial_number_len, &d, &dlen) != 1
|| x509_public_key_encryption_algor_from_der(pke_algor, params, params_len, &d, &dlen) != 1
|| asn1_octet_string_from_der(enced_key, enced_key_len, &d, &dlen) != 1
// || asn1_length_is_zero(dlen) != 1
|| asn1_length_is_zero(dlen) != 1
) {
error_print();
return -1;
@@ -1839,7 +1818,7 @@ int cms_signed_and_enveloped_data_encipher_to_der(
&issuer, &issuer_len, &serial, &serial_len) != 1
|| cms_signer_infos_add_signer_info(
signer_infos, &signer_infos_len, sizeof(signer_infos),
&sm3_ctx, signers->sign_key,
&sm3_ctx, signers[i].sign_key,
issuer, issuer_len, serial, serial_len,
NULL, 0, NULL, 0) != 1) {
error_print();
@@ -1871,7 +1850,7 @@ int cms_signed_and_enveloped_data_encipher_to_der(
shared_info2, shared_info2_len,
out, outlen) != 1
|| cms_implicit_signers_certs_to_der(0, signers, signers_cnt, out, outlen) != 1
|| asn1_implicit_set_to_der(1, signers_crls, signers_crls_len, out, outlen) != 1
|| asn1_implicit_set_to_der(1, signers_crls, signers_crls_len, out, outlen) < 0
|| asn1_set_to_der(signer_infos, signer_infos_len, out, outlen) != 1) {
error_print();
return -1;
@@ -1908,7 +1887,7 @@ int cms_signed_and_enveloped_data_decipher_from_der(
uint8_t key[32];
size_t keylen;
SM3_CTX sm3_ctx;
uint8_t content_info_header[128];
uint8_t content_info_header[256];
size_t content_info_header_len = 0;
uint8_t *p = content_info_header;

View File

@@ -86,12 +86,22 @@ static int test_cms_content_info(void)
if (cms_content_info_to_der(OID_cms_data, data, sizeof(data), &p, &len) != 1
|| cms_content_info_from_der(&oid, &d, &dlen, &cp, &len) != 1
|| asn1_check(oid == OID_cms_data) != 1
// || asn1_check(dlen == sizeof(data)) != 1
// || asn1_check(memcmp(data, d, dlen) == 0) != 1
|| asn1_length_is_zero(len) != 1) {
error_print();
return -1;
}
// OID_cms_data content is wrapped in OCTET STRING, parse to get raw data
{
const uint8_t *raw;
size_t rawlen;
if (asn1_octet_string_from_der(&raw, &rawlen, &d, &dlen) != 1
|| asn1_check(rawlen == sizeof(data)) != 1
|| asn1_check(memcmp(data, raw, rawlen) == 0) != 1
|| asn1_length_is_zero(dlen) != 1) {
error_print();
return -1;
}
}
printf("%s() ok\n", __FUNCTION__);
return 1;
@@ -882,10 +892,40 @@ int test_cms_enveloped_data(void)
const uint8_t *shared_info2;
size_t rcpt_infos_len, shared_info1_len, shared_info2_len;
/*
* 从证书中提取规范化的 issuer 和 serial而不是直接使用 rand_bytes 的原始序列号。
*
* 原因rand_bytes 生成的序列号首字节可能为 0x00。在 ASN.1 DER 编码中,
* INTEGER 类型要求最小化编码——前导 0x00 会被去除(仅当需要符号位时保留)。
* 因此经过 x509_cert_sign_to_der 编码再解析后serial 的长度可能比原始
* rand_bytes 的输出少 1 字节。若直接使用原始 serial 与 decipher 中解析
* 出的 serial 做 memcmp 比较,会因长度不匹配导致随机失败(概率约 1/256
*
* 正确做法:通过 x509_cert_get_issuer_and_serial_number 从证书中提取
* 规范化的 serial保证 encipher 和 decipher 两端使用完全一致的字节串。
*/
{
const uint8_t *rcpt_cert;
size_t rcpt_cert_len;
const uint8_t *rcpt_issuer;
size_t rcpt_issuer_len;
const uint8_t *rcpt_cert_serial;
size_t rcpt_cert_serial_len;
const uint8_t *pcerts = certs;
size_t pcerts_len = certslen;
if (asn1_any_from_der(&rcpt_cert, &rcpt_cert_len, &pcerts, &pcerts_len) != 1
|| x509_cert_get_issuer_and_serial_number(rcpt_cert, rcpt_cert_len,
&rcpt_issuer, &rcpt_issuer_len,
&rcpt_cert_serial, &rcpt_cert_serial_len) != 1) {
error_print();
return -1;
}
if (cms_enveloped_data_decrypt_from_der(
&x509_key1,
name1, name1_len,
serial1, sizeof(serial1),
rcpt_issuer, rcpt_issuer_len,
rcpt_cert_serial, rcpt_cert_serial_len,
&content_type, out, &outlen,
&rcpt_infos, &rcpt_infos_len,
&shared_info1, &shared_info1_len,
@@ -894,6 +934,7 @@ int test_cms_enveloped_data(void)
error_print();
return -1;
}
}
printf("%s() ok\n", __FUNCTION__);
return 1;
@@ -901,16 +942,155 @@ int test_cms_enveloped_data(void)
static int test_cms_signed_and_enveloped_data(void)
{
/*
SM2_KEY sign_key;
SM2_KEY decr_key;
uint8_t sign_serial[20];
int algor = OID_ec_public_key;
int algor_param = OID_sm2;
X509_KEY sign_x509_key;
X509_KEY rcpt_x509_key;
uint8_t sign_name[256];
size_t sign_name_len;
*/
uint8_t sign_serial[20];
uint8_t rcpt_name[256];
size_t rcpt_name_len;
uint8_t rcpt_serial[20];
time_t not_before, not_after;
uint8_t signer_cert[2048];
size_t signer_cert_len = 0;
uint8_t rcpt_certs[2048];
size_t rcpt_certs_len = 0;
CMS_CERTS_AND_KEY signers[1];
uint8_t key[16];
uint8_t iv[16];
uint8_t in[64];
uint8_t out[256];
size_t outlen;
int content_type;
uint8_t buf[8192];
uint8_t *p;
const uint8_t *cp;
size_t len;
const uint8_t *d;
size_t dlen;
// prepare keys, certs and test data
if (x509_key_generate(&sign_x509_key, algor, &algor_param, sizeof(algor_param)) != 1
|| x509_key_generate(&rcpt_x509_key, algor, &algor_param, sizeof(algor_param)) != 1
|| time(&not_before) == -1
|| x509_validity_add_days(&not_after, not_before, 365) != 1
|| rand_bytes(sign_serial, sizeof(sign_serial)) != 1
|| rand_bytes(rcpt_serial, sizeof(rcpt_serial)) != 1
|| rand_bytes(key, sizeof(key)) != 1
|| rand_bytes(iv, sizeof(iv)) != 1
|| rand_bytes(in, sizeof(in)) != 1
|| x509_name_set(sign_name, &sign_name_len, sizeof(sign_name),
"CN", "Beijing", "Haidian", "PKU", "CS", "Signer") != 1
|| x509_name_set(rcpt_name, &rcpt_name_len, sizeof(rcpt_name),
"CN", "Beijing", "Haidian", "PKU", "CS", "Recipient") != 1) {
error_print();
return -1;
}
p = signer_cert;
if (x509_cert_sign_to_der(
X509_version_v3, sign_serial, sizeof(sign_serial),
OID_sm2sign_with_sm3,
sign_name, sign_name_len,
not_before, not_after,
sign_name, sign_name_len,
&sign_x509_key, NULL, 0, NULL, 0, NULL, 0,
&sign_x509_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH,
&p, &signer_cert_len) != 1) {
error_print();
return -1;
}
signers[0].certs = signer_cert;
signers[0].certs_len = signer_cert_len;
signers[0].sign_key = &sign_x509_key;
p = rcpt_certs;
if (x509_cert_sign_to_der(
X509_version_v3, rcpt_serial, sizeof(rcpt_serial),
OID_sm2sign_with_sm3,
rcpt_name, rcpt_name_len,
not_before, not_after,
rcpt_name, rcpt_name_len,
&rcpt_x509_key, NULL, 0, NULL, 0, NULL, 0,
&rcpt_x509_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH,
&p, &rcpt_certs_len) != 1) {
error_print();
return -1;
}
// encipher
p = buf;
cp = buf;
len = 0;
if (cms_signed_and_enveloped_data_encipher_to_der(
signers, 1,
rcpt_certs, rcpt_certs_len,
OID_sm4_cbc, key, sizeof(key), iv, sizeof(iv),
OID_cms_data, in, sizeof(in),
NULL, 0,
NULL, 0,
NULL, 0,
&p, &len) != 1) {
error_print();
return -1;
}
// decipher
const uint8_t *rcpt_infos;
const uint8_t *shared_info1;
const uint8_t *shared_info2;
const uint8_t *signer_certs;
const uint8_t *signer_crls;
const uint8_t *signer_infos;
size_t rcpt_infos_len, shared_info1_len, shared_info2_len;
size_t signer_certs_len2, signer_crls_len, signer_infos_len;
// 同上(参见 test_cms_enveloped_data 中详细中文注释):从证书中提取规范化
// issuer/serial避免 ASN.1 INTEGER 编码标准化导致随机 memcmp 失败
const uint8_t *rcpt_issuer;
size_t rcpt_issuer_len;
const uint8_t *rcpt_cert_serial;
size_t rcpt_cert_serial_len;
if (x509_cert_get_issuer_and_serial_number(rcpt_certs, rcpt_certs_len,
&rcpt_issuer, &rcpt_issuer_len,
&rcpt_cert_serial, &rcpt_cert_serial_len) != 1) {
error_print();
return -1;
}
if (cms_signed_and_enveloped_data_decipher_from_der(
&rcpt_x509_key,
rcpt_issuer, rcpt_issuer_len,
rcpt_cert_serial, rcpt_cert_serial_len,
&content_type, out, &outlen,
&rcpt_infos, &rcpt_infos_len,
&shared_info1, &shared_info1_len,
&shared_info2, &shared_info2_len,
&signer_certs, &signer_certs_len2,
&signer_crls, &signer_crls_len,
&signer_infos, &signer_infos_len,
NULL, 0,
NULL, 0,
&cp, &len) != 1) {
error_print();
return -1;
}
if (content_type != OID_cms_data
|| outlen != sizeof(in)
|| memcmp(in, out, outlen) != 0) {
error_print();
return -1;
}
printf("%s() ok\n", __FUNCTION__);
return 1;
@@ -1028,6 +1208,7 @@ int main(int argc, char **argv)
if (test_cms_signed_data() != 1) goto err;
if (test_cms_recipient_info() != 1) goto err;
if (test_cms_enveloped_data() != 1) goto err;
if (test_cms_signed_and_enveloped_data() != 1) goto err;
if (test_cms_key_agreement_info() != 1) goto err;
printf("%s all tests passed\n", __FILE__);