From 431a22e2e9e3266b9c71ffbdb4df4830e1287baf Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 21 May 2026 14:23:35 +0800 Subject: [PATCH] Update TLS 1.3 Cross-validation with OpenSSL --- include/gmssl/secp256r1_key.h | 2 + src/secp256r1_key.c | 34 +++++++- src/tls13.c | 141 +++++++++++++++++++++------------- src/x509_cer.c | 2 + tools/gmssl.c | 2 +- tools/p256keygen.c | 20 ++++- tools/tls13_server.c | 13 +++- 7 files changed, 153 insertions(+), 61 deletions(-) diff --git a/include/gmssl/secp256r1_key.h b/include/gmssl/secp256r1_key.h index dbf853d3..5b7231ec 100644 --- a/include/gmssl/secp256r1_key.h +++ b/include/gmssl/secp256r1_key.h @@ -49,6 +49,8 @@ int secp256r1_private_key_info_decrypt_from_der(SECP256R1_KEY *ec_key, const uint8_t **attrs, size_t *attrs_len, const char *pass, const uint8_t **in, size_t *inlen); +int secp256r1_private_key_to_pem(const SECP256R1_KEY *key, FILE *fp); +int secp256r1_private_key_from_pem(SECP256R1_KEY *key, FILE *fp); int secp256r1_private_key_info_encrypt_to_pem(const SECP256R1_KEY *key, const char *pass, FILE *fp); int secp256r1_private_key_info_decrypt_from_pem(SECP256R1_KEY *key, const char *pass, FILE *fp); diff --git a/src/secp256r1_key.c b/src/secp256r1_key.c index 10e9d09b..97f82823 100644 --- a/src/secp256r1_key.c +++ b/src/secp256r1_key.c @@ -541,8 +541,38 @@ int secp256r1_private_key_info_decrypt_from_pem(SECP256R1_KEY *key, const char * return 1; } +// FIXME: side-channel of Base64 +int secp256r1_private_key_to_pem(const SECP256R1_KEY *a, FILE *fp) +{ + uint8_t buf[512]; + uint8_t *p = buf; + size_t len = 0; + if (secp256r1_private_key_to_der(a, &p, &len) != 1) { + error_print(); + return -1; + } + if (pem_write(fp, "EC PRIVATE KEY", buf, len) <= 0) { + error_print(); + return -1; + } + return 1; +} +int secp256r1_private_key_from_pem(SECP256R1_KEY *a, FILE *fp) +{ + uint8_t buf[512]; + const uint8_t *cp = buf; + size_t len; - - + if (pem_read(fp, "EC PRIVATE KEY", buf, &len, sizeof(buf)) != 1) { + error_print(); + return -1; + } + if (secp256r1_private_key_from_der(a, &cp, &len) != 1 + || len > 0) { + error_print(); + return -1; + } + return 1; +} diff --git a/src/tls13.c b/src/tls13.c index bbdd1426..c57c182d 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -281,6 +281,28 @@ int tls13_record_decrypt(const BLOCK_CIPHER_KEY *key, const uint8_t iv[12], } +int tls13_hkdf_extract(const DIGEST *digest, const uint8_t salt[32], const uint8_t in[32], uint8_t out[32]) +{ + size_t saltlen; + size_t inlen; + size_t outlen; + + if (!digest || !salt || !in || !out) { + error_print(); + return -1; + } + saltlen = digest->digest_size; + inlen = digest->digest_size; + + if (hkdf_extract(digest, salt, saltlen, in, inlen, out, &outlen) != 1) { + error_print(); + return -1; + } + // outlen == digest->digest_size + + return 1; +} + /* HKDF-Expand-Label(Secret, Label, Context, Length) = HKDF-Expand(Secret, HkdfLabel, Length); @@ -289,68 +311,70 @@ HKDF-Expand-Label(Secret, Label, Context, Length) = uint16 length = Length; opaque label<7..255> = "tls13 " + Label; opaque context<0..255> = Context; } - -Derive-Secret(Secret, Label, Messages) = - HKDF-Expand-Label(Secret, Label, Hash(Messages), Hash.length) - */ - - -// TLS 1.3 的密钥生成过程都是通过HKDF-Extract和Derive-Secret这两个函数实现的 -// HKDF-Extract 的输出长度完全是由哈希长度决定的,输出是 Derive-Secret 的输入 -// Derive-Secret 的输出长度也是由哈希长度决定的 - - -// 这个函数掩盖了hkdf_extract,并且假定使用的哈希函数的哈希值的长度是32,这个没有必要,并且不清晰 -int tls13_hkdf_extract(const DIGEST *digest, const uint8_t salt[32], const uint8_t in[32], uint8_t out[32]) -{ - size_t dgstlen; - - if (hkdf_extract(digest, salt, 32, in, 32, out, &dgstlen) != 1 - || dgstlen != 32) { - error_print(); - return -1; - } - return 1; -} - -// 增加secret_len作为输入的参数 -// Expand-Label int tls13_hkdf_expand_label(const DIGEST *digest, const uint8_t secret[32], const char *label, const uint8_t *context, size_t context_len, size_t outlen, uint8_t *out) { - uint8_t label_len; uint8_t hkdf_label[2 + 256 + 256]; uint8_t *p = hkdf_label; size_t hkdf_label_len = 0; + size_t secret_len; + size_t label_len; + + if (!digest || !secret || !label || !outlen || !out) { + error_print(); + return -1; + } + if (strlen(label) > 255 - strlen("tls13 ")) { + error_print(); + return -1; + } + if (outlen > 65535) { + error_print(); + return -1; + } + secret_len = digest->digest_size; + label_len = strlen("tls13 ") + strlen(label); - label_len = (uint8_t)(strlen("tls13 ") + strlen(label)); //FIXME: check length < 255 tls_uint16_to_bytes((uint16_t)outlen, &p, &hkdf_label_len); - tls_uint8_to_bytes(label_len, &p, &hkdf_label_len); + tls_uint8_to_bytes((uint8_t)label_len, &p, &hkdf_label_len); tls_array_to_bytes((uint8_t *)"tls13 ", strlen("tls13 "), &p, &hkdf_label_len); tls_array_to_bytes((uint8_t *)label, strlen(label), &p, &hkdf_label_len); tls_uint8array_to_bytes(context, context_len, &p, &hkdf_label_len); - hkdf_expand(digest, secret, 32, hkdf_label, hkdf_label_len, outlen, out); +// format_bytes(stderr, 0, 0, "HkdfLabel", hkdf_label, hkdf_label_len); + + + hkdf_expand(digest, secret, secret_len, hkdf_label, hkdf_label_len, outlen, out); return 1; } -// 增加secret_len -// 输出长度是由digest决定的,应该提供一个输出长度 outlen +/* +Derive-Secret(Secret, Label, Messages) = + HKDF-Expand-Label(Secret, Label, Hash(Messages), Hash.length) + +一般来说derive_secret的输入是message,但是传递给下面的是hash,但是我们这里直接传递了hash + +*/ int tls13_derive_secret(const uint8_t secret[32], const char *label, const DIGEST_CTX *dgst_ctx, uint8_t out[32]) { DIGEST_CTX ctx = *dgst_ctx; - uint8_t dgst[64]; - size_t dgstlen; + size_t outlen = 32; + uint8_t context[32]; + size_t context_len; - if (digest_finish(&ctx, dgst, &dgstlen) != 1) { + if (digest_finish(&ctx, context, &context_len) != 1) { error_print(); return -1; } - if (tls13_hkdf_expand_label(dgst_ctx->digest, secret, label, dgst, 32, dgstlen, out) != 1) { + // 这个值是对的 + + //context_len = 0; + + if (tls13_hkdf_expand_label(dgst_ctx->digest, secret, label, context, context_len, outlen, out) != 1) { error_print(); return -1; } @@ -361,6 +385,14 @@ int tls13_derive_secret(const uint8_t secret[32], const char *label, const DIGES /* +如果early_secret是对的,那么应该首先从early_secret 生成一个derived_secret + +然后再用derived_secret和ecdhe生成handshake_secret + +但是在openssl中这两个步骤被合并到一起了,不知道具体是怎么做的 + + + 0 | v @@ -454,7 +486,7 @@ int tls13_generate_early_keys(TLS_CONNECT *conn) block_cipher_set_encrypt_key(&conn->client_write_key, conn->cipher, client_write_key); tls_seq_num_reset(conn->client_seq_num); - format_print(stderr, 0, 0, "generate early_keys\n"); + format_print(stderr, 0, 0, "generate_early_keys\n"); format_bytes(stderr, 0, 4, "early_secret", conn->early_secret, conn->digest->digest_size); format_bytes(stderr, 0, 4, "client_early_traffic_secret", conn->client_early_traffic_secret, conn->digest->digest_size); format_bytes(stderr, 0, 4, "client_write_key", client_write_key, client_write_key_len); @@ -468,8 +500,9 @@ int tls13_generate_early_keys(TLS_CONNECT *conn) int tls13_generate_handshake_secrets(TLS_CONNECT *conn) { const uint8_t zeros[32] = {0}; - uint8_t pre_master_secret[32] = {0}; - size_t pre_master_secret_len; + uint8_t ecdhe_shared_secret[32]; + size_t ecdhe_shared_secret_len; + uint8_t derived_secret[32]; DIGEST_CTX null_dgst_ctx; if (!conn || !conn->digest) { @@ -485,7 +518,7 @@ int tls13_generate_handshake_secrets(TLS_CONNECT *conn) } if (x509_key_exchange(&conn->key_exchanges[conn->key_exchange_idx], conn->peer_key_exchange, conn->peer_key_exchange_len, - pre_master_secret, &pre_master_secret_len) != 1) { + ecdhe_shared_secret, &ecdhe_shared_secret_len) != 1) { error_print(); return -1; } @@ -497,19 +530,21 @@ int tls13_generate_handshake_secrets(TLS_CONNECT *conn) } /* [1] */ tls13_hkdf_extract(conn->digest, zeros, conn->psk, conn->early_secret); - /* [5] */ tls13_derive_secret(conn->early_secret, "derived", &null_dgst_ctx, conn->handshake_secret); - /* [6] */ tls13_hkdf_extract(conn->digest, conn->handshake_secret, conn->pre_master_secret, conn->handshake_secret); + /* [5] */ tls13_derive_secret(conn->early_secret, "derived", &null_dgst_ctx, derived_secret); + /* [6] */ tls13_hkdf_extract(conn->digest, derived_secret, ecdhe_shared_secret, conn->handshake_secret); /* [7] */ tls13_derive_secret(conn->handshake_secret, "c hs traffic", &conn->dgst_ctx, conn->client_handshake_traffic_secret); /* [8] */ tls13_derive_secret(conn->handshake_secret, "s hs traffic", &conn->dgst_ctx, conn->server_handshake_traffic_secret); - format_print(stderr, 0, 0, "generate handshake_secrets\n"); + format_print(stderr, 0, 0, "generate_handshake_secrets\n"); format_bytes(stderr, 0, 4, "early_secret", conn->early_secret, conn->digest->digest_size); - format_bytes(stderr, 0, 4, "pre_master_secret", pre_master_secret, pre_master_secret_len); - format_bytes(stderr, 0, 4, "handshake_secret", conn->handshake_secret, conn->digest->digest_size); + format_bytes(stderr, 0, 4, "derived_secret", derived_secret, conn->digest->digest_size); + format_bytes(stderr, 0, 4, "ecdhe_shared_secret", ecdhe_shared_secret, ecdhe_shared_secret_len); + format_bytes(stderr, 0, 4, "handshake_secret",conn->handshake_secret, conn->digest->digest_size); format_bytes(stderr, 0, 4, "client_handshake_traffic_secret", conn->client_handshake_traffic_secret, conn->digest->digest_size); format_bytes(stderr, 0, 4, "server_handshake_traffic_secret", conn->server_handshake_traffic_secret, conn->digest->digest_size); - gmssl_secure_clear(pre_master_secret, sizeof(pre_master_secret)); + gmssl_secure_clear(ecdhe_shared_secret, sizeof(ecdhe_shared_secret)); + gmssl_secure_clear(derived_secret, sizeof(derived_secret)); return 1; } @@ -531,7 +566,7 @@ int tls13_generate_master_secret(TLS_CONNECT *conn) /* [9] */ tls13_derive_secret(conn->handshake_secret, "derived", &null_dgst_ctx, conn->master_secret); /* [10] */ tls13_hkdf_extract(conn->digest, conn->master_secret, zeros, conn->master_secret); - format_print(stderr, 0, 0, "generate master_secret\n"); + format_print(stderr, 0, 0, "generate_master_secret\n"); format_bytes(stderr, 0, 4, "master_secret", conn->master_secret, conn->digest->digest_size); return 1; } @@ -553,7 +588,7 @@ int tls13_generate_client_handshake_keys(TLS_CONNECT *conn) block_cipher_set_encrypt_key(&conn->client_write_key, conn->cipher, client_write_key); tls_seq_num_reset(conn->client_seq_num); - format_print(stderr, 0, 0, "generate client_handshake_keys\n"); + format_print(stderr, 0, 0, "generate_client_handshake_keys\n"); format_bytes(stderr, 0, 4, "client_write_key", client_write_key, client_write_key_len); format_bytes(stderr, 0, 4, "client_write_iv", conn->client_write_iv, TLS13_IV_SIZE); format_print(stderr, 0, 4, "client_seq_num: %"PRIu64"\n", GETU64(conn->client_seq_num)); @@ -579,7 +614,7 @@ int tls13_generate_server_handshake_keys(TLS_CONNECT *conn) block_cipher_set_encrypt_key(&conn->server_write_key, conn->cipher, server_write_key); tls_seq_num_reset(conn->server_seq_num); - format_print(stderr, 0, 0, "generate server_handshake_keys\n"); + format_print(stderr, 0, 0, "generate_server_handshake_keys\n"); format_bytes(stderr, 0, 4, "server_write_key", server_write_key, server_write_key_len); format_bytes(stderr, 0, 4, "server_write_iv", conn->server_write_iv, TLS13_IV_SIZE); format_print(stderr, 0, 4, "server_seq_num: %"PRIu64"\n", GETU64(conn->server_seq_num)); @@ -614,7 +649,7 @@ int tls13_update_client_application_secret(TLS_CONNECT *conn) tls13_hkdf_expand_label(conn->digest, conn->client_application_traffic_secret, "traffic upd", NULL, 0, conn->digest->digest_size, conn->client_application_traffic_secret); - format_print(stderr, 0, 0, "update client_application_secret\n"); + format_print(stderr, 0, 0, "update_client_application_secret\n"); format_bytes(stderr, 0, 4, "client_application_traffic_secret", conn->client_application_traffic_secret, conn->digest->digest_size); return 1; } @@ -629,7 +664,7 @@ int tls13_update_server_application_secret(TLS_CONNECT *conn) tls13_hkdf_expand_label(conn->digest, conn->server_application_traffic_secret, "traffic upd", NULL, 0, conn->digest->digest_size, conn->server_application_traffic_secret); - format_print(stderr, 0, 0, "update server_application_secret\n"); + format_print(stderr, 0, 0, "update_server_application_secret\n"); format_bytes(stderr, 0, 4, "server_application_traffic_secret", conn->server_application_traffic_secret, conn->digest->digest_size); return 1; } @@ -651,7 +686,7 @@ int tls13_generate_client_application_keys(TLS_CONNECT *conn) block_cipher_set_encrypt_key(&conn->client_write_key, conn->cipher, client_write_key); tls_seq_num_reset(conn->client_seq_num); - format_print(stderr, 0, 0, "update client_application_keys\n"); + format_print(stderr, 0, 0, "update_client_application_keys\n"); format_bytes(stderr, 0, 4, "client_write_key", client_write_key, client_write_key_len); format_bytes(stderr, 0, 4, "client_write_iv", conn->client_write_iv, TLS13_IV_SIZE); format_print(stderr, 0, 4, "client_seq_num: %"PRIu64"\n", GETU64(conn->client_seq_num)); @@ -677,7 +712,7 @@ int tls13_generate_server_application_keys(TLS_CONNECT *conn) block_cipher_set_encrypt_key(&conn->server_write_key, conn->cipher, server_write_key); tls_seq_num_reset(conn->server_seq_num); - format_print(stderr, 0, 0, "update server_application_keys\n"); + format_print(stderr, 0, 0, "update_server_application_keys\n"); format_bytes(stderr, 0, 4, "server_write_key", server_write_key, server_write_key_len); format_bytes(stderr, 0, 4, "server_write_iv", conn->server_write_iv, TLS13_IV_SIZE); format_print(stderr, 0, 4, "server_seq_num: %"PRIu64"\n", GETU64(conn->server_seq_num)); diff --git a/src/x509_cer.c b/src/x509_cer.c index d88704cf..4977eb0d 100644 --- a/src/x509_cer.c +++ b/src/x509_cer.c @@ -1890,6 +1890,8 @@ int x509_cert_check(const uint8_t *cert, size_t certlen, int cert_type, return 1; } +// 这个函数应该打印到底是哪个证书验证出错了 + 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) { diff --git a/tools/gmssl.c b/tools/gmssl.c index 7475f376..85654950 100644 --- a/tools/gmssl.c +++ b/tools/gmssl.c @@ -158,7 +158,7 @@ static const char *options = " cmssign Generate CMS SignedData\n" " cmsverify Verify CMS SignedData\n" #ifdef ENABLE_SECP256R1 - " p256keygen Generate P-256 (secp256r1, prime256v1) keypair\n" + " p256keygen Generate P-256 (secp256r1, prime256v1) keypair\n" #endif #ifdef ENABLE_LMS " lmskeygen Generate LMS-SM3 (Leighton-Micali Signature) keypair\n" diff --git a/tools/p256keygen.c b/tools/p256keygen.c index c2fbb515..de3def3c 100644 --- a/tools/p256keygen.c +++ b/tools/p256keygen.c @@ -24,6 +24,7 @@ static const char *options = " -pass pass Password to encrypt the private key\n" " -out pem Output password-encrypted PKCS #8 private key in PEM format\n" " -pubout pem Output public key in PEM format\n" +" -export pem Output non-encrypted PKCS#8 private key in PEM format\n" "\n" "Examples\n" "\n" @@ -38,8 +39,10 @@ int p256keygen_main(int argc, char **argv) char *pass = NULL; char *outfile = NULL; char *puboutfile = NULL; + char *exportfile = NULL; FILE *outfp = stdout; FILE *puboutfp = stdout; + FILE *exportfp = NULL; int curve_oid = OID_secp256r1; X509_KEY key; @@ -71,7 +74,14 @@ int p256keygen_main(int argc, char **argv) if (--argc < 1) goto bad; puboutfile = *(++argv); if (!(puboutfp = fopen(puboutfile, "wb"))) { - fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, outfile, strerror(errno)); + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, puboutfile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-export")) { + if (--argc < 1) goto bad; + exportfile = *(++argv); + if (!(exportfp = fopen(exportfile, "wb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, exportfile, strerror(errno)); goto end; } } else { @@ -91,7 +101,6 @@ bad: goto end; } - if (x509_key_generate(&key, OID_ec_public_key, &curve_oid, sizeof(curve_oid)) != 1) { fprintf(stderr, "gmssl %s: inner failure\n", prog); return -1; @@ -104,6 +113,13 @@ bad: fprintf(stderr, "gmssl %s: inner failure\n", prog); goto end; } + if (exportfp) { + if (secp256r1_private_key_to_pem(&key.u.secp256r1_key, exportfp) != 1) { + fprintf(stderr, "gmssl %s: inner failure\n", prog); + goto end; + } + } + ret = 0; end: diff --git a/tools/tls13_server.c b/tools/tls13_server.c index c32be8e2..85596e57 100644 --- a/tools/tls13_server.c +++ b/tools/tls13_server.c @@ -33,6 +33,8 @@ // 或者P256的私钥应该用AES-128 + SHA-256加密 +// 应该首先打印openssl的密钥序列,early_secret, pre_master_secret, 以及 handshake_secret 等 + static const char *options = "[-port num] -cert file -key file -pass str [-cacert file]"; @@ -109,12 +111,12 @@ static const char *help = "\n" "Generate P-256 certificates\n" "\n" -" gmssl p256keygen -pass 1234 -out p256rootcakey.pem\n" +" gmssl p256keygen -pass 1234 -out p256rootcakey.pem -export p256rootcakey.exp\n" " gmssl certgen -C CN -ST Beijing -L Haidian -O PKU -OU CS -CN P256ROOTCA -days 3650 \\\n" " -key p256rootcakey.pem -pass 1234 -out p256rootcacert.pem \\\n" " -key_usage keyCertSign -key_usage cRLSign -ca\n" "\n" -" gmssl p256keygen -pass 1234 -out p256cakey.pem\n" +" gmssl p256keygen -pass 1234 -out p256cakey.pem -export p256cakey.exp\n" " gmssl reqgen -C CN -ST Beijing -L Haidian -O PKU -OU CS -CN \"P256 Sub CA\" \\\n" " -key p256cakey.pem -pass 1234 -out p256careq.pem\n" " gmssl reqsign -in p256careq.pem -days 365 -key_usage keyCertSign \\\n" @@ -122,7 +124,7 @@ static const char *help = " -ca -path_len_constraint 0 \\\n" " -out p256cacert.pem\n" "\n" -" gmssl p256keygen -pass 1234 -out p256signkey.pem\n" +" gmssl p256keygen -pass 1234 -out p256signkey.pem -export p256signkey.exp\n" " gmssl reqgen -C CN -ST Beijing -L Haidian -O PKU -OU CS -CN 127.0.0.1 \\\n" " -key p256signkey.pem -pass 1234 -out p256signreq.pem\n" " gmssl reqsign -in p256signreq.pem -days 365 -key_usage digitalSignature \\\n" @@ -144,6 +146,11 @@ static const char *help = " gmssl tls13_client -host 127.0.0.1 -port 4430 -cacert rootcacerts.pem \\\n" " -cipher_suite TLS_AES_128_GCM_SHA256 -supported_group prime256v1 -sig_alg ecdsa_secp256r1_sha256\n" "\n" +" add `SSL_CTX_clear_options(ctx, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);` to openssl apps/s_server.c\n" +" /usr/local/bin/openssl s_server -accept 4430 -cert p256signcert.pem -cert_chain p256cacert.pem -key p256signkey.exp \\\n" +" -tls1_3 -ciphersuites TLS_AES_128_GCM_SHA256 -named_curve prime256v1 \\\n" +" -trace -keylogfile sslkeys.log\n" +"\n" "TLS 1.3 SNI\n" "\n" " sudo gmssl tls13_server -port 4430 \\\n"