diff --git a/CMakeLists.txt b/CMakeLists.txt index 52d96ced..138129c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ set(src src/x509_alg.c src/x509_cer.c src/x509_ext.c + src/x509_vrf.c src/x509_req.c src/x509_crl.c src/x509_new.c @@ -191,6 +192,7 @@ set(tests x509_alg x509_str x509_ext + x509_vrf x509_req x509_crl x509_key @@ -818,7 +820,7 @@ endif() # set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_VENDOR "GmSSL develop team") -set(CPACK_PACKAGE_VERSION "3.2.0-dev.1087") +set(CPACK_PACKAGE_VERSION "3.2.0-dev.1088") 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 c14d72e4..648b47ba 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.1087" +#define GMSSL_VERSION_STR "GmSSL 3.2.0-dev.1088" int gmssl_version_num(void); const char *gmssl_version_str(void); diff --git a/src/x509_cer.c b/src/x509_cer.c index 70fb0ef7..432050f4 100644 --- a/src/x509_cer.c +++ b/src/x509_cer.c @@ -1833,7 +1833,6 @@ int x509_cert_check(const uint8_t *cert, size_t certlen, int cert_type, size_t extslen; int sig_algor; - if (x509_cert_get_details(cert, certlen, &version, // version &serial, &serial_len, // serial @@ -1871,12 +1870,12 @@ int x509_cert_check(const uint8_t *cert, size_t certlen, int cert_type, return -1; } - // check issuer and subject not empty + // check issuer and subject name requirements if (x509_name_check(issuer, issuer_len) != 1) { error_print(); return -1; } - if (x509_name_check(subject, subject_len) != 1) { + if (x509_cert_check_subject(cert, certlen, cert_type) != 1) { error_print(); return -1; } diff --git a/src/x509_vrf.c b/src/x509_vrf.c new file mode 100644 index 00000000..5e408e43 --- /dev/null +++ b/src/x509_vrf.c @@ -0,0 +1,218 @@ +/* + * Copyright 2014-2026 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +static int x509_general_name_check(int choice, const uint8_t *d, size_t dlen) +{ + const uint8_t *p; + size_t len; + const uint8_t *q; + size_t qlen; + uint32_t nodes[32]; + size_t nodes_cnt; + int tag; + int ret; + + if (!d || !dlen) { + error_print(); + return -1; + } + + switch (choice) { + case X509_gn_other_name: + if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1 + || asn1_object_identifier_from_der(nodes, &nodes_cnt, &p, &len) != 1 + || asn1_explicit_from_der(0, &q, &qlen, &p, &len) != 1 + || asn1_length_is_zero(len) != 1) { + error_print(); + return -1; + } + if (!qlen) { + error_print(); + return -1; + } + break; + + case X509_gn_rfc822_name: + case X509_gn_dns_name: + case X509_gn_uniform_resource_identifier: + if (asn1_string_is_ia5_string((const char *)d, dlen) != 1) { + error_print(); + return -1; + } + break; + + case X509_gn_x400_address: + if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1 + || !len) { + error_print(); + return -1; + } + break; + + case X509_gn_directory_name: + if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1 + || x509_name_check(p, len) != 1) { + error_print(); + return -1; + } + break; + + case X509_gn_edi_party_name: + if (asn1_sequence_from_der(&p, &len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + if ((ret = x509_explicit_directory_name_from_der(0, &tag, &q, &qlen, &p, &len)) < 0) { + error_print(); + return -1; + } + if (ret && x509_directory_name_check(tag, q, qlen) != 1) { + error_print(); + return -1; + } + if (x509_explicit_directory_name_from_der(1, &tag, &q, &qlen, &p, &len) != 1 + || x509_directory_name_check(tag, q, qlen) != 1 + || asn1_length_is_zero(len) != 1) { + error_print(); + return -1; + } + break; + + case X509_gn_ip_address: + if (dlen != 4 && dlen != 16) { + error_print(); + return -1; + } + break; + + case X509_gn_registered_id: + if (asn1_object_identifier_from_octets(nodes, &nodes_cnt, d, dlen) != 1) { + error_print(); + return -1; + } + break; + + default: + error_print(); + return -1; + } + + return 1; +} + +static int x509_general_names_check(const uint8_t *d, size_t dlen) +{ + int choice; + const uint8_t *name; + size_t namelen; + + if (!d || !dlen) { + error_print(); + return -1; + } + + while (dlen) { + if (x509_general_name_from_der(&choice, &name, &namelen, &d, &dlen) != 1 + || x509_general_name_check(choice, name, namelen) != 1) { + error_print(); + return -1; + } + } + + return 1; +} + +int x509_cert_check_subject(const uint8_t *cert, size_t certlen, int cert_type) +{ + int ret; + int is_cacert; + const uint8_t *subject; + size_t subject_len; + const uint8_t *exts; + size_t extslen; + const uint8_t *val; + size_t vlen; + const uint8_t *gns; + size_t gnslen; + int critical; + + if (!cert || !certlen) { + error_print(); + return -1; + } + + switch (cert_type) { + case X509_cert_server_auth: + case X509_cert_client_auth: + case X509_cert_server_key_encipher: + case X509_cert_client_key_encipher: + is_cacert = 0; + break; + case X509_cert_ca: + case X509_cert_root_ca: + case X509_cert_crl_sign: + is_cacert = 1; + break; + default: + error_print(); + return -1; + } + + if (x509_cert_get_subject(cert, certlen, &subject, &subject_len) != 1) { + error_print(); + return -1; + } + if ((ret = x509_name_check(subject, subject_len)) < 0) { + error_print(); + return -1; + } + + if (ret == 0) { + if (is_cacert) { + error_print(); + return -1; + } + + if (x509_cert_get_exts(cert, certlen, &exts, &extslen) != 1) { + error_print(); + return -1; + } + if (x509_exts_get_ext_by_oid(exts, extslen, OID_ce_subject_alt_name, &critical, &val, &vlen) != 1) { + error_print(); + return -1; + } + if (critical != X509_critical) { + error_print(); + return -1; + } + if (asn1_sequence_from_der(&gns, &gnslen, &val, &vlen) != 1 + || asn1_length_is_zero(vlen) != 1 + || x509_general_names_check(gns, gnslen) != 1) { + error_print(); + return -1; + } + } + + return 1; +} diff --git a/tests/x509_vrftest.c b/tests/x509_vrftest.c new file mode 100644 index 00000000..422d5bdc --- /dev/null +++ b/tests/x509_vrftest.c @@ -0,0 +1,132 @@ +/* + * Copyright 2014-2026 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static int set_x509_name(uint8_t *name, size_t *namelen, size_t maxlen) +{ + *namelen = 0; + if (x509_name_add_country_name(name, namelen, maxlen, "CN") != 1 + || x509_name_add_locality_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"Haidian", strlen("Haidian")) != 1 + || x509_name_add_state_or_province_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"Beijing", strlen("Beijing")) != 1 + || x509_name_add_organization_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"PKU", strlen("PKU")) != 1 + || x509_name_add_organizational_unit_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"CS", strlen("CS")) != 1 + || x509_name_add_common_name(name, namelen, maxlen, ASN1_TAG_PrintableString, (uint8_t *)"CA", strlen("CA")) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int test_x509_cert_check_subject(void) +{ + int algor = OID_ec_public_key; + int algor_param = OID_sm2; + uint8_t serial[20] = { 0x01, 0x00 }; + uint8_t issuer[256]; + size_t issuer_len = 0; + time_t not_before, not_after; + uint8_t empty_subject[1] = {0}; + X509_KEY x509_key; + uint8_t gns[256]; + size_t gnslen; + uint8_t exts[512]; + size_t extslen; + uint8_t cert[1024]; + uint8_t *p; + size_t certlen; + int path_len_constraint; + + set_x509_name(issuer, &issuer_len, sizeof(issuer)); + time(¬_before); + x509_validity_add_days(¬_after, not_before, 365); + + if (x509_key_generate(&x509_key, algor, &algor_param, sizeof(algor_param)) != 1) { + error_print(); + return -1; + } + + gnslen = 0; + extslen = 0; + p = cert; + certlen = 0; + if (x509_general_names_add_dns_name(gns, &gnslen, sizeof(gns), "www.example.com") != 1 + || x509_exts_add_subject_alt_name(exts, &extslen, sizeof(exts), + X509_critical, gns, gnslen) != 1 + || x509_cert_sign_to_der( + X509_version_v3, + serial, sizeof(serial), + OID_sm2sign_with_sm3, + issuer, issuer_len, + not_before, not_after, + empty_subject, 0, + &x509_key, + NULL, 0, + NULL, 0, + exts, extslen, + &x509_key, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID), + &p, &certlen) != 1 + || x509_cert_check_subject(cert, certlen, 0) != 1 + || x509_cert_check(cert, certlen, X509_cert_server_auth, &path_len_constraint) != 1) { + error_print(); + return -1; + } + + gnslen = 0; + extslen = 0; + p = cert; + certlen = 0; + if (x509_general_names_add_dns_name(gns, &gnslen, sizeof(gns), "www.example.com") != 1 + || x509_exts_add_subject_alt_name(exts, &extslen, sizeof(exts), + X509_non_critical, gns, gnslen) != 1 + || x509_cert_sign_to_der( + X509_version_v3, + serial, sizeof(serial), + OID_sm2sign_with_sm3, + issuer, issuer_len, + not_before, not_after, + empty_subject, 0, + &x509_key, + NULL, 0, + NULL, 0, + exts, extslen, + &x509_key, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID), + &p, &certlen) != 1) { + error_print(); + return -1; + } + if (x509_cert_check_subject(cert, certlen, 0) == 1) { + error_print(); + return -1; + } + + printf("%s() ok\n", __FUNCTION__); + return 1; +} + +int main(void) +{ + if (test_x509_cert_check_subject() != 1) goto err; + + printf("%s all tests passed\n", __FILE__); + return 0; +err: + error_print(); + return 1; +}