From dadd2a3e0d7389423567f0370a365fb6bab37f40 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Wed, 24 Jun 2026 08:12:01 +0800 Subject: [PATCH] Add ECDHE cipher suites to TLCP --- CMakeLists.txt | 7 +- cmake/tlcp_commands.cmake | 29 ++- cmake/tool_cert.cmake | 14 ++ include/gmssl/sm2.h | 16 ++ include/gmssl/tls.h | 4 + include/gmssl/version.h | 2 +- src/sm2_exch.c | 242 ++++++++++++++++++ src/tlcp.c | 501 +++++++++++++++++++++++++++++++------- src/tls.c | 41 ++++ src/tls12.c | 124 +++++++++- src/tls_cert.c | 48 +++- src/tls_vrf.c | 16 +- src/x509_cer.c | 6 +- tests/sm2_exchtest.c | 90 +++++++ tools/tlcp_client.c | 46 +++- tools/tlcp_help.h | 2 + 16 files changed, 1083 insertions(+), 105 deletions(-) create mode 100644 tests/sm2_exchtest.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 79109c84..8a69ac7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -244,6 +244,7 @@ set(tests sm2_key sm2_sign sm2_enc + sm2_exch block_cipher digest hkdf @@ -873,6 +874,8 @@ if(ENABLE_TLS AND NOT WIN32) add_test(NAME tool_tlcp_sm4_gcm_sni COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_sm4_gcm_sni -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") add_test(NAME tool_tlcp_sm4_cbc_sni COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_sm4_cbc_sni -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") add_test(NAME tool_tlcp_sm4_gcm_client_cert COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_sm4_gcm_client_cert -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") + add_test(NAME tool_tlcp_ecdhe_sm4_cbc_client_cert COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_ecdhe_sm4_cbc_client_cert -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") + add_test(NAME tool_tlcp_ecdhe_sm4_gcm_client_cert COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tlcp_ecdhe_sm4_gcm_client_cert -P "${CMAKE_SOURCE_DIR}/cmake/tlcp_commands.cmake") add_test(NAME tool_tls12_sm4_gcm_sni COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls12_sm4_gcm_sni -P "${CMAKE_SOURCE_DIR}/cmake/tls12_commands.cmake") add_test(NAME tool_tls12_sm4_cbc_sni COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls12_sm4_cbc_sni -P "${CMAKE_SOURCE_DIR}/cmake/tls12_commands.cmake") add_test(NAME tool_tls12_sm4_gcm_client_cert COMMAND ${CMAKE_COMMAND} -DTEST_CASE=tls12_sm4_gcm_client_cert -P "${CMAKE_SOURCE_DIR}/cmake/tls12_commands.cmake") @@ -890,6 +893,8 @@ if(ENABLE_TLS AND NOT WIN32) tool_tlcp_sm4_gcm_sni tool_tlcp_sm4_cbc_sni tool_tlcp_sm4_gcm_client_cert + tool_tlcp_ecdhe_sm4_cbc_client_cert + tool_tlcp_ecdhe_sm4_gcm_client_cert tool_tls12_sm4_gcm_sni tool_tls12_sm4_cbc_sni tool_tls12_sm4_gcm_client_cert @@ -942,7 +947,7 @@ endif() # set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_VENDOR "GmSSL develop team") -set(CPACK_PACKAGE_VERSION "3.3.0-dev.1162") +set(CPACK_PACKAGE_VERSION "3.3.0-dev.1163") set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md) set(CPACK_NSIS_MODIFY_PATH ON) include(CPack) diff --git a/cmake/tlcp_commands.cmake b/cmake/tlcp_commands.cmake index 329b09b7..ff90fd3d 100644 --- a/cmake/tlcp_commands.cmake +++ b/cmake/tlcp_commands.cmake @@ -5,6 +5,8 @@ gmssl_require_file(sm2_tlcp_server_certs.pem) gmssl_require_file(sm2_tlcp_server_keys.pem) gmssl_require_file(sm2_tls_client_certs.pem) gmssl_require_file(sm2_tls_client_key.pem) +gmssl_require_file(sm2_tlcp_client_certs.pem) +gmssl_require_file(sm2_tlcp_client_keys.pem) if(NOT DEFINED TEST_CASE) set(TEST_CASE tlcp_sm4_gcm_sni) @@ -25,6 +27,18 @@ elseif(TEST_CASE STREQUAL tlcp_sm4_gcm_client_cert) set(TEST_PORT 4436) set(TEST_CIPHER_SUITE TLS_ECC_SM4_GCM_SM3) set(TEST_CLIENT_CERT ON) +elseif(TEST_CASE STREQUAL tlcp_ecdhe_sm4_cbc_client_cert) + set(TEST_NAME tlcp_ecdhe_sm4_cbc_client_cert) + set(TEST_PORT 4437) + set(TEST_CIPHER_SUITE TLS_ECDHE_SM4_CBC_SM3) + set(TEST_CLIENT_CERT ON) + set(TEST_CLIENT_DOUBLE_CERT ON) +elseif(TEST_CASE STREQUAL tlcp_ecdhe_sm4_gcm_client_cert) + set(TEST_NAME tlcp_ecdhe_sm4_gcm_client_cert) + set(TEST_PORT 4438) + set(TEST_CIPHER_SUITE TLS_ECDHE_SM4_GCM_SM3) + set(TEST_CLIENT_CERT ON) + set(TEST_CLIENT_DOUBLE_CERT ON) else() message(FATAL_ERROR "unknown TLCP test case: ${TEST_CASE}") endif() @@ -50,10 +64,17 @@ if(TEST_CLIENT_CERT) list(APPEND TEST_SERVER_ARGS -cacert sm2_root_ca_cert.pem -cert_request) - list(APPEND TEST_CLIENT_ARGS - -cert sm2_tls_client_certs.pem - -key sm2_tls_client_key.pem - -pass P@ssw0rd) + if(TEST_CLIENT_DOUBLE_CERT) + list(APPEND TEST_CLIENT_ARGS + -cert sm2_tlcp_client_certs.pem + -key sm2_tlcp_client_keys.pem + -pass P@ssw0rd) + else() + list(APPEND TEST_CLIENT_ARGS + -cert sm2_tls_client_certs.pem + -key sm2_tls_client_key.pem + -pass P@ssw0rd) + endif() endif() gmssl_run_tls_command_test( diff --git a/cmake/tool_cert.cmake b/cmake/tool_cert.cmake index 0c36f4d3..5881b1b8 100644 --- a/cmake/tool_cert.cmake +++ b/cmake/tool_cert.cmake @@ -187,6 +187,20 @@ gmssl_generate_end_entity(SM2 sm2_tls_client "GmSSL SM2 TLS Client" gmssl_write_bundle(sm2_tls_client_certs.pem sm2_tls_client_cert.pem sm2_tls_client_ca_cert.pem) +# SM2 TLCP client chain reuses the SM2 TLS client CA and adds an encryption certificate. +gmssl_generate_end_entity(SM2 sm2_tlcp_client_sign "GmSSL SM2 TLCP Client" + sm2_tls_client_ca_cert.pem sm2_tls_client_ca_key.pem + digitalSignature clientAuth "" OFF) +gmssl_generate_end_entity(SM2 sm2_tlcp_client_enc "GmSSL SM2 TLCP Client" + sm2_tls_client_ca_cert.pem sm2_tls_client_ca_key.pem + keyEncipherment clientAuth "" OFF) +gmssl_write_bundle(sm2_tlcp_client_certs.pem + sm2_tlcp_client_sign_cert.pem + sm2_tlcp_client_enc_cert.pem + sm2_tls_client_ca_cert.pem) +gmssl_write_bundle(sm2_tlcp_client_keys.pem + sm2_tlcp_client_sign_key.pem sm2_tlcp_client_enc_key.pem) + # P256 TLS client chain: root -> client CA -> client certificate gmssl_generate_ca(P256 p256_tls_client_ca "GmSSL P256 TLS Client CA" p256_root_ca_cert.pem p256_root_ca_key.pem 0) diff --git a/include/gmssl/sm2.h b/include/gmssl/sm2.h index eb71eee6..c8bdba13 100644 --- a/include/gmssl/sm2.h +++ b/include/gmssl/sm2.h @@ -254,6 +254,22 @@ int sm2_encrypt_fixlen(const SM2_KEY *key, const uint8_t *in, size_t inlen, int int sm2_do_ecdh(const SM2_KEY *key, const SM2_KEY *peer_key, uint8_t out[32]); int sm2_ecdh(const SM2_KEY *key, const uint8_t uncompressed_point[65], uint8_t out[32]); +int sm2_key_exchange(int is_initiator, + const SM2_KEY *key, const char *id, size_t idlen, + const SM2_KEY *peer_public_key, const char *peer_id, size_t peer_idlen, + const SM2_KEY *key_exchange, const uint8_t peer_key_exchange[65], + uint8_t optional_shared_point[65], size_t shared_key_len, uint8_t *shared_key); +int sm2_key_exchange_compute_confirm(int is_initiator, + const SM2_KEY *key, const char *id, size_t idlen, + const SM2_KEY *peer_public_key, const char *peer_id, size_t peer_idlen, + const SM2_KEY *key_exchange, const uint8_t peer_key_exchange[65], + const uint8_t shared_point[65], uint8_t confirm[32]); +int sm2_key_exchange_verify_confirm(int is_initiator, + const SM2_KEY *key, const char *id, size_t idlen, + const SM2_KEY *peer_public_key, const char *peer_id, size_t peer_idlen, + const SM2_KEY *key_exchange, const uint8_t peer_key_exchange[65], + const uint8_t shared_point[65], const uint8_t confirm[32]); + typedef struct { sm2_z256_t k; diff --git a/include/gmssl/tls.h b/include/gmssl/tls.h index 8194b39d..7b953aff 100644 --- a/include/gmssl/tls.h +++ b/include/gmssl/tls.h @@ -1008,8 +1008,12 @@ int tls_ctx_set_certificate_and_key(TLS_CTX *ctx, const char *chainfile, const char *keyfile, const char *keypass); int tlcp_ctx_add_server_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, const char *keyfile, const char *keypass); +int tlcp_ctx_add_client_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, + const char *keyfile, const char *keypass); int tls_ctx_set_tlcp_server_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, const char *keyfile, const char *keypass); +int tls_ctx_set_tlcp_client_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, + const char *keyfile, const char *keypass); void tls_ctx_cleanup(TLS_CTX *ctx); int tls_ctx_add_certificate_chain_and_key(TLS_CTX *ctx, const char *chainfile, diff --git a/include/gmssl/version.h b/include/gmssl/version.h index 830677c7..42009989 100644 --- a/include/gmssl/version.h +++ b/include/gmssl/version.h @@ -18,7 +18,7 @@ extern "C" { #define GMSSL_VERSION_NUM 30300 -#define GMSSL_VERSION_STR "GmSSL 3.3.0-dev.1162" +#define GMSSL_VERSION_STR "GmSSL 3.3.0-dev.1163" int gmssl_version_num(void); const char *gmssl_version_str(void); diff --git a/src/sm2_exch.c b/src/sm2_exch.c index cb1979a2..9858e9dc 100644 --- a/src/sm2_exch.c +++ b/src/sm2_exch.c @@ -55,3 +55,245 @@ int sm2_ecdh(const SM2_KEY *key, const uint8_t uncompressed_point[65], uint8_t o gmssl_secure_clear(x, sizeof(sm2_z256_t)); return 1; } + +static int sm2_z256_point_get_x_hat(const SM2_Z256_POINT *P, sm2_z256_t x_hat) +{ + sm2_z256_t x; + + if (sm2_z256_point_get_xy(P, x, NULL) != 1) { + error_print(); + return -1; + } + + // x' = 2^127 + (x mod 2^127) + x_hat[0] = x[0]; + x_hat[1] = (x[1] & 0x7fffffffffffffff) | 0x8000000000000000; + x_hat[2] = 0; + x_hat[3] = 0; + + gmssl_secure_clear(x, sizeof(x)); + return 1; +} + +int sm2_key_exchange(int is_initiator, + const SM2_KEY *key, const char *id, size_t idlen, + const SM2_KEY *peer_public_key, const char *peer_id, size_t peer_idlen, + const SM2_KEY *key_exchange, const uint8_t peer_key_exchange[65], + uint8_t optional_shared_point[65], size_t shared_key_len, uint8_t *shared_key) +{ + SM2_Z256_POINT peer_point; + SM2_Z256_POINT point; + sm2_z256_t local_x_hat; + sm2_z256_t peer_x_hat; + sm2_z256_t t; + uint8_t za[32]; + uint8_t zb[32]; + uint8_t kdf_input[128]; + int ret = -1; + + if (!key || !id || !peer_public_key || !peer_id || !key_exchange + || !peer_key_exchange || !shared_key_len || !shared_key) { + error_print(); + return -1; + } + if (idlen > SM2_MAX_ID_LENGTH || peer_idlen > SM2_MAX_ID_LENGTH) { + error_print(); + return -1; + } + if (sm2_z256_point_from_octets(&peer_point, peer_key_exchange, 65) != 1) { + error_print(); + goto end; + } + if (is_initiator) { + if (sm2_compute_z(za, &key->public_key, id, idlen) != 1 + || sm2_compute_z(zb, &peer_public_key->public_key, peer_id, peer_idlen) != 1) { + error_print(); + goto end; + } + } else { + if (sm2_compute_z(za, &peer_public_key->public_key, peer_id, peer_idlen) != 1 + || sm2_compute_z(zb, &key->public_key, id, idlen) != 1) { + error_print(); + goto end; + } + } + + if (sm2_z256_point_get_x_hat(&key_exchange->public_key, local_x_hat) != 1 + || sm2_z256_point_get_x_hat(&peer_point, peer_x_hat) != 1) { + error_print(); + goto end; + } + + sm2_z256_modn_mul(t, local_x_hat, key_exchange->private_key); + sm2_z256_modn_add(t, t, key->private_key); + + sm2_z256_point_mul(&point, peer_x_hat, &peer_point); + sm2_z256_point_add(&point, &peer_public_key->public_key, &point); + if (sm2_z256_point_is_at_infinity(&point)) { + error_print(); + goto end; + } + + sm2_z256_point_mul(&point, t, &point); + if (sm2_z256_point_is_at_infinity(&point)) { + error_print(); + goto end; + } + if (optional_shared_point + && sm2_z256_point_to_uncompressed_octets(&point, optional_shared_point) != 1) { + error_print(); + goto end; + } + + sm2_z256_point_to_bytes(&point, kdf_input); + memcpy(kdf_input + 64, za, 32); + memcpy(kdf_input + 96, zb, 32); + if (sm2_kdf(kdf_input, sizeof(kdf_input), shared_key_len, shared_key) != 1) { + error_print(); + goto end; + } + if (mem_is_zero(shared_key, shared_key_len)) { + error_print(); + goto end; + } + + ret = 1; + +end: + gmssl_secure_clear(&peer_point, sizeof(peer_point)); + gmssl_secure_clear(&point, sizeof(point)); + gmssl_secure_clear(local_x_hat, sizeof(local_x_hat)); + gmssl_secure_clear(peer_x_hat, sizeof(peer_x_hat)); + gmssl_secure_clear(t, sizeof(t)); + gmssl_secure_clear(za, sizeof(za)); + gmssl_secure_clear(zb, sizeof(zb)); + gmssl_secure_clear(kdf_input, sizeof(kdf_input)); + return ret; +} + +static int sm2_key_exchange_compute_confirm_ex(int is_initiator, + const SM2_KEY *key, const char *id, size_t idlen, + const SM2_KEY *peer_public_key, const char *peer_id, size_t peer_idlen, + const SM2_KEY *key_exchange, const uint8_t peer_key_exchange[65], + const uint8_t shared_point[65], int is_initiator_confirm, uint8_t confirm[32]) +{ + SM2_Z256_POINT uv; + SM2_Z256_POINT peer_point; + SM2_Z256_POINT ra; + SM2_Z256_POINT rb; + uint8_t za[32]; + uint8_t zb[32]; + uint8_t xy[64]; + uint8_t ra_bytes[64]; + uint8_t rb_bytes[64]; + uint8_t hash[32]; + uint8_t prefix = is_initiator_confirm ? 0x03 : 0x02; + SM3_CTX ctx; + int ret = -1; + + if (!key || !id || !peer_public_key || !peer_id || !key_exchange + || !peer_key_exchange || !shared_point || !confirm) { + error_print(); + return -1; + } + if (idlen > SM2_MAX_ID_LENGTH || peer_idlen > SM2_MAX_ID_LENGTH) { + error_print(); + return -1; + } + if (sm2_z256_point_from_octets(&uv, shared_point, 65) != 1 + || sm2_z256_point_from_octets(&peer_point, peer_key_exchange, 65) != 1) { + error_print(); + goto end; + } + if (is_initiator) { + ra = key_exchange->public_key; + rb = peer_point; + if (sm2_compute_z(za, &key->public_key, id, idlen) != 1 + || sm2_compute_z(zb, &peer_public_key->public_key, peer_id, peer_idlen) != 1) { + error_print(); + goto end; + } + } else { + ra = peer_point; + rb = key_exchange->public_key; + if (sm2_compute_z(za, &peer_public_key->public_key, peer_id, peer_idlen) != 1 + || sm2_compute_z(zb, &key->public_key, id, idlen) != 1) { + error_print(); + goto end; + } + } + + sm2_z256_point_to_bytes(&uv, xy); + sm2_z256_point_to_bytes(&ra, ra_bytes); + sm2_z256_point_to_bytes(&rb, rb_bytes); + + sm3_init(&ctx); + sm3_update(&ctx, xy, 32); + sm3_update(&ctx, za, sizeof(za)); + sm3_update(&ctx, zb, sizeof(zb)); + sm3_update(&ctx, ra_bytes, sizeof(ra_bytes)); + sm3_update(&ctx, rb_bytes, sizeof(rb_bytes)); + sm3_finish(&ctx, hash); + + sm3_init(&ctx); + sm3_update(&ctx, &prefix, sizeof(prefix)); + sm3_update(&ctx, xy + 32, 32); + sm3_update(&ctx, hash, sizeof(hash)); + sm3_finish(&ctx, confirm); + + ret = 1; + +end: + gmssl_secure_clear(&uv, sizeof(uv)); + gmssl_secure_clear(&peer_point, sizeof(peer_point)); + gmssl_secure_clear(&ra, sizeof(ra)); + gmssl_secure_clear(&rb, sizeof(rb)); + gmssl_secure_clear(za, sizeof(za)); + gmssl_secure_clear(zb, sizeof(zb)); + gmssl_secure_clear(xy, sizeof(xy)); + gmssl_secure_clear(ra_bytes, sizeof(ra_bytes)); + gmssl_secure_clear(rb_bytes, sizeof(rb_bytes)); + gmssl_secure_clear(hash, sizeof(hash)); + gmssl_secure_clear(&ctx, sizeof(ctx)); + return ret; +} + +int sm2_key_exchange_compute_confirm(int is_initiator, + const SM2_KEY *key, const char *id, size_t idlen, + const SM2_KEY *peer_public_key, const char *peer_id, size_t peer_idlen, + const SM2_KEY *key_exchange, const uint8_t peer_key_exchange[65], + const uint8_t shared_point[65], uint8_t confirm[32]) +{ + if (sm2_key_exchange_compute_confirm_ex(is_initiator, + key, id, idlen, peer_public_key, peer_id, peer_idlen, + key_exchange, peer_key_exchange, shared_point, is_initiator, confirm) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm2_key_exchange_verify_confirm(int is_initiator, + const SM2_KEY *key, const char *id, size_t idlen, + const SM2_KEY *peer_public_key, const char *peer_id, size_t peer_idlen, + const SM2_KEY *key_exchange, const uint8_t peer_key_exchange[65], + const uint8_t shared_point[65], const uint8_t confirm[32]) +{ + uint8_t expected[32]; + int ret; + + if (!confirm) { + error_print(); + return -1; + } + if (sm2_key_exchange_compute_confirm_ex(is_initiator, + key, id, idlen, peer_public_key, peer_id, peer_idlen, + key_exchange, peer_key_exchange, shared_point, !is_initiator, expected) != 1) { + error_print(); + return -1; + } + + ret = gmssl_secure_memcmp(expected, confirm, sizeof(expected)) == 0 ? 1 : 0; + gmssl_secure_clear(expected, sizeof(expected)); + return ret; +} diff --git a/src/tlcp.c b/src/tlcp.c index 228ee2e2..0fd1201e 100644 --- a/src/tlcp.c +++ b/src/tlcp.c @@ -39,13 +39,77 @@ const size_t tlcp_signature_algorithms_cnt = const int tlcp_cipher_suites[] = { TLS_cipher_ecc_sm4_cbc_sm3, TLS_cipher_ecc_sm4_gcm_sm3, + TLS_cipher_ecdhe_sm4_cbc_sm3, + TLS_cipher_ecdhe_sm4_gcm_sm3, }; const size_t tlcp_cipher_suites_cnt = sizeof(tlcp_cipher_suites)/sizeof(tlcp_cipher_suites[0]); +static int tlcp_cipher_suite_is_ecc(int cipher_suite) +{ + switch (cipher_suite) { + case TLS_cipher_ecc_sm4_cbc_sm3: + case TLS_cipher_ecc_sm4_gcm_sm3: + return 1; + default: + return 0; + } +} + +static int tlcp_cipher_suite_is_ecdhe(int cipher_suite) +{ + switch (cipher_suite) { + case TLS_cipher_ecdhe_sm4_cbc_sm3: + case TLS_cipher_ecdhe_sm4_gcm_sm3: + return 1; + default: + return 0; + } +} + +/*-- + +当采用ECDHE_SM4_CBC/GCM_SM3时 + + 双方采用SM2密钥交换算法,也就是 sm2_key_exchange 函数完成密钥交换 + 其中服务器作为发起方,客户端做为接收方 + + 当采用这个套件时,要求服务器方必须发起cert_reqeust,发送CertificateRequest + 客户端必须响应不为空的client Certificate + + 客户端响应的证书链必须是SM2的双证书证书链 + + 服务器加密证书中的公钥作为发起方的持久公钥,即sm2_key_exchange的参数key + 服务器ServerKeyExchagne中的public公钥作为临时公钥,即_key_exchange的参数key_exchange + + 客户端加密证书中的公钥作为响应方的持久公钥 + 客户端ClientKeyExchagne中的公钥作为响应方的临时公钥 + + 双方的id,peed_id参数使用默认的SM2_DEFAULT_ID + + 在密钥交换时,使用sm2_key_exchange进行密钥交换 + + + +ServerKeyExchange + +select (KeyExchangeAlgorithm) { + case ECC: + digitall-signed struct { + opaque client_random[32]; + opaque server_random[32]; + opaque ASN1.Cert<1..2^24-1>; + } signed_params; + + case ECDHE: + ServerECDHEParams params; + digitally-signed struct { + opaque client_random[32]; + opaque server_random[32]; + ServerECDHEParams params; + -/* ServerKeyExchange select (KeyExchangeAlgorithm) { @@ -82,8 +146,52 @@ select (KeyExchangeAlgorithm) { } ServerKeyExchange; `signed_params` is DER signature encoded in uint16array + +struct { + ECParameters curve_params; + ECPoint public; +} ServerECDHEParams; + +struct { + ClientCertificateType certificate_type<1..2^8-1>; + DistinguishedName certificate_authorities<0..2^16-1>; +} CertificateRequest; + +enum { + ecdsa_sign(64), ibc_params(80), +} ClientCertificateType; + +struct { + select (KeyExchangeAlgorithm) { + case ECC: + opaque ECCEncryptedPreMasterSecret<0..2^16-1>; + case ECDHE: + Opaque ClientECDHEParams<1..2^16-1>; + case IBSDH: + Opaque ClientIBSDHParams<1..2^16-1>; + case IBC: + opaque IBCEncryptedPreMasterSecret<0..2^16-1>; + } exchange_keys; +} ClientKeyExchange; + + +struct { + ECParameters curve_params; 值为SM2命名曲线 + ECPoint pubulic; +} ClientECDHEParams; + +ClientIBSDHParams SM9的密钥交换数据 + +IBCEncryptedPreMasterSecret SM9密文加密的pre_master_secret + + + + + */ + + int tlcp_server_key_exchange_ecc_print(FILE *fp, const uint8_t *data, size_t datalen, int fmt, int ind) { const uint8_t *sig; @@ -214,22 +322,7 @@ int tlcp_record_get_handshake_server_key_exchange(const uint8_t *record, } -/* -struct { - select (KeyExchangeAlgorithm) { - case ECC: - opaque ECCEncryptedPreMasterSecret<0..2^16-1>; - case ECDHE: - Opaque ClientECDHEParams<1..2^16-1>; - case IBSDH: - Opaque ClientIBSDHParams<1..2^16-1>; - case IBC: - opaque IBCEncryptedPreMasterSecret<0..2^16-1>; - case RSA: - opaque RSAEncryptedPreMasterSecret<0..2^16-1>; - } exchange_keys; -} ClientKeyExchange; -*/ + int tlcp_record_set_handshake_client_key_exchange(uint8_t *record, size_t *recordlen, const uint8_t *enced_pms, size_t enced_pms_len) @@ -817,6 +910,8 @@ int tlcp_recv_server_certificate(TLS_CONNECT *conn) int tlcp_recv_server_key_exchange(TLS_CONNECT *conn) { int ret; + const uint8_t *server_ecdh_params; + size_t server_ecdh_params_len; const uint8_t *sig; size_t siglen; const uint8_t *sign_cert; @@ -857,36 +952,95 @@ int tlcp_recv_server_key_exchange(TLS_CONNECT *conn) tls_send_alert(conn, TLS_alert_protocol_version); return -1; } - if (tlcp_record_get_handshake_server_key_exchange(conn->record, - TLS_server_key_exchange_ecc, NULL, 0, &sig, &siglen) != 1) { - error_print(); - tls_send_alert(conn, TLS_alert_unexpected_message); - return -1; - } // verify ServerKeyExchange if (x509_certs_get_cert_by_index(conn->peer_cert_chain, conn->peer_cert_chain_len, 0, &sign_cert, &sign_cert_len) != 1 - || x509_cert_get_subject_public_key(sign_cert, sign_cert_len, &sign_key) != 1 - || x509_certs_get_cert_by_index(conn->peer_cert_chain, conn->peer_cert_chain_len, 1, &enc_cert, &enc_cert_len) != 1) { + || x509_cert_get_subject_public_key(sign_cert, sign_cert_len, &sign_key) != 1) { error_print(); return -1; } - tls_uint24_to_bytes(enc_cert_len, &enc_cert_header_ptr, &enc_cert_header_len); + if (sign_key.algor != OID_ec_public_key || sign_key.algor_param != OID_sm2) { + error_print(); + tls_send_alert(conn, TLS_alert_bad_certificate); + return -1; + } - if (sm2_verify_init(&verify_ctx, &sign_key.u.sm2_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1 - || sm2_verify_update(&verify_ctx, conn->client_random, 32) != 1 - || sm2_verify_update(&verify_ctx, conn->server_random, 32) != 1 - || sm2_verify_update(&verify_ctx, enc_cert_header, enc_cert_header_len) != 1 - || sm2_verify_update(&verify_ctx, enc_cert, enc_cert_len) != 1) { + if (tlcp_cipher_suite_is_ecdhe(conn->cipher_suite)) { + const uint8_t *server_key_exchange; + size_t server_key_exchange_len; + const uint8_t *params; + size_t params_len; + + if (tlcp_record_get_handshake_server_key_exchange(conn->record, + TLS_server_key_exchange_ecdhe, &server_ecdh_params, &server_ecdh_params_len, + &sig, &siglen) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_unexpected_message); + return -1; + } + if (sm2_verify_init(&verify_ctx, &sign_key.u.sm2_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1 + || sm2_verify_update(&verify_ctx, conn->client_random, 32) != 1 + || sm2_verify_update(&verify_ctx, conn->server_random, 32) != 1 + || sm2_verify_update(&verify_ctx, server_ecdh_params, server_ecdh_params_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (sm2_verify_finish(&verify_ctx, sig, siglen) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_decrypt_error); + return -1; + } + params = server_ecdh_params; + params_len = server_ecdh_params_len; + if (tls_server_ecdh_params_from_bytes(&conn->key_exchange_group, + &server_key_exchange, &server_key_exchange_len, ¶ms, ¶ms_len) != 1 + || tls_length_is_zero(params_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_decode_error); + return -1; + } + if (conn->key_exchange_group != TLS_curve_sm2p256v1 + || server_key_exchange_len != sizeof(conn->peer_key_exchange)) { + error_print(); + tls_send_alert(conn, TLS_alert_illegal_parameter); + return -1; + } + memcpy(conn->peer_key_exchange, server_key_exchange, server_key_exchange_len); + conn->peer_key_exchange_len = server_key_exchange_len; + } else if (tlcp_cipher_suite_is_ecc(conn->cipher_suite)) { + if (tlcp_record_get_handshake_server_key_exchange(conn->record, + TLS_server_key_exchange_ecc, NULL, 0, &sig, &siglen) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_unexpected_message); + return -1; + } + if (x509_certs_get_cert_by_index(conn->peer_cert_chain, conn->peer_cert_chain_len, 1, + &enc_cert, &enc_cert_len) != 1) { + error_print(); + return -1; + } + tls_uint24_to_bytes(enc_cert_len, &enc_cert_header_ptr, &enc_cert_header_len); + + if (sm2_verify_init(&verify_ctx, &sign_key.u.sm2_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1 + || sm2_verify_update(&verify_ctx, conn->client_random, 32) != 1 + || sm2_verify_update(&verify_ctx, conn->server_random, 32) != 1 + || sm2_verify_update(&verify_ctx, enc_cert_header, enc_cert_header_len) != 1 + || sm2_verify_update(&verify_ctx, enc_cert, enc_cert_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (sm2_verify_finish(&verify_ctx, sig, siglen) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_decrypt_error); + return -1; + } + } else { error_print(); tls_send_alert(conn, TLS_alert_internal_error); return -1; } - if (sm2_verify_finish(&verify_ctx, sig, siglen) != 1) { - error_print(); - tls_send_alert(conn, TLS_alert_decrypt_error); - return -1; - } return 1; } @@ -981,33 +1135,90 @@ int tlcp_send_client_key_exchange(TLS_CONNECT *conn) X509_KEY public_key; uint8_t enced_pre_master_secret[SM2_MAX_CIPHERTEXT_SIZE]; size_t enced_pre_master_secret_len; + uint8_t client_ecdh_params[69]; + uint8_t *client_ecdh_params_ptr = client_ecdh_params; + size_t client_ecdh_params_len = 0; + int curve_oid = tls_named_curve_oid(TLS_curve_sm2p256v1); if (conn->verbose) tls_trace("send ClientKeyExchange\n"); - if (x509_certs_get_cert_by_index(conn->peer_cert_chain, conn->peer_cert_chain_len, 1, - &enc_cert, &enc_cert_len) != 1 - || x509_cert_get_subject_public_key(enc_cert, enc_cert_len, &public_key) != 1) { - error_print(); - return -1; - } + if (tlcp_cipher_suite_is_ecdhe(conn->cipher_suite)) { + X509_KEY *enc_key; - if (tlcp_generate_pre_master_secret(conn) != 1 - || tls_derive_master_secret(conn) != 1 - || tls_derive_key_block(conn) != 1 - || tls_init_application_keys(conn) != 1) { - error_print(); - return -1; - } + if (!conn->client_certificate_verify || !conn->cert_chain_idx || !conn->peer_key_exchange_len) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (x509_certs_get_cert_by_index(conn->peer_cert_chain, conn->peer_cert_chain_len, 1, + &enc_cert, &enc_cert_len) != 1 + || x509_cert_get_subject_public_key(enc_cert, enc_cert_len, &public_key) != 1) { + error_print(); + return -1; + } + enc_key = &conn->ctx->enc_keys[conn->cert_chain_idx - 1]; + if (public_key.algor != OID_ec_public_key || public_key.algor_param != OID_sm2 + || enc_key->algor != OID_ec_public_key || enc_key->algor_param != OID_sm2) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (x509_key_generate(&conn->key_exchanges[0], OID_ec_public_key, + &curve_oid, sizeof(curve_oid)) != 1 + || tls_server_ecdh_params_to_bytes(&conn->key_exchanges[0], + &client_ecdh_params_ptr, &client_ecdh_params_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (client_ecdh_params_len != sizeof(client_ecdh_params)) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (sm2_key_exchange(0, &enc_key->u.sm2_key, + SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH, + &public_key.u.sm2_key, + SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH, + &conn->key_exchanges[0].u.sm2_key, + conn->peer_key_exchange, NULL, 48, conn->pre_master_secret) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + conn->pre_master_secret_len = 48; + if (tlcp_record_set_handshake_client_key_exchange(conn->record, &conn->recordlen, + client_ecdh_params, client_ecdh_params_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + } else if (tlcp_cipher_suite_is_ecc(conn->cipher_suite)) { + if (x509_certs_get_cert_by_index(conn->peer_cert_chain, conn->peer_cert_chain_len, 1, + &enc_cert, &enc_cert_len) != 1 + || x509_cert_get_subject_public_key(enc_cert, enc_cert_len, &public_key) != 1) { + error_print(); + return -1; + } - if (sm2_encrypt(&public_key.u.sm2_key, conn->pre_master_secret, 48, - enced_pre_master_secret, &enced_pre_master_secret_len) != 1) { - error_print(); - tls_send_alert(conn, TLS_alert_internal_error); - return -1; - } - if (tlcp_record_set_handshake_client_key_exchange(conn->record, &conn->recordlen, - enced_pre_master_secret, enced_pre_master_secret_len) != 1) { + if (tlcp_generate_pre_master_secret(conn) != 1) { + error_print(); + return -1; + } + if (sm2_encrypt(&public_key.u.sm2_key, conn->pre_master_secret, 48, + enced_pre_master_secret, &enced_pre_master_secret_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (tlcp_record_set_handshake_client_key_exchange(conn->record, &conn->recordlen, + enced_pre_master_secret, enced_pre_master_secret_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + } else { error_print(); tls_send_alert(conn, TLS_alert_internal_error); return -1; @@ -1026,6 +1237,13 @@ int tlcp_send_client_key_exchange(TLS_CONNECT *conn) error_print(); return -1; } + + if (tls_derive_master_secret(conn) != 1 + || tls_derive_key_block(conn) != 1 + || tls_init_application_keys(conn) != 1) { + error_print(); + return -1; + } } if ((ret = tls_send_record(conn)) != 1) { @@ -1201,6 +1419,11 @@ int tlcp_recv_client_hello(TLS_CONNECT *conn) break; case TLS_cipher_ecdhe_sm4_cbc_sm3: case TLS_cipher_ecdhe_sm4_gcm_sm3: + conn->sig_alg = TLS_sig_sm2sig_sm3; + conn->signature_algorithms[0] = TLS_sig_sm2sig_sm3; + conn->key_exchange_group = TLS_curve_sm2p256v1; + conn->client_certificate_verify = 1; + break; default: error_print(); return -1; @@ -1546,6 +1769,10 @@ int tlcp_send_server_key_exchange(TLS_CONNECT *conn) uint8_t enc_cert_header[3]; uint8_t *enc_cert_header_ptr = enc_cert_header; size_t enc_cert_header_len = 0; + uint8_t server_ecdh_params[69]; + uint8_t *server_ecdh_params_ptr = server_ecdh_params; + size_t server_ecdh_params_len = 0; + int curve_oid = tls_named_curve_oid(TLS_curve_sm2p256v1); X509_KEY *sign_key; SM2_SIGN_CTX sign_ctx; uint8_t sigbuf[SM2_MAX_SIGNATURE_SIZE]; @@ -1574,19 +1801,54 @@ int tlcp_send_server_key_exchange(TLS_CONNECT *conn) tls_send_alert(conn, TLS_alert_internal_error); return -1; } - if (sm2_sign_init(&sign_ctx, &sign_key->u.sm2_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1 - || sm2_sign_update(&sign_ctx, conn->client_random, 32) != 1 - || sm2_sign_update(&sign_ctx, conn->server_random, 32) != 1 - || sm2_sign_update(&sign_ctx, enc_cert_header, enc_cert_header_len) != 1 - || sm2_sign_update(&sign_ctx, enc_cert, enc_cert_len) != 1 - || sm2_sign_finish(&sign_ctx, sigbuf, &siglen) != 1) { - error_print(); - tls_send_alert(conn, TLS_alert_internal_error); - return -1; - } - - if (tlcp_record_set_handshake_server_key_exchange(conn->record, &conn->recordlen, - TLS_server_key_exchange_ecc, NULL, 0, sigbuf, siglen) != 1) { + if (tlcp_cipher_suite_is_ecdhe(conn->cipher_suite)) { + if (x509_key_generate(&conn->key_exchanges[0], OID_ec_public_key, + &curve_oid, sizeof(curve_oid)) != 1 + || tls_server_ecdh_params_to_bytes(&conn->key_exchanges[0], + &server_ecdh_params_ptr, &server_ecdh_params_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (server_ecdh_params_len != sizeof(server_ecdh_params)) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (sm2_sign_init(&sign_ctx, &sign_key->u.sm2_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1 + || sm2_sign_update(&sign_ctx, conn->client_random, 32) != 1 + || sm2_sign_update(&sign_ctx, conn->server_random, 32) != 1 + || sm2_sign_update(&sign_ctx, server_ecdh_params, server_ecdh_params_len) != 1 + || sm2_sign_finish(&sign_ctx, sigbuf, &siglen) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (tlcp_record_set_handshake_server_key_exchange(conn->record, &conn->recordlen, + TLS_server_key_exchange_ecdhe, server_ecdh_params, server_ecdh_params_len, + sigbuf, siglen) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + } else if (tlcp_cipher_suite_is_ecc(conn->cipher_suite)) { + if (sm2_sign_init(&sign_ctx, &sign_key->u.sm2_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1 + || sm2_sign_update(&sign_ctx, conn->client_random, 32) != 1 + || sm2_sign_update(&sign_ctx, conn->server_random, 32) != 1 + || sm2_sign_update(&sign_ctx, enc_cert_header, enc_cert_header_len) != 1 + || sm2_sign_update(&sign_ctx, enc_cert, enc_cert_len) != 1 + || sm2_sign_finish(&sign_ctx, sigbuf, &siglen) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (tlcp_record_set_handshake_server_key_exchange(conn->record, &conn->recordlen, + TLS_server_key_exchange_ecc, NULL, 0, sigbuf, siglen) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + } else { error_print(); tls_send_alert(conn, TLS_alert_internal_error); return -1; @@ -1773,7 +2035,6 @@ int tlcp_recv_client_key_exchange(TLS_CONNECT *conn) return -1; } - // decrypt enced_pre_master_secret if (!conn->cert_chain_idx) { error_print(); tls_send_alert(conn, TLS_alert_internal_error); @@ -1785,25 +2046,82 @@ int tlcp_recv_client_key_exchange(TLS_CONNECT *conn) tls_send_alert(conn, TLS_alert_internal_error); return -1; } - if (sm2_decrypt(&enc_key->u.sm2_key, enced_pms, enced_pms_len, - pre_master_secret, &pre_master_secret_len) != 1) { - error_print(); - tls_send_alert(conn, TLS_alert_decrypt_error); - return -1; - } - if (pre_master_secret_len != 48) { - gmssl_secure_clear(pre_master_secret, pre_master_secret_len); - error_print(); - tls_send_alert(conn, TLS_alert_illegal_parameter); - return -1; - } - memcpy(conn->pre_master_secret, pre_master_secret, pre_master_secret_len); - conn->pre_master_secret_len = pre_master_secret_len; - gmssl_secure_clear(pre_master_secret, pre_master_secret_len); - if (tlcp_check_pre_master_secret(conn) != 1) { + if (tlcp_cipher_suite_is_ecdhe(conn->cipher_suite)) { + int key_exchange_group; + const uint8_t *client_key_exchange; + size_t client_key_exchange_len; + const uint8_t *params = enced_pms; + size_t params_len = enced_pms_len; + const uint8_t *enc_cert; + size_t enc_cert_len; + X509_KEY peer_public_key; + + if (tls_server_ecdh_params_from_bytes(&key_exchange_group, + &client_key_exchange, &client_key_exchange_len, ¶ms, ¶ms_len) != 1 + || tls_length_is_zero(params_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_decode_error); + return -1; + } + if (key_exchange_group != TLS_curve_sm2p256v1 + || client_key_exchange_len != sizeof(conn->peer_key_exchange)) { + error_print(); + tls_send_alert(conn, TLS_alert_illegal_parameter); + return -1; + } + if (x509_certs_get_cert_by_index(conn->client_certs, conn->client_certs_len, 1, + &enc_cert, &enc_cert_len) != 1 + || x509_cert_get_subject_public_key(enc_cert, enc_cert_len, &peer_public_key) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_bad_certificate); + return -1; + } + if (peer_public_key.algor != OID_ec_public_key || peer_public_key.algor_param != OID_sm2 + || conn->key_exchanges[0].algor != OID_ec_public_key + || conn->key_exchanges[0].algor_param != OID_sm2) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + if (sm2_key_exchange(1, &enc_key->u.sm2_key, + SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH, + &peer_public_key.u.sm2_key, + SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH, + &conn->key_exchanges[0].u.sm2_key, + client_key_exchange, NULL, 48, conn->pre_master_secret) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_decrypt_error); + return -1; + } + memcpy(conn->peer_key_exchange, client_key_exchange, client_key_exchange_len); + conn->peer_key_exchange_len = client_key_exchange_len; + conn->pre_master_secret_len = 48; + } else if (tlcp_cipher_suite_is_ecc(conn->cipher_suite)) { + if (sm2_decrypt(&enc_key->u.sm2_key, enced_pms, enced_pms_len, + pre_master_secret, &pre_master_secret_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_decrypt_error); + return -1; + } + if (pre_master_secret_len != 48) { + gmssl_secure_clear(pre_master_secret, pre_master_secret_len); + error_print(); + tls_send_alert(conn, TLS_alert_illegal_parameter); + return -1; + } + memcpy(conn->pre_master_secret, pre_master_secret, pre_master_secret_len); + conn->pre_master_secret_len = pre_master_secret_len; + gmssl_secure_clear(pre_master_secret, pre_master_secret_len); + + if (tlcp_check_pre_master_secret(conn) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_illegal_parameter); + return -1; + } + } else { error_print(); - tls_send_alert(conn, TLS_alert_illegal_parameter); + tls_send_alert(conn, TLS_alert_internal_error); return -1; } if (tls_derive_master_secret(conn) != 1 @@ -1877,6 +2195,7 @@ int tlcp_send(TLS_CONNECT *conn, const uint8_t *in, size_t inlen, size_t *sentle switch (conn->cipher_suite) { case TLS_cipher_ecc_sm4_cbc_sm3: + case TLS_cipher_ecdhe_sm4_cbc_sm3: if (tls_cbc_encrypt(hmac_ctx, enc_key, seq_num, conn->databuf, conn->databuf + 5, tls_record_data_length(conn->databuf), conn->record + 5, &recordlen) != 1) { @@ -1886,6 +2205,7 @@ int tlcp_send(TLS_CONNECT *conn, const uint8_t *in, size_t inlen, size_t *sentle break; case TLS_cipher_ecc_sm4_gcm_sm3: + case TLS_cipher_ecdhe_sm4_gcm_sm3: if (tls_gcm_encrypt(enc_key, iv, seq_num, conn->databuf, conn->databuf + 5, tls_record_data_length(conn->databuf), conn->record + 5, &recordlen) != 1) { @@ -1977,6 +2297,11 @@ int tlcp_do_client_handshake(TLS_CONNECT *conn) case TLS_state_certificate_request: ret = tlcp_recv_certificate_request(conn); + if (ret == 0 && tlcp_cipher_suite_is_ecdhe(conn->cipher_suite)) { + error_print(); + tls_send_alert(conn, TLS_alert_handshake_failure); + return -1; + } if (ret == 1) conn->client_certificate_verify = 1; next_state = TLS_state_server_hello_done; break; diff --git a/src/tls.c b/src/tls.c index 4360353f..d5c5e371 100644 --- a/src/tls.c +++ b/src/tls.c @@ -3077,6 +3077,29 @@ static int tls_ctx_get_certificate_chain(const TLS_CTX *ctx, size_t idx, return 1; } +static int tls_cipher_suite_is_tlcp_ecdhe(int cipher_suite) +{ + switch (cipher_suite) { + case TLS_cipher_ecdhe_sm4_cbc_sm3: + case TLS_cipher_ecdhe_sm4_gcm_sm3: + return 1; + default: + return 0; + } +} + +static int tls_ctx_has_tlcp_ecdhe_cipher_suite(const TLS_CTX *ctx) +{ + size_t i; + + for (i = 0; i < ctx->cipher_suites_cnt; i++) { + if (tls_cipher_suite_is_tlcp_ecdhe(ctx->cipher_suites[i])) { + return 1; + } + } + return 0; +} + static int tls_ctx_check(const TLS_CTX *ctx) { const int *supported_cipher_suites = NULL; @@ -3088,6 +3111,7 @@ static int tls_ctx_check(const TLS_CTX *ctx) const uint8_t *cert_chains; size_t cert_chains_len; size_t cert_chains_cnt = 0; + int tlcp_client_needs_double_certs = 0; size_t i; if (!ctx) { @@ -3137,6 +3161,9 @@ static int tls_ctx_check(const TLS_CTX *ctx) return -1; } } + if (ctx->protocol == TLS_protocol_tlcp && ctx->is_client) { + tlcp_client_needs_double_certs = tls_ctx_has_tlcp_ecdhe_cipher_suite(ctx); + } if (ctx->supported_groups_cnt > sizeof(ctx->supported_groups)/sizeof(ctx->supported_groups[0])) { error_print(); @@ -3249,6 +3276,7 @@ static int tls_ctx_check(const TLS_CTX *ctx) const uint8_t *cert_chain; size_t cert_chain_len; size_t certs_cnt; + size_t key_idx = cert_chains_cnt; if (tls_uint24array_from_bytes(&cert_chain, &cert_chain_len, &cert_chains, &cert_chains_len) != 1 @@ -3264,6 +3292,14 @@ static int tls_ctx_check(const TLS_CTX *ctx) error_print(); return -1; } + if (tlcp_client_needs_double_certs) { + if (certs_cnt < 2 + || ctx->enc_keys[key_idx].algor != OID_ec_public_key + || ctx->enc_keys[key_idx].algor_param != OID_sm2) { + error_print(); + return -1; + } + } cert_chains_cnt++; } @@ -3272,6 +3308,11 @@ static int tls_ctx_check(const TLS_CTX *ctx) error_print(); return -1; } + } else if (tlcp_client_needs_double_certs) { + if (!ctx->cert_chains_len || !ctx->x509_keys_cnt || cert_chains_cnt != ctx->x509_keys_cnt) { + error_print(); + return -1; + } } else if (ctx->cert_chains_len) { if (!ctx->x509_keys_cnt || cert_chains_cnt != ctx->x509_keys_cnt) { error_print(); diff --git a/src/tls12.c b/src/tls12.c index fbff8359..ccd6675a 100644 --- a/src/tls12.c +++ b/src/tls12.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1676,9 +1677,91 @@ int tls_recv_server_hello_done(TLS_CONNECT *conn) return 1; } +static int tlcp_cert_is_encryption_cert(const uint8_t *cert, size_t certlen) +{ + int ret; + int critical; + const uint8_t *exts; + size_t extslen; + const uint8_t *val; + size_t vlen; + int bits; + + if (!cert || !certlen) { + error_print(); + return -1; + } + if ((ret = x509_cert_get_exts(cert, certlen, &exts, &extslen)) != 1) { + if (ret) error_print(); + return ret; + } + if ((ret = x509_exts_get_ext_by_oid(exts, extslen, OID_ce_key_usage, + &critical, &val, &vlen)) != 1) { + if (ret) error_print(); + return ret; + } + if (x509_key_usage_from_der(&bits, &val, &vlen) != 1 + || asn1_length_is_zero(vlen) != 1) { + error_print(); + return -1; + } + return (bits & X509_KU_KEY_ENCIPHERMENT) ? 1 : 0; +} + +static int tlcp_client_certs_without_encryption_cert(const uint8_t *certs, size_t certslen, + uint8_t *out, size_t *outlen, size_t maxlen) +{ + const uint8_t *p = certs; + size_t len = certslen; + size_t cert_idx = 0; + + if (!certs || !certslen || !out || !outlen) { + error_print(); + return -1; + } + + *outlen = 0; + while (len) { + const uint8_t *cert_der = p; + const uint8_t *cert; + size_t certlen; + size_t derlen; + int skip = 0; + + if (x509_cert_from_der(&cert, &certlen, &p, &len) != 1) { + error_print(); + return -1; + } + derlen = (size_t)(p - cert_der); + if (cert_idx == 1) { + int ret = tlcp_cert_is_encryption_cert(cert, certlen); + if (ret < 0) { + error_print(); + return -1; + } + skip = ret; + } + if (!skip) { + if (*outlen > maxlen || derlen > maxlen - *outlen) { + error_print(); + return -1; + } + memcpy(out + *outlen, cert_der, derlen); + *outlen += derlen; + } + cert_idx++; + } + return 1; +} + int tls_send_client_certificate(TLS_CONNECT *conn) { int ret; + const uint8_t *client_certs; + size_t client_certs_len; + uint8_t client_certs_without_enc[TLS_MAX_CERTIFICATES_SIZE]; + size_t client_certs_without_enc_len; + if(conn->verbose) tls_trace("send client Certificate\n"); if (conn->client_certs_len == 0) { @@ -1687,8 +1770,23 @@ int tls_send_client_certificate(TLS_CONNECT *conn) } if (conn->recordlen == 0) { + client_certs = conn->client_certs; + client_certs_len = conn->client_certs_len; + if (conn->protocol == TLS_protocol_tlcp + && (conn->cipher_suite == TLS_cipher_ecc_sm4_cbc_sm3 + || conn->cipher_suite == TLS_cipher_ecc_sm4_gcm_sm3)) { + if (tlcp_client_certs_without_encryption_cert(conn->client_certs, conn->client_certs_len, + client_certs_without_enc, &client_certs_without_enc_len, + sizeof(client_certs_without_enc)) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_internal_error); + return -1; + } + client_certs = client_certs_without_enc; + client_certs_len = client_certs_without_enc_len; + } if (tls_record_set_handshake_certificate(conn->record, &conn->recordlen, - conn->client_certs, conn->client_certs_len) != 1) { + client_certs, client_certs_len) != 1) { error_print(); tls_send_alert(conn, TLS_alert_internal_error); return -1; @@ -2747,6 +2845,30 @@ int tls_recv_client_certificate(TLS_CONNECT *conn) } conn->verify_result = verify_result; + if (conn->protocol == TLS_protocol_tlcp + && (conn->cipher_suite == TLS_cipher_ecdhe_sm4_cbc_sm3 + || conn->cipher_suite == TLS_cipher_ecdhe_sm4_gcm_sm3)) { + const uint8_t *enc_cert; + size_t enc_cert_len; + + if (x509_certs_get_cert_by_index(conn->client_certs, conn->client_certs_len, + 1, &enc_cert, &enc_cert_len) != 1) { + error_print(); + tls_send_alert(conn, TLS_alert_bad_certificate); + return -1; + } + ret = tlcp_cert_is_encryption_cert(enc_cert, enc_cert_len); + if (ret < 0) { + error_print(); + tls_send_alert(conn, TLS_alert_bad_certificate); + return -1; + } else if (ret == 0) { + error_print(); + tls_send_alert(conn, TLS_alert_unsupported_certificate); + return -1; + } + } + if (digest_update(&conn->dgst_ctx, conn->record + 5, conn->recordlen - 5) != 1) { error_print(); diff --git a/src/tls_cert.c b/src/tls_cert.c index 8eb9d3b9..47735744 100644 --- a/src/tls_cert.c +++ b/src/tls_cert.c @@ -287,7 +287,7 @@ int tls_ctx_set_certificate_and_key(TLS_CTX *ctx, const char *chainfile, return 1; } -int tlcp_ctx_add_server_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, +static int tlcp_ctx_add_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, const char *keyfile, const char *keypass) { int ret = -1; @@ -314,7 +314,7 @@ int tlcp_ctx_add_server_certificate_and_keys(TLS_CTX *ctx, const char *chainfile error_print(); return -1; } - if (ctx->protocol != TLS_protocol_tlcp || ctx->is_client) { + if (ctx->protocol != TLS_protocol_tlcp) { error_print(); return -1; } @@ -397,6 +397,34 @@ end: return ret; } +int tlcp_ctx_add_server_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, + const char *keyfile, const char *keypass) +{ + if (!ctx || ctx->is_client) { + error_print(); + return -1; + } + if (tlcp_ctx_add_certificate_and_keys(ctx, chainfile, keyfile, keypass) != 1) { + error_print(); + return -1; + } + return 1; +} + +int tlcp_ctx_add_client_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, + const char *keyfile, const char *keypass) +{ + if (!ctx || !ctx->is_client) { + error_print(); + return -1; + } + if (tlcp_ctx_add_certificate_and_keys(ctx, chainfile, keyfile, keypass) != 1) { + error_print(); + return -1; + } + return 1; +} + int tls_ctx_set_tlcp_server_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, const char *keyfile, const char *keypass) { @@ -412,6 +440,21 @@ int tls_ctx_set_tlcp_server_certificate_and_keys(TLS_CTX *ctx, const char *chain return 1; } +int tls_ctx_set_tlcp_client_certificate_and_keys(TLS_CTX *ctx, const char *chainfile, + const char *keyfile, const char *keypass) +{ + if (!ctx || ctx->cert_chains_len || ctx->x509_keys_cnt) { + error_print(); + return -1; + } + if (tlcp_ctx_add_client_certificate_and_keys(ctx, chainfile, + keyfile, keypass) != 1) { + error_print(); + return -1; + } + return 1; +} + int tls_authorities_issued_certificate(const uint8_t *ca_names, size_t ca_names_len, const uint8_t *certs, size_t certslen) { const uint8_t *cert; @@ -584,4 +627,3 @@ int tls12_cert_chains_select(const uint8_t *cert_chains, size_t cert_chains_len, return 0; } - diff --git a/src/tls_vrf.c b/src/tls_vrf.c index 05eca140..9289b2ee 100644 --- a/src/tls_vrf.c +++ b/src/tls_vrf.c @@ -101,6 +101,17 @@ static int tls12_signature_scheme_from_cipher_suite(int cipher_suite) } } +static int tls_cipher_suite_is_tlcp_ecdhe(int cipher_suite) +{ + switch (cipher_suite) { + case TLS_cipher_ecdhe_sm4_cbc_sm3: + case TLS_cipher_ecdhe_sm4_gcm_sm3: + return 1; + default: + return 0; + } +} + static int tls_cert_chain_check_name(const uint8_t *cert_chain, size_t cert_chain_len, const uint8_t *host_name, size_t host_name_len, int *verify_result) { @@ -398,7 +409,10 @@ int tls_cert_chain_verify( } if (verify_chain) { - if (protocol == TLS_protocol_tlcp && cert_chain_type == X509_cert_chain_server) { + if (protocol == TLS_protocol_tlcp + && (cert_chain_type == X509_cert_chain_server + || (cert_chain_type == X509_cert_chain_client + && tls_cipher_suite_is_tlcp_ecdhe(cipher_suite)))) { ret = x509_certs_verify_tlcp(cert_chain, cert_chain_len, cert_chain_type, cacerts, cacerts_len, crl, crl_len, ocsp, ocsp_len, verify_depth, verify_result); diff --git a/src/x509_cer.c b/src/x509_cer.c index 5790ea7c..c7f3f3cd 100644 --- a/src/x509_cer.c +++ b/src/x509_cer.c @@ -2116,7 +2116,7 @@ int x509_certs_verify(const uint8_t *certs, size_t certslen, int certs_type, return 1; } -// 只有 TLCP 的服务器证书链才是双证书,客户端证书和TLS12是一样的 +// TLCP ECC server and TLCP ECDHE client certificate chains use dual entity certificates. int x509_certs_verify_tlcp(const uint8_t *certs, size_t certslen, int certs_type, const uint8_t *rootcerts, size_t rootcertslen, const uint8_t *crl, size_t crl_len, @@ -2144,6 +2144,10 @@ int x509_certs_verify_tlcp(const uint8_t *certs, size_t certslen, int certs_type sign_cert_type = X509_cert_server_auth; kenc_cert_type = X509_cert_server_key_encipher; break; + case X509_cert_chain_client: + sign_cert_type = X509_cert_client_auth; + kenc_cert_type = X509_cert_client_key_encipher; + break; default: error_print(); x509_verify_set_result(verify_result, X509_verify_err_certificate); diff --git a/tests/sm2_exchtest.c b/tests/sm2_exchtest.c new file mode 100644 index 00000000..f2b880f1 --- /dev/null +++ b/tests/sm2_exchtest.c @@ -0,0 +1,90 @@ +/* + * 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 + + +static int test_sm2_key_exchange(void) +{ + SM2_KEY a; + SM2_KEY b; + SM2_KEY ra; + SM2_KEY rb; + uint8_t ra_octets[65]; + uint8_t rb_octets[65]; + uint8_t ua[65]; + uint8_t vb[65]; + uint8_t ska[48]; + uint8_t skb[48]; + uint8_t sa[32]; + uint8_t sb[32]; + const char ida[] = "Alice"; + const char idb[] = "Bob"; + + if (sm2_key_generate(&a) != 1 + || sm2_key_generate(&b) != 1 + || sm2_key_generate(&ra) != 1 + || sm2_key_generate(&rb) != 1 + || sm2_z256_point_to_uncompressed_octets(&ra.public_key, ra_octets) != 1 + || sm2_z256_point_to_uncompressed_octets(&rb.public_key, rb_octets) != 1) { + error_print(); + return -1; + } + + if (sm2_key_exchange(1, &a, ida, sizeof(ida) - 1, &b, idb, sizeof(idb) - 1, + &ra, rb_octets, ua, sizeof(ska), ska) != 1 + || sm2_key_exchange(0, &b, idb, sizeof(idb) - 1, &a, ida, sizeof(ida) - 1, + &rb, ra_octets, vb, sizeof(skb), skb) != 1) { + error_print(); + return -1; + } + if (memcmp(ska, skb, sizeof(ska)) != 0 || memcmp(ua, vb, sizeof(ua)) != 0) { + error_print(); + return -1; + } + + if (sm2_key_exchange_compute_confirm(1, &a, ida, sizeof(ida) - 1, &b, idb, sizeof(idb) - 1, + &ra, rb_octets, ua, sa) != 1 + || sm2_key_exchange_compute_confirm(0, &b, idb, sizeof(idb) - 1, &a, ida, sizeof(ida) - 1, + &rb, ra_octets, vb, sb) != 1) { + error_print(); + return -1; + } + if (sm2_key_exchange_verify_confirm(1, &a, ida, sizeof(ida) - 1, &b, idb, sizeof(idb) - 1, + &ra, rb_octets, ua, sb) != 1 + || sm2_key_exchange_verify_confirm(0, &b, idb, sizeof(idb) - 1, &a, ida, sizeof(ida) - 1, + &rb, ra_octets, vb, sa) != 1) { + error_print(); + return -1; + } + + sb[0] ^= 0x01; + if (sm2_key_exchange_verify_confirm(1, &a, ida, sizeof(ida) - 1, &b, idb, sizeof(idb) - 1, + &ra, rb_octets, ua, sb) != 0) { + error_print(); + return -1; + } + + printf("%s() ok\n", __FUNCTION__); + return 1; +} + +int main(void) +{ + if (test_sm2_key_exchange() != 1) goto err; + printf("%s all tests passed\n", __FILE__); + return 0; +err: + error_print(); + return -1; +} diff --git a/tools/tlcp_client.c b/tools/tlcp_client.c index 2c638cdf..7486305d 100644 --- a/tools/tlcp_client.c +++ b/tools/tlcp_client.c @@ -37,8 +37,8 @@ static const char *help = " -sig_alg str Supported signature algorithms\n" " -cacert pem Trusted CA certificate(s) in PEM format\n" " -verify_depth num Certificate verification depth\n" -" -cert pem Client certificate(s) in PEM format\n" -" -key pem Private key of client certificate in PEM format\n" +" -cert pem Client certificate(s) in PEM format, TLCP ECDHE requires a double certificate chain\n" +" -key pem Private key of client certificate in PEM format, TLCP ECDHE requires signing and encryption keys\n" " -pass password Password of encrypted private key\n" " -client_cert_optional Allow client send empty Certificate\n" " -get path Send a GET request with given path of URI\n" @@ -53,6 +53,29 @@ static const char *help = #include "tlcp_help.h" "\n"; +static int tlcp_cipher_suite_is_ecdhe(int cipher_suite) +{ + switch (cipher_suite) { + case TLS_cipher_ecdhe_sm4_cbc_sm3: + case TLS_cipher_ecdhe_sm4_gcm_sm3: + return 1; + default: + return 0; + } +} + +static int tlcp_cipher_suites_have_ecdhe(const int *cipher_suites, size_t cipher_suites_cnt) +{ + size_t i; + + for (i = 0; i < cipher_suites_cnt; i++) { + if (tlcp_cipher_suite_is_ecdhe(cipher_suites[i])) { + return 1; + } + } + return 0; +} + static int do_handshake_select(TLS_CONNECT *conn) { @@ -228,6 +251,7 @@ int tlcp_client_main(int argc, char *argv[]) char *infile = NULL; char *certoutfile = NULL; int verbose = 0; + int has_ecdhe_cipher_suite = 0; struct sockaddr_in server; tls_socket_t sock = tls_socket_invalid(); TLS_CTX ctx; @@ -379,6 +403,11 @@ bad: fprintf(stderr, "%s: '-get' and '-in' should not be used together\n", prog); return -1; } + has_ecdhe_cipher_suite = tlcp_cipher_suites_have_ecdhe(cipher_suites, cipher_suites_cnt); + if (has_ecdhe_cipher_suite && (!certfile || !keyfile || !pass)) { + fprintf(stderr, "%s: TLCP ECDHE cipher suites require '-cert', '-key' and '-pass' with a double certificate chain\n", prog); + return -1; + } if (tls_socket_lib_init() != 1) { error_print(); @@ -447,9 +476,16 @@ bad: fprintf(stderr, "%s: option '-pass' missing\n", prog); goto end; } - if (tls_ctx_set_certificate_and_key(&ctx, certfile, keyfile, pass) != 1) { - fprintf(stderr, "%s: failed to load client certificate\n", prog); - goto end; + if (has_ecdhe_cipher_suite) { + if (tls_ctx_set_tlcp_client_certificate_and_keys(&ctx, certfile, keyfile, pass) != 1) { + fprintf(stderr, "%s: failed to load TLCP client double certificate chain and keys\n", prog); + goto end; + } + } else { + if (tls_ctx_set_certificate_and_key(&ctx, certfile, keyfile, pass) != 1) { + fprintf(stderr, "%s: failed to load client certificate\n", prog); + goto end; + } } } diff --git a/tools/tlcp_help.h b/tools/tlcp_help.h index 1d458f5c..17ffed6f 100644 --- a/tools/tlcp_help.h +++ b/tools/tlcp_help.h @@ -11,6 +11,8 @@ "Supported cipher suites:\n" " TLS_ECC_SM4_CBC_SM3\n" " TLS_ECC_SM4_GCM_SM3\n" +" TLS_ECDHE_SM4_CBC_SM3\n" +" TLS_ECDHE_SM4_GCM_SM3\n" "\n" "\n" "Examples\n"