diff --git a/CMakeLists.txt b/CMakeLists.txt index d9c39e4b..52d96ced 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/include/gmssl/cms.h b/include/gmssl/cms.h index cd0f2f2d..bc282872 100644 --- a/include/gmssl/cms.h +++ b/include/gmssl/cms.h @@ -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, diff --git a/include/gmssl/version.h b/include/gmssl/version.h index 64cd5a3f..c14d72e4 100644 --- a/include/gmssl/version.h +++ b/include/gmssl/version.h @@ -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); diff --git a/src/cms.c b/src/cms.c index 781d1988..e39369cc 100644 --- a/src/cms.c +++ b/src/cms.c @@ -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; diff --git a/tests/cmstest.c b/tests/cmstest.c index 91b3b4e2..b42bbd80 100644 --- a/tests/cmstest.c +++ b/tests/cmstest.c @@ -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,17 +892,48 @@ int test_cms_enveloped_data(void) const uint8_t *shared_info2; size_t rcpt_infos_len, shared_info1_len, shared_info2_len; - if (cms_enveloped_data_decrypt_from_der( - &x509_key1, - name1, name1_len, - serial1, sizeof(serial1), - &content_type, out, &outlen, - &rcpt_infos, &rcpt_infos_len, - &shared_info1, &shared_info1_len, - &shared_info2, &shared_info2_len, - &cp, &len) != 1) { - error_print(); - return -1; + /* + * 从证书中提取规范化的 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, + 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, + &cp, &len) != 1) { + error_print(); + return -1; + } } printf("%s() ok\n", __FUNCTION__); @@ -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(¬_before) == -1 + || x509_validity_add_days(¬_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__);