From c65edf1042a4319710b86cc8803005251611869a Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 19 Jun 2026 10:10:22 +0800 Subject: [PATCH] Update X509 NameConstraints --- CMakeLists.txt | 2 +- include/gmssl/version.h | 2 +- include/gmssl/x509_cer.h | 5 + src/x509_ext.c | 9 +- src/x509_vrf.c | 401 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 413 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e85dc316..ed5684d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -820,7 +820,7 @@ endif() # set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_VENDOR "GmSSL develop team") -set(CPACK_PACKAGE_VERSION "3.2.0-dev.1096") +set(CPACK_PACKAGE_VERSION "3.2.0-dev.1097") set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md) set(CPACK_NSIS_MODIFY_PATH ON) include(CPack) diff --git a/include/gmssl/version.h b/include/gmssl/version.h index 4f421d3d..07759ca2 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.1096" +#define GMSSL_VERSION_STR "GmSSL 3.2.0-dev.1097" int gmssl_version_num(void); const char *gmssl_version_str(void); diff --git a/include/gmssl/x509_cer.h b/include/gmssl/x509_cer.h index a36c7509..3a0c9965 100644 --- a/include/gmssl/x509_cer.h +++ b/include/gmssl/x509_cer.h @@ -329,6 +329,9 @@ typedef enum { int x509_cert_check(const uint8_t *cert, size_t certlen, int cert_type, int *path_len_constraint); int x509_cert_check_subject(const uint8_t *cert, size_t certlen, int is_cacert); +int x509_cert_check_name_constraints(const uint8_t *cert, size_t certlen, + const uint8_t *name_constraints, size_t name_constraints_len); +int x509_cert_is_self_issued(const uint8_t *cert, size_t certlen); /* IssuerAndSerialNumber ::= SEQUENCE { @@ -374,6 +377,8 @@ int x509_certs_verify(const uint8_t *certs, size_t certslen, int certs_type, const uint8_t *rootcerts, size_t rootcertslen, int depth, int *verify_result); int x509_certs_verify_tlcp(const uint8_t *certs, size_t certslen, int certs_type, const uint8_t *rootcerts, size_t rootcertslen, int depth, int *verify_result); +int x509_certs_check_name_constraints(const uint8_t *cert_chain, size_t cert_chain_len, + const uint8_t *rootcacert, size_t rootcacertlen); int x509_certs_get_subjects(const uint8_t *certs, size_t certslen, uint8_t *names, size_t *nameslen); int x509_certs_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen); diff --git a/src/x509_ext.c b/src/x509_ext.c index 1340c931..2eda7aff 100644 --- a/src/x509_ext.c +++ b/src/x509_ext.c @@ -2255,7 +2255,14 @@ int x509_general_subtree_from_der( error_print(); return -1; } - if (minimum && *minimum < 0) *minimum = 0; + /* + minimum 是 DEFAULT 0。asn1_implicit_int_from_der() 在字段缺省时输出 -1, + 这里统一转换为 0,便于调用方直接按 GeneralSubtree 语义判断。 + 注意 maximum 没有默认值,缺省时应继续保持 -1,用来区分 absent 和 present。 + */ + if (minimum && *minimum < 0) { + *minimum = 0; + } return 1; } diff --git a/src/x509_vrf.c b/src/x509_vrf.c index 0d7ce717..5d6ec1e7 100644 --- a/src/x509_vrf.c +++ b/src/x509_vrf.c @@ -1498,9 +1498,12 @@ int x509_general_name_is_in_general_subtree(int name_choice, const uint8_t *name } /* - * RFC 5280 的证书路径验证配置中 minimum 必须为 0,maximum 必须缺省。 - * 这里先把非 profile 形式作为不支持的错误处理,避免后续误判子树范围。 - */ + 当前证书验证只支持 RFC 5280 profile 中的 GeneralSubtree: + minimum 必须为 0,maximum 必须缺省。x509_general_subtree_from_der() + 已经把缺省 minimum 转换为 0,并用 maximum == -1 表示缺省。 + 如果遇到 minimum != 0 或 maximum present,当前实现不能静默忽略, + 必须作为不支持的语义错误返回 -1。 + */ if (minimum != 0 || maximum >= 0) { error_print(); return -1; @@ -1543,6 +1546,398 @@ int x509_general_name_is_in_general_subtrees(int name_choice, const uint8_t *nam // end of general name in subtree +static int x509_general_subtree_from_der_for_verify(int *base_choice, + const uint8_t **base, size_t *base_len, + const uint8_t *general_subtree, size_t general_subtree_len) +{ + int minimum = -1; + int maximum = -1; + if (!base_choice || !base || !base_len || !general_subtree || !general_subtree_len) { + error_print(); + return -1; + } + if (x509_general_subtree_from_der(base_choice, base, base_len, + &minimum, &maximum, &general_subtree, &general_subtree_len) != 1 + || asn1_length_is_zero(general_subtree_len) != 1) { + error_print(); + return -1; + } + if (minimum != 0 || maximum >= 0) { + error_print(); + return -1; + } + return 1; +} +static int x509_general_name_is_permitted_by_general_subtrees(int name_choice, + const uint8_t *name, size_t name_len, + const uint8_t *general_subtrees, size_t general_subtrees_len) +{ + int ret; + int base_choice; + const uint8_t *general_subtree; + size_t general_subtree_len; + const uint8_t *base; + size_t base_len; + int has_base_choice = 0; + if (!name || !name_len || !general_subtrees || !general_subtrees_len) { + error_print(); + return -1; + } + + while (general_subtrees_len) { + if (asn1_any_from_der(&general_subtree, &general_subtree_len, + &general_subtrees, &general_subtrees_len) != 1) { + error_print(); + return -1; + } + if (x509_general_subtree_from_der_for_verify(&base_choice, &base, &base_len, + general_subtree, general_subtree_len) != 1) { + error_print(); + return -1; + } + if (base_choice != name_choice) { + continue; + } + has_base_choice = 1; + if ((ret = x509_general_name_is_in_subtree(name_choice, name, name_len, + base_choice, base, base_len)) < 0) { + error_print(); + return -1; + } + if (ret) { + return 1; + } + } + if (has_base_choice) { + return 0; + } + return 1; +} + +static int x509_general_name_check_name_constraints(int choice, + const uint8_t *name, size_t name_len, + const uint8_t *permitted_subtrees, size_t permitted_subtrees_len, + const uint8_t *excluded_subtrees, size_t excluded_subtrees_len) +{ + int ret; + + if (!name || !name_len) { + error_print(); + return -1; + } + + if (excluded_subtrees_len) { + if (!excluded_subtrees) { + error_print(); + return -1; + } + if ((ret = x509_general_name_is_in_general_subtrees(choice, name, name_len, + excluded_subtrees, excluded_subtrees_len)) < 0) { + error_print(); + return -1; + } + if (ret) { + return 0; + } + } + + if (permitted_subtrees_len) { + if (!permitted_subtrees) { + error_print(); + return -1; + } + if ((ret = x509_general_name_is_permitted_by_general_subtrees(choice, name, name_len, + permitted_subtrees, permitted_subtrees_len)) < 0) { + error_print(); + return -1; + } + return ret; + } + return 1; +} + +static int x509_cert_subject_alt_name_check_name_constraints(const uint8_t *cert, size_t certlen, + const uint8_t *permitted_subtrees, size_t permitted_subtrees_len, + const uint8_t *excluded_subtrees, size_t excluded_subtrees_len) +{ + int ret; + int critical; + int choice; + const uint8_t *exts; + size_t extslen; + const uint8_t *val; + size_t vlen; + const uint8_t *general_names; + size_t general_names_len; + const uint8_t *name; + size_t name_len; + + if (!cert || !certlen) { + error_print(); + return -1; + } + if ((ret = x509_cert_get_exts(cert, certlen, &exts, &extslen)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + /* + 证书没有扩展时也就没有 subjectAltName 可检查。 + 这里把“未找到扩展”的 0 转换为“没有 SAN 违反约束”的 1。 + */ + return 1; + } + if ((ret = x509_exts_get_ext_by_oid(exts, extslen, OID_ce_subject_alt_name, + &critical, &val, &vlen)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + /* + 没有 subjectAltName 扩展时,本函数只负责 SAN 的 NameConstraints 检查。 + 这里把“未找到 SAN”的 0 转换为“SAN 检查通过”的 1。 + */ + return 1; + } + if (x509_general_names_from_der(&general_names, &general_names_len, &val, &vlen) != 1 + || asn1_length_is_zero(vlen) != 1) { + error_print(); + return -1; + } + + while (general_names_len) { + if (x509_general_name_from_der(&choice, &name, &name_len, + &general_names, &general_names_len) != 1) { + error_print(); + return -1; + } + if ((ret = x509_general_name_check_name_constraints(choice, name, name_len, + permitted_subtrees, permitted_subtrees_len, + excluded_subtrees, excluded_subtrees_len)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + } + return 1; +} + +int x509_cert_check_name_constraints(const uint8_t *cert, size_t certlen, + const uint8_t *name_constraints, size_t name_constraints_len) +{ + int ret; + const uint8_t *p; + size_t len; + const uint8_t *permitted_subtrees; + size_t permitted_subtrees_len; + const uint8_t *excluded_subtrees; + size_t excluded_subtrees_len; + const uint8_t *subject; + size_t subject_len; + + if (!cert || !certlen || !name_constraints || !name_constraints_len) { + error_print(); + return -1; + } + + p = name_constraints; + len = name_constraints_len; + if (x509_name_constraints_from_der(&permitted_subtrees, &permitted_subtrees_len, + &excluded_subtrees, &excluded_subtrees_len, &p, &len) != 1 + || asn1_length_is_zero(len) != 1) { + error_print(); + return -1; + } + + if (x509_cert_get_subject(cert, certlen, &subject, &subject_len) != 1) { + error_print(); + return -1; + } + if (subject_len) { + if ((ret = x509_general_name_check_name_constraints(X509_gn_directory_name, + subject, subject_len, permitted_subtrees, permitted_subtrees_len, + excluded_subtrees, excluded_subtrees_len)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + } + + if ((ret = x509_cert_subject_alt_name_check_name_constraints(cert, certlen, + permitted_subtrees, permitted_subtrees_len, + excluded_subtrees, excluded_subtrees_len)) < 0) { + error_print(); + return -1; + } + return ret; +} + +static int x509_cert_get_name_constraints(const uint8_t *cert, size_t certlen, + const uint8_t **name_constraints, size_t *name_constraints_len) +{ + int ret; + int critical; + const uint8_t *exts; + size_t extslen; + + if (!cert || !certlen || !name_constraints || !name_constraints_len) { + error_print(); + return -1; + } + *name_constraints = NULL; + *name_constraints_len = 0; + + if ((ret = x509_cert_get_exts(cert, certlen, &exts, &extslen)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + if ((ret = x509_exts_get_ext_by_oid(exts, extslen, OID_ce_name_constraints, + &critical, name_constraints, name_constraints_len)) < 0) { + error_print(); + return -1; + } + return ret; +} + +static int x509_certs_check_name_constraints_by_name_constraints( + const uint8_t *cert_chain, size_t cert_chain_len, + const uint8_t *name_constraints, size_t name_constraints_len) +{ + int ret; + const uint8_t *cert; + size_t certlen; + + if (!name_constraints || !name_constraints_len) { + error_print(); + return -1; + } + while (cert_chain_len) { + if (x509_cert_from_der(&cert, &certlen, &cert_chain, &cert_chain_len) != 1) { + error_print(); + return -1; + } + if ((ret = x509_cert_check_name_constraints(cert, certlen, + name_constraints, name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + } + return 1; +} + +int x509_cert_is_self_issued(const uint8_t *cert, size_t certlen) +{ + int ret; + const uint8_t *issuer; + size_t issuer_len; + const uint8_t *subject; + size_t subject_len; + + if (!cert || !certlen) { + error_print(); + return -1; + } + if (x509_cert_get_issuer(cert, certlen, &issuer, &issuer_len) != 1 + || x509_cert_get_subject(cert, certlen, &subject, &subject_len) != 1) { + error_print(); + return -1; + } + if ((ret = x509_name_normalized_equ(issuer, issuer_len, subject, subject_len)) < 0) { + error_print(); + return -1; + } + return ret; +} + +int x509_certs_check_name_constraints(const uint8_t *cert_chain, size_t cert_chain_len, + const uint8_t *rootcacert, size_t rootcacertlen) +{ + int ret; + const uint8_t *chain; + size_t chain_len; + const uint8_t *cert; + size_t certlen; + const uint8_t *name_constraints; + size_t name_constraints_len; + size_t checked_certs_len = 0; + + if (!cert_chain || !cert_chain_len + || (!rootcacert && rootcacertlen) + || (rootcacert && !rootcacertlen)) { + error_print(); + return -1; + } + + if (rootcacert) { + if ((ret = x509_cert_get_name_constraints(rootcacert, rootcacertlen, + &name_constraints, &name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret) { + /* + 根 CA 不在 cert_chain 中。传入根 CA 时,它的 NameConstraints + 只在这里额外约束 cert_chain 中最靠近根的那张下级证书。 + */ + if (x509_certs_get_last(cert_chain, cert_chain_len, &cert, &certlen) != 1) { + error_print(); + return -1; + } + if ((ret = x509_cert_check_name_constraints(cert, certlen, + name_constraints, name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + } + } + + chain = cert_chain; + chain_len = cert_chain_len; + while (chain_len) { + if (x509_cert_from_der(&cert, &certlen, &chain, &chain_len) != 1) { + error_print(); + return -1; + } + + /* + 证书链按被验证证书到上级 CA 的顺序排列。当前证书中的 + NameConstraints 只约束它之前已经出现的下级证书,不约束自己。 + */ + if (checked_certs_len) { + if ((ret = x509_cert_get_name_constraints(cert, certlen, + &name_constraints, &name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret) { + if ((ret = x509_certs_check_name_constraints_by_name_constraints( + cert_chain, checked_certs_len, + name_constraints, name_constraints_len)) < 0) { + error_print(); + return -1; + } + if (ret == 0) { + return 0; + } + } + } + checked_certs_len += certlen; + } + return 1; +}