From 3322a5fc7bc6f86f27e1f3012f7e26d3bdf4a61b Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Fri, 27 Feb 2026 11:02:22 +0800 Subject: [PATCH] Update TLS 1.3 --- include/gmssl/tls.h | 13 ++++++---- src/tls.c | 57 ++++++++++++++++++++++++++++++++++++++++++-- src/tls12.c | 12 +++++----- src/tls13.c | 31 ++++++++++++------------ src/tls_ext.c | 5 ++-- src/tls_trace.c | 15 ++++++++++-- tools/tls13_client.c | 37 +++++++++++++++++++++++++++- tools/tls13_server.c | 36 +++++++++++++++++++++++++++- 8 files changed, 171 insertions(+), 35 deletions(-) diff --git a/include/gmssl/tls.h b/include/gmssl/tls.h index 2802b589..2c6345eb 100644 --- a/include/gmssl/tls.h +++ b/include/gmssl/tls.h @@ -105,7 +105,7 @@ const char *tls_cipher_suite_name(int cipher); int tls_cipher_suites_select(const uint8_t *client_ciphers, size_t client_ciphers_len, const int *server_ciphers, size_t server_ciphers_cnt, int *selected_cipher); int tls_cipher_suite_in_list(int cipher, const int *list, size_t list_count); - +int tls_cipher_suite_support_protocol(int cipher, int protocol); typedef enum { TLS_compression_null = 0, @@ -510,7 +510,7 @@ int tls13_key_share_entry_to_bytes(const SM2_Z256_POINT *point, uint8_t **out, s int tls13_client_key_share_ext_to_bytes(const SM2_Z256_POINT *point, uint8_t **out, size_t *outlen); int tls13_server_key_share_ext_to_bytes(const SM2_Z256_POINT *point, uint8_t **out, size_t *outlen); int tls13_process_client_key_share(const uint8_t *ext_data, size_t ext_datalen, - const SM2_KEY *server_ecdhe_key, SM2_Z256_POINT *client_ecdhe_public, + const SM2_KEY *server_ecdhe_key, SM2_KEY *client_ecdhe_public, uint8_t **out, size_t *outlen); int tls13_process_server_key_share(const uint8_t *ext_data, size_t ext_datalen, SM2_Z256_POINT *point); @@ -762,10 +762,8 @@ enum { typedef struct { int is_client; - int protocol; - /* 服务器端在初始化之后,会创建一个server_ciphers列表 在接收到client_ciphers之后,和自己的server_ciphers对比,选择出conn->cipher @@ -776,10 +774,14 @@ typedef struct { 在接收到服务器的cipher后,要判断这个cipher是否在自己的client_ciphers之中 但是客户端是需要缓存ciphers的,这样才能够判断返回的cipher是否在自己的ciphers之中 + + + + + 下面的问题是在CONN中要维护哪些信息? */ - int cipher_suites[TLS_MAX_CIPHER_SUITES_COUNT]; size_t cipher_suites_cnt; int cipher_suite; @@ -911,6 +913,7 @@ int tlcp_recv_client_key_exchange(TLS_CONNECT *conn); void tls_clean_record(TLS_CONNECT *conn); +int tls_print_record(FILE *fp, int fmt, int ind, const char *label, TLS_CONNECT *conn); int tls_init(TLS_CONNECT *conn, const TLS_CTX *ctx); int tls_set_socket(TLS_CONNECT *conn, tls_socket_t sock); diff --git a/src/tls.c b/src/tls.c index 09a776b1..f4ae2ce4 100644 --- a/src/tls.c +++ b/src/tls.c @@ -1455,6 +1455,56 @@ int tls_cipher_suite_in_list(int cipher, const int *list, size_t list_count) return 0; } +static const int tlcp_ciphers[] = { + TLS_cipher_ecc_sm4_cbc_sm3, + TLS_cipher_ecc_sm4_gcm_sm3, + TLS_cipher_ibc_sm4_cbc_sm3, + TLS_cipher_ibc_sm4_gcm_sm3, +}; + +static const int tls12_ciphers[] = { + TLS_cipher_ecdhe_sm4_cbc_sm3, + TLS_cipher_ecdhe_sm4_gcm_sm3, + TLS_cipher_ecdhe_ecdsa_with_aes_128_cbc_sha256, +}; + +static const int tls13_ciphers[] = { + TLS_cipher_sm4_gcm_sm3, +}; + +int tls_cipher_suite_support_protocol(int cipher, int protocol) +{ + const int *ciphers; + size_t ciphers_cnt; + + + switch (protocol) { + case TLS_protocol_tlcp: + ciphers = tlcp_ciphers; + ciphers_cnt = sizeof(tlcp_ciphers)/sizeof(tlcp_ciphers[0]); + break; + case TLS_protocol_tls12: + ciphers = tls12_ciphers; + ciphers_cnt = sizeof(tls12_ciphers)/sizeof(tls12_ciphers[0]); + break; + case TLS_protocol_tls13: + ciphers = tls13_ciphers; + ciphers_cnt = sizeof(tls13_ciphers)/sizeof(tls13_ciphers[0]); + break; + default: + error_print(); + return -1; + } + + if (!tls_cipher_suite_in_list(cipher, ciphers, ciphers_cnt)) { + error_print(); + return 0; + } + return 1; +} + + + /* 尽可能的发送数据,直到发送完整的报文,或者send 返回错误 如果send 返回EAGAIN,那么向上层返回WANT_WRITE @@ -2096,8 +2146,6 @@ int tls_ctx_init(TLS_CTX *ctx, int protocol, int is_client) return 1; } - -// FIXME: 根据protocol,核对输入的ciphers是否满足protocol的条件 int tls_ctx_set_cipher_suites(TLS_CTX *ctx, const int *cipher_suites, size_t cipher_suites_cnt) { size_t i; @@ -2110,6 +2158,7 @@ int tls_ctx_set_cipher_suites(TLS_CTX *ctx, const int *cipher_suites, size_t cip error_print(); return -1; } + for (i = 0; i < cipher_suites_cnt; i++) { if (!tls_cipher_suite_name(cipher_suites[i])) { error_print(); @@ -2117,6 +2166,10 @@ int tls_ctx_set_cipher_suites(TLS_CTX *ctx, const int *cipher_suites, size_t cip } } for (i = 0; i < cipher_suites_cnt; i++) { + if (!tls_cipher_suite_support_protocol(cipher_suites[i], ctx->protocol)) { + error_print(); + return -1; + } ctx->cipher_suites[i] = cipher_suites[i]; } ctx->cipher_suites_cnt = cipher_suites_cnt; diff --git a/src/tls12.c b/src/tls12.c index cac299c7..cb957900 100644 --- a/src/tls12.c +++ b/src/tls12.c @@ -27,11 +27,6 @@ #include #include -static const int tls12_ciphers[] = { - TLS_cipher_ecdhe_sm4_cbc_sm3, - TLS_cipher_ecdhe_sm4_gcm_sm3, - TLS_cipher_ecdhe_ecdsa_with_aes_128_cbc_sha256, -}; // 现在client_certificate_verify做的是不好的 @@ -60,13 +55,18 @@ static const int tls12_ciphers[] = { */ // 实际上这个功能本质上是把缓冲区的数据发出去 +static const int tls12_ciphers[] = { + TLS_cipher_ecdhe_sm4_cbc_sm3, + TLS_cipher_ecdhe_sm4_gcm_sm3, + TLS_cipher_ecdhe_ecdsa_with_aes_128_cbc_sha256, +}; int tls12_record_print(FILE *fp, const uint8_t *record, size_t recordlen, int format, int indent) { // 目前只支持TLCP的ECC公钥加密套件,因此不论用哪个套件解析都是一样的 // 如果未来支持ECDHE套件,可以将函数改为宏,直接传入 (conn->cipher_suite << 8) - format |= tls12_ciphers[0] << 8; + format |= tls12_ciphers[0] << 8; // 应该是KeyExchange需要这个参数 return tls_record_print(fp, record, recordlen, format, indent); } diff --git a/src/tls13.c b/src/tls13.c index c6b0a472..bf5a3d5e 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -640,7 +640,7 @@ int tls13_client_hello_exts_set(uint8_t *exts, size_t *extslen, size_t maxlen, } int tls13_process_client_hello_exts(const uint8_t *exts, size_t extslen, - const SM2_KEY *server_ecdhe_key, SM2_Z256_POINT *client_ecdhe_public, + const SM2_KEY *server_ecdhe_key, SM2_KEY *client_ecdhe_public, uint8_t *server_exts, size_t *server_exts_len, size_t server_exts_maxlen) { size_t len = 0; @@ -735,7 +735,7 @@ int tls_client_key_shares_from_bytes(SM2_Z256_POINT *sm2_point, const uint8_t ** } // FIXME: should be a process function -int tls13_server_hello_extensions_get(const uint8_t *exts, size_t extslen, SM2_Z256_POINT *sm2_point) +int tls13_server_hello_extensions_get(const uint8_t *exts, size_t extslen, SM2_KEY *sm2_key) { uint16_t version; while (extslen) { @@ -759,7 +759,8 @@ int tls13_server_hello_extensions_get(const uint8_t *exts, size_t extslen, SM2_Z } break; case TLS_extension_key_share: - if (tls13_process_server_key_share(ext_data, ext_datalen, sm2_point) != 1) { + memset(sm2_key, 0, sizeof(SM2_KEY)); + if (tls13_process_server_key_share(ext_data, ext_datalen, &sm2_key->public_key) != 1) { error_print(); return -1; } @@ -1502,7 +1503,7 @@ int tls13_do_connect(TLS_CONNECT *conn) size_t server_verify_data_len; SM2_KEY client_ecdhe; - SM2_Z256_POINT server_ecdhe_public; + SM2_KEY server_ecdhe_public; X509_KEY server_sign_key; const DIGEST *digest = DIGEST_sm3(); @@ -1589,7 +1590,7 @@ int tls13_do_connect(TLS_CONNECT *conn) goto end; } conn->cipher_suite = cipher_suite; - if (tls13_server_hello_extensions_get(server_exts, server_exts_len, &server_ecdhe_public) != 1) { + if (tls13_server_hello_extensions_get(server_exts, server_exts_len, &server_ecdhe_public) != 1) { error_print(); tls_send_alert(conn, TLS_alert_handshake_failure); goto end; @@ -1609,12 +1610,11 @@ int tls13_do_connect(TLS_CONNECT *conn) uint8_t client_write_iv[12] uint8_t server_write_iv[12] */ - sm2_do_ecdh(&client_ecdhe, &server_ecdhe_public, &server_ecdhe_public); - uint8_t share_point[64]; - sm2_z256_point_to_bytes(&server_ecdhe_public, share_point); + uint8_t share_point_x[32]; + sm2_do_ecdh(&client_ecdhe, &server_ecdhe_public, share_point_x); /* [1] */ tls13_hkdf_extract(digest, zeros, psk, early_secret); /* [5] */ tls13_derive_secret(early_secret, "derived", &null_dgst_ctx, handshake_secret); - /* [6] */ tls13_hkdf_extract(digest, handshake_secret, share_point, handshake_secret); + /* [6] */ tls13_hkdf_extract(digest, handshake_secret, share_point_x, handshake_secret); /* [7] */ tls13_derive_secret(handshake_secret, "c hs traffic", &dgst_ctx, client_handshake_traffic_secret); /* [8] */ tls13_derive_secret(handshake_secret, "s hs traffic", &dgst_ctx, server_handshake_traffic_secret); /* [9] */ tls13_derive_secret(handshake_secret, "derived", &null_dgst_ctx, master_secret); @@ -1864,7 +1864,7 @@ int tls13_do_connect(TLS_CONNECT *conn) // send {CertificateVerify*} tls_trace("send {CertificateVerify*}\n"); client_sign_algor = TLS_sig_sm2sig_sm3; // FIXME: 应该放在conn里面 - tls13_sign_certificate_verify(TLS_client_mode, &conn->sign_key, TLS13_SM2_ID, TLS13_SM2_ID_LENGTH, &dgst_ctx, sig, &siglen); + tls13_sign_certificate_verify(TLS_client_mode, &conn->sign_key.u.sm2_key, TLS13_SM2_ID, TLS13_SM2_ID_LENGTH, &dgst_ctx, sig, &siglen); if (tls13_record_set_handshake_certificate_verify(record, &recordlen, client_sign_algor, sig, siglen) != 1) { error_print(); @@ -1986,7 +1986,7 @@ int tls13_do_accept(TLS_CONNECT *conn) size_t server_exts_len; SM2_KEY server_ecdhe; - SM2_Z256_POINT client_ecdhe_public; + SM2_KEY client_ecdhe_public; X509_KEY client_sign_key; const BLOCK_CIPHER *cipher = NULL; const DIGEST *digest = NULL; @@ -2098,13 +2098,12 @@ int tls13_do_accept(TLS_CONNECT *conn) digest_update(&dgst_ctx, record + 5, recordlen - 5); - sm2_do_ecdh(&server_ecdhe, &client_ecdhe_public, &client_ecdhe_public); - uint8_t share_point[64];//FIXME: 应该重新考虑TLS中如何使用sm2_do_ecdh还是sm2_ecdh - sm2_z256_point_to_bytes(&client_ecdhe_public, share_point); + uint8_t share_point_x[32]; + sm2_do_ecdh(&server_ecdhe, &client_ecdhe_public, share_point_x); /* 1 */ tls13_hkdf_extract(digest, zeros, psk, early_secret); /* 5 */ tls13_derive_secret(early_secret, "derived", &null_dgst_ctx, handshake_secret); - /* 6 */ tls13_hkdf_extract(digest, handshake_secret, share_point, handshake_secret); + /* 6 */ tls13_hkdf_extract(digest, handshake_secret, share_point_x, handshake_secret); /* 7 */ tls13_derive_secret(handshake_secret, "c hs traffic", &dgst_ctx, client_handshake_traffic_secret); /* 8 */ tls13_derive_secret(handshake_secret, "s hs traffic", &dgst_ctx, server_handshake_traffic_secret); /* 9 */ tls13_derive_secret(handshake_secret, "derived", &null_dgst_ctx, master_secret); @@ -2204,7 +2203,7 @@ int tls13_do_accept(TLS_CONNECT *conn) // send Server {CertificateVerify} tls_trace("send {CertificateVerify}\n"); - tls13_sign_certificate_verify(TLS_server_mode, &conn->sign_key, TLS13_SM2_ID, TLS13_SM2_ID_LENGTH, &dgst_ctx, sig, &siglen); + tls13_sign_certificate_verify(TLS_server_mode, &conn->sign_key.u.sm2_key, TLS13_SM2_ID, TLS13_SM2_ID_LENGTH, &dgst_ctx, sig, &siglen); if (tls13_record_set_handshake_certificate_verify(record, &recordlen, TLS_sig_sm2sig_sm3, sig, siglen) != 1) { error_print(); diff --git a/src/tls_ext.c b/src/tls_ext.c index 784c5350..be744515 100644 --- a/src/tls_ext.c +++ b/src/tls_ext.c @@ -706,7 +706,7 @@ int tls13_client_key_share_ext_to_bytes(const SM2_Z256_POINT *point, uint8_t **o } int tls13_process_client_key_share(const uint8_t *ext_data, size_t ext_datalen, - const SM2_KEY *server_ecdhe_key, SM2_Z256_POINT *client_ecdhe_public, + const SM2_KEY *server_ecdhe_key, SM2_KEY *client_ecdhe_public, uint8_t **out, size_t *outlen) { const uint8_t *client_shares; @@ -743,7 +743,8 @@ int tls13_process_client_key_share(const uint8_t *ext_data, size_t ext_datalen, error_print(); return -1; } - if (sm2_z256_point_from_octets(client_ecdhe_public, key_exchange, key_exchange_len) != 1) { + memset(client_ecdhe_public, 0, sizeof(SM2_KEY)); + if (sm2_z256_point_from_octets(&client_ecdhe_public->public_key, key_exchange, key_exchange_len) != 1) { error_print(); return -1; } diff --git a/src/tls_trace.c b/src/tls_trace.c index 7d8e3ffa..6e06fa38 100644 --- a/src/tls_trace.c +++ b/src/tls_trace.c @@ -707,6 +707,12 @@ int tls_server_key_exchange_ecdhe_print(FILE *fp, const uint8_t *data, size_t da return 1; } +// +// 这个函数依赖输入的cipher_suite,才能判断如何解析ServerKeyExchange +// 显然这个信息无法通过基础的format提供了,并且这个底层的信息一直需要从最上层提供,这就非常不好了 +// 目前来看,cipher_suite是否能够提供足够的信息呢? +// ServerKeyExchange, ClientKeyExchange的格式是由cipher_suite决定的 + int tls_server_key_exchange_print(FILE *fp, const uint8_t *data, size_t datalen, int format, int indent) { int cipher_suite = (format >> 8) & 0xffff; @@ -1072,8 +1078,6 @@ int tls13_record_print(FILE *fp, int format, int indent, const uint8_t *record, // FIXME: 根据RFC来考虑这个函数的参数,从底向上逐步修改每个函数的接口参数 -// 仅从record数据是不能判断这个record是TLS 1.2还是TLS 1.3 -// 不同协议上,同名的握手消息,其格式也是不一样的。这真是太恶心了!!!! // 当消息为ClientKeyExchange,ServerKeyExchange,需要密码套件中的密钥交换算法信息 // 当消息为加密的Finished,记录类型为Handshake,但是记录负载数据中没有Handshake头 @@ -1081,6 +1085,13 @@ int tls13_record_print(FILE *fp, int format, int indent, const uint8_t *record, // // supported_versions 的格式由handshake_type 是否为ClientHello, ServerHello 决定 // record中是包含这个信息的,但是在exts中没有这个信息 + +int tls_print_record(FILE *fp, int fmt, int ind, const char *label, TLS_CONNECT *conn) +{ + tls_record_print(fp, conn->record, conn->recordlen, fmt, ind); + return 1; +} + int tls_record_print(FILE *fp, const uint8_t *record, size_t recordlen, int format, int indent) { const uint8_t *data; diff --git a/tools/tls13_client.c b/tools/tls13_client.c index ee94ed79..b7567ce4 100644 --- a/tools/tls13_client.c +++ b/tools/tls13_client.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 The GmSSL Project. All Rights Reserved. + * 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. @@ -25,6 +25,40 @@ static const char *http_get = static const char *options = "-host str [-port num] [-cacert file] [-cert file -key file -pass str]"; +static const char *help = +"Options\n" +"\n" +" -host str Server's hostname\n" +" -port num Server's port number, default 443\n" +" -cacert file Root CA certificate\n" +" -cert file Client's certificate chain in PEM format\n" +" -key file Client's encrypted private key in PEM format\n" +" -pass str Password to decrypt private key\n" +"\n" +"Examples\n" +"\n" +" gmssl sm2keygen -pass 1234 -out rootcakey.pem\n" +" gmssl certgen -C CN -ST Beijing -L Haidian -O PKU -OU CS -CN ROOTCA -days 3650 \\\n" +" -key rootcakey.pem -pass 1234 -out rootcacert.pem \\\n" +" -key_usage keyCertSign -key_usage cRLSign -ca\n" +"\n" +" gmssl sm2keygen -pass 1234 -out cakey.pem\n" +" gmssl reqgen -C CN -ST Beijing -L Haidian -O PKU -OU CS -CN \"Sub CA\" \\\n" +" -key cakey.pem -pass 1234 -out careq.pem\n" +" gmssl reqsign -in careq.pem -days 365 -key_usage keyCertSign -cacert rootcacert.pem -key rootcakey.pem -pass 1234 \\\n" +" -out cacert.pem -ca -path_len_constraint 0\n" +"\n" +" gmssl sm2keygen -pass 1234 -out signkey.pem\n" +" gmssl reqgen -C CN -ST Beijing -L Haidian -O PKU -OU CS -CN localhost -key signkey.pem -pass 1234 -out signreq.pem\n" +" gmssl reqsign -in signreq.pem -days 365 -key_usage digitalSignature -cacert cacert.pem -key cakey.pem -pass 1234 -out signcert.pem\n" +"\n" +" cat signcert.pem > certs.pem\n" +" cat cacert.pem >> certs.pem\n" +"\n" +" sudo gmssl tls13_server -port 4430 -cert certs.pem -key signkey.pem -pass 1234\n" +" gmssl tls13_client -host 127.0.0.1 -port 4430 -cacert rootcacert.pem\n" +"\n"; + int tls13_client_main(int argc, char *argv[]) { int ret = -1; @@ -53,6 +87,7 @@ int tls13_client_main(int argc, char *argv[]) while (argc >= 1) { if (!strcmp(*argv, "-help")) { printf("usage: %s %s\n", prog, options); + printf("%s\n", help); return 0; } else if (!strcmp(*argv, "-host")) { if (--argc < 1) goto bad; diff --git a/tools/tls13_server.c b/tools/tls13_server.c index f9e352a1..ae82fa7c 100644 --- a/tools/tls13_server.c +++ b/tools/tls13_server.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * 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. @@ -20,6 +20,39 @@ static const char *options = "[-port num] -cert file -key file -pass str [-cacert file]"; +static const char *help = +"Options\n" +"\n" +" -port num Listening port number, default 443\n" +" -cert file Server's certificate chain in PEM format\n" +" -key file Server's encrypted private key in PEM format\n" +" -pass str Password to decrypt private key\n" +" -cacert file CA certificate for client certificate verification\n" +"\n" +"Examples\n" +"\n" +" gmssl sm2keygen -pass 1234 -out rootcakey.pem\n" +" gmssl certgen -C CN -ST Beijing -L Haidian -O PKU -OU CS -CN ROOTCA -days 3650 \\\n" +" -key rootcakey.pem -pass 1234 -out rootcacert.pem \\\n" +" -key_usage keyCertSign -key_usage cRLSign -ca\n" +"\n" +" gmssl sm2keygen -pass 1234 -out cakey.pem\n" +" gmssl reqgen -C CN -ST Beijing -L Haidian -O PKU -OU CS -CN \"Sub CA\" \\\n" +" -key cakey.pem -pass 1234 -out careq.pem\n" +" gmssl reqsign -in careq.pem -days 365 -key_usage keyCertSign -cacert rootcacert.pem -key rootcakey.pem -pass 1234 \\\n" +" -out cacert.pem -ca -path_len_constraint 0\n" +"\n" +" gmssl sm2keygen -pass 1234 -out signkey.pem\n" +" gmssl reqgen -C CN -ST Beijing -L Haidian -O PKU -OU CS -CN localhost -key signkey.pem -pass 1234 -out signreq.pem\n" +" gmssl reqsign -in signreq.pem -days 365 -key_usage digitalSignature -cacert cacert.pem -key cakey.pem -pass 1234 -out signcert.pem\n" +"\n" +" cat signcert.pem > certs.pem\n" +" cat cacert.pem >> certs.pem\n" +"\n" +" sudo gmssl tls13_server -port 4430 -cert certs.pem -key signkey.pem -pass 1234\n" +" gmssl tls13_client -host 127.0.0.1 -port 4430 -cacert rootcacert.pem\n" +"\n"; + int tls13_server_main(int argc , char **argv) { int ret = 1; @@ -50,6 +83,7 @@ int tls13_server_main(int argc , char **argv) while (argc > 0) { if (!strcmp(*argv, "-help")) { printf("usage: %s %s\n", prog, options); + printf("%s\n", help); return 0; } else if (!strcmp(*argv, "-port")) { if (--argc < 1) goto bad;