From 09c96fa2e09098c849a2b81aa946abb13d3c03c1 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Wed, 24 Jun 2026 18:21:27 +0800 Subject: [PATCH] Update QUIC/SM9/ZUC --- CMakeLists.txt | 14 +- cmake/tool_sm2.cmake | 210 +++++++ cmake/tool_sm4.cmake | 27 +- cmake/tool_sm9.cmake | 111 ++++ cmake/tool_zuc.cmake | 22 + include/gmssl/quic.h | 80 +++ include/gmssl/version.h | 2 +- src/quic.c | 1222 +++++++++++++++++++++++++++++++++++++ src/sm9_cms.c | 897 +++++++++++++++++++++++++++ tests/quictest.c | 301 +++++++++- tools/gmssl.c | 35 ++ tools/quic_client.c | 1270 +++++++++++++++++++++++++++++++++++++++ tools/quic_help.h | 35 ++ tools/quic_server.c | 1010 +++++++++++++++++++++++++++++++ tools/sm2exch.c | 929 ++++++++++++++++++++++++++++ tools/sm4.c | 31 - tools/sm9exch.c | 771 ++++++++++++++++++++++++ tools/sm9keygen.c | 21 +- tools/zuc256.c | 150 +++++ tools/zuc_128_eea3.c | 310 ++++++++++ tools/zuc_128_eia3.c | 299 +++++++++ 21 files changed, 7702 insertions(+), 45 deletions(-) create mode 100644 src/sm9_cms.c create mode 100644 tools/quic_client.c create mode 100644 tools/quic_help.h create mode 100644 tools/quic_server.c create mode 100644 tools/sm2exch.c create mode 100644 tools/sm9exch.c create mode 100644 tools/zuc256.c create mode 100644 tools/zuc_128_eea3.c create mode 100644 tools/zuc_128_eia3.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b51dccdc..a0e17f64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,7 +221,7 @@ set(src set(tools tools/gmssl.c tools/version.c - tools/sm4.c +# tools/sm4.c tools/sm4_cbc.c tools/sm4_ctr.c tools/sm4_gcm.c @@ -233,6 +233,7 @@ set(tools tools/sm2verify.c tools/sm2encrypt.c tools/sm2decrypt.c + tools/sm2exch.c tools/rand.c tools/certgen.c tools/certparse.c @@ -507,7 +508,8 @@ if (ENABLE_SM9) tools/sm9sign.c tools/sm9verify.c tools/sm9encrypt.c - tools/sm9decrypt.c) + tools/sm9decrypt.c + tools/sm9exch.c) list(APPEND tests sm9) endif() @@ -657,7 +659,11 @@ if (ENABLE_ZUC) message(STATUS "ENABLE_ZUC is ON") add_definitions(-DENABLE_ZUC) list(APPEND src src/zuc.c src/zuc_modes.c) - list(APPEND tools tools/zuc.c) + list(APPEND tools + tools/zuc.c + tools/zuc256.c + tools/zuc_128_eea3.c + tools/zuc_128_eia3.c) list(APPEND tests zuc) endif() @@ -1010,7 +1016,7 @@ endif() # set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_VENDOR "GmSSL develop team") -set(CPACK_PACKAGE_VERSION "3.3.0-dev.1168") +set(CPACK_PACKAGE_VERSION "3.3.0-dev.1169") set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md) set(CPACK_NSIS_MODIFY_PATH ON) include(CPack) diff --git a/cmake/tool_sm2.cmake b/cmake/tool_sm2.cmake index 04ef92c9..c8f02a73 100644 --- a/cmake/tool_sm2.cmake +++ b/cmake/tool_sm2.cmake @@ -64,6 +64,216 @@ if(NOT "${TEST_STDOUT}" STREQUAL "${SECRET_MESSAGE}") message(FATAL_ERROR "stdout: ${TEST_STDOUT}") endif() +execute_process( + COMMAND bin/gmssl sm2keygen -pass P@ssw0rd -out sm2_peer.pem -pubout sm2_peer_pub.pem + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage init + -exch_keyout sm2exch_alice_ra.pem -exch_pass P@ssw0rd + -out sm2exch_ra.bin + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage respond + -key sm2_peer.pem -pass P@ssw0rd -id Bob + -peer_pubkey sm2pub.pem -peer_id Alice -in sm2exch_ra.bin + -exch_keyout sm2exch_bob_rb.pem -exch_pass P@ssw0rd + -secret_state_out sm2exch_bob_secret_state.bin + -out sm2exch_rb_sb.bin + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage confirm + -key sm2.pem -pass P@ssw0rd -id Alice + -peer_pubkey sm2_peer_pub.pem -peer_id Bob + -exch_key sm2exch_alice_ra.pem -exch_pass P@ssw0rd + -in sm2exch_rb_sb.bin + -keylen 48 -keyout sm2exch_alice_key.bin + -secret_state_out sm2exch_alice_secret_state.bin + -out sm2exch_sa.bin + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage finish + -key sm2_peer.pem -pass P@ssw0rd -id Bob + -peer_pubkey sm2pub.pem -peer_id Alice + -exch_key sm2exch_bob_rb.pem -exch_pass P@ssw0rd + -secret_state sm2exch_bob_secret_state.bin + -in sm2exch_sa.bin + -keylen 48 -keyout sm2exch_bob_key.bin + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files sm2exch_alice_key.bin sm2exch_bob_key.bin + RESULT_VARIABLE TEST_RESULT +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "SM2 key exchange output mismatch") +endif() + +execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files sm2exch_alice_secret_state.bin sm2exch_bob_secret_state.bin + RESULT_VARIABLE TEST_RESULT +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "SM2 key exchange secret_state mismatch") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage init -bin + -exch_keyout sm2exch_bin_alice_ra.pem -exch_pass P@ssw0rd + -out sm2exch_bin_ra.bin + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage respond -bin + -key sm2_peer.pem -pass P@ssw0rd -id Bob + -peer_pubkey sm2pub.pem -peer_id Alice -in sm2exch_bin_ra.bin + -exch_keyout sm2exch_bin_bob_rb.pem -exch_pass P@ssw0rd + -secret_state_out sm2exch_bin_bob_secret_state.bin + -out sm2exch_bin_rb_sb.bin + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage confirm -bin + -key sm2.pem -pass P@ssw0rd -id Alice + -peer_pubkey sm2_peer_pub.pem -peer_id Bob + -exch_key sm2exch_bin_alice_ra.pem -exch_pass P@ssw0rd + -in sm2exch_bin_rb_sb.bin + -keylen 48 -keyout sm2exch_bin_alice_key.bin + -secret_state_out sm2exch_bin_alice_secret_state.bin + -out sm2exch_bin_sa.bin + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage finish -bin + -key sm2_peer.pem -pass P@ssw0rd -id Bob + -peer_pubkey sm2pub.pem -peer_id Alice + -exch_key sm2exch_bin_bob_rb.pem -exch_pass P@ssw0rd + -secret_state sm2exch_bin_bob_secret_state.bin + -in sm2exch_bin_sa.bin + -keylen 48 -keyout sm2exch_bin_bob_key.bin + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files sm2exch_bin_alice_key.bin sm2exch_bin_bob_key.bin + RESULT_VARIABLE TEST_RESULT +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "SM2 binary key exchange output mismatch") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage init + -exch_keyout sm2exch_stdio_alice_ra.pem -exch_pass P@ssw0rd + OUTPUT_FILE sm2exch_stdio_ra.hex + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage respond + -key sm2_peer.pem -pass P@ssw0rd -id Bob + -peer_pubkey sm2pub.pem -peer_id Alice + -exch_keyout sm2exch_stdio_bob_rb.pem -exch_pass P@ssw0rd + -secret_state_out sm2exch_stdio_bob_secret_state.hex + INPUT_FILE sm2exch_stdio_ra.hex + OUTPUT_FILE sm2exch_stdio_rb_sb.hex + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage confirm + -key sm2.pem -pass P@ssw0rd -id Alice + -peer_pubkey sm2_peer_pub.pem -peer_id Bob + -exch_key sm2exch_stdio_alice_ra.pem -exch_pass P@ssw0rd + -keylen 48 -keyout sm2exch_stdio_alice_key.hex + -secret_state_out sm2exch_stdio_alice_secret_state.hex + INPUT_FILE sm2exch_stdio_rb_sb.hex + OUTPUT_FILE sm2exch_stdio_sa.hex + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND bin/gmssl sm2exch -stage finish + -key sm2_peer.pem -pass P@ssw0rd -id Bob + -peer_pubkey sm2pub.pem -peer_id Alice + -exch_key sm2exch_stdio_bob_rb.pem -exch_pass P@ssw0rd + -secret_state sm2exch_stdio_bob_secret_state.hex + -keylen 48 -keyout sm2exch_stdio_bob_key.hex + INPUT_FILE sm2exch_stdio_sa.hex + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "stderr: ${TEST_STDERR}") +endif() + +execute_process( + COMMAND ${CMAKE_COMMAND} -E compare_files sm2exch_stdio_alice_key.hex sm2exch_stdio_bob_key.hex + RESULT_VARIABLE TEST_RESULT +) +if(NOT ${TEST_RESULT} EQUAL 0) + message(FATAL_ERROR "SM2 stdio key exchange output mismatch") +endif() + execute_process( COMMAND bin/gmssl sm2sign -key sm2.pem -pass P@ssw0rd -id Alice -in message.txt -out sm2_id.sig RESULT_VARIABLE TEST_RESULT diff --git a/cmake/tool_sm4.cmake b/cmake/tool_sm4.cmake index ffb1bcc5..f5efb65b 100644 --- a/cmake/tool_sm4.cmake +++ b/cmake/tool_sm4.cmake @@ -2,7 +2,6 @@ include("${CMAKE_CURRENT_LIST_DIR}/tool_helpers.cmake") set(SM4_KEY 0123456789abcdeffedcba9876543210) set(SM4_IV 00000000000000000000000000000000) -set(SM4_HMAC_KEY 0123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210) set(SM4_XTS_KEY 0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff) set(SM4_FF1_KEY 2b7e151628aed2a6abf7158809cf4f3c) set(SM4_TEXT "0123456789abcdef0123456789abcdef") @@ -23,23 +22,39 @@ gmssl_run(sm4_cbc -decrypt -key ${SM4_KEY} -iv ${SM4_IV} -in tool_sm4_cbc_kat.cipher -out tool_sm4_cbc_kat.decrypt) gmssl_files_equal(tool_sm4_cbc_kat.plain tool_sm4_cbc_kat.decrypt) +file(WRITE tool_sm4_unified_cbc_kat.plain "0123456789abcdef") +gmssl_run(sm4 -cbc -encrypt -key ${SM4_KEY} -iv ${SM4_IV} + -in tool_sm4_unified_cbc_kat.plain -out tool_sm4_unified_cbc_kat.cipher) +gmssl_expect_file_hex(tool_sm4_unified_cbc_kat.cipher + "e6887b77dbabb572ffa07fed7548b192ceaace11f2b90b94c2b7a4d9382e471e") +gmssl_run(sm4 -cbc -decrypt -key ${SM4_KEY} -iv ${SM4_IV} + -in tool_sm4_unified_cbc_kat.cipher -out tool_sm4_unified_cbc_kat.decrypt) +gmssl_files_equal(tool_sm4_unified_cbc_kat.plain tool_sm4_unified_cbc_kat.decrypt) + gmssl_symmetric_roundtrip(tool_sm4_cbc sm4_cbc -key ${SM4_KEY} -iv ${SM4_IV}) gmssl_symmetric_roundtrip(tool_sm4_ctr sm4_ctr -key ${SM4_KEY} -iv ${SM4_IV}) gmssl_symmetric_roundtrip(tool_sm4_gcm sm4_gcm -key ${SM4_KEY} -iv 000000000000000000000000 -aad_hex 001122 -taglen 16) -gmssl_symmetric_roundtrip(tool_sm4_cbc_sm3_hmac sm4_cbc_sm3_hmac -key ${SM4_HMAC_KEY} -iv ${SM4_IV} -aad_hex 001122) -gmssl_symmetric_roundtrip(tool_sm4_ctr_sm3_hmac sm4_ctr_sm3_hmac -key ${SM4_HMAC_KEY} -iv ${SM4_IV} -aad_hex 001122) + +gmssl_symmetric_roundtrip(tool_sm4_unified_cbc sm4 -cbc -key ${SM4_KEY} -iv ${SM4_IV}) +gmssl_symmetric_roundtrip(tool_sm4_unified_ctr sm4 -ctr -key ${SM4_KEY} -iv ${SM4_IV}) +gmssl_symmetric_roundtrip(tool_sm4_unified_gcm sm4 -gcm -key ${SM4_KEY} -iv 000000000000000000000000 -aad_hex 001122 -taglen 16) if(ENABLE_SM4_ECB) gmssl_symmetric_roundtrip(tool_sm4_ecb sm4_ecb -key ${SM4_KEY}) + gmssl_symmetric_roundtrip(tool_sm4_unified_ecb sm4 -ecb -key ${SM4_KEY}) endif() if(ENABLE_SM4_CFB) gmssl_symmetric_roundtrip(tool_sm4_cfb sm4_cfb -sbytes 16 -key ${SM4_KEY} -iv ${SM4_IV}) + gmssl_symmetric_roundtrip(tool_sm4_unified_cfb sm4 -cfb -sbytes 16 -key ${SM4_KEY} -iv ${SM4_IV}) + gmssl_symmetric_roundtrip(tool_sm4_unified_cfb8 sm4 -cfb -sbytes 1 -key ${SM4_KEY} -iv ${SM4_IV}) endif() if(ENABLE_SM4_OFB) gmssl_symmetric_roundtrip(tool_sm4_ofb sm4_ofb -key ${SM4_KEY} -iv ${SM4_IV}) + gmssl_symmetric_roundtrip(tool_sm4_unified_ofb sm4 -ofb -key ${SM4_KEY} -iv ${SM4_IV}) endif() if(ENABLE_SM4_CCM) gmssl_symmetric_roundtrip(tool_sm4_ccm sm4_ccm -key ${SM4_KEY} -iv 000000000000000000000000 -aad_hex 001122 -taglen 16) + gmssl_symmetric_roundtrip(tool_sm4_unified_ccm sm4 -ccm -key ${SM4_KEY} -iv 000000000000000000000000 -aad_hex 001122 -taglen 16) endif() if(ENABLE_SM4_XTS) file(WRITE tool_sm4_xts.plain "0123456789abcdef0123456789abcdef") @@ -48,6 +63,12 @@ if(ENABLE_SM4_XTS) gmssl_run(sm4_xts -decrypt -key ${SM4_XTS_KEY} -iv ${SM4_IV} -data_unit_size 32 -in tool_sm4_xts.cipher -out tool_sm4_xts.decrypt) gmssl_files_equal(tool_sm4_xts.plain tool_sm4_xts.decrypt) + file(WRITE tool_sm4_unified_xts.plain "0123456789abcdef0123456789abcdef") + gmssl_run(sm4 -xts -encrypt -key ${SM4_XTS_KEY} -iv ${SM4_IV} -data_unit_size 32 + -in tool_sm4_unified_xts.plain -out tool_sm4_unified_xts.cipher) + gmssl_run(sm4 -xts -decrypt -key ${SM4_XTS_KEY} -iv ${SM4_IV} -data_unit_size 32 + -in tool_sm4_unified_xts.cipher -out tool_sm4_unified_xts.decrypt) + gmssl_files_equal(tool_sm4_unified_xts.plain tool_sm4_unified_xts.decrypt) endif() if(ENABLE_SM4_FF1) gmssl_expect_stdout("2326982895499381\n" diff --git a/cmake/tool_sm9.cmake b/cmake/tool_sm9.cmake index 5f4dd761..9076f50b 100644 --- a/cmake/tool_sm9.cmake +++ b/cmake/tool_sm9.cmake @@ -24,8 +24,119 @@ gmssl_run(sm9keygen -alg sm9encrypt -in tool_sm9_enc_msk.pem -inpass ${SM9_PASS} -id ${SM9_ID} -out tool_sm9_enc_key.pem -outpass ${SM9_USER_PASS}) +gmssl_run(sm9keygen -alg sm9keyagreement + -in tool_sm9_enc_msk.pem -inpass ${SM9_PASS} + -id Bob + -out tool_sm9_bob_exch_key.pem -outpass ${SM9_USER_PASS}) gmssl_run(sm9encrypt -pubmaster tool_sm9_enc_mpk.pem -id ${SM9_ID} -in tool_sm9_message.txt -out tool_sm9_cipher.der) gmssl_run(sm9decrypt -key tool_sm9_enc_key.pem -pass ${SM9_USER_PASS} -id ${SM9_ID} -in tool_sm9_cipher.der -out tool_sm9_plain.txt) gmssl_expect_file_text(tool_sm9_plain.txt "${SM9_TEXT}") + +gmssl_run(sm9keygen -alg sm9keyagreement + -in tool_sm9_enc_msk.pem -inpass ${SM9_PASS} + -id ${SM9_ID} + -out tool_sm9_alice_exch_key.pem -outpass ${SM9_USER_PASS}) +gmssl_run(sm9exch -stage init + -pubmaster tool_sm9_enc_mpk.pem -peer_id Bob + -exch_keyout tool_sm9_alice_ra.pem -out tool_sm9_ra.bin) +gmssl_run(sm9exch -stage respond + -pubmaster tool_sm9_enc_mpk.pem + -key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob + -peer_id ${SM9_ID} -in tool_sm9_ra.bin + -exch_keyout tool_sm9_bob_rb.pem -out tool_sm9_rb_sb.bin) +gmssl_run(sm9exch -stage confirm + -pubmaster tool_sm9_enc_mpk.pem + -key tool_sm9_alice_exch_key.pem -pass ${SM9_USER_PASS} -id ${SM9_ID} + -peer_id Bob -exch_key tool_sm9_alice_ra.pem + -in tool_sm9_rb_sb.bin + -keylen 48 -keyout tool_sm9_alice_shared_key.bin + -out tool_sm9_sa.bin) +gmssl_run(sm9exch -stage finish + -pubmaster tool_sm9_enc_mpk.pem + -key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob + -peer_id ${SM9_ID} + -exch_key tool_sm9_bob_rb.pem + -in tool_sm9_sa.bin + -keylen 48 -keyout tool_sm9_bob_shared_key.bin) +gmssl_files_equal(tool_sm9_alice_shared_key.bin tool_sm9_bob_shared_key.bin) + +gmssl_run(sm9exch -stage init -bin + -pubmaster tool_sm9_enc_mpk.pem -peer_id Bob + -exch_keyout tool_sm9_bin_alice_ra.pem -out tool_sm9_bin_ra.bin) +gmssl_run(sm9exch -stage respond -bin + -pubmaster tool_sm9_enc_mpk.pem + -key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob + -peer_id ${SM9_ID} -in tool_sm9_bin_ra.bin + -exch_keyout tool_sm9_bin_bob_rb.pem -out tool_sm9_bin_rb_sb.bin) +gmssl_run(sm9exch -stage confirm -bin + -pubmaster tool_sm9_enc_mpk.pem + -key tool_sm9_alice_exch_key.pem -pass ${SM9_USER_PASS} -id ${SM9_ID} + -peer_id Bob -exch_key tool_sm9_bin_alice_ra.pem + -in tool_sm9_bin_rb_sb.bin + -keylen 48 -keyout tool_sm9_bin_alice_shared_key.bin + -out tool_sm9_bin_sa.bin) +gmssl_run(sm9exch -stage finish -bin + -pubmaster tool_sm9_enc_mpk.pem + -key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob + -peer_id ${SM9_ID} + -exch_key tool_sm9_bin_bob_rb.pem + -in tool_sm9_bin_sa.bin + -keylen 48 -keyout tool_sm9_bin_bob_shared_key.bin) +gmssl_files_equal(tool_sm9_bin_alice_shared_key.bin tool_sm9_bin_bob_shared_key.bin) + +execute_process( + COMMAND ${GMSSL_BIN} sm9exch -stage init + -pubmaster tool_sm9_enc_mpk.pem -peer_id Bob + -exch_keyout tool_sm9_stdio_alice_ra.pem + OUTPUT_FILE tool_sm9_stdio_ra.hex + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT TEST_RESULT EQUAL 0) + message(FATAL_ERROR "command failed: ${GMSSL_BIN} sm9exch -stage init\nstderr: ${TEST_STDERR}") +endif() +execute_process( + COMMAND ${GMSSL_BIN} sm9exch -stage respond + -pubmaster tool_sm9_enc_mpk.pem + -key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob + -peer_id ${SM9_ID} + -exch_keyout tool_sm9_stdio_bob_rb.pem + INPUT_FILE tool_sm9_stdio_ra.hex + OUTPUT_FILE tool_sm9_stdio_rb_sb.hex + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT TEST_RESULT EQUAL 0) + message(FATAL_ERROR "command failed: ${GMSSL_BIN} sm9exch -stage respond\nstderr: ${TEST_STDERR}") +endif() +execute_process( + COMMAND ${GMSSL_BIN} sm9exch -stage confirm + -pubmaster tool_sm9_enc_mpk.pem + -key tool_sm9_alice_exch_key.pem -pass ${SM9_USER_PASS} -id ${SM9_ID} + -peer_id Bob -exch_key tool_sm9_stdio_alice_ra.pem + -keylen 48 -keyout tool_sm9_stdio_alice_shared_key.hex + INPUT_FILE tool_sm9_stdio_rb_sb.hex + OUTPUT_FILE tool_sm9_stdio_sa.hex + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT TEST_RESULT EQUAL 0) + message(FATAL_ERROR "command failed: ${GMSSL_BIN} sm9exch -stage confirm\nstderr: ${TEST_STDERR}") +endif() +execute_process( + COMMAND ${GMSSL_BIN} sm9exch -stage finish + -pubmaster tool_sm9_enc_mpk.pem + -key tool_sm9_bob_exch_key.pem -pass ${SM9_USER_PASS} -id Bob + -peer_id ${SM9_ID} + -exch_key tool_sm9_stdio_bob_rb.pem + -keylen 48 -keyout tool_sm9_stdio_bob_shared_key.hex + INPUT_FILE tool_sm9_stdio_sa.hex + RESULT_VARIABLE TEST_RESULT + ERROR_VARIABLE TEST_STDERR +) +if(NOT TEST_RESULT EQUAL 0) + message(FATAL_ERROR "command failed: ${GMSSL_BIN} sm9exch -stage finish\nstderr: ${TEST_STDERR}") +endif() +gmssl_files_equal(tool_sm9_stdio_alice_shared_key.hex tool_sm9_stdio_bob_shared_key.hex) diff --git a/cmake/tool_zuc.cmake b/cmake/tool_zuc.cmake index 1eba9835..9c741167 100644 --- a/cmake/tool_zuc.cmake +++ b/cmake/tool_zuc.cmake @@ -8,3 +8,25 @@ gmssl_run(zuc -key ${ZUC_KEY} -iv ${ZUC_IV} -in tool_zuc.plain -out tool_zuc.cip gmssl_expect_file_hex(tool_zuc.cipher "178fec4735b5b4edbfed84d4fc7cda00") gmssl_run(zuc -key ${ZUC_KEY} -iv ${ZUC_IV} -in tool_zuc.cipher -out tool_zuc.decrypt) gmssl_files_equal(tool_zuc.plain tool_zuc.decrypt) + +gmssl_run(zuc_128_eea3 + -key 173d14ba5003731d7a60049470f00a29 + -count 0x66035492 -bearer 15 -direction 0 + -in_hex 6cf65340735552ab0c9752fa6f9025fe0bd675d9005875b2 + -out tool_zuc_128_eea3.cipher) +gmssl_expect_file_hex(tool_zuc_128_eea3.cipher + "a6c85fc66afb8533aafc2518dfe784940ee1e4b030238cc8") +gmssl_expect_stdout("390a91b7\n" zuc_128_eia3 + -key 00000000000000000000000000000000 + -count 0 -bearer 0 -direction 0 -in_hex 00) + +gmssl_run(zuc256 + -key 0000000000000000000000000000000000000000000000000000000000000000 + -iv 0000000000000000000000000000000000000000000000 + -in tool_zuc.plain -out tool_zuc256.cipher) +gmssl_expect_file_hex(tool_zuc256.cipher "68e108e51a361ad5e2c509585ad9ae65") +gmssl_run(zuc256 + -key 0000000000000000000000000000000000000000000000000000000000000000 + -iv 0000000000000000000000000000000000000000000000 + -in tool_zuc256.cipher -out tool_zuc256.decrypt) +gmssl_files_equal(tool_zuc.plain tool_zuc256.decrypt) diff --git a/include/gmssl/quic.h b/include/gmssl/quic.h index 1308b5e2..77671171 100644 --- a/include/gmssl/quic.h +++ b/include/gmssl/quic.h @@ -13,6 +13,9 @@ #include #include +#include +#include +#include #ifdef __cplusplus @@ -31,6 +34,40 @@ extern "C" { #define QUIC_TRANSPORT_PARAM_MAX_SIZE 512 +typedef enum { + QUIC_packet_initial = 0, + QUIC_packet_0_rtt = 1, + QUIC_packet_handshake = 2, + QUIC_packet_retry = 3, +} QUIC_PACKET_TYPE; + +typedef enum { + QUIC_frame_padding = 0x00, + QUIC_frame_ping = 0x01, + QUIC_frame_ack = 0x02, + QUIC_frame_ack_ecn = 0x03, + QUIC_frame_reset_stream = 0x04, + QUIC_frame_stop_sending = 0x05, + QUIC_frame_crypto = 0x06, + QUIC_frame_new_token = 0x07, + QUIC_frame_stream_base = 0x08, + QUIC_frame_max_data = 0x10, + QUIC_frame_max_stream_data = 0x11, + QUIC_frame_max_streams_bidi = 0x12, + QUIC_frame_max_streams_uni = 0x13, + QUIC_frame_data_blocked = 0x14, + QUIC_frame_stream_data_blocked = 0x15, + QUIC_frame_streams_blocked_bidi = 0x16, + QUIC_frame_streams_blocked_uni = 0x17, + QUIC_frame_new_connection_id = 0x18, + QUIC_frame_retire_connection_id = 0x19, + QUIC_frame_path_challenge = 0x1a, + QUIC_frame_path_response = 0x1b, + QUIC_frame_connection_close = 0x1c, + QUIC_frame_connection_close_app = 0x1d, + QUIC_frame_handshake_done = 0x1e, +} QUIC_FRAME_TYPE; + typedef enum { QUIC_transport_param_original_destination_connection_id = 0x00, QUIC_transport_param_max_idle_timeout = 0x01, @@ -70,6 +107,25 @@ typedef struct { uint8_t hp[QUIC_INITIAL_HP_KEY_SIZE]; // hp = header protection key } QUIC_INITIAL_KEYS; +typedef struct { + int cipher_suite; + uint8_t key[QUIC_INITIAL_KEY_SIZE]; + uint8_t iv[QUIC_INITIAL_IV_SIZE]; + uint8_t hp[QUIC_INITIAL_HP_KEY_SIZE]; /* hp is Header Protection. QUIC protects packet number bytes and selected header bits separately from payload AEAD. */ +} QUIC_PACKET_KEYS; + +typedef struct { + int type; + uint64_t packet_number; + size_t packet_len; + uint8_t dcid[20]; + size_t dcid_len; + uint8_t scid[20]; + size_t scid_len; + uint8_t plaintext[4096]; + size_t plaintext_len; +} QUIC_DECRYPTED_PACKET; + typedef struct { uint64_t id; uint8_t data[QUIC_TRANSPORT_PARAM_MAX_SIZE]; @@ -104,6 +160,30 @@ int quic_derive_initial_secrets(const uint8_t *dcid, size_t dcid_len, QUIC_INITI int quic_derive_initial_client_keys(const QUIC_INITIAL_SECRETS *secrets, QUIC_INITIAL_KEYS *keys); int quic_derive_initial_server_keys(const QUIC_INITIAL_SECRETS *secrets, QUIC_INITIAL_KEYS *keys); int quic_derive_initial_keys(const uint8_t secret[QUIC_INITIAL_SECRET_SIZE], QUIC_INITIAL_KEYS *keys); +int quic_packet_keys_derive(const DIGEST *digest, const uint8_t secret[32], int cipher_suite, QUIC_PACKET_KEYS *keys); +int quic_packet_keys_from_initial(const QUIC_INITIAL_KEYS *initial_keys, QUIC_PACKET_KEYS *keys); + +int quic_packet_total_length(const uint8_t *packet, size_t packet_len, size_t *total_len); +int quic_long_packet_decrypt(const QUIC_PACKET_KEYS *keys, const uint8_t *packet, size_t packet_len, int expected_type, QUIC_DECRYPTED_PACKET *out); +int quic_long_packet_encrypt(const QUIC_PACKET_KEYS *keys, int type, const uint8_t *dcid, size_t dcid_len, + const uint8_t *scid, size_t scid_len, uint64_t packet_number, const uint8_t *frames, size_t frames_len, + uint8_t *packet, size_t *packet_len); +int quic_short_packet_decrypt(const QUIC_PACKET_KEYS *keys, const uint8_t *packet, size_t packet_len, + const uint8_t *dcid, size_t dcid_len, QUIC_DECRYPTED_PACKET *out); +int quic_short_packet_encrypt(const QUIC_PACKET_KEYS *keys, const uint8_t *dcid, size_t dcid_len, + uint64_t packet_number, const uint8_t *frames, size_t frames_len, uint8_t *packet, size_t *packet_len); + +int quic_client_hello_to_bytes_ex(TLS_CONNECT *conn, const QUIC_TRANSPORT_PARAMS *params, uint8_t **out, size_t *outlen); +int quic_client_hello_to_bytes(TLS_CONNECT *conn, uint8_t **out, size_t *outlen); +int quic_server_hello_to_bytes(TLS_CONNECT *conn, uint8_t **out, size_t *outlen); + +const char *quic_packet_type_name(int type); +const char *quic_frame_type_name(uint64_t type); +const char *quic_encryption_level_name(int level); +int quic_packet_print(FILE *fp, int fmt, int ind, const uint8_t *packet, size_t packetlen); +int quic_frames_print(FILE *fp, int fmt, int ind, int level, const uint8_t *frames, size_t frameslen); +int quic_frame_print(FILE *fp, int fmt, int ind, int level, const uint8_t **in, size_t *inlen); +int quic_crypto_data_print(FILE *fp, int fmt, int ind, int level, const uint8_t *data, size_t datalen); #ifdef __cplusplus diff --git a/include/gmssl/version.h b/include/gmssl/version.h index e82458ad..e02cceea 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.1168" +#define GMSSL_VERSION_STR "GmSSL 3.3.0-dev.1169" int gmssl_version_num(void); const char *gmssl_version_str(void); diff --git a/src/quic.c b/src/quic.c index 7aabe2b3..4089d3d7 100644 --- a/src/quic.c +++ b/src/quic.c @@ -9,7 +9,11 @@ +#include #include +#include +#include +#include #include #include #include @@ -24,6 +28,45 @@ static const uint8_t quic_v1_initial_salt[20] = { 0xcc, 0xbb, 0x7f, 0x0a, }; +int tls13_generate_early_keys(TLS_CONNECT *conn); +int tls13_generate_handshake_secrets(TLS_CONNECT *conn); +int tls13_generate_master_secret(TLS_CONNECT *conn); +int tls13_generate_client_handshake_keys(TLS_CONNECT *conn); +int tls13_generate_server_handshake_keys(TLS_CONNECT *conn); +int tls13_handshake_print(FILE *fp, int fmt, int ind, const uint8_t *handshake, size_t handshake_len); + + +static int quic_tls_handshake_to_bytes(TLS_CONNECT *conn, int level, uint8_t **out, size_t *outlen) +{ + const uint8_t *handshake; + size_t handshake_len; + + if (!conn || !outlen) { + error_print(); + return -1; + } + if (conn->recordlen < TLS_RECORD_HEADER_SIZE + TLS_HANDSHAKE_HEADER_SIZE + || tls_record_type(conn->record) != TLS_record_handshake + || tls_record_length(conn->record) != conn->recordlen) { + error_print(); + return -1; + } + + handshake = conn->record + TLS_RECORD_HEADER_SIZE; + handshake_len = conn->recordlen - TLS_RECORD_HEADER_SIZE; + if (out && *out) { + memcpy(*out, handshake, handshake_len); + *out += handshake_len; + } + *outlen += handshake_len; + memcpy(conn->plain_record, handshake, handshake_len); + conn->plain_recordlen = handshake_len; + if (conn->verbose) { + quic_crypto_data_print(stderr, 0, 0, level, handshake, handshake_len); + } + return 1; +} + size_t quic_varint_size(uint64_t val) { @@ -120,6 +163,428 @@ int quic_varint_from_bytes(uint64_t *val, const uint8_t **in, size_t *inlen) return 1; } +const char *quic_packet_type_name(int type) +{ + switch (type) { + case QUIC_packet_initial: + return "Initial"; + case QUIC_packet_0_rtt: + return "0-RTT"; + case QUIC_packet_handshake: + return "Handshake"; + case QUIC_packet_retry: + return "Retry"; + default: + return "Unknown"; + } +} + +const char *quic_frame_type_name(uint64_t type) +{ + switch (type) { + case QUIC_frame_padding: + return "PADDING"; + case QUIC_frame_ping: + return "PING"; + case QUIC_frame_ack: + return "ACK"; + case QUIC_frame_ack_ecn: + return "ACK_ECN"; + case QUIC_frame_reset_stream: + return "RESET_STREAM"; + case QUIC_frame_stop_sending: + return "STOP_SENDING"; + case QUIC_frame_crypto: + return "CRYPTO"; + case QUIC_frame_new_token: + return "NEW_TOKEN"; + case QUIC_frame_max_data: + return "MAX_DATA"; + case QUIC_frame_max_stream_data: + return "MAX_STREAM_DATA"; + case QUIC_frame_max_streams_bidi: + return "MAX_STREAMS_BIDI"; + case QUIC_frame_max_streams_uni: + return "MAX_STREAMS_UNI"; + case QUIC_frame_data_blocked: + return "DATA_BLOCKED"; + case QUIC_frame_stream_data_blocked: + return "STREAM_DATA_BLOCKED"; + case QUIC_frame_streams_blocked_bidi: + return "STREAMS_BLOCKED_BIDI"; + case QUIC_frame_streams_blocked_uni: + return "STREAMS_BLOCKED_UNI"; + case QUIC_frame_new_connection_id: + return "NEW_CONNECTION_ID"; + case QUIC_frame_retire_connection_id: + return "RETIRE_CONNECTION_ID"; + case QUIC_frame_path_challenge: + return "PATH_CHALLENGE"; + case QUIC_frame_path_response: + return "PATH_RESPONSE"; + case QUIC_frame_connection_close: + return "CONNECTION_CLOSE"; + case QUIC_frame_connection_close_app: + return "CONNECTION_CLOSE_APP"; + case QUIC_frame_handshake_done: + return "HANDSHAKE_DONE"; + default: + if (type >= 0x08 && type <= 0x0f) { + return "STREAM"; + } + return "UNKNOWN"; + } +} + +const char *quic_encryption_level_name(int level) +{ + switch (level) { + case QUIC_encryption_initial: + return "initial"; + case QUIC_encryption_early_data: + return "early_data"; + case QUIC_encryption_handshake: + return "handshake"; + case QUIC_encryption_application: + return "application"; + default: + return "unknown"; + } +} + +static int quic_bytes_get(const uint8_t **in, size_t *inlen, const uint8_t **data, size_t datalen) +{ + if (!in || !*in || !inlen || !data || *inlen < datalen) { + error_print(); + return -1; + } + *data = *in; + *in += datalen; + *inlen -= datalen; + return 1; +} + +int quic_crypto_data_print(FILE *fp, int fmt, int ind, int level, const uint8_t *data, size_t datalen) +{ + const uint8_t *p = data; + size_t len = datalen; + + if (!fp || (!data && datalen)) { + error_print(); + return -1; + } + + format_print(fp, fmt, ind, "CRYPTO Data\n"); + ind += 4; + format_print(fp, fmt, ind, "Level: %s\n", quic_encryption_level_name(level)); + format_print(fp, fmt, ind, "Length: %zu\n", datalen); + + while (len) { + size_t handshake_len; + + if (len < TLS_HANDSHAKE_HEADER_SIZE) { + format_bytes(fp, fmt, ind, "fragment", p, len); + return 1; + } + handshake_len = TLS_HANDSHAKE_HEADER_SIZE + (((size_t)p[1] << 16) | ((size_t)p[2] << 8) | p[3]); + if (handshake_len > len) { + format_bytes(fp, fmt, ind, "fragment", p, len); + return 1; + } + if (tls13_handshake_print(fp, fmt, ind, p, handshake_len) != 1) { + format_bytes(fp, fmt, ind, "handshake", p, handshake_len); + } + p += handshake_len; + len -= handshake_len; + } + + return 1; +} + +int quic_frame_print(FILE *fp, int fmt, int ind, int level, const uint8_t **in, size_t *inlen) +{ + const uint8_t *p; + size_t len; + uint64_t type; + uint64_t val; + + if (!fp || !in || !*in || !inlen || !*inlen) { + error_print(); + return -1; + } + + p = *in; + len = *inlen; + if (quic_varint_from_bytes(&type, &p, &len) != 1) { + error_print(); + return -1; + } + + format_print(fp, fmt, ind, "Frame\n"); + ind += 4; + format_print(fp, fmt, ind, "Type: %s (0x%" PRIx64 ")\n", quic_frame_type_name(type), type); + + switch (type) { + case QUIC_frame_padding: + while (len && *p == 0) { + p++; + len--; + } + format_print(fp, fmt, ind, "Length: %zu\n", (size_t)(p - *in)); + break; + case QUIC_frame_ping: + case QUIC_frame_handshake_done: + break; + case QUIC_frame_ack: + case QUIC_frame_ack_ecn: + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Largest Acknowledged: %" PRIu64 "\n", val); + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "ACK Delay: %" PRIu64 "\n", val); + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "ACK Range Count: %" PRIu64 "\n", val); + { + uint64_t range_count = val; + uint64_t i; + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "First ACK Range: %" PRIu64 "\n", val); + for (i = 0; i < range_count; i++) { + uint64_t gap; + uint64_t ack_range; + if (quic_varint_from_bytes(&gap, &p, &len) != 1 || quic_varint_from_bytes(&ack_range, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "ACK Range[%" PRIu64 "]: gap=%" PRIu64 ", range=%" PRIu64 "\n", i, gap, ack_range); + } + } + if (type == QUIC_frame_ack_ecn) { + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "ECT0 Count: %" PRIu64 "\n", val); + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "ECT1 Count: %" PRIu64 "\n", val); + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "CE Count: %" PRIu64 "\n", val); + } + break; + case QUIC_frame_reset_stream: + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Stream ID: %" PRIu64 "\n", val); + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Application Error Code: %" PRIu64 "\n", val); + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Final Size: %" PRIu64 "\n", val); + break; + case QUIC_frame_stop_sending: + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Stream ID: %" PRIu64 "\n", val); + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Application Error Code: %" PRIu64 "\n", val); + break; + case QUIC_frame_crypto: + { + uint64_t offset; + uint64_t data_len; + const uint8_t *data; + if (quic_varint_from_bytes(&offset, &p, &len) != 1 || quic_varint_from_bytes(&data_len, &p, &len) != 1) return -1; + if (data_len > len || quic_bytes_get(&p, &len, &data, (size_t)data_len) != 1) return -1; + format_print(fp, fmt, ind, "Offset: %" PRIu64 "\n", offset); + format_print(fp, fmt, ind, "Length: %" PRIu64 "\n", data_len); + quic_crypto_data_print(fp, fmt, ind, level, data, (size_t)data_len); + } + break; + case QUIC_frame_new_token: + { + const uint8_t *token; + if (quic_varint_from_bytes(&val, &p, &len) != 1 || val > len || quic_bytes_get(&p, &len, &token, (size_t)val) != 1) return -1; + format_bytes(fp, fmt, ind, "Token", token, (size_t)val); + } + break; + case QUIC_frame_max_data: + case QUIC_frame_max_streams_bidi: + case QUIC_frame_max_streams_uni: + case QUIC_frame_data_blocked: + case QUIC_frame_streams_blocked_bidi: + case QUIC_frame_streams_blocked_uni: + case QUIC_frame_retire_connection_id: + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Value: %" PRIu64 "\n", val); + break; + case QUIC_frame_max_stream_data: + case QUIC_frame_stream_data_blocked: + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Stream ID: %" PRIu64 "\n", val); + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Value: %" PRIu64 "\n", val); + break; + case QUIC_frame_new_connection_id: + { + uint64_t seq; + uint64_t retire_prior_to; + const uint8_t *cid; + const uint8_t *token; + if (quic_varint_from_bytes(&seq, &p, &len) != 1 || quic_varint_from_bytes(&retire_prior_to, &p, &len) != 1) return -1; + if (!len) return -1; + val = *p++; + len--; + if (val > len || val > 20 || quic_bytes_get(&p, &len, &cid, (size_t)val) != 1 || quic_bytes_get(&p, &len, &token, 16) != 1) return -1; + format_print(fp, fmt, ind, "Sequence Number: %" PRIu64 "\n", seq); + format_print(fp, fmt, ind, "Retire Prior To: %" PRIu64 "\n", retire_prior_to); + format_bytes(fp, fmt, ind, "Connection ID", cid, (size_t)val); + format_bytes(fp, fmt, ind, "Stateless Reset Token", token, 16); + } + break; + case QUIC_frame_path_challenge: + case QUIC_frame_path_response: + { + const uint8_t *data; + if (quic_bytes_get(&p, &len, &data, 8) != 1) return -1; + format_bytes(fp, fmt, ind, "Data", data, 8); + } + break; + case QUIC_frame_connection_close: + case QUIC_frame_connection_close_app: + { + uint64_t reason_len; + const uint8_t *reason; + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Error Code: %" PRIu64 "\n", val); + if (type == QUIC_frame_connection_close) { + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Frame Type: %s (0x%" PRIx64 ")\n", quic_frame_type_name(val), val); + } + if (quic_varint_from_bytes(&reason_len, &p, &len) != 1 || reason_len > len || quic_bytes_get(&p, &len, &reason, (size_t)reason_len) != 1) return -1; + format_bytes(fp, fmt, ind, "Reason Phrase", reason, (size_t)reason_len); + } + break; + default: + if (type >= 0x08 && type <= 0x0f) { + uint64_t stream_id; + uint64_t offset = 0; + uint64_t data_len; + const uint8_t *data; + if (quic_varint_from_bytes(&stream_id, &p, &len) != 1) return -1; + if (type & 0x04) { + if (quic_varint_from_bytes(&offset, &p, &len) != 1) return -1; + } + if (type & 0x02) { + if (quic_varint_from_bytes(&data_len, &p, &len) != 1) return -1; + } else { + data_len = len; + } + if (data_len > len || quic_bytes_get(&p, &len, &data, (size_t)data_len) != 1) return -1; + format_print(fp, fmt, ind, "Stream ID: %" PRIu64 "\n", stream_id); + format_print(fp, fmt, ind, "FIN: %d\n", (type & 0x01) ? 1 : 0); + format_print(fp, fmt, ind, "Offset: %" PRIu64 "\n", offset); + format_print(fp, fmt, ind, "Length: %" PRIu64 "\n", data_len); + format_bytes(fp, fmt, ind, "Data", data, (size_t)data_len); + break; + } + format_bytes(fp, fmt, ind, "Raw", p, len); + p += len; + len = 0; + break; + } + + *in = p; + *inlen = len; + return 1; +} + +int quic_frames_print(FILE *fp, int fmt, int ind, int level, const uint8_t *frames, size_t frameslen) +{ + const uint8_t *p = frames; + size_t len = frameslen; + + if (!fp || (!frames && frameslen)) { + error_print(); + return -1; + } + + format_print(fp, fmt, ind, "Frames\n"); + ind += 4; + format_print(fp, fmt, ind, "Level: %s\n", quic_encryption_level_name(level)); + format_print(fp, fmt, ind, "Length: %zu\n", frameslen); + + while (len) { + if (quic_frame_print(fp, fmt, ind, level, &p, &len) != 1) { + error_print(); + return -1; + } + } + + return 1; +} + +int quic_packet_print(FILE *fp, int fmt, int ind, const uint8_t *packet, size_t packetlen) +{ + const uint8_t *p = packet; + size_t len = packetlen; + uint8_t first; + + if (!fp || !packet || !packetlen) { + error_print(); + return -1; + } + + first = *p++; + len--; + format_print(fp, fmt, ind, "QUIC Packet\n"); + ind += 4; + format_print(fp, fmt, ind, "Header Form: %s\n", (first & 0x80) ? "long" : "short"); + format_print(fp, fmt, ind, "Fixed Bit: %d\n", (first & 0x40) ? 1 : 0); + + if (first & 0x80) { + uint32_t version; + uint8_t type = (first >> 4) & 0x03; + uint8_t pn_len = (first & 0x03) + 1; + uint8_t cid_len; + const uint8_t *cid; + uint64_t val; + + if (len < 4) return -1; + version = ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3]; + p += 4; + len -= 4; + format_print(fp, fmt, ind, "Type: %s (%u)\n", quic_packet_type_name(type), type); + format_print(fp, fmt, ind, "Version: 0x%08x\n", version); + format_print(fp, fmt, ind, "Packet Number Length: %u\n", pn_len); + + if (!len) return -1; + cid_len = *p++; + len--; + if (cid_len > len || quic_bytes_get(&p, &len, &cid, cid_len) != 1) return -1; + format_bytes(fp, fmt, ind, "Destination Connection ID", cid, cid_len); + if (!len) return -1; + cid_len = *p++; + len--; + if (cid_len > len || quic_bytes_get(&p, &len, &cid, cid_len) != 1) return -1; + format_bytes(fp, fmt, ind, "Source Connection ID", cid, cid_len); + + if (type == QUIC_packet_retry) { + if (len >= 16) { + format_bytes(fp, fmt, ind, "Retry Token", p, len - 16); + format_bytes(fp, fmt, ind, "Retry Integrity Tag", p + len - 16, 16); + } else { + format_bytes(fp, fmt, ind, "Retry Token", p, len); + } + return 1; + } + + if (type == QUIC_packet_initial) { + const uint8_t *token; + if (quic_varint_from_bytes(&val, &p, &len) != 1 || val > len || quic_bytes_get(&p, &len, &token, (size_t)val) != 1) return -1; + format_bytes(fp, fmt, ind, "Token", token, (size_t)val); + } + if (quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + format_print(fp, fmt, ind, "Length: %" PRIu64 "\n", val); + format_bytes(fp, fmt, ind, "Protected Payload", p, len); + } else { + format_print(fp, fmt, ind, "Key Phase: %d\n", (first & 0x04) ? 1 : 0); + format_print(fp, fmt, ind, "Packet Number Length: %u\n", (first & 0x03) + 1); + format_bytes(fp, fmt, ind, "Short Header Packet", packet, packetlen); + } + + return 1; +} + void quic_transport_params_init(QUIC_TRANSPORT_PARAMS *params) { if (params) { @@ -369,3 +834,760 @@ int quic_derive_initial_keys(const uint8_t secret[QUIC_INITIAL_SECRET_SIZE], QUI return -1; #endif } + +int quic_packet_keys_derive(const DIGEST *digest, const uint8_t secret[32], int cipher_suite, QUIC_PACKET_KEYS *keys) +{ + if (!digest || !secret || !keys) { + error_print(); + return -1; + } + keys->cipher_suite = cipher_suite; + if (tls13_hkdf_expand_label(digest, secret, "quic key", NULL, 0, sizeof(keys->key), keys->key) != 1 + || tls13_hkdf_expand_label(digest, secret, "quic iv", NULL, 0, sizeof(keys->iv), keys->iv) != 1 + || tls13_hkdf_expand_label(digest, secret, "quic hp", NULL, 0, sizeof(keys->hp), keys->hp) != 1) { + error_print(); + return -1; + } + return 1; +} + +int quic_packet_keys_from_initial(const QUIC_INITIAL_KEYS *initial_keys, QUIC_PACKET_KEYS *keys) +{ + if (!initial_keys || !keys) { + error_print(); + return -1; + } + keys->cipher_suite = TLS_cipher_aes_128_gcm_sha256; + memcpy(keys->key, initial_keys->key, sizeof(keys->key)); + memcpy(keys->iv, initial_keys->iv, sizeof(keys->iv)); + memcpy(keys->hp, initial_keys->hp, sizeof(keys->hp)); + return 1; +} + +int quic_packet_total_length(const uint8_t *packet, size_t packet_len, size_t *total_len) +{ + const uint8_t *p; + size_t len; + uint8_t dcid_len; + uint8_t scid_len; + uint64_t val; + uint64_t payload_len; + + if (!packet || !total_len || !packet_len) { + error_print(); + return -1; + } + if (!(packet[0] & 0x80)) { + *total_len = packet_len; + return 1; + } + if (packet_len < 7) { + error_print(); + return -1; + } + p = packet + 5; + len = packet_len - 5; + dcid_len = *p++; + len--; + if (dcid_len > len) { + error_print(); + return -1; + } + p += dcid_len; + len -= dcid_len; + if (!len) { + error_print(); + return -1; + } + scid_len = *p++; + len--; + if (scid_len > len) { + error_print(); + return -1; + } + p += scid_len; + len -= scid_len; + if (((packet[0] >> 4) & 0x03) == QUIC_packet_initial) { + if (quic_varint_from_bytes(&val, &p, &len) != 1 || val > len) { + error_print(); + return -1; + } + p += val; + len -= val; + } + if (quic_varint_from_bytes(&payload_len, &p, &len) != 1 || payload_len > len) { + error_print(); + return -1; + } + *total_len = (size_t)(p - packet) + (size_t)payload_len; + return 1; +} + +static int quic_packet_aead_encrypt(const QUIC_PACKET_KEYS *keys, const uint8_t nonce[12], + const uint8_t *aad, size_t aad_len, const uint8_t *in, size_t inlen, uint8_t *out) +{ + AES_KEY aes_key; + SM4_KEY sm4_key; + uint8_t ccm_plaintext[4096]; + const uint8_t *ccm_in = in; + + if (!keys || !nonce || !aad || !out || (!in && inlen)) { + error_print(); + return -1; + } + switch (keys->cipher_suite) { + case TLS_cipher_aes_128_gcm_sha256: + if (aes_set_encrypt_key(&aes_key, keys->key, sizeof(keys->key)) != 1) { + error_print(); + return -1; + } + return aes_gcm_encrypt(&aes_key, nonce, 12, aad, aad_len, in, inlen, out, 16, out + inlen); + case TLS_cipher_aes_128_ccm_sha256: +#ifdef ENABLE_AES_CCM + if (aes_set_encrypt_key(&aes_key, keys->key, sizeof(keys->key)) != 1) { + error_print(); + return -1; + } + if (in == out && inlen) { + if (inlen > sizeof(ccm_plaintext)) { + error_print(); + return -1; + } + memcpy(ccm_plaintext, in, inlen); + ccm_in = ccm_plaintext; + } + return aes_ccm_encrypt(&aes_key, nonce, 12, aad, aad_len, ccm_in, inlen, out, 16, out + inlen); +#else + error_print(); + return -1; +#endif + case TLS_cipher_sm4_gcm_sm3: + sm4_set_encrypt_key(&sm4_key, keys->key); + return sm4_gcm_encrypt(&sm4_key, nonce, 12, aad, aad_len, in, inlen, out, 16, out + inlen); + case TLS_cipher_sm4_ccm_sm3: +#ifdef ENABLE_SM4_CCM + sm4_set_encrypt_key(&sm4_key, keys->key); + if (in == out && inlen) { + if (inlen > sizeof(ccm_plaintext)) { + error_print(); + return -1; + } + memcpy(ccm_plaintext, in, inlen); + ccm_in = ccm_plaintext; + } + return sm4_ccm_encrypt(&sm4_key, nonce, 12, aad, aad_len, ccm_in, inlen, out, 16, out + inlen); +#else + error_print(); + return -1; +#endif + default: + error_print(); + return -1; + } +} + +static int quic_packet_aead_decrypt(const QUIC_PACKET_KEYS *keys, const uint8_t nonce[12], + const uint8_t *aad, size_t aad_len, const uint8_t *in, size_t inlen, uint8_t *out) +{ + AES_KEY aes_key; + SM4_KEY sm4_key; + + if (!keys || !nonce || !aad || !in || !out || inlen < 16) { + error_print(); + return -1; + } + switch (keys->cipher_suite) { + case TLS_cipher_aes_128_gcm_sha256: + if (aes_set_encrypt_key(&aes_key, keys->key, sizeof(keys->key)) != 1) { + error_print(); + return -1; + } + return aes_gcm_decrypt(&aes_key, nonce, 12, aad, aad_len, in, inlen - 16, in + inlen - 16, 16, out); + case TLS_cipher_aes_128_ccm_sha256: +#ifdef ENABLE_AES_CCM + if (aes_set_encrypt_key(&aes_key, keys->key, sizeof(keys->key)) != 1) { + error_print(); + return -1; + } + return aes_ccm_decrypt(&aes_key, nonce, 12, aad, aad_len, in, inlen - 16, in + inlen - 16, 16, out); +#else + error_print(); + return -1; +#endif + case TLS_cipher_sm4_gcm_sm3: + sm4_set_encrypt_key(&sm4_key, keys->key); + return sm4_gcm_decrypt(&sm4_key, nonce, 12, aad, aad_len, in, inlen - 16, in + inlen - 16, 16, out); + case TLS_cipher_sm4_ccm_sm3: +#ifdef ENABLE_SM4_CCM + sm4_set_encrypt_key(&sm4_key, keys->key); + return sm4_ccm_decrypt(&sm4_key, nonce, 12, aad, aad_len, in, inlen - 16, in + inlen - 16, 16, out); +#else + error_print(); + return -1; +#endif + default: + error_print(); + return -1; + } +} + +static int quic_packet_hp_mask(const QUIC_PACKET_KEYS *keys, const uint8_t sample[16], uint8_t mask[16]) +{ + AES_KEY aes_key; + SM4_KEY sm4_key; + + if (!keys || !sample || !mask) { + error_print(); + return -1; + } + switch (keys->cipher_suite) { + case TLS_cipher_aes_128_gcm_sha256: + case TLS_cipher_aes_128_ccm_sha256: + if (aes_set_encrypt_key(&aes_key, keys->hp, sizeof(keys->hp)) != 1) { + error_print(); + return -1; + } + aes_encrypt(&aes_key, sample, mask); + return 1; + case TLS_cipher_sm4_gcm_sm3: + case TLS_cipher_sm4_ccm_sm3: + sm4_set_encrypt_key(&sm4_key, keys->hp); + sm4_encrypt(&sm4_key, sample, mask); + return 1; + default: + error_print(); + return -1; + } +} + +int quic_long_packet_decrypt(const QUIC_PACKET_KEYS *keys, const uint8_t *packet, size_t packet_len, int expected_type, QUIC_DECRYPTED_PACKET *out) +{ + uint8_t buf[4096]; + const uint8_t *p; + size_t len; + uint64_t val; + uint64_t long_packet_len; + uint8_t dcid_len; + uint8_t scid_len; + size_t pn_offset; + size_t pn_len; + uint64_t packet_number = 0; + uint8_t nonce[12]; + uint8_t mask[16]; + size_t aad_len; + size_t ciphertext_len; + size_t i; + + if (!keys || !packet || !out || packet_len > sizeof(buf) || packet_len < 7 || !(packet[0] & 0x80)) { + error_print(); + return -1; + } + memset(out, 0, sizeof(*out)); + memcpy(buf, packet, packet_len); + out->type = (buf[0] >> 4) & 0x03; + if (out->type != expected_type) { + return 0; + } + p = buf + 5; + len = packet_len - 5; + if (!len) return -1; + dcid_len = *p++; + len--; + if (dcid_len > len || dcid_len > sizeof(out->dcid)) return -1; + memcpy(out->dcid, p, dcid_len); + out->dcid_len = dcid_len; + p += dcid_len; + len -= dcid_len; + if (!len) return -1; + scid_len = *p++; + len--; + if (scid_len > len || scid_len > sizeof(out->scid)) return -1; + memcpy(out->scid, p, scid_len); + out->scid_len = scid_len; + p += scid_len; + len -= scid_len; + if (out->type == QUIC_packet_initial) { + if (quic_varint_from_bytes(&val, &p, &len) != 1 || val > len) return -1; + p += val; + len -= val; + } + if (quic_varint_from_bytes(&long_packet_len, &p, &len) != 1 || long_packet_len > len) return -1; + out->packet_len = (size_t)(p - buf) + (size_t)long_packet_len; + pn_offset = (size_t)(p - buf); + if (pn_offset + 4 + 16 > packet_len) return -1; + if (quic_packet_hp_mask(keys, buf + pn_offset + 4, mask) != 1) return -1; + buf[0] ^= mask[0] & 0x0f; + pn_len = (buf[0] & 0x03) + 1; + if (pn_len > long_packet_len) return -1; + for (i = 0; i < pn_len; i++) { + buf[pn_offset + i] ^= mask[i + 1]; + packet_number = (packet_number << 8) | buf[pn_offset + i]; + } + memcpy(nonce, keys->iv, sizeof(nonce)); + for (i = 0; i < 8; i++) { + nonce[sizeof(nonce) - 1 - i] ^= (uint8_t)(packet_number >> (8 * i)); + } + aad_len = pn_offset + pn_len; + ciphertext_len = (size_t)long_packet_len - pn_len; + if (ciphertext_len < 16 || ciphertext_len - 16 > sizeof(out->plaintext) + || quic_packet_aead_decrypt(keys, nonce, buf, aad_len, buf + aad_len, ciphertext_len, out->plaintext) != 1) { + error_print(); + return -1; + } + out->packet_number = packet_number; + out->plaintext_len = ciphertext_len - 16; + return 1; +} + +int quic_long_packet_encrypt(const QUIC_PACKET_KEYS *keys, int type, const uint8_t *dcid, size_t dcid_len, + const uint8_t *scid, size_t scid_len, uint64_t packet_number, const uint8_t *frames, size_t frames_len, + uint8_t *packet, size_t *packet_len) +{ + uint8_t *p = packet; + uint8_t *pn; + uint8_t *payload; + size_t len = 0; + size_t payload_len = frames_len + 16; + uint8_t nonce[12]; + uint8_t mask[16]; + size_t i; + + if (!keys || !dcid || dcid_len > 20 || !scid || scid_len > 20 || !frames || !packet || !packet_len) { + error_print(); + return -1; + } + *p++ = (uint8_t)(0xc0 | (type << 4) | 0x03); len++; + *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x01; len += 4; + *p++ = (uint8_t)dcid_len; len++; + memcpy(p, dcid, dcid_len); p += dcid_len; len += dcid_len; + *p++ = (uint8_t)scid_len; len++; + memcpy(p, scid, scid_len); p += scid_len; len += scid_len; + if (type == QUIC_packet_initial && quic_varint_to_bytes(0, &p, &len) != 1) { + error_print(); + return -1; + } + if (quic_varint_to_bytes(4 + payload_len, &p, &len) != 1) { + error_print(); + return -1; + } + pn = p; + *p++ = (uint8_t)(packet_number >> 24); + *p++ = (uint8_t)(packet_number >> 16); + *p++ = (uint8_t)(packet_number >> 8); + *p++ = (uint8_t)packet_number; + len += 4; + payload = p; + memcpy(payload, frames, frames_len); + memcpy(nonce, keys->iv, sizeof(nonce)); + for (i = 0; i < 8; i++) { + nonce[sizeof(nonce) - 1 - i] ^= (uint8_t)(packet_number >> (8 * i)); + } + if (quic_packet_aead_encrypt(keys, nonce, packet, len, payload, frames_len, payload) != 1) { + error_print(); + return -1; + } + if (quic_packet_hp_mask(keys, payload, mask) != 1) return -1; + packet[0] ^= mask[0] & 0x0f; + for (i = 0; i < 4; i++) { + pn[i] ^= mask[i + 1]; + } + *packet_len = len + payload_len; + return 1; +} + +int quic_short_packet_decrypt(const QUIC_PACKET_KEYS *keys, const uint8_t *packet, size_t packet_len, + const uint8_t *dcid, size_t dcid_len, QUIC_DECRYPTED_PACKET *out) +{ + uint8_t buf[4096]; + size_t pn_offset; + size_t pn_len; + uint64_t packet_number = 0; + uint8_t nonce[12]; + uint8_t mask[16]; + size_t aad_len; + size_t ciphertext_len; + size_t i; + + if (!keys || !packet || !dcid || !out || packet_len > sizeof(buf) || packet_len < 1 + dcid_len + 4 + 16 || (packet[0] & 0x80)) { + error_print(); + return -1; + } + memset(out, 0, sizeof(*out)); + memcpy(buf, packet, packet_len); + out->type = QUIC_encryption_application; + memcpy(out->dcid, dcid, dcid_len); + out->dcid_len = dcid_len; + pn_offset = 1 + dcid_len; + if (quic_packet_hp_mask(keys, buf + pn_offset + 4, mask) != 1) return -1; + buf[0] ^= mask[0] & 0x1f; + pn_len = (buf[0] & 0x03) + 1; + if (pn_offset + pn_len + 16 > packet_len) { + error_print(); + return -1; + } + for (i = 0; i < pn_len; i++) { + buf[pn_offset + i] ^= mask[i + 1]; + packet_number = (packet_number << 8) | buf[pn_offset + i]; + } + memcpy(nonce, keys->iv, sizeof(nonce)); + for (i = 0; i < 8; i++) { + nonce[sizeof(nonce) - 1 - i] ^= (uint8_t)(packet_number >> (8 * i)); + } + aad_len = pn_offset + pn_len; + ciphertext_len = packet_len - aad_len; + if (ciphertext_len < 16 || ciphertext_len - 16 > sizeof(out->plaintext) + || quic_packet_aead_decrypt(keys, nonce, buf, aad_len, buf + aad_len, ciphertext_len, out->plaintext) != 1) { + error_print(); + return -1; + } + out->packet_number = packet_number; + out->packet_len = packet_len; + out->plaintext_len = ciphertext_len - 16; + return 1; +} + +int quic_short_packet_encrypt(const QUIC_PACKET_KEYS *keys, const uint8_t *dcid, size_t dcid_len, + uint64_t packet_number, const uint8_t *frames, size_t frames_len, uint8_t *packet, size_t *packet_len) +{ + uint8_t *pn; + uint8_t *payload; + size_t len = 0; + size_t payload_len = frames_len + 16; + uint8_t nonce[12]; + uint8_t mask[16]; + size_t i; + + if (!keys || !dcid || dcid_len > 20 || !frames || !packet || !packet_len) { + error_print(); + return -1; + } + packet[len++] = 0x43; + memcpy(packet + len, dcid, dcid_len); + len += dcid_len; + pn = packet + len; + packet[len++] = (uint8_t)(packet_number >> 24); + packet[len++] = (uint8_t)(packet_number >> 16); + packet[len++] = (uint8_t)(packet_number >> 8); + packet[len++] = (uint8_t)packet_number; + payload = packet + len; + memcpy(payload, frames, frames_len); + memcpy(nonce, keys->iv, sizeof(nonce)); + for (i = 0; i < 8; i++) { + nonce[sizeof(nonce) - 1 - i] ^= (uint8_t)(packet_number >> (8 * i)); + } + if (quic_packet_aead_encrypt(keys, nonce, packet, len, payload, frames_len, payload) != 1) { + error_print(); + return -1; + } + if (quic_packet_hp_mask(keys, payload, mask) != 1) return -1; + packet[0] ^= mask[0] & 0x1f; + for (i = 0; i < 4; i++) { + pn[i] ^= mask[i + 1]; + } + *packet_len = len + payload_len; + return 1; +} + +int quic_client_hello_to_bytes_ex(TLS_CONNECT *conn, const QUIC_TRANSPORT_PARAMS *params, uint8_t **out, size_t *outlen) +{ + const uint8_t *legacy_session_id = NULL; + size_t legacy_session_id_len = 0; + uint8_t exts[TLS_MAX_EXTENSIONS_SIZE]; + uint8_t *pexts = exts; + size_t extslen = 0; + + if (!conn || !outlen) { + error_print(); + return -1; + } + if (conn->recordlen) { + return quic_tls_handshake_to_bytes(conn, QUIC_encryption_initial, out, outlen); + } + + if (conn->verbose) tls_trace("build QUIC ClientHello\n"); + + tls_record_set_protocol(conn->record, TLS_protocol_tls1); + + if (tls13_random_generate(conn->client_random) != 1) { + error_print(); + return -1; + } + + conn->session_id_len = 0; + + if (tls13_client_supported_versions_ext_to_bytes(conn->ctx->supported_versions, + conn->ctx->supported_versions_cnt, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + + if (conn->ctx->supported_groups_cnt) { + if (tls_supported_groups_ext_to_bytes(conn->ctx->supported_groups, + conn->ctx->supported_groups_cnt, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->ctx->signature_algorithms_cnt) { + if (tls_signature_algorithms_ext_to_bytes(conn->ctx->signature_algorithms, + conn->ctx->signature_algorithms_cnt, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->signature_algorithms_cert) { + if (tls13_signature_algorithms_cert_ext_to_bytes(conn->ctx->signature_algorithms, + conn->ctx->signature_algorithms_cnt, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->certificate_authorities) { + if (tls13_certificate_authorities_ext_to_bytes(conn->ctx->ca_names, + conn->ctx->ca_names_len, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->key_share) { + size_t i; + + for (i = 0; i < conn->ctx->key_exchanges_cnt && i < conn->ctx->supported_groups_cnt; i++) { + int curve_oid = tls_named_curve_oid(conn->ctx->supported_groups[i]); + if (x509_key_generate(&conn->key_exchanges[i], OID_ec_public_key, &curve_oid, sizeof(curve_oid)) != 1) { + error_print(); + return -1; + } + } + conn->key_exchanges_cnt = i; + if (tls13_key_share_client_hello_ext_to_bytes(conn->key_exchanges, + conn->key_exchanges_cnt, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->server_name) { + if (tls_server_name_ext_to_bytes(conn->host_name, conn->host_name_len, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->ctx->alpn_protocols_cnt) { + if (tls_application_layer_protocol_negotiation_ext_to_bytes(conn->ctx->alpn_protocols, + conn->ctx->alpn_protocols_cnt, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (params) { + uint8_t transport_params[QUIC_TRANSPORT_PARAM_MAX_SIZE]; + uint8_t *ptransport_params = transport_params; + size_t transport_params_len = 0; + if (quic_transport_params_to_bytes(params, &ptransport_params, &transport_params_len) != 1 + || tls_ext_to_bytes(TLS_extension_quic_transport_parameters, + transport_params, transport_params_len, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->key_exchange_modes & (TLS_KE_PSK_DHE|TLS_KE_PSK)) { + if (tls13_psk_key_exchange_modes_ext_to_bytes(conn->key_exchange_modes, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->status_request) { + if (tls_client_status_request_ext_to_bytes(TLS_certificate_status_type_ocsp, + conn->status_request_responder_id_list, conn->status_request_responder_id_list_len, + conn->status_request_exts, conn->status_request_exts_len, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + conn->status_request = 0; + } + + if (conn->signed_certificate_timestamp) { + if (tls_ext_to_bytes(TLS_extension_signed_certificate_timestamp, NULL, 0, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + conn->signed_certificate_timestamp = 0; + } + + if (conn->early_data) { + if (tls_ext_to_bytes(TLS_extension_early_data, NULL, 0, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->post_handshake_auth) { + if (tls_ext_to_bytes(TLS_extension_post_handshake_auth, NULL, 0, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->pre_shared_key) { + uint8_t *ptruncated_exts = pexts; + size_t truncated_extslen = extslen; + uint8_t binders[256]; + uint8_t *pbinders = binders; + size_t binderslen = 0; + + (void)pbinders; + if (!conn->psk_identities_len || !conn->psk_keys_len || !conn->psk_cipher_suites_cnt) { + error_print(); + return -1; + } + if (tls13_psk_binders_generate_empty(conn->psk_cipher_suites, conn->psk_cipher_suites_cnt, binders, &binderslen) != 1 + || tls13_client_pre_shared_key_ext_to_bytes(conn->psk_identities, conn->psk_identities_len, + binders, binderslen, &ptruncated_exts, &truncated_extslen) != 1) { + error_print(); + return -1; + } + if (tls_record_set_handshake_client_hello(conn->record, &conn->recordlen, + TLS_protocol_tls12, conn->client_random, legacy_session_id, legacy_session_id_len, + conn->ctx->cipher_suites, conn->ctx->cipher_suites_cnt, exts, truncated_extslen) != 1) { + error_print(); + return -1; + } + if (tls13_psk_binders_generate(conn->psk_cipher_suites, conn->psk_cipher_suites_cnt, + conn->psk_identity_types, conn->psk_keys, conn->psk_keys_len, + conn->record + TLS_RECORD_HEADER_SIZE, conn->recordlen - TLS_RECORD_HEADER_SIZE - 2 - binderslen, + binders, &binderslen) != 1 + || tls13_client_pre_shared_key_ext_to_bytes(conn->psk_identities, conn->psk_identities_len, + binders, binderslen, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (tls_record_set_handshake_client_hello(conn->record, &conn->recordlen, + TLS_protocol_tls12, conn->client_random, legacy_session_id, legacy_session_id_len, + conn->ctx->cipher_suites, conn->ctx->cipher_suites_cnt, exts, extslen) != 1) { + error_print(); + return -1; + } + + if (conn->early_data) { + if (tls13_generate_early_keys(conn) != 1) { + error_print(); + return -1; + } + } + + if (quic_tls_handshake_to_bytes(conn, QUIC_encryption_initial, out, outlen) != 1) { + error_print(); + return -1; + } + + return 1; +} + +int quic_client_hello_to_bytes(TLS_CONNECT *conn, uint8_t **out, size_t *outlen) +{ + return quic_client_hello_to_bytes_ex(conn, NULL, out, outlen); +} + +int quic_server_hello_to_bytes(TLS_CONNECT *conn, uint8_t **out, size_t *outlen) +{ + uint8_t exts[256]; + uint8_t *pexts = exts; + size_t extslen = 0; + + if (!conn || !outlen) { + error_print(); + return -1; + } + if (conn->recordlen) { + return quic_tls_handshake_to_bytes(conn, QUIC_encryption_initial, out, outlen); + } + + if (conn->verbose) tls_trace("build QUIC ServerHello\n"); + + tls_record_set_protocol(conn->record, TLS_protocol_tls12); + + if (tls13_random_generate(conn->server_random) != 1) { + error_print(); + return -1; + } + + if (tls13_server_supported_versions_ext_to_bytes(conn->protocol, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + + if (conn->key_exchange_modes & (TLS_KE_CERT_DHE|TLS_KE_PSK_DHE)) { + int curve_oid; + + if (!conn->key_exchange_group) { + error_print(); + return -1; + } + if ((curve_oid = tls_named_curve_oid(conn->key_exchange_group)) == OID_undef) { + error_print(); + return -1; + } + if (x509_key_generate(&conn->key_exchanges[0], OID_ec_public_key, &curve_oid, sizeof(curve_oid)) != 1) { + error_print(); + return -1; + } + conn->key_exchange_idx = 0; + conn->key_exchanges_cnt = 1; + if (tls13_key_share_server_hello_ext_to_bytes(&conn->key_exchanges[0], &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (conn->selected_psk_identity) { + if (tls13_server_pre_shared_key_ext_to_bytes(conn->selected_psk_identity, &pexts, &extslen) != 1) { + error_print(); + return -1; + } + } + + if (tls_record_set_handshake_server_hello(conn->record, &conn->recordlen, + TLS_protocol_tls12, conn->server_random, conn->session_id, conn->session_id_len, + conn->cipher_suite, exts, extslen) != 1) { + error_print(); + return -1; + } + + if (digest_update(&conn->dgst_ctx, conn->record + TLS_RECORD_HEADER_SIZE, conn->recordlen - TLS_RECORD_HEADER_SIZE) != 1 + || tls13_generate_handshake_secrets(conn) != 1) { + error_print(); + return -1; + } + tls13_generate_master_secret(conn); + + if (tls13_generate_server_handshake_keys(conn) != 1) { + error_print(); + return -1; + } + if (!conn->early_data) { + if (tls13_generate_client_handshake_keys(conn) != 1) { + error_print(); + return -1; + } + } + + if (quic_tls_handshake_to_bytes(conn, QUIC_encryption_initial, out, outlen) != 1) { + error_print(); + return -1; + } + + tls_client_verify_update(&conn->client_verify_ctx, conn->record + TLS_RECORD_HEADER_SIZE, conn->recordlen - TLS_RECORD_HEADER_SIZE); + tls_clean_record(conn); + return 1; +} diff --git a/src/sm9_cms.c b/src/sm9_cms.c new file mode 100644 index 00000000..6aaa5ef8 --- /dev/null +++ b/src/sm9_cms.c @@ -0,0 +1,897 @@ +/* +GM/T 0081-2020 ASN.1 definitions + +IdentifierRevocationLists ::= SET OF IdentifierRevocationList + +ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + +DigestAlgorithmIdentifier ::= AlgorithmIdentifier + +DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + +KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + +Version ::= INTEGER(1) + +ContentInfo ::= SEQUENCE { + contentType ContentType, + content [0] EXPLICIT contentType OPTIONAL +} + +ContentType ::= OBJECT IDENTIFIER + +Identifier ::= SEQUENCE { + version EXPLICIT VERSION DEFAULT v1, + ibcType OBJECT IDENTIFIER, + ibcTypeAlias [0] OCTET STRING OPTIONAL, + identityData OCTET STRING, + validStart UTCTIME, + validEnd [1] UTCTIME OPTIONAL, + extensions [2] Extensions OPTIONAL +} + +Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + +Extension ::= SEQUENCE { + extnID OBJECT IDENTIFIER, + critical BOOLEAN DEFAULT FALSE, + extnValue OCTET STRING +} + +DistricInfo ::= SEQUENCE { + district IA5String, + districtNo INTEGER, +} + +Validity ::= SEQUENCE { + notBefore Time, + notAfter Time, + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime + } +} + +IBCSysParamsPublishInfo ::= SEQUENCE { + ibcSysParams IBCSysParams, + signatureAlgorithm OBJECT IDENTIFIER, + signatureValue BIT STRING +} + +AlgorithmIdentifier ::= SEQUENCE { + Algorithm OBJECT IDENTIFIER, + Parameters ANY DEFINED BY algorithm OPTIONAL +} + +BeSignParamsPubInfo ::= SEQUENCE { + ibcSysParams IBCSysParams, + signatureAlgorithm OBJECT IDENTIFIER +} + +IDAppAttr ::= SEQUENCE { + versoin Version DEFAULT v1, + serialNumber IdentifierSerialNumber, + subjectId Identifier, + sysParamsPublishInfo IBCSysParamsPublishInfo, + extensions [0] EXPLICIT Externsions OPTIONAL +} + +version ::= INTEGER { v1(0) } + +IdentifierSerialNumber ::= INTEGER + +Externsions ::= SEQUENCE SIZE (1..MAX) OF Extension + +Data ::= OCTET STRING + +SignedData ::= SEQUENCE { + version Version, + digestAlgorithms DigestAlgorithmIdentifiers, + contentInfo ContentInfo, + ibcSysParamsPublishInfos [0] IMPLICIT IBCSysParamsPublishInfos OPTIONAL, + irls [1] IMPLICIT IdentifierRevocationLists OPTIONAL, + signerInfos SignerInfos +} + +IBCSysParamsPublishInfos ::= SET OF IBCSysParamsPublishInfo + +DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + +SignerInfos ::= SET OF SignerInfo + +SignerInfo ::= SEQUENCE { + version Version, + issuerIdentifier Identifier, + digestAlgorithm DigestAlgorithmIdentifier, + authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, + digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + encryptedDigest SM9Signature, + unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL +} + +EnvelopedData ::= SEQUENCE { + version Version, + recipientInfos RecipientInfos, + encryptedContentInfo EncryptedContentInfo +} + +RecipientInfos ::= SET OF RecipientInfo + +EncryptedContentInfo ::= SEQUENCE { + contentType ContentType, + contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + sharedInfo [0] OCTET STRING OPTIONAL, + sharedInfo2 [1] OCTET STRING OPTIONAL, + encryptedContent [2] IMPLICIT EncryptedContent OPTIONAL +} + +EncryptedContent ::= OCTET STRING + +RecipientInfo ::= SEQUENCE { + Version Version, + issuerIdentifier Identifier, + keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + encryptedKey SM9cipher +} + +SignedAndEnvelopedData ::= SEQUENCE { + version Version, + recipientInfos RecipientInfos, + digestAlgorithms DigestAlgorithmIdentifiers, + encryptedContentInfo EncryptedContentInfo, + idAppAttrInfos [0] IMPLICIT IDAppAttrInfos OPTIONAL, + irls [1] IMPLICIT IdentifierRevocationLists OPTIONAL, + signerInfos SignerInfos +} + +EncryptedData ::= SEQUENCE { + Version Version, + encryptedContentInfo EncryptedContentInfo +} + +//-- 这之后的类型都需要实现 +KeyAgreementInfo ::= SEQUENCE { + version Version(1), + tempKey SM9MastEncryptPublicKey, + userIDA OCTET STRING + userIDB OCTET STRING + hid OCTET STRING +} + +IdentifierRevocationList ::= SEQUENCE { + tbsIdList TBSIdList, + signInfos SignerInfos +} + +TBSIdList ::= SEQUENCE { + Version(1) Version, + signatureOID AlgorithmIdentifier, + issuerIdentifier Identifier, + thisUpdate GeneralizedTime, + nextUpdate [0] GeneralizedTime OPTIONAL, + revokedIds RevokedIds, + irlExtensions [1] EXPLICIT Extensions OPTIONAL +} + +Version ::= INTEGER(1) + +RevokedIds ::= SEQUENCE OF RevokedId + +RevokedId ::= SEQUENCE { + id OCTET STRING, + revocationDate GeneralizedTime, + IrlEntryExtensions [0] Extensions OPTIONAL +} + +IBCSysParams ::= SEQUENCE { + version INTEGER { v2(2) }, + districtName IA5String, + districtSerial INTEGER, + validity ValidityPeriod, + ibcPublicParameters IBCPublicParameters, + ibcIdentityType OBJECT IDENTIFIER, + issuerID Identifier, + ibcParamExtensions IBCParamExtensions OPTIONAL +} + +IBCPublicParameters ::= SEQUENCE (1..MAX) OF IBCPublicParameter + +IBCPublicParameter ::= SEQUENCE { + ibcAlgorithm OBJECT IDENTIFIER, + publicParameterData OCTET STRING +} + +SM9PublicParameterData ::= SEQUENCE { + pkgID OCTET STRING, + encMastPublicKey SM9EncryptMasterPublicKey, + signMastPublicKey SM9SignMasterPublicKey +} + +IBCParamExtensions ::= SEQUENCE OF IBCParamExtension + +IBCParamExtension ::= SEQUENCE { + ibcParamExtensionOID OBJECT IDENTIFIER, + ibcParamExtensionValue OCTET STRING +} + +IbcParamExt OBJECT IDENTIFIER ::= { + ibcs ibcs3(3) parameter-extensions(2) +} +*/ + +#include +#include +#include +#include +#include +#include +#include + + +int sm9_cms_algorithm_identifier_to_der( + const uint32_t *algorithm, size_t algorithm_cnt, + const uint8_t *parameters, size_t parameters_len, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (asn1_object_identifier_to_der(algorithm, algorithm_cnt, NULL, &len) != 1 + || asn1_any_to_der(parameters, parameters_len, NULL, &len) < 0 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_object_identifier_to_der(algorithm, algorithm_cnt, out, outlen) != 1 + || asn1_any_to_der(parameters, parameters_len, out, outlen) < 0) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_algorithm_identifier_from_der( + uint32_t *algorithm, size_t *algorithm_cnt, + const uint8_t **parameters, size_t *parameters_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + *parameters = NULL; + *parameters_len = 0; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (asn1_object_identifier_from_der(algorithm, algorithm_cnt, &d, &dlen) != 1 + || asn1_any_from_der(parameters, parameters_len, &d, &dlen) < 0 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_extensions_to_der(const uint8_t *extensions, size_t extensions_len, + uint8_t **out, size_t *outlen) +{ + if (asn1_sequence_of_to_der(extensions, extensions_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_extensions_from_der(const uint8_t **extensions, size_t *extensions_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + + if ((ret = asn1_sequence_of_from_der(extensions, extensions_len, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + return 1; +} + +int sm9_cms_explicit_extensions_to_der(int index, + const uint8_t *extensions, size_t extensions_len, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (!extensions) { + return 0; + } + if (sm9_cms_extensions_to_der(extensions, extensions_len, NULL, &len) != 1 + || asn1_explicit_header_to_der(index, len, out, outlen) != 1 + || sm9_cms_extensions_to_der(extensions, extensions_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_explicit_extensions_from_der(int index, + const uint8_t **extensions, size_t *extensions_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + if ((ret = asn1_explicit_from_der(index, &d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + else { + *extensions = NULL; + *extensions_len = 0; + } + return ret; + } + if (sm9_cms_extensions_from_der(extensions, extensions_len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_implicit_extensions_to_der(int index, + const uint8_t *extensions, size_t extensions_len, + uint8_t **out, size_t *outlen) +{ + if (!extensions) { + return 0; + } + if (asn1_implicit_sequence_to_der(index, extensions, extensions_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_implicit_extensions_from_der(int index, + const uint8_t **extensions, size_t *extensions_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + + if ((ret = asn1_implicit_sequence_from_der(index, extensions, extensions_len, in, inlen)) < 0) { + error_print(); + return -1; + } + return ret; +} + +int sm9_cms_key_agreement_info_to_der( + int version, + const SM9_ENC_MASTER_KEY *temp_key, + const uint8_t *user_id_a, size_t user_id_a_len, + const uint8_t *user_id_b, size_t user_id_b_len, + const uint8_t *hid, size_t hid_len, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (version != 1) { + error_print(); + return -1; + } + if (asn1_int_to_der(version, NULL, &len) != 1 + || sm9_enc_master_public_key_to_der(temp_key, NULL, &len) != 1 + || asn1_octet_string_to_der(user_id_a, user_id_a_len, NULL, &len) != 1 + || asn1_octet_string_to_der(user_id_b, user_id_b_len, NULL, &len) != 1 + || asn1_octet_string_to_der(hid, hid_len, NULL, &len) != 1 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_int_to_der(version, out, outlen) != 1 + || sm9_enc_master_public_key_to_der(temp_key, out, outlen) != 1 + || asn1_octet_string_to_der(user_id_a, user_id_a_len, out, outlen) != 1 + || asn1_octet_string_to_der(user_id_b, user_id_b_len, out, outlen) != 1 + || asn1_octet_string_to_der(hid, hid_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_key_agreement_info_from_der( + int *version, + SM9_ENC_MASTER_KEY *temp_key, + const uint8_t **user_id_a, size_t *user_id_a_len, + const uint8_t **user_id_b, size_t *user_id_b_len, + const uint8_t **hid, size_t *hid_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (asn1_int_from_der(version, &d, &dlen) != 1 + || sm9_enc_master_public_key_from_der(temp_key, &d, &dlen) != 1 + || asn1_octet_string_from_der(user_id_a, user_id_a_len, &d, &dlen) != 1 + || asn1_octet_string_from_der(user_id_b, user_id_b_len, &d, &dlen) != 1 + || asn1_octet_string_from_der(hid, hid_len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + if (*version != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_revoked_id_to_der( + const uint8_t *id, size_t id_len, + time_t revocation_date, + const uint8_t *irl_entry_extensions, size_t irl_entry_extensions_len, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (asn1_octet_string_to_der(id, id_len, NULL, &len) != 1 + || asn1_generalized_time_to_der(revocation_date, NULL, &len) != 1 + || sm9_cms_implicit_extensions_to_der(0, irl_entry_extensions, irl_entry_extensions_len, NULL, &len) < 0 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_octet_string_to_der(id, id_len, out, outlen) != 1 + || asn1_generalized_time_to_der(revocation_date, out, outlen) != 1 + || sm9_cms_implicit_extensions_to_der(0, irl_entry_extensions, irl_entry_extensions_len, out, outlen) < 0) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_revoked_id_from_der( + const uint8_t **id, size_t *id_len, + time_t *revocation_date, + const uint8_t **irl_entry_extensions, size_t *irl_entry_extensions_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (asn1_octet_string_from_der(id, id_len, &d, &dlen) != 1 + || asn1_generalized_time_from_der(revocation_date, &d, &dlen) != 1 + || sm9_cms_implicit_extensions_from_der(0, irl_entry_extensions, irl_entry_extensions_len, &d, &dlen) < 0 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_revoked_ids_to_der(const uint8_t *revoked_ids, size_t revoked_ids_len, + uint8_t **out, size_t *outlen) +{ + if (asn1_sequence_of_to_der(revoked_ids, revoked_ids_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_revoked_ids_from_der(const uint8_t **revoked_ids, size_t *revoked_ids_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + + if ((ret = asn1_sequence_of_from_der(revoked_ids, revoked_ids_len, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + return 1; +} + +int sm9_cms_tbs_id_list_to_der( + int version, + const uint32_t *signature_oid, size_t signature_oid_cnt, + const uint8_t *signature_params, size_t signature_params_len, + const uint8_t *issuer_identifier, size_t issuer_identifier_len, + time_t this_update, time_t next_update, + const uint8_t *revoked_ids, size_t revoked_ids_len, + const uint8_t *irl_extensions, size_t irl_extensions_len, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (version != 1) { + error_print(); + return -1; + } + if (asn1_int_to_der(version, NULL, &len) != 1 + || sm9_cms_algorithm_identifier_to_der(signature_oid, signature_oid_cnt, + signature_params, signature_params_len, NULL, &len) != 1 + || asn1_any_to_der(issuer_identifier, issuer_identifier_len, NULL, &len) != 1 + || asn1_generalized_time_to_der(this_update, NULL, &len) != 1 + || asn1_implicit_generalized_time_to_der(0, next_update, NULL, &len) < 0 + || sm9_cms_revoked_ids_to_der(revoked_ids, revoked_ids_len, NULL, &len) != 1 + || sm9_cms_explicit_extensions_to_der(1, irl_extensions, irl_extensions_len, NULL, &len) < 0 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_int_to_der(version, out, outlen) != 1 + || sm9_cms_algorithm_identifier_to_der(signature_oid, signature_oid_cnt, + signature_params, signature_params_len, out, outlen) != 1 + || asn1_any_to_der(issuer_identifier, issuer_identifier_len, out, outlen) != 1 + || asn1_generalized_time_to_der(this_update, out, outlen) != 1 + || asn1_implicit_generalized_time_to_der(0, next_update, out, outlen) < 0 + || sm9_cms_revoked_ids_to_der(revoked_ids, revoked_ids_len, out, outlen) != 1 + || sm9_cms_explicit_extensions_to_der(1, irl_extensions, irl_extensions_len, out, outlen) < 0) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_tbs_id_list_from_der( + int *version, + uint32_t *signature_oid, size_t *signature_oid_cnt, + const uint8_t **signature_params, size_t *signature_params_len, + const uint8_t **issuer_identifier, size_t *issuer_identifier_len, + time_t *this_update, time_t *next_update, + const uint8_t **revoked_ids, size_t *revoked_ids_len, + const uint8_t **irl_extensions, size_t *irl_extensions_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (asn1_int_from_der(version, &d, &dlen) != 1 + || sm9_cms_algorithm_identifier_from_der(signature_oid, signature_oid_cnt, + signature_params, signature_params_len, &d, &dlen) != 1 + || asn1_any_from_der(issuer_identifier, issuer_identifier_len, &d, &dlen) != 1 + || asn1_generalized_time_from_der(this_update, &d, &dlen) != 1 + || asn1_implicit_generalized_time_from_der(0, next_update, &d, &dlen) < 0 + || sm9_cms_revoked_ids_from_der(revoked_ids, revoked_ids_len, &d, &dlen) != 1 + || sm9_cms_explicit_extensions_from_der(1, irl_extensions, irl_extensions_len, &d, &dlen) < 0 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + if (*version != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_signer_infos_to_der(const uint8_t *signer_infos, size_t signer_infos_len, + uint8_t **out, size_t *outlen) +{ + if (asn1_set_of_to_der(signer_infos, signer_infos_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_signer_infos_from_der(const uint8_t **signer_infos, size_t *signer_infos_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + + if ((ret = asn1_set_of_from_der(signer_infos, signer_infos_len, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + return 1; +} + +int sm9_cms_identifier_revocation_list_to_der( + const uint8_t *tbs_id_list, size_t tbs_id_list_len, + const uint8_t *signer_infos, size_t signer_infos_len, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (asn1_any_to_der(tbs_id_list, tbs_id_list_len, NULL, &len) != 1 + || sm9_cms_signer_infos_to_der(signer_infos, signer_infos_len, NULL, &len) != 1 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_any_to_der(tbs_id_list, tbs_id_list_len, out, outlen) != 1 + || sm9_cms_signer_infos_to_der(signer_infos, signer_infos_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_identifier_revocation_list_from_der( + const uint8_t **tbs_id_list, size_t *tbs_id_list_len, + const uint8_t **signer_infos, size_t *signer_infos_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (asn1_any_from_der(tbs_id_list, tbs_id_list_len, &d, &dlen) != 1 + || sm9_cms_signer_infos_from_der(signer_infos, signer_infos_len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_validity_period_to_der(time_t not_before, time_t not_after, + uint8_t **out, size_t *outlen) +{ + return x509_validity_to_der(not_before, not_after, out, outlen); +} + +int sm9_cms_validity_period_from_der(time_t *not_before, time_t *not_after, + const uint8_t **in, size_t *inlen) +{ + return x509_validity_from_der(not_before, not_after, in, inlen); +} + +int sm9_cms_ibc_public_parameter_to_der( + const uint32_t *ibc_algorithm, size_t ibc_algorithm_cnt, + const uint8_t *public_parameter_data, size_t public_parameter_data_len, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (asn1_object_identifier_to_der(ibc_algorithm, ibc_algorithm_cnt, NULL, &len) != 1 + || asn1_octet_string_to_der(public_parameter_data, public_parameter_data_len, NULL, &len) != 1 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_object_identifier_to_der(ibc_algorithm, ibc_algorithm_cnt, out, outlen) != 1 + || asn1_octet_string_to_der(public_parameter_data, public_parameter_data_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_ibc_public_parameter_from_der( + uint32_t *ibc_algorithm, size_t *ibc_algorithm_cnt, + const uint8_t **public_parameter_data, size_t *public_parameter_data_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (asn1_object_identifier_from_der(ibc_algorithm, ibc_algorithm_cnt, &d, &dlen) != 1 + || asn1_octet_string_from_der(public_parameter_data, public_parameter_data_len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_ibc_public_parameters_to_der( + const uint8_t *ibc_public_parameters, size_t ibc_public_parameters_len, + uint8_t **out, size_t *outlen) +{ + if (asn1_sequence_of_to_der(ibc_public_parameters, ibc_public_parameters_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_ibc_public_parameters_from_der( + const uint8_t **ibc_public_parameters, size_t *ibc_public_parameters_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + + if ((ret = asn1_sequence_of_from_der(ibc_public_parameters, ibc_public_parameters_len, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + return 1; +} + +int sm9_cms_sm9_public_parameter_data_to_der( + const uint8_t *pkg_id, size_t pkg_id_len, + const SM9_ENC_MASTER_KEY *enc_mast_public_key, + const SM9_SIGN_MASTER_KEY *sign_mast_public_key, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (asn1_octet_string_to_der(pkg_id, pkg_id_len, NULL, &len) != 1 + || sm9_enc_master_public_key_to_der(enc_mast_public_key, NULL, &len) != 1 + || sm9_sign_master_public_key_to_der(sign_mast_public_key, NULL, &len) != 1 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_octet_string_to_der(pkg_id, pkg_id_len, out, outlen) != 1 + || sm9_enc_master_public_key_to_der(enc_mast_public_key, out, outlen) != 1 + || sm9_sign_master_public_key_to_der(sign_mast_public_key, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_sm9_public_parameter_data_from_der( + const uint8_t **pkg_id, size_t *pkg_id_len, + SM9_ENC_MASTER_KEY *enc_mast_public_key, + SM9_SIGN_MASTER_KEY *sign_mast_public_key, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (asn1_octet_string_from_der(pkg_id, pkg_id_len, &d, &dlen) != 1 + || sm9_enc_master_public_key_from_der(enc_mast_public_key, &d, &dlen) != 1 + || sm9_sign_master_public_key_from_der(sign_mast_public_key, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_ibc_param_extension_to_der( + const uint32_t *ibc_param_extension_oid, size_t ibc_param_extension_oid_cnt, + const uint8_t *ibc_param_extension_value, size_t ibc_param_extension_value_len, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (asn1_object_identifier_to_der(ibc_param_extension_oid, ibc_param_extension_oid_cnt, NULL, &len) != 1 + || asn1_octet_string_to_der(ibc_param_extension_value, ibc_param_extension_value_len, NULL, &len) != 1 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_object_identifier_to_der(ibc_param_extension_oid, ibc_param_extension_oid_cnt, out, outlen) != 1 + || asn1_octet_string_to_der(ibc_param_extension_value, ibc_param_extension_value_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_ibc_param_extension_from_der( + uint32_t *ibc_param_extension_oid, size_t *ibc_param_extension_oid_cnt, + const uint8_t **ibc_param_extension_value, size_t *ibc_param_extension_value_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (asn1_object_identifier_from_der(ibc_param_extension_oid, ibc_param_extension_oid_cnt, &d, &dlen) != 1 + || asn1_octet_string_from_der(ibc_param_extension_value, ibc_param_extension_value_len, &d, &dlen) != 1 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_ibc_param_extensions_to_der( + const uint8_t *ibc_param_extensions, size_t ibc_param_extensions_len, + uint8_t **out, size_t *outlen) +{ + if (!ibc_param_extensions) { + return 0; + } + if (asn1_sequence_of_to_der(ibc_param_extensions, ibc_param_extensions_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_ibc_param_extensions_from_der( + const uint8_t **ibc_param_extensions, size_t *ibc_param_extensions_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + + if ((ret = asn1_sequence_of_from_der(ibc_param_extensions, ibc_param_extensions_len, in, inlen)) < 0) { + error_print(); + return -1; + } + return ret; +} + +int sm9_cms_ibc_sys_params_to_der( + int version, + const char *district_name, size_t district_name_len, + int district_serial, + time_t not_before, time_t not_after, + const uint8_t *ibc_public_parameters, size_t ibc_public_parameters_len, + const uint32_t *ibc_identity_type, size_t ibc_identity_type_cnt, + const uint8_t *issuer_id, size_t issuer_id_len, + const uint8_t *ibc_param_extensions, size_t ibc_param_extensions_len, + uint8_t **out, size_t *outlen) +{ + size_t len = 0; + + if (version != 2) { + error_print(); + return -1; + } + if (asn1_int_to_der(version, NULL, &len) != 1 + || asn1_ia5_string_to_der(district_name, district_name_len, NULL, &len) != 1 + || asn1_int_to_der(district_serial, NULL, &len) != 1 + || sm9_cms_validity_period_to_der(not_before, not_after, NULL, &len) != 1 + || sm9_cms_ibc_public_parameters_to_der(ibc_public_parameters, ibc_public_parameters_len, NULL, &len) != 1 + || asn1_object_identifier_to_der(ibc_identity_type, ibc_identity_type_cnt, NULL, &len) != 1 + || asn1_any_to_der(issuer_id, issuer_id_len, NULL, &len) != 1 + || sm9_cms_ibc_param_extensions_to_der(ibc_param_extensions, ibc_param_extensions_len, NULL, &len) < 0 + || asn1_sequence_header_to_der(len, out, outlen) != 1 + || asn1_int_to_der(version, out, outlen) != 1 + || asn1_ia5_string_to_der(district_name, district_name_len, out, outlen) != 1 + || asn1_int_to_der(district_serial, out, outlen) != 1 + || sm9_cms_validity_period_to_der(not_before, not_after, out, outlen) != 1 + || sm9_cms_ibc_public_parameters_to_der(ibc_public_parameters, ibc_public_parameters_len, out, outlen) != 1 + || asn1_object_identifier_to_der(ibc_identity_type, ibc_identity_type_cnt, out, outlen) != 1 + || asn1_any_to_der(issuer_id, issuer_id_len, out, outlen) != 1 + || sm9_cms_ibc_param_extensions_to_der(ibc_param_extensions, ibc_param_extensions_len, out, outlen) < 0) { + error_print(); + return -1; + } + return 1; +} + +int sm9_cms_ibc_sys_params_from_der( + int *version, + const char **district_name, size_t *district_name_len, + int *district_serial, + time_t *not_before, time_t *not_after, + const uint8_t **ibc_public_parameters, size_t *ibc_public_parameters_len, + uint32_t *ibc_identity_type, size_t *ibc_identity_type_cnt, + const uint8_t **issuer_id, size_t *issuer_id_len, + const uint8_t **ibc_param_extensions, size_t *ibc_param_extensions_len, + const uint8_t **in, size_t *inlen) +{ + int ret; + const uint8_t *d; + size_t dlen; + + if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { + if (ret < 0) error_print(); + return ret; + } + if (asn1_int_from_der(version, &d, &dlen) != 1 + || asn1_ia5_string_from_der(district_name, district_name_len, &d, &dlen) != 1 + || asn1_int_from_der(district_serial, &d, &dlen) != 1 + || sm9_cms_validity_period_from_der(not_before, not_after, &d, &dlen) != 1 + || sm9_cms_ibc_public_parameters_from_der(ibc_public_parameters, ibc_public_parameters_len, &d, &dlen) != 1 + || asn1_object_identifier_from_der(ibc_identity_type, ibc_identity_type_cnt, &d, &dlen) != 1 + || asn1_any_from_der(issuer_id, issuer_id_len, &d, &dlen) != 1 + || sm9_cms_ibc_param_extensions_from_der(ibc_param_extensions, ibc_param_extensions_len, &d, &dlen) < 0 + || asn1_length_is_zero(dlen) != 1) { + error_print(); + return -1; + } + if (*version != 2) { + error_print(); + return -1; + } + return 1; +} diff --git a/tests/quictest.c b/tests/quictest.c index 3950b7db..76cb7ee9 100644 --- a/tests/quictest.c +++ b/tests/quictest.c @@ -182,11 +182,310 @@ static int quic_test_initial_keys(void) return 1; } +static int quic_test_aes_128_gcm_sha256_packet_protection(void) +{ +#ifdef ENABLE_SHA2 + const uint8_t secret[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }; + const uint8_t dcid[] = { 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08 }; + const uint8_t scid[] = { 0x08, 0x75, 0x6d, 0x35, 0x14, 0xf0, 0x9a, 0x1b }; + const uint8_t frames[] = { QUIC_frame_ping, QUIC_frame_padding, QUIC_frame_padding }; + QUIC_PACKET_KEYS keys; + QUIC_DECRYPTED_PACKET decrypted; + uint8_t packet[256]; + size_t packet_len = 0; + + memset(&keys, 0, sizeof(keys)); + memset(&decrypted, 0, sizeof(decrypted)); + + if (quic_packet_keys_derive(DIGEST_sha256(), secret, TLS_cipher_aes_128_gcm_sha256, &keys) != 1 + || quic_long_packet_encrypt(&keys, QUIC_packet_initial, dcid, sizeof(dcid), scid, sizeof(scid), + 1, frames, sizeof(frames), packet, &packet_len) != 1 + || quic_long_packet_decrypt(&keys, packet, packet_len, QUIC_packet_initial, &decrypted) != 1) { + error_print(); + return -1; + } + if (decrypted.packet_number != 1 + || decrypted.plaintext_len != sizeof(frames) + || memcmp(decrypted.plaintext, frames, sizeof(frames)) != 0) { + error_print(); + return -1; + } + + memset(packet, 0, sizeof(packet)); + memset(&decrypted, 0, sizeof(decrypted)); + if (quic_short_packet_encrypt(&keys, dcid, sizeof(dcid), 2, frames, sizeof(frames), packet, &packet_len) != 1 + || quic_short_packet_decrypt(&keys, packet, packet_len, dcid, sizeof(dcid), &decrypted) != 1) { + error_print(); + return -1; + } + if (decrypted.packet_number != 2 + || decrypted.plaintext_len != sizeof(frames) + || memcmp(decrypted.plaintext, frames, sizeof(frames)) != 0) { + error_print(); + return -1; + } +#endif + + printf("%s() ok\n", __FUNCTION__); + return 1; +} + +static int quic_test_sm4_gcm_sm3_packet_protection(void) +{ + const uint8_t secret[32] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }; + const uint8_t dcid[] = { 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08 }; + const uint8_t scid[] = { 0x08, 0x75, 0x6d, 0x35, 0x14, 0xf0, 0x9a, 0x1b }; + const uint8_t frames[] = { QUIC_frame_ping, QUIC_frame_padding, QUIC_frame_padding }; + QUIC_PACKET_KEYS keys; + QUIC_DECRYPTED_PACKET decrypted; + uint8_t packet[256]; + size_t packet_len = 0; + + memset(&keys, 0, sizeof(keys)); + memset(&decrypted, 0, sizeof(decrypted)); + + if (quic_packet_keys_derive(DIGEST_sm3(), secret, TLS_cipher_sm4_gcm_sm3, &keys) != 1 + || quic_long_packet_encrypt(&keys, QUIC_packet_handshake, dcid, sizeof(dcid), scid, sizeof(scid), + 3, frames, sizeof(frames), packet, &packet_len) != 1 + || quic_long_packet_decrypt(&keys, packet, packet_len, QUIC_packet_handshake, &decrypted) != 1) { + error_print(); + return -1; + } + if (decrypted.packet_number != 3 + || decrypted.plaintext_len != sizeof(frames) + || memcmp(decrypted.plaintext, frames, sizeof(frames)) != 0) { + error_print(); + return -1; + } + + memset(packet, 0, sizeof(packet)); + memset(&decrypted, 0, sizeof(decrypted)); + if (quic_short_packet_encrypt(&keys, dcid, sizeof(dcid), 4, frames, sizeof(frames), packet, &packet_len) != 1 + || quic_short_packet_decrypt(&keys, packet, packet_len, dcid, sizeof(dcid), &decrypted) != 1) { + error_print(); + return -1; + } + if (decrypted.packet_number != 4 + || decrypted.plaintext_len != sizeof(frames) + || memcmp(decrypted.plaintext, frames, sizeof(frames)) != 0) { + error_print(); + return -1; + } + + printf("%s() ok\n", __FUNCTION__); + return 1; +} + +static int quic_test_sm4_ccm_sm3_packet_protection(void) +{ +#ifdef ENABLE_SM4_CCM + const uint8_t secret[32] = { + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + }; + const uint8_t dcid[] = { 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08 }; + const uint8_t scid[] = { 0x08, 0x75, 0x6d, 0x35, 0x14, 0xf0, 0x9a, 0x1b }; + const uint8_t frames[] = { QUIC_frame_ping, QUIC_frame_padding, QUIC_frame_padding }; + QUIC_PACKET_KEYS keys; + QUIC_DECRYPTED_PACKET decrypted; + uint8_t packet[256]; + size_t packet_len = 0; + + memset(&keys, 0, sizeof(keys)); + memset(&decrypted, 0, sizeof(decrypted)); + + if (quic_packet_keys_derive(DIGEST_sm3(), secret, TLS_cipher_sm4_ccm_sm3, &keys) != 1 + || quic_long_packet_encrypt(&keys, QUIC_packet_handshake, dcid, sizeof(dcid), scid, sizeof(scid), + 5, frames, sizeof(frames), packet, &packet_len) != 1 + || quic_long_packet_decrypt(&keys, packet, packet_len, QUIC_packet_handshake, &decrypted) != 1) { + error_print(); + return -1; + } + if (decrypted.packet_number != 5 + || decrypted.plaintext_len != sizeof(frames) + || memcmp(decrypted.plaintext, frames, sizeof(frames)) != 0) { + error_print(); + return -1; + } + + memset(packet, 0, sizeof(packet)); + memset(&decrypted, 0, sizeof(decrypted)); + if (quic_short_packet_encrypt(&keys, dcid, sizeof(dcid), 6, frames, sizeof(frames), packet, &packet_len) != 1 + || quic_short_packet_decrypt(&keys, packet, packet_len, dcid, sizeof(dcid), &decrypted) != 1) { + error_print(); + return -1; + } + if (decrypted.packet_number != 6 + || decrypted.plaintext_len != sizeof(frames) + || memcmp(decrypted.plaintext, frames, sizeof(frames)) != 0) { + error_print(); + return -1; + } +#endif + + printf("%s() ok\n", __FUNCTION__); + return 1; +} + +static int quic_test_client_hello_to_bytes(void) +{ + TLS_CTX ctx; + TLS_CONNECT conn; + int cipher_suites[] = { TLS_cipher_sm4_gcm_sm3 }; + int supported_groups[] = { TLS_curve_sm2p256v1 }; + int signature_algorithms[] = { TLS_sig_sm2sig_sm3 }; + uint8_t buf[TLS_MAX_RECORD_SIZE]; + uint8_t *p = buf; + size_t len = 0; + size_t handshake_len; + + if (tls_ctx_init(&ctx, TLS_protocol_tls13, TLS_client_mode) != 1 + || tls_ctx_set_cipher_suites(&ctx, cipher_suites, sizeof(cipher_suites)/sizeof(cipher_suites[0])) != 1 + || tls_ctx_set_supported_groups(&ctx, supported_groups, sizeof(supported_groups)/sizeof(supported_groups[0])) != 1 + || tls_ctx_set_signature_algorithms(&ctx, signature_algorithms, sizeof(signature_algorithms)/sizeof(signature_algorithms[0])) != 1 + || tls_init(&conn, &ctx) != 1) { + error_print(); + return -1; + } + + if (quic_client_hello_to_bytes(&conn, &p, &len) != 1) { + error_print(); + tls_ctx_cleanup(&ctx); + return -1; + } + if (len < TLS_HANDSHAKE_HEADER_SIZE || p != buf + len) { + error_print(); + tls_ctx_cleanup(&ctx); + return -1; + } + if (buf[0] != TLS_handshake_client_hello || buf[0] == TLS_record_handshake) { + error_print(); + tls_ctx_cleanup(&ctx); + return -1; + } + handshake_len = ((size_t)buf[1] << 16) | ((size_t)buf[2] << 8) | buf[3]; + if (handshake_len + TLS_HANDSHAKE_HEADER_SIZE != len) { + error_print(); + tls_ctx_cleanup(&ctx); + return -1; + } + if (conn.plain_recordlen != len || memcmp(conn.plain_record, buf, len) != 0) { + error_print(); + tls_ctx_cleanup(&ctx); + return -1; + } + + tls_ctx_cleanup(&ctx); + printf("%s() ok\n", __FUNCTION__); + return 1; +} + +static int quic_test_verbose_print(void) +{ + TLS_CTX ctx; + TLS_CONNECT conn; + int cipher_suites[] = { TLS_cipher_sm4_gcm_sm3 }; + int supported_groups[] = { TLS_curve_sm2p256v1 }; + int signature_algorithms[] = { TLS_sig_sm2sig_sm3 }; + uint8_t handshake[TLS_MAX_RECORD_SIZE]; + uint8_t frames[TLS_MAX_RECORD_SIZE]; + uint8_t packet[64]; + uint8_t *p = handshake; + uint8_t *q = frames; + uint8_t *r = packet; + size_t handshake_len = 0; + size_t frames_len = 0; + size_t packet_len = 0; + FILE *fp; + + fp = tmpfile(); + if (!fp) { + error_print(); + return -1; + } + + if (tls_ctx_init(&ctx, TLS_protocol_tls13, TLS_client_mode) != 1 + || tls_ctx_set_cipher_suites(&ctx, cipher_suites, sizeof(cipher_suites)/sizeof(cipher_suites[0])) != 1 + || tls_ctx_set_supported_groups(&ctx, supported_groups, sizeof(supported_groups)/sizeof(supported_groups[0])) != 1 + || tls_ctx_set_signature_algorithms(&ctx, signature_algorithms, sizeof(signature_algorithms)/sizeof(signature_algorithms[0])) != 1 + || tls_init(&conn, &ctx) != 1) { + error_print(); + fclose(fp); + return -1; + } + + if (quic_client_hello_to_bytes(&conn, &p, &handshake_len) != 1 + || quic_crypto_data_print(fp, 0, 0, QUIC_encryption_initial, handshake, handshake_len) != 1) { + error_print(); + tls_ctx_cleanup(&ctx); + fclose(fp); + return -1; + } + + if (quic_varint_to_bytes(QUIC_frame_ping, &q, &frames_len) != 1 + || quic_varint_to_bytes(QUIC_frame_crypto, &q, &frames_len) != 1 + || quic_varint_to_bytes(0, &q, &frames_len) != 1 + || quic_varint_to_bytes(handshake_len, &q, &frames_len) != 1) { + error_print(); + tls_ctx_cleanup(&ctx); + fclose(fp); + return -1; + } + memcpy(q, handshake, handshake_len); + q += handshake_len; + frames_len += handshake_len; + if (quic_frames_print(fp, 0, 0, QUIC_encryption_initial, frames, frames_len) != 1) { + error_print(); + tls_ctx_cleanup(&ctx); + fclose(fp); + return -1; + } + + *r++ = 0xc0; + *r++ = 0x00; *r++ = 0x00; *r++ = 0x00; *r++ = 0x01; + *r++ = 0x08; + memcpy(r, "12345678", 8); r += 8; + *r++ = 0x08; + memcpy(r, "87654321", 8); r += 8; + *r++ = 0x00; + *r++ = 0x01; + *r++ = 0x00; + packet_len = (size_t)(r - packet); + if (quic_packet_print(fp, 0, 0, packet, packet_len) != 1) { + error_print(); + tls_ctx_cleanup(&ctx); + fclose(fp); + return -1; + } + + tls_ctx_cleanup(&ctx); + fclose(fp); + printf("%s() ok\n", __FUNCTION__); + return 1; +} + int main(void) { if (quic_test_varint() != 1 || quic_test_transport_params() != 1 - || quic_test_initial_keys() != 1) { + || quic_test_initial_keys() != 1 + || quic_test_aes_128_gcm_sha256_packet_protection() != 1 + || quic_test_sm4_gcm_sm3_packet_protection() != 1 + || quic_test_sm4_ccm_sm3_packet_protection() != 1 + || quic_test_client_hello_to_bytes() != 1 + || quic_test_verbose_print() != 1) { error_print(); return 1; } diff --git a/tools/gmssl.c b/tools/gmssl.c index 236dac32..070cda83 100644 --- a/tools/gmssl.c +++ b/tools/gmssl.c @@ -35,6 +35,7 @@ extern int sm2sign_main(int argc, char **argv); extern int sm2verify_main(int argc, char **argv); extern int sm2encrypt_main(int argc, char **argv); extern int sm2decrypt_main(int argc, char **argv); +extern int sm2exch_main(int argc, char **argv); extern int sm3_main(int argc, char **argv); extern int sm3hmac_main(int argc, char **argv); extern int sm3_pbkdf2_main(int argc, char **argv); @@ -64,6 +65,9 @@ extern int sm4_cbc_mac_main(int argc, char **argv); #endif #ifdef ENABLE_ZUC extern int zuc_main(int argc, char **argv); +extern int zuc256_main(int argc, char **argv); +extern int zuc_128_eea3_main(int argc, char **argv); +extern int zuc_128_eia3_main(int argc, char **argv); #endif #ifdef ENABLE_GHASH extern int ghash_main(int argc, char **argv); @@ -75,6 +79,7 @@ extern int sm9sign_main(int argc, char **argv); extern int sm9verify_main(int argc, char **argv); extern int sm9encrypt_main(int argc, char **argv); extern int sm9decrypt_main(int argc, char **argv); +extern int sm9exch_main(int argc, char **argv); #endif #ifdef ENABLE_CMS extern int cmsparse_main(int argc, char **argv); @@ -92,6 +97,10 @@ extern int tls13_client_main(int argc, char **argv); extern int tls13_server_main(int argc, char **argv); extern int sctverify_main(int argc, char **argv); #endif +#ifdef ENABLE_QUIC +extern int quic_client_main(int argc, char **argv); +extern int quic_server_main(int argc, char **argv); +#endif #ifdef ENABLE_SECP256R1 extern int p256keygen_main(int argc, char **argv); #endif @@ -147,9 +156,11 @@ static const char *options = " sm2verify Verify SM2 signature\n" " sm2encrypt Encrypt with SM2 public key\n" " sm2decrypt Decrypt with SM2 private key\n" + " sm2exch SM2 key exchange\n" " sm3 Generate SM3 hash\n" " sm3hmac Generate SM3 HMAC tag\n" " sm3_pbkdf2 Hash password into key using PBKDF2 algoritm\n" + " sm4 Encrypt or decrypt with SM4\n" " sm4_gcm Encrypt or decrypt with SM4 GCM\n" " sm4_cbc Encrypt or decrypt with SM4 CBC\n" " sm4_ctr Encrypt or decrypt with SM4 CTR\n" @@ -179,6 +190,9 @@ static const char *options = #endif #ifdef ENABLE_ZUC " zuc Encrypt or decrypt with ZUC\n" + " zuc256 Encrypt or decrypt with ZUC-256\n" + " zuc_128_eea3 Encrypt or decrypt with ZUC 128-EEA3\n" + " zuc_128_eia3 Generate ZUC 128-EIA3 MAC\n" #endif #ifdef ENABLE_SM9 " sm9setup Generate SM9 master secret\n" @@ -187,6 +201,7 @@ static const char *options = " sm9verify Verify SM9 signature\n" " sm9encrypt SM9 public key encryption\n" " sm9decrypt SM9 decryption\n" + " sm9exch SM9 key exchange\n" #endif " reqgen Generate certificate signing request (CSR)\n" " reqsign Generate certificate from CSR\n" @@ -259,6 +274,10 @@ static const char *options = " tls12_server TLS 1.2 server\n" " tls13_client TLS 1.3 client\n" " tls13_server TLS 1.3 server\n" +#endif +#ifdef ENABLE_QUIC + " quic_client QUIC client\n" + " quic_server QUIC server\n" #endif "\n" "run `gmssl -help` to print help of the given command\n" @@ -326,6 +345,8 @@ int main(int argc, char **argv) return sm2encrypt_main(argc, argv); } else if (!strcmp(*argv, "sm2decrypt")) { return sm2decrypt_main(argc, argv); + } else if (!strcmp(*argv, "sm2exch")) { + return sm2exch_main(argc, argv); } else if (!strcmp(*argv, "sm3")) { return sm3_main(argc, argv); } else if (!strcmp(*argv, "sm3hmac")) { @@ -373,6 +394,12 @@ int main(int argc, char **argv) #ifdef ENABLE_ZUC } else if (!strcmp(*argv, "zuc")) { return zuc_main(argc, argv); + } else if (!strcmp(*argv, "zuc256")) { + return zuc256_main(argc, argv); + } else if (!strcmp(*argv, "zuc_128_eea3")) { + return zuc_128_eea3_main(argc, argv); + } else if (!strcmp(*argv, "zuc_128_eia3")) { + return zuc_128_eia3_main(argc, argv); #endif #ifdef ENABLE_SM9 } else if (!strcmp(*argv, "sm9setup")) { @@ -387,6 +414,8 @@ int main(int argc, char **argv) return sm9encrypt_main(argc, argv); } else if (!strcmp(*argv, "sm9decrypt")) { return sm9decrypt_main(argc, argv); + } else if (!strcmp(*argv, "sm9exch")) { + return sm9exch_main(argc, argv); #endif #ifdef ENABLE_CMS } else if (!strcmp(*argv, "cmsparse")) { @@ -416,6 +445,12 @@ int main(int argc, char **argv) } else if (!strcmp(*argv, "sctverify")) { return sctverify_main(argc, argv); #endif +#ifdef ENABLE_QUIC + } else if (!strcmp(*argv, "quic_client")) { + return quic_client_main(argc, argv); + } else if (!strcmp(*argv, "quic_server")) { + return quic_server_main(argc, argv); +#endif #ifdef ENABLE_SECP256R1 } else if (!strcmp(*argv, "p256keygen")) { return p256keygen_main(argc, argv); diff --git a/tools/quic_client.c b/tools/quic_client.c new file mode 100644 index 00000000..1fa4d10c --- /dev/null +++ b/tools/quic_client.c @@ -0,0 +1,1270 @@ +/* + * 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 +#include + +int tls13_generate_handshake_secrets(TLS_CONNECT *conn); +int tls13_generate_master_secret(TLS_CONNECT *conn); +int tls13_generate_client_handshake_keys(TLS_CONNECT *conn); +int tls13_generate_server_handshake_keys(TLS_CONNECT *conn); +int tls13_generate_application_secrets(TLS_CONNECT *conn); +int tls13_generate_client_application_keys(TLS_CONNECT *conn); +int tls13_generate_server_application_keys(TLS_CONNECT *conn); +int tls13_record_get_handshake_encrypted_extensions(const uint8_t *record, const uint8_t **exts, size_t *extslen); + +static const char *options = "-host str [-port num] [-server_name str] [-cipher_suite str] [-supported_group str] [-sig_alg str] [-get path] [-verbose]"; + +static const char *help = +"Options\n" +"\n" +" -host str Server's hostname\n" +" -port num Server's UDP port number, default 443\n" +" -server_name str Send server_name (SNI) request\n" +" -cipher_suite str TLS 1.3 cipher suite, default TLS_AES_128_GCM_SHA256\n" +" -supported_group str Supported elliptic curve, default prime256v1\n" +" -sig_alg str Supported signature algorithm, default ecdsa_secp256r1_sha256\n" +" -get path HTTP/3 request path kept for future use\n" +" -verbose Print QUIC packet, frame and TLS handshake messages\n" +"\n" +#include "quic_help.h" +; + +static int quic_tls_record_from_handshake(const uint8_t *handshake, size_t handshake_len, uint8_t *record, size_t *record_len) +{ + if (!handshake || !record || !record_len || handshake_len > TLS_MAX_PLAINTEXT_SIZE) { + error_print(); + return -1; + } + record[0] = TLS_record_handshake; + record[1] = 0x03; + record[2] = 0x03; + record[3] = (uint8_t)(handshake_len >> 8); + record[4] = (uint8_t)handshake_len; + memcpy(record + TLS_RECORD_HEADER_SIZE, handshake, handshake_len); + *record_len = TLS_RECORD_HEADER_SIZE + handshake_len; + return 1; +} + +static int quic_handshake_header_get(int *type, const uint8_t **data, size_t *datalen, const uint8_t *handshake, size_t handshake_len) +{ + if (!type || !data || !datalen || !handshake || handshake_len < TLS_HANDSHAKE_HEADER_SIZE) { + error_print(); + return -1; + } + *type = handshake[0]; + *datalen = ((size_t)handshake[1] << 16) | ((size_t)handshake[2] << 8) | handshake[3]; + if (*datalen + TLS_HANDSHAKE_HEADER_SIZE > handshake_len) { + error_print(); + return -1; + } + *data = handshake + TLS_HANDSHAKE_HEADER_SIZE; + return 1; +} + +static int quic_client_server_hello_process(TLS_CONNECT *conn, const uint8_t *client_hello, size_t client_hello_len, const uint8_t *server_hello, size_t server_hello_len) +{ + uint8_t record[TLS_MAX_RECORD_SIZE]; + size_t record_len; + int legacy_version; + const uint8_t *random; + const uint8_t *legacy_session_id_echo; + size_t legacy_session_id_echo_len; + int cipher_suite; + const uint8_t *exts; + size_t extslen; + const uint8_t *supported_versions = NULL; + size_t supported_versions_len = 0; + const uint8_t *key_share = NULL; + size_t key_share_len = 0; + int selected_version; + int key_exchange_group; + const uint8_t *key_exchange; + size_t key_exchange_len; + + if (!conn || !client_hello || !client_hello_len || !server_hello || !server_hello_len) { + error_print(); + return -1; + } + if (quic_tls_record_from_handshake(server_hello, server_hello_len, record, &record_len) != 1 + || tls_record_get_handshake_server_hello(record, &legacy_version, &random, + &legacy_session_id_echo, &legacy_session_id_echo_len, &cipher_suite, &exts, &extslen) != 1) { + error_print(); + return -1; + } + if (legacy_version != TLS_protocol_tls12 + || legacy_session_id_echo_len != conn->session_id_len + || memcmp(legacy_session_id_echo, conn->session_id, conn->session_id_len) != 0 + || tls_type_is_in_list(cipher_suite, conn->ctx->cipher_suites, conn->ctx->cipher_suites_cnt) != 1) { + error_print(); + return -1; + } + memcpy(conn->server_random, random, 32); + conn->cipher_suite = cipher_suite; + if (tls_cipher_suite_get(cipher_suite, &conn->cipher, &conn->digest) != 1) { + error_print(); + return -1; + } + while (extslen) { + int ext_type; + const uint8_t *ext_data; + size_t ext_datalen; + if (tls_ext_from_bytes(&ext_type, &ext_data, &ext_datalen, &exts, &extslen) != 1) { + error_print(); + return -1; + } + switch (ext_type) { + case TLS_extension_supported_versions: + supported_versions = ext_data; + supported_versions_len = ext_datalen; + break; + case TLS_extension_key_share: + key_share = ext_data; + key_share_len = ext_datalen; + break; + default: + error_print(); + return -1; + } + } + if (!supported_versions || !key_share + || tls13_server_supported_versions_from_bytes(&selected_version, supported_versions, supported_versions_len) != 1 + || selected_version != TLS_protocol_tls13 + || tls13_key_share_server_hello_from_bytes(&key_exchange_group, &key_exchange, &key_exchange_len, key_share, key_share_len) != 1 + || key_exchange_len != 65) { + error_print(); + return -1; + } + conn->protocol = selected_version; + conn->key_exchange_group = key_exchange_group; + memcpy(conn->peer_key_exchange, key_exchange, key_exchange_len); + conn->peer_key_exchange_len = key_exchange_len; + conn->key_exchange_modes &= TLS_KE_CERT_DHE; + if (!conn->key_exchange_modes + || digest_init(&conn->dgst_ctx, conn->digest) != 1 + || digest_update(&conn->dgst_ctx, client_hello, client_hello_len) != 1 + || digest_update(&conn->dgst_ctx, server_hello, server_hello_len) != 1 + || tls13_generate_handshake_secrets(conn) != 1) { + error_print(); + return -1; + } + tls13_generate_master_secret(conn); + if (tls13_generate_server_handshake_keys(conn) != 1 + || tls13_generate_client_handshake_keys(conn) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_client_handshake_flight_process(TLS_CONNECT *conn, const uint8_t *handshake, size_t handshake_len, int verbose) +{ + const uint8_t *p = handshake; + size_t len = handshake_len; + uint8_t record[TLS_MAX_RECORD_SIZE]; + size_t record_len; + int got_finished = 0; + + if (!conn || !handshake) { + error_print(); + return -1; + } + while (len) { + int type; + const uint8_t *data; + size_t datalen; + const uint8_t *msg = p; + size_t msglen; + + if (quic_handshake_header_get(&type, &data, &datalen, p, len) != 1) { + error_print(); + return -1; + } + msglen = TLS_HANDSHAKE_HEADER_SIZE + datalen; + if (verbose) { + quic_crypto_data_print(stderr, 0, 4, QUIC_encryption_handshake, msg, msglen); + } + if (quic_tls_record_from_handshake(msg, msglen, record, &record_len) != 1) { + error_print(); + return -1; + } + switch (type) { + case TLS_handshake_encrypted_extensions: + if (tls13_record_get_handshake_encrypted_extensions(record, &data, &datalen) != 1) { + error_print(); + return -1; + } + if (digest_update(&conn->dgst_ctx, msg, msglen) != 1) { + error_print(); + return -1; + } + break; + case TLS_handshake_certificate: { + const uint8_t *request_context; + size_t request_context_len; + const uint8_t *ocsp; + size_t ocsp_len; + const uint8_t *sct; + size_t sct_len; + if (tls13_record_get_handshake_certificate(record, &request_context, &request_context_len, + conn->peer_cert_chain, &conn->peer_cert_chain_len, sizeof(conn->peer_cert_chain), &ocsp, &ocsp_len, &sct, &sct_len) != 1) { + error_print(); + return -1; + } + if (digest_update(&conn->dgst_ctx, msg, msglen) != 1) { + error_print(); + return -1; + } + break; + } + case TLS_handshake_certificate_verify: { + int sig_alg; + const uint8_t *sig; + size_t siglen; + const uint8_t *cert; + size_t certlen; + X509_KEY public_key; + if (tls13_record_get_handshake_certificate_verify(record, &sig_alg, &sig, &siglen) != 1 + || x509_certs_get_cert_by_index(conn->peer_cert_chain, conn->peer_cert_chain_len, 0, &cert, &certlen) != 1 + || x509_cert_get_subject_public_key(cert, certlen, &public_key) != 1 + || tls13_verify_certificate_verify(TLS_server_mode, sig_alg, &public_key, &conn->dgst_ctx, sig, siglen) != 1 + || digest_update(&conn->dgst_ctx, msg, msglen) != 1) { + error_print(); + return -1; + } + break; + } + case TLS_handshake_finished: { + const uint8_t *server_verify_data; + size_t server_verify_data_len; + uint8_t verify_data[64]; + size_t verify_data_len; + if (tls13_compute_verify_data(conn->server_handshake_traffic_secret, &conn->dgst_ctx, verify_data, &verify_data_len) != 1 + || tls13_record_get_handshake_finished(record, &server_verify_data, &server_verify_data_len) != 1 + || server_verify_data_len != verify_data_len + || memcmp(server_verify_data, verify_data, verify_data_len) != 0 + || digest_update(&conn->dgst_ctx, msg, msglen) != 1 + || tls13_generate_application_secrets(conn) != 1 + || tls13_generate_server_application_keys(conn) != 1) { + error_print(); + return -1; + } + got_finished = 1; + break; + } + default: + error_print(); + return -1; + } + p += msglen; + len -= msglen; + } + return got_finished ? 1 : 0; +} + +static int quic_client_finished_to_bytes(TLS_CONNECT *conn, uint8_t *out, size_t *outlen) +{ + uint8_t record[TLS_MAX_RECORD_SIZE]; + size_t record_len = 0; + uint8_t verify_data[64]; + size_t verify_data_len; + const uint8_t *handshake; + size_t handshake_len; + + if (!conn || !out || !outlen) { + error_print(); + return -1; + } + memset(record, 0, sizeof(record)); + record[1] = 0x03; + record[2] = 0x03; + if (tls13_compute_verify_data(conn->client_handshake_traffic_secret, &conn->dgst_ctx, verify_data, &verify_data_len) != 1 + || tls13_record_set_handshake_finished(record, &record_len, verify_data, verify_data_len) != 1) { + error_print(); + return -1; + } + handshake = record + TLS_RECORD_HEADER_SIZE; + handshake_len = record_len - TLS_RECORD_HEADER_SIZE; + memcpy(out, handshake, handshake_len); + *outlen = handshake_len; + if (digest_update(&conn->dgst_ctx, handshake, handshake_len) != 1 + || tls13_generate_client_application_keys(conn) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_ack_frame_to_bytes(uint64_t packet_number, uint8_t **out, size_t *outlen) +{ + if (quic_varint_to_bytes(QUIC_frame_ack, out, outlen) != 1 + || quic_varint_to_bytes(packet_number, out, outlen) != 1 + || quic_varint_to_bytes(0, out, outlen) != 1 + || quic_varint_to_bytes(0, out, outlen) != 1 + || quic_varint_to_bytes(0, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_crypto_frame_to_bytes(uint64_t offset, const uint8_t *data, size_t datalen, uint8_t **out, size_t *outlen) +{ + if (!data && datalen) { + error_print(); + return -1; + } + if (quic_varint_to_bytes(QUIC_frame_crypto, out, outlen) != 1 + || quic_varint_to_bytes(offset, out, outlen) != 1 + || quic_varint_to_bytes(datalen, out, outlen) != 1) { + error_print(); + return -1; + } + if (out && *out) { + memcpy(*out, data, datalen); + *out += datalen; + } + *outlen += datalen; + return 1; +} + +static int quic_client_initial_packet_to_bytes(const uint8_t *dcid, size_t dcid_len, + const uint8_t *scid, size_t scid_len, const uint8_t *frames, size_t frames_len, + uint8_t *packet, size_t *packet_len) +{ + uint8_t *p = packet; + uint8_t *pn; + uint8_t *payload; + size_t len = 0; + size_t plain_payload_len; + size_t padding_len; + size_t payload_len; + QUIC_INITIAL_SECRETS secrets; + QUIC_INITIAL_KEYS keys; + AES_KEY aes_key; + AES_KEY hp_key; + uint8_t nonce[QUIC_INITIAL_IV_SIZE]; + uint8_t mask[16]; + uint64_t packet_number = 1; + size_t i; + + if (!dcid || dcid_len > 20 || !scid || scid_len > 20 || !frames || !packet || !packet_len) { + error_print(); + return -1; + } + + plain_payload_len = frames_len; + padding_len = plain_payload_len < 1162 ? 1162 - plain_payload_len : 0; + payload_len = plain_payload_len + padding_len + 16; + + *p++ = 0xc3; len++; + *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x01; len += 4; + *p++ = (uint8_t)dcid_len; len++; + memcpy(p, dcid, dcid_len); p += dcid_len; len += dcid_len; + *p++ = (uint8_t)scid_len; len++; + memcpy(p, scid, scid_len); p += scid_len; len += scid_len; + if (quic_varint_to_bytes(0, &p, &len) != 1 + || quic_varint_to_bytes(4 + payload_len, &p, &len) != 1) { + error_print(); + return -1; + } + pn = p; + *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x01; len += 4; + payload = p; + memcpy(payload, frames, frames_len); + memset(payload + frames_len, 0, padding_len); + + if (quic_derive_initial_secrets(dcid, dcid_len, &secrets) != 1 + || quic_derive_initial_client_keys(&secrets, &keys) != 1 + || aes_set_encrypt_key(&aes_key, keys.key, sizeof(keys.key)) != 1 + || aes_set_encrypt_key(&hp_key, keys.hp, sizeof(keys.hp)) != 1) { + error_print(); + return -1; + } + memcpy(nonce, keys.iv, sizeof(nonce)); + for (i = 0; i < 8; i++) { + nonce[sizeof(nonce) - 1 - i] ^= (uint8_t)(packet_number >> (8 * i)); + } + if (aes_gcm_encrypt(&aes_key, nonce, sizeof(nonce), packet, len, + payload, plain_payload_len + padding_len, payload, 16, + payload + plain_payload_len + padding_len) != 1) { + error_print(); + return -1; + } + aes_encrypt(&hp_key, payload, mask); + packet[0] ^= mask[0] & 0x0f; + for (i = 0; i < 4; i++) { + pn[i] ^= mask[i + 1]; + } + p += payload_len; + len += payload_len; + + *packet_len = len; + return 1; +} + +static int quic_stream_frame_to_bytes(uint64_t stream_id, int fin, const uint8_t *data, size_t datalen, uint8_t **out, size_t *outlen) +{ + uint64_t type = QUIC_frame_stream_base | 0x02 | (fin ? 0x01 : 0); + + if ((!data && datalen) || quic_varint_to_bytes(type, out, outlen) != 1 + || quic_varint_to_bytes(stream_id, out, outlen) != 1 + || quic_varint_to_bytes(datalen, out, outlen) != 1) { + error_print(); + return -1; + } + if (out && *out) { + memcpy(*out, data, datalen); + *out += datalen; + } + *outlen += datalen; + return 1; +} + +static int quic_http3_varint_to_bytes(uint64_t val, uint8_t **out, size_t *outlen) +{ + return quic_varint_to_bytes(val, out, outlen); +} + +static int quic_qpack_string_to_bytes(const char *s, uint8_t **out, size_t *outlen) +{ + size_t len; + + if (!s || !outlen) { + error_print(); + return -1; + } + len = strlen(s); + if (len > 127) { + error_print(); + return -1; + } + if (out && *out) { + *(*out)++ = (uint8_t)len; + memcpy(*out, s, len); + *out += len; + } + *outlen += 1 + len; + return 1; +} + +static int quic_http3_request_headers_to_bytes(const char *authority, const char *path, uint8_t *out, size_t *outlen) +{ + uint8_t block[512]; + uint8_t *p = block; + size_t len = 0; + uint8_t *q = out; + + if (!authority || !path || !out || !outlen) { + error_print(); + return -1; + } + block[len++] = 0x00; + block[len++] = 0x00; + block[len++] = 0xd1; /* QPACK static table: :method: GET */ + block[len++] = 0xd7; /* QPACK static table: :scheme: https */ + if (!strcmp(path, "/")) { + block[len++] = 0xc1; /* QPACK static table: :path: / */ + } else { + block[len++] = 0x51; /* literal with static name reference :path */ + p = block + len; + if (quic_qpack_string_to_bytes(path, &p, &len) != 1) { + error_print(); + return -1; + } + } + block[len++] = 0x50; /* literal with static name reference :authority */ + p = block + len; + if (quic_qpack_string_to_bytes(authority, &p, &len) != 1) { + error_print(); + return -1; + } + if (quic_http3_varint_to_bytes(0x01, &q, outlen) != 1 + || quic_http3_varint_to_bytes(len, &q, outlen) != 1) { + error_print(); + return -1; + } + memcpy(q, block, len); + *outlen += len; + return 1; +} + +static int quic_http3_client_data_to_frames(const char *authority, const char *path, uint8_t *frames, size_t *frames_len) +{ + uint8_t control_stream[] = { 0x00, 0x04, 0x00 }; + uint8_t qpack_encoder_stream[] = { 0x02 }; + uint8_t qpack_decoder_stream[] = { 0x03 }; + uint8_t headers[512]; + uint8_t *p = frames; + size_t headers_len = 0; + + if (!authority || !path || !frames || !frames_len) { + error_print(); + return -1; + } + *frames_len = 0; + if (quic_http3_request_headers_to_bytes(authority, path, headers, &headers_len) != 1 + || quic_stream_frame_to_bytes(2, 0, control_stream, sizeof(control_stream), &p, frames_len) != 1 + || quic_stream_frame_to_bytes(6, 0, qpack_encoder_stream, sizeof(qpack_encoder_stream), &p, frames_len) != 1 + || quic_stream_frame_to_bytes(10, 0, qpack_decoder_stream, sizeof(qpack_decoder_stream), &p, frames_len) != 1 + || quic_stream_frame_to_bytes(0, 1, headers, headers_len, &p, frames_len) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_http3_stream_data_print(const uint8_t *data, size_t datalen, int *got_data, int verbose) +{ + const uint8_t *p = data; + size_t len = datalen; + + if (!data || !got_data) { + error_print(); + return -1; + } + while (len) { + uint64_t frame_type, frame_len; + const uint8_t *frame_data; + if (quic_varint_from_bytes(&frame_type, &p, &len) != 1 + || quic_varint_from_bytes(&frame_len, &p, &len) != 1 + || frame_len > len) { + return 0; + } + frame_data = p; + p += frame_len; + len -= frame_len; + if (verbose) { + fprintf(stderr, "HTTP/3 frame type=%llu len=%llu\n", + (unsigned long long)frame_type, (unsigned long long)frame_len); + } + if (frame_type == 0x00 && frame_len) { + fwrite(frame_data, 1, (size_t)frame_len, stdout); + *got_data = 1; + } + } + return 1; +} + +static int quic_frames_crypto_collect(const uint8_t *frames, size_t frames_len, uint8_t *crypto, size_t *crypto_len, size_t crypto_max, uint64_t *largest_ack) +{ + const uint8_t *p = frames; + size_t len = frames_len; + + if (!frames || !crypto || !crypto_len || !largest_ack) { + error_print(); + return -1; + } + while (len) { + uint64_t type; + if (quic_varint_from_bytes(&type, &p, &len) != 1) { + error_print(); + return -1; + } + switch (type) { + case QUIC_frame_padding: + while (len && *p == 0) { + p++; + len--; + } + break; + case QUIC_frame_ack: + case QUIC_frame_ack_ecn: { + uint64_t ack_delay; + uint64_t ack_range_count; + uint64_t first_ack_range; + uint64_t i; + if (quic_varint_from_bytes(largest_ack, &p, &len) != 1 + || quic_varint_from_bytes(&ack_delay, &p, &len) != 1 + || quic_varint_from_bytes(&ack_range_count, &p, &len) != 1 + || quic_varint_from_bytes(&first_ack_range, &p, &len) != 1) { + error_print(); + return -1; + } + for (i = 0; i < ack_range_count; i++) { + uint64_t gap; + uint64_t ack_range; + if (quic_varint_from_bytes(&gap, &p, &len) != 1 || quic_varint_from_bytes(&ack_range, &p, &len) != 1) { + error_print(); + return -1; + } + } + if (type == QUIC_frame_ack_ecn) { + uint64_t ect0, ect1, ce; + if (quic_varint_from_bytes(&ect0, &p, &len) != 1 + || quic_varint_from_bytes(&ect1, &p, &len) != 1 + || quic_varint_from_bytes(&ce, &p, &len) != 1) { + error_print(); + return -1; + } + } + break; + } + case QUIC_frame_crypto: { + uint64_t off; + uint64_t data_len; + if (quic_varint_from_bytes(&off, &p, &len) != 1 + || quic_varint_from_bytes(&data_len, &p, &len) != 1 + || data_len > len || off + data_len > crypto_max) { + error_print(); + return -1; + } + memcpy(crypto + off, p, (size_t)data_len); + if (*crypto_len < off + data_len) { + *crypto_len = (size_t)(off + data_len); + } + p += data_len; + len -= data_len; + break; + } + default: + error_print(); + return -1; + } + } + return 1; +} + +static int quic_application_frames_process(const uint8_t *frames, size_t frames_len, uint64_t *largest_ack, int *got_stream_data, int verbose) +{ + const uint8_t *p = frames; + size_t len = frames_len; + + if (!frames || !largest_ack || !got_stream_data) { + error_print(); + return -1; + } + while (len) { + uint64_t type; + if (quic_varint_from_bytes(&type, &p, &len) != 1) { + error_print(); + return -1; + } + if (type == QUIC_frame_padding) { + while (len && *p == 0) { + p++; + len--; + } + } else if (type == QUIC_frame_ack || type == QUIC_frame_ack_ecn) { + uint64_t ack_delay, ack_range_count, first_ack_range, i; + if (quic_varint_from_bytes(largest_ack, &p, &len) != 1 + || quic_varint_from_bytes(&ack_delay, &p, &len) != 1 + || quic_varint_from_bytes(&ack_range_count, &p, &len) != 1 + || quic_varint_from_bytes(&first_ack_range, &p, &len) != 1) { + error_print(); + return -1; + } + for (i = 0; i < ack_range_count; i++) { + uint64_t gap, ack_range; + if (quic_varint_from_bytes(&gap, &p, &len) != 1 || quic_varint_from_bytes(&ack_range, &p, &len) != 1) { + error_print(); + return -1; + } + } + if (type == QUIC_frame_ack_ecn) { + uint64_t ect0, ect1, ce; + if (quic_varint_from_bytes(&ect0, &p, &len) != 1 || quic_varint_from_bytes(&ect1, &p, &len) != 1 || quic_varint_from_bytes(&ce, &p, &len) != 1) { + error_print(); + return -1; + } + } + } else if (type == QUIC_frame_handshake_done) { + if (verbose) { + fprintf(stderr, "recv HANDSHAKE_DONE\n"); + } + } else if (type == QUIC_frame_new_token) { + uint64_t token_len; + if (quic_varint_from_bytes(&token_len, &p, &len) != 1 || token_len > len) { + error_print(); + return -1; + } + p += token_len; + len -= token_len; + } else if (type == QUIC_frame_crypto) { + uint64_t offset, data_len; + const uint8_t *data; + if (quic_varint_from_bytes(&offset, &p, &len) != 1 + || quic_varint_from_bytes(&data_len, &p, &len) != 1 + || data_len > len) { + error_print(); + return -1; + } + data = p; + p += data_len; + len -= data_len; + if (verbose) { + fprintf(stderr, "recv application CRYPTO offset=%llu len=%llu\n", + (unsigned long long)offset, (unsigned long long)data_len); + quic_crypto_data_print(stderr, 0, 4, QUIC_encryption_application, data, (size_t)data_len); + } + } else if (type == QUIC_frame_new_connection_id) { + uint64_t seq, retire_prior_to, cid_len; + if (quic_varint_from_bytes(&seq, &p, &len) != 1 + || quic_varint_from_bytes(&retire_prior_to, &p, &len) != 1 + || quic_varint_from_bytes(&cid_len, &p, &len) != 1 + || cid_len > 20 || cid_len + 16 > len) { + error_print(); + return -1; + } + p += cid_len + 16; + len -= cid_len + 16; + } else if ((type & 0xf8) == QUIC_frame_stream_base) { + uint64_t stream_id, offset = 0, data_len; + const uint8_t *data; + if (quic_varint_from_bytes(&stream_id, &p, &len) != 1) { + error_print(); + return -1; + } + if (type & 0x04) { + if (quic_varint_from_bytes(&offset, &p, &len) != 1) { + error_print(); + return -1; + } + } + if (type & 0x02) { + if (quic_varint_from_bytes(&data_len, &p, &len) != 1 || data_len > len) { + error_print(); + return -1; + } + } else { + data_len = len; + } + data = p; + p += data_len; + len -= data_len; + if (verbose) { + fprintf(stderr, "recv STREAM id=%llu offset=%llu len=%llu fin=%d\n", + (unsigned long long)stream_id, (unsigned long long)offset, (unsigned long long)data_len, (int)(type & 0x01)); + format_bytes(stderr, 0, 4, "stream_data", data, (size_t)data_len); + } + if (data_len && stream_id == 0) { + if (quic_http3_stream_data_print(data, (size_t)data_len, got_stream_data, verbose) != 1) { + fwrite(data, 1, (size_t)data_len, stdout); + *got_stream_data = 1; + } + } + } else if (type == QUIC_frame_connection_close || type == QUIC_frame_connection_close_app) { + uint64_t error_code, frame_type = 0, reason_len; + if (quic_varint_from_bytes(&error_code, &p, &len) != 1) return -1; + if (type == QUIC_frame_connection_close && quic_varint_from_bytes(&frame_type, &p, &len) != 1) return -1; + if (quic_varint_from_bytes(&reason_len, &p, &len) != 1 || reason_len > len) return -1; + if (verbose) { + fprintf(stderr, "recv CONNECTION_CLOSE error=%llu frame=%llu\n", + (unsigned long long)error_code, (unsigned long long)frame_type); + } + p += reason_len; + len -= reason_len; + } else { + error_print(); + return -1; + } + } + return 1; +} + +static int quic_client_initial_packet_decrypt_and_print(FILE *fp, const uint8_t *original_dcid, size_t original_dcid_len, + const uint8_t *packet, size_t packet_len) +{ + uint8_t buf[1500]; + uint8_t plaintext[1500]; + const uint8_t *p; + size_t len; + uint64_t long_packet_len; + uint8_t type; + uint8_t dcid_len; + uint8_t scid_len; + size_t pn_offset; + size_t pn_len; + uint64_t packet_number = 0; + QUIC_INITIAL_SECRETS secrets; + QUIC_INITIAL_KEYS keys; + AES_KEY aes_key; + AES_KEY hp_key; + uint8_t nonce[QUIC_INITIAL_IV_SIZE]; + uint8_t mask[16]; + size_t aad_len; + size_t ciphertext_len; + size_t i; + + if (!fp || !original_dcid || !packet || packet_len > sizeof(buf) || packet_len < 7) { + error_print(); + return -1; + } + memcpy(buf, packet, packet_len); + + if (!(buf[0] & 0x80)) { + return 0; + } + type = (buf[0] >> 4) & 0x03; + if (type != QUIC_packet_initial) { + return 0; + } + + p = buf + 5; + len = packet_len - 5; + if (!len) return -1; + dcid_len = *p++; + len--; + if (dcid_len > len) return -1; + p += dcid_len; + len -= dcid_len; + if (!len) return -1; + scid_len = *p++; + len--; + if (scid_len > len) return -1; + p += scid_len; + len -= scid_len; + if (quic_varint_from_bytes(&long_packet_len, &p, &len) != 1) return -1; /* token length */ + if (long_packet_len > len) return -1; + p += long_packet_len; + len -= long_packet_len; + if (quic_varint_from_bytes(&long_packet_len, &p, &len) != 1) return -1; /* packet length */ + if (long_packet_len > len) return -1; + pn_offset = (size_t)(p - buf); + if (pn_offset + 4 + 16 > packet_len) return -1; + + if (quic_derive_initial_secrets(original_dcid, original_dcid_len, &secrets) != 1 + || quic_derive_initial_server_keys(&secrets, &keys) != 1 + || aes_set_encrypt_key(&aes_key, keys.key, sizeof(keys.key)) != 1 + || aes_set_encrypt_key(&hp_key, keys.hp, sizeof(keys.hp)) != 1) { + error_print(); + return -1; + } + aes_encrypt(&hp_key, buf + pn_offset + 4, mask); + buf[0] ^= mask[0] & 0x0f; + pn_len = (buf[0] & 0x03) + 1; + if (pn_len > long_packet_len) return -1; + for (i = 0; i < pn_len; i++) { + buf[pn_offset + i] ^= mask[i + 1]; + packet_number = (packet_number << 8) | buf[pn_offset + i]; + } + + memcpy(nonce, keys.iv, sizeof(nonce)); + for (i = 0; i < 8; i++) { + nonce[sizeof(nonce) - 1 - i] ^= (uint8_t)(packet_number >> (8 * i)); + } + aad_len = pn_offset + pn_len; + ciphertext_len = (size_t)long_packet_len - pn_len; + if (ciphertext_len < 16 + || aes_gcm_decrypt(&aes_key, nonce, sizeof(nonce), buf, aad_len, + buf + aad_len, ciphertext_len - 16, buf + aad_len + ciphertext_len - 16, 16, plaintext) != 1) { + error_print(); + return -1; + } + + format_print(fp, 0, 0, "Decrypted Initial Packet\n"); + format_print(fp, 0, 4, "Packet Number: %llu\n", (unsigned long long)packet_number); + quic_frames_print(fp, 0, 4, QUIC_encryption_initial, plaintext, ciphertext_len - 16); + return 1; +} + +static int quic_client_probe(const char *prog, const char *host, int port, + const char *server_name, int cipher_suite, int supported_group, int sig_alg, const char *request_path, int verbose) +{ + int ret = -1; + TLS_CTX ctx; + TLS_CONNECT conn; + char *alpn = "h3"; + uint8_t handshake[TLS_MAX_RECORD_SIZE]; + uint8_t frames[TLS_MAX_RECORD_SIZE]; + uint8_t packet[1500]; + uint8_t response[4096]; + uint8_t dcid[8] = { 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08 }; + uint8_t scid[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + QUIC_TRANSPORT_PARAMS params; + uint8_t *p = handshake; + uint8_t *q = frames; + size_t handshake_len = 0; + size_t frames_len = 0; + size_t packet_len = 0; + struct sockaddr_in server; + tls_socket_t sock = tls_socket_invalid(); + fd_set rfds; + struct timeval timeout; + tls_ret_t n; + + memset(&ctx, 0, sizeof(ctx)); + memset(&conn, 0, sizeof(conn)); + + if (tls_ctx_init(&ctx, TLS_protocol_tls13, TLS_client_mode) != 1 + || tls_ctx_set_cipher_suites(&ctx, &cipher_suite, 1) != 1 + || tls_ctx_set_supported_groups(&ctx, &supported_group, 1) != 1 + || tls_ctx_set_signature_algorithms(&ctx, &sig_alg, 1) != 1 + || tls_ctx_set_application_layer_protocol_negotiation(&ctx, &alpn, 1) != 1 + || tls_init(&conn, &ctx) != 1) { + error_print(); + goto end; + } + if (verbose && tls_ctx_set_verbose(&ctx, TLS_verbose) != 1) { + error_print(); + goto end; + } + if (tls_set_hostname(&conn, server_name ? server_name : host) != 1) { + error_print(); + goto end; + } + if (server_name && tls_set_server_name(&conn) != 1) { + error_print(); + goto end; + } + conn.verbose = verbose ? TLS_verbose : 0; + + quic_transport_params_init(¶ms); + if (quic_transport_params_add(¶ms, QUIC_transport_param_initial_source_connection_id, scid, sizeof(scid)) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_max_udp_payload_size, 1200) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_data, 1048576) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_stream_data_bidi_local, 65536) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_stream_data_bidi_remote, 65536) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_stream_data_uni, 65536) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_streams_bidi, 16) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_streams_uni, 3) != 1) { + error_print(); + goto end; + } + if (quic_client_hello_to_bytes_ex(&conn, ¶ms, &p, &handshake_len) != 1) { + error_print(); + goto end; + } + if (quic_varint_to_bytes(QUIC_frame_crypto, &q, &frames_len) != 1 + || quic_varint_to_bytes(0, &q, &frames_len) != 1 + || quic_varint_to_bytes(handshake_len, &q, &frames_len) != 1) { + error_print(); + goto end; + } + memcpy(q, handshake, handshake_len); + q += handshake_len; + frames_len += handshake_len; + + if (verbose) { + quic_frames_print(stderr, 0, 0, QUIC_encryption_initial, frames, frames_len); + } + if (quic_client_initial_packet_to_bytes(dcid, sizeof(dcid), scid, sizeof(scid), + frames, frames_len, packet, &packet_len) != 1) { + error_print(); + goto end; + } + if (verbose) { + quic_packet_print(stderr, 0, 0, packet, packet_len); + } + + if (tls_socket_lib_init() != 1 + || tls_socket_get_addr(host, port, &server) != 1 + || tls_socket_create(&sock, AF_INET, SOCK_DGRAM, 0) != 1 + || tls_socket_connect(sock, &server) != 1) { + error_print(); + goto end; + } + n = tls_socket_send(sock, packet, packet_len, 0); + if (n != (tls_ret_t)packet_len) { + fprintf(stderr, "%s: send QUIC Initial probe failed\n", prog); + goto end; + } + if (verbose) { + fprintf(stderr, "send QUIC Initial probe: %zu bytes\n", packet_len); + } + + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + timeout.tv_sec = 2; + timeout.tv_usec = 0; + if (select((int)(sock + 1), &rfds, NULL, NULL, &timeout) > 0) { + n = tls_socket_recv(sock, response, sizeof(response), 0); + if (n > 0) { + QUIC_INITIAL_SECRETS initial_secrets; + QUIC_INITIAL_KEYS client_initial_keys; + QUIC_INITIAL_KEYS server_initial_keys; + QUIC_PACKET_KEYS client_initial_packet_keys; + QUIC_PACKET_KEYS initial_server_packet_keys; + QUIC_PACKET_KEYS server_handshake_packet_keys; + QUIC_PACKET_KEYS client_handshake_packet_keys; + QUIC_PACKET_KEYS server_application_packet_keys; + QUIC_PACKET_KEYS client_application_packet_keys; + QUIC_DECRYPTED_PACKET initial_packet; + QUIC_DECRYPTED_PACKET handshake_packet; + QUIC_DECRYPTED_PACKET application_packet; + uint8_t initial_crypto[TLS_MAX_RECORD_SIZE]; + uint8_t handshake_crypto[TLS_MAX_RECORD_SIZE]; + uint8_t client_finished[TLS_MAX_RECORD_SIZE]; + uint8_t request_frames[TLS_MAX_RECORD_SIZE]; + uint8_t send_frames[TLS_MAX_RECORD_SIZE]; + uint8_t send_packet[1500]; + uint8_t *r = send_frames; + size_t initial_crypto_len = 0; + size_t handshake_crypto_len = 0; + size_t client_finished_len = 0; + size_t request_frames_len = 0; + size_t send_frames_len = 0; + size_t send_packet_len = 0; + size_t response_len = (size_t)n; + size_t off = 0; + uint64_t largest_ack = 0; + uint64_t server_initial_packet_number = 0; + uint64_t server_handshake_packet_number = 0; + uint64_t client_application_packet_number = 0; + int got_stream_data = 0; + int loop_count; + if (verbose) { + fprintf(stderr, "recv UDP datagram: %ld bytes\n", (long)n); + quic_packet_print(stderr, 0, 0, response, (size_t)n); + } + if (quic_derive_initial_secrets(dcid, sizeof(dcid), &initial_secrets) != 1 + || quic_derive_initial_client_keys(&initial_secrets, &client_initial_keys) != 1 + || quic_derive_initial_server_keys(&initial_secrets, &server_initial_keys) != 1 + || quic_packet_keys_from_initial(&client_initial_keys, &client_initial_packet_keys) != 1 + || quic_packet_keys_from_initial(&server_initial_keys, &initial_server_packet_keys) != 1 + || quic_long_packet_decrypt(&initial_server_packet_keys, response, response_len, QUIC_packet_initial, &initial_packet) != 1) { + error_print(); + goto end; + } + if (verbose) { + format_print(stderr, 0, 0, "Decrypted Initial Packet\n"); + format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)initial_packet.packet_number); + quic_frames_print(stderr, 0, 4, QUIC_encryption_initial, initial_packet.plaintext, initial_packet.plaintext_len); + } + server_initial_packet_number = initial_packet.packet_number; + if (quic_frames_crypto_collect(initial_packet.plaintext, initial_packet.plaintext_len, + initial_crypto, &initial_crypto_len, sizeof(initial_crypto), &largest_ack) != 1 + || quic_client_server_hello_process(&conn, handshake, handshake_len, initial_crypto, initial_crypto_len) != 1 + || quic_packet_keys_derive(conn.digest, conn.server_handshake_traffic_secret, conn.cipher_suite, &server_handshake_packet_keys) != 1 + || quic_packet_keys_derive(conn.digest, conn.client_handshake_traffic_secret, conn.cipher_suite, &client_handshake_packet_keys) != 1) { + error_print(); + goto end; + } + off = initial_packet.packet_len; + if (off >= response_len + || quic_long_packet_decrypt(&server_handshake_packet_keys, response + off, response_len - off, QUIC_packet_handshake, &handshake_packet) != 1) { + error_print(); + goto end; + } + if (verbose) { + format_print(stderr, 0, 0, "Decrypted Handshake Packet\n"); + format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)handshake_packet.packet_number); + quic_frames_print(stderr, 0, 4, QUIC_encryption_handshake, handshake_packet.plaintext, handshake_packet.plaintext_len); + } + server_handshake_packet_number = handshake_packet.packet_number; + largest_ack = 0; + if (quic_frames_crypto_collect(handshake_packet.plaintext, handshake_packet.plaintext_len, + handshake_crypto, &handshake_crypto_len, sizeof(handshake_crypto), &largest_ack) != 1 + || quic_client_handshake_flight_process(&conn, handshake_crypto, handshake_crypto_len, verbose) != 1 + || quic_client_finished_to_bytes(&conn, client_finished, &client_finished_len) != 1) { + error_print(); + goto end; + } + if (quic_ack_frame_to_bytes(server_handshake_packet_number, &r, &send_frames_len) != 1 + || quic_crypto_frame_to_bytes(0, client_finished, client_finished_len, &r, &send_frames_len) != 1 + || quic_long_packet_encrypt(&client_handshake_packet_keys, QUIC_packet_handshake, + initial_packet.scid, initial_packet.scid_len, scid, sizeof(scid), 1, + send_frames, send_frames_len, send_packet, &send_packet_len) != 1) { + error_print(); + goto end; + } + if (verbose) { + format_print(stderr, 0, 0, "send QUIC Handshake Packet\n"); + quic_frames_print(stderr, 0, 4, QUIC_encryption_handshake, send_frames, send_frames_len); + quic_packet_print(stderr, 0, 4, send_packet, send_packet_len); + } + n = tls_socket_send(sock, send_packet, send_packet_len, 0); + if (n != (tls_ret_t)send_packet_len) { + fprintf(stderr, "%s: send QUIC Handshake Finished failed\n", prog); + goto end; + } + r = send_frames; + send_frames_len = 0; + if (quic_ack_frame_to_bytes(server_initial_packet_number, &r, &send_frames_len) != 1 + || quic_long_packet_encrypt(&client_initial_packet_keys, QUIC_packet_initial, + initial_packet.scid, initial_packet.scid_len, scid, sizeof(scid), 2, + send_frames, send_frames_len, send_packet, &send_packet_len) != 1) { + error_print(); + goto end; + } + n = tls_socket_send(sock, send_packet, send_packet_len, 0); + if (n != (tls_ret_t)send_packet_len) { + fprintf(stderr, "%s: send QUIC Initial ACK failed\n", prog); + goto end; + } + if (quic_packet_keys_derive(conn.digest, conn.server_application_traffic_secret, conn.cipher_suite, &server_application_packet_keys) != 1 + || quic_packet_keys_derive(conn.digest, conn.client_application_traffic_secret, conn.cipher_suite, &client_application_packet_keys) != 1 + || quic_http3_client_data_to_frames(server_name ? server_name : host, request_path ? request_path : "/", request_frames, &request_frames_len) != 1 + || quic_short_packet_encrypt(&client_application_packet_keys, initial_packet.scid, initial_packet.scid_len, + client_application_packet_number++, request_frames, request_frames_len, send_packet, &send_packet_len) != 1) { + error_print(); + goto end; + } + if (verbose) { + format_print(stderr, 0, 0, "send QUIC 1-RTT HTTP/3 Request\n"); + quic_frames_print(stderr, 0, 4, QUIC_encryption_application, request_frames, request_frames_len); + quic_packet_print(stderr, 0, 4, send_packet, send_packet_len); + } + n = tls_socket_send(sock, send_packet, send_packet_len, 0); + if (n != (tls_ret_t)send_packet_len) { + fprintf(stderr, "%s: send QUIC 1-RTT request failed\n", prog); + goto end; + } + off += handshake_packet.packet_len; + if (off < response_len + && quic_short_packet_decrypt(&server_application_packet_keys, response + off, response_len - off, scid, sizeof(scid), &application_packet) == 1) { + if (verbose) { + format_print(stderr, 0, 0, "Decrypted 1-RTT Packet\n"); + format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)application_packet.packet_number); + quic_frames_print(stderr, 0, 4, QUIC_encryption_application, application_packet.plaintext, application_packet.plaintext_len); + } + if (quic_application_frames_process(application_packet.plaintext, application_packet.plaintext_len, &largest_ack, &got_stream_data, verbose) != 1) { + error_print(); + goto end; + } + } + for (loop_count = 0; loop_count < 8 && !got_stream_data; loop_count++) { + size_t datagram_off; + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + timeout.tv_sec = 2; + timeout.tv_usec = 0; + if (select((int)(sock + 1), &rfds, NULL, NULL, &timeout) <= 0) { + break; + } + n = tls_socket_recv(sock, response, sizeof(response), 0); + if (n <= 0) { + break; + } + if (verbose) { + fprintf(stderr, "recv UDP datagram: %ld bytes\n", (long)n); + quic_packet_print(stderr, 0, 0, response, (size_t)n); + } + for (datagram_off = 0; datagram_off < (size_t)n && !got_stream_data; ) { + size_t current_packet_len; + if (quic_packet_total_length(response + datagram_off, (size_t)n - datagram_off, ¤t_packet_len) != 1 || !current_packet_len) { + break; + } + if (response[datagram_off] & 0x80) { + datagram_off += current_packet_len; + continue; + } + if (quic_short_packet_decrypt(&server_application_packet_keys, response + datagram_off, current_packet_len, scid, sizeof(scid), &application_packet) != 1) { + break; + } + if (verbose) { + format_print(stderr, 0, 0, "Decrypted 1-RTT Packet\n"); + format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)application_packet.packet_number); + quic_frames_print(stderr, 0, 4, QUIC_encryption_application, application_packet.plaintext, application_packet.plaintext_len); + } + largest_ack = 0; + if (quic_application_frames_process(application_packet.plaintext, application_packet.plaintext_len, &largest_ack, &got_stream_data, verbose) != 1) { + error_print(); + goto end; + } + r = send_frames; + send_frames_len = 0; + if (quic_ack_frame_to_bytes(application_packet.packet_number, &r, &send_frames_len) == 1 + && quic_short_packet_encrypt(&client_application_packet_keys, initial_packet.scid, initial_packet.scid_len, + client_application_packet_number++, send_frames, send_frames_len, send_packet, &send_packet_len) == 1) { + tls_socket_send(sock, send_packet, send_packet_len, 0); + } + break; + } + } + if (verbose) { + fprintf(stderr, "QUIC TLS handshake completed\n"); + } + ret = got_stream_data ? 1 : 0; + } + } else { + fprintf(stderr, "%s: no response from server\n", prog); + ret = 0; + } + +end: + if (tls_socket_is_valid(sock)) tls_socket_close(sock); + tls_socket_lib_cleanup(); + tls_ctx_cleanup(&ctx); + return ret; +} + +int quic_client_main(int argc, char **argv) +{ + char *prog = argv[0]; + char *host = NULL; + char *server_name = NULL; + char *get = NULL; + int cipher_suite = TLS_cipher_aes_128_gcm_sha256; + int supported_group = TLS_curve_secp256r1; + int sig_alg = TLS_sig_ecdsa_secp256r1_sha256; + int port = 443; + int verbose = 0; + int ret; + + argc--; + argv++; + if (argc < 1) { + fprintf(stderr, "usage: %s %s\n", prog, options); + return 1; + } + while (argc > 0) { + 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; + host = *(++argv); + } else if (!strcmp(*argv, "-port")) { + if (--argc < 1) goto bad; + port = atoi(*(++argv)); + } else if (!strcmp(*argv, "-server_name")) { + if (--argc < 1) goto bad; + server_name = *(++argv); + } else if (!strcmp(*argv, "-cipher_suite")) { + if (--argc < 1) goto bad; + if ((cipher_suite = tls_cipher_suite_from_name(*(++argv))) == 0) { + fprintf(stderr, "%s: invalid cipher suite '%s'\n", prog, *argv); + return 1; + } + } else if (!strcmp(*argv, "-supported_group")) { + if (--argc < 1) goto bad; + if ((supported_group = tls_named_curve_from_name(*(++argv))) == 0) { + fprintf(stderr, "%s: invalid supported group '%s'\n", prog, *argv); + return 1; + } + } else if (!strcmp(*argv, "-sig_alg")) { + if (--argc < 1) goto bad; + if ((sig_alg = tls_signature_scheme_from_name(*(++argv))) == 0) { + fprintf(stderr, "%s: invalid signature algorithm '%s'\n", prog, *argv); + return 1; + } + } else if (!strcmp(*argv, "-get")) { + if (--argc < 1) goto bad; + get = *(++argv); + (void)get; + } else if (!strcmp(*argv, "-verbose")) { + verbose = 1; + } else { + fprintf(stderr, "%s: invalid option '%s'\n", prog, *argv); + return 1; +bad: + fprintf(stderr, "%s: option '%s' argument required\n", prog, *argv); + return 1; + } + argc--; + argv++; + } + if (!host) { + fprintf(stderr, "%s: '-host' option required\n", prog); + return 1; + } + + ret = quic_client_probe(prog, host, port, server_name, cipher_suite, supported_group, sig_alg, get, verbose); + return ret == 1 ? 0 : 1; +} diff --git a/tools/quic_help.h b/tools/quic_help.h new file mode 100644 index 00000000..07b293ab --- /dev/null +++ b/tools/quic_help.h @@ -0,0 +1,35 @@ +"\n" +"Supported cipher suites:\n" +#if defined(ENABLE_AES) && defined(ENABLE_SHA2) +" TLS_AES_128_GCM_SHA256\n" +#ifdef ENABLE_AES_CCM +" TLS_AES_128_CCM_SHA256\n" +#endif +#endif +" TLS_SM4_GCM_SM3\n" +#ifdef ENABLE_SM4_CCM +" TLS_SM4_CCM_SM3\n" +#endif +"\n" +"Examples\n" +"\n" +" gmssl quic_server -port 4433 -cert p256_tls_server_certs.pem -key p256_tls_server_key.pem \\\n" +" -pass P@ssw0rd -cipher_suite TLS_AES_128_GCM_SHA256 -supported_group prime256v1 \\\n" +" -sig_alg ecdsa_secp256r1_sha256 -verbose\n" +" gmssl quic_client -host 127.0.0.1 -port 4433 -server_name localhost \\\n" +" -cipher_suite TLS_AES_128_GCM_SHA256 -supported_group prime256v1 \\\n" +" -sig_alg ecdsa_secp256r1_sha256 -get / -verbose\n" +"\n" +" gmssl quic_server -port 4434 -cert sm2_tls_server_certs.pem -key sm2_tls_server_key.pem \\\n" +" -pass P@ssw0rd -cipher_suite TLS_SM4_GCM_SM3 -supported_group sm2p256v1 \\\n" +" -sig_alg sm2sig_sm3 -verbose\n" +" gmssl quic_client -host 127.0.0.1 -port 4434 -server_name localhost \\\n" +" -cipher_suite TLS_SM4_GCM_SM3 -supported_group sm2p256v1 -sig_alg sm2sig_sm3 \\\n" +" -get / -verbose\n" +"\n" +" ngtcp2/examples/osslserver --groups=P-256 --ciphers=TLS_AES_128_GCM_SHA256 \\\n" +" -d htdocs 127.0.0.1 4435 p256-key.pem p256-cert.pem\n" +" gmssl quic_client -host 127.0.0.1 -port 4435 -server_name localhost \\\n" +" -cipher_suite TLS_AES_128_GCM_SHA256 -supported_group prime256v1 \\\n" +" -sig_alg ecdsa_secp256r1_sha256 -get / -verbose\n" +"\n" diff --git a/tools/quic_server.c b/tools/quic_server.c new file mode 100644 index 00000000..5183fd59 --- /dev/null +++ b/tools/quic_server.c @@ -0,0 +1,1010 @@ +/* + * 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 + +int tls13_generate_application_secrets(TLS_CONNECT *conn); +int tls13_generate_client_application_keys(TLS_CONNECT *conn); +int tls13_generate_server_application_keys(TLS_CONNECT *conn); +int tls13_record_set_handshake_encrypted_extensions(uint8_t *record, size_t *recordlen, const uint8_t *exts, size_t extslen); + +static const char *options = "[-port num] -cert pem -key pem [-pass str] [-cipher_suite str] [-supported_group str] [-sig_alg str] [-verbose]"; + +static const char *help = +"Options\n" +"\n" +" -port num Listening UDP port number, default 443\n" +" -cert pem Server's certificate chain in PEM format\n" +" -key pem Server's encrypted private key in PEM format\n" +" -pass str Password to decrypt private key\n" +" -cipher_suite str TLS 1.3 cipher suite, default TLS_AES_128_GCM_SHA256 and TLS_AES_128_CCM_SHA256\n" +" -supported_group str Supported elliptic curve, default prime256v1\n" +" -sig_alg str Supported signature algorithm, default ecdsa_secp256r1_sha256\n" +" -verbose Print QUIC packet, frame and TLS handshake messages\n" +"\n" +#include "quic_help.h" +; + +static int quic_tls_record_from_handshake(const uint8_t *handshake, size_t handshake_len, uint8_t *record, size_t *record_len) +{ + if (!handshake || !record || !record_len || handshake_len > TLS_MAX_PLAINTEXT_SIZE) { + error_print(); + return -1; + } + record[0] = TLS_record_handshake; + record[1] = 0x03; + record[2] = 0x03; + record[3] = (uint8_t)(handshake_len >> 8); + record[4] = (uint8_t)handshake_len; + memcpy(record + TLS_RECORD_HEADER_SIZE, handshake, handshake_len); + *record_len = TLS_RECORD_HEADER_SIZE + handshake_len; + return 1; +} + +static int quic_long_packet_connection_ids_get(const uint8_t *packet, size_t packet_len, + const uint8_t **dcid, size_t *dcid_len, const uint8_t **scid, size_t *scid_len) +{ + const uint8_t *p; + size_t len; + uint8_t n; + + if (!packet || packet_len < 7 || !(packet[0] & 0x80) || !dcid || !dcid_len || !scid || !scid_len) { + error_print(); + return -1; + } + p = packet + 5; + len = packet_len - 5; + n = *p++; + len--; + if (n > len) { + error_print(); + return -1; + } + *dcid = p; + *dcid_len = n; + p += n; + len -= n; + if (!len) { + error_print(); + return -1; + } + n = *p++; + len--; + if (n > len) { + error_print(); + return -1; + } + *scid = p; + *scid_len = n; + return 1; +} + +static int quic_ack_frame_to_bytes(uint64_t packet_number, uint8_t **out, size_t *outlen) +{ + if (quic_varint_to_bytes(QUIC_frame_ack, out, outlen) != 1 + || quic_varint_to_bytes(packet_number, out, outlen) != 1 + || quic_varint_to_bytes(0, out, outlen) != 1 + || quic_varint_to_bytes(0, out, outlen) != 1 + || quic_varint_to_bytes(0, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_crypto_frame_to_bytes(uint64_t offset, const uint8_t *data, size_t datalen, uint8_t **out, size_t *outlen) +{ + if ((!data && datalen) + || quic_varint_to_bytes(QUIC_frame_crypto, out, outlen) != 1 + || quic_varint_to_bytes(offset, out, outlen) != 1 + || quic_varint_to_bytes(datalen, out, outlen) != 1) { + error_print(); + return -1; + } + if (out && *out) { + memcpy(*out, data, datalen); + *out += datalen; + } + *outlen += datalen; + return 1; +} + +static int quic_stream_frame_to_bytes(uint64_t stream_id, uint64_t offset, int fin, const uint8_t *data, size_t datalen, uint8_t **out, size_t *outlen) +{ + uint64_t type = QUIC_frame_stream_base | 0x02 | (offset ? 0x04 : 0) | (fin ? 0x01 : 0); + + if ((!data && datalen) + || quic_varint_to_bytes(type, out, outlen) != 1 + || quic_varint_to_bytes(stream_id, out, outlen) != 1) { + error_print(); + return -1; + } + if (offset && quic_varint_to_bytes(offset, out, outlen) != 1) { + error_print(); + return -1; + } + if (quic_varint_to_bytes(datalen, out, outlen) != 1) { + error_print(); + return -1; + } + if (out && *out) { + memcpy(*out, data, datalen); + *out += datalen; + } + *outlen += datalen; + return 1; +} + +static int quic_frames_crypto_collect(const uint8_t *frames, size_t frames_len, + uint8_t *crypto, size_t *crypto_len, size_t crypto_max, uint64_t *largest_ack) +{ + const uint8_t *p = frames; + size_t len = frames_len; + + if (!frames || !crypto || !crypto_len || !largest_ack) { + error_print(); + return -1; + } + while (len) { + uint64_t type; + if (quic_varint_from_bytes(&type, &p, &len) != 1) { + error_print(); + return -1; + } + if (type == QUIC_frame_padding) { + while (len && *p == 0) { + p++; + len--; + } + } else if (type == QUIC_frame_ping) { + continue; + } else if (type == QUIC_frame_ack || type == QUIC_frame_ack_ecn) { + uint64_t ack_delay, ack_range_count, first_ack_range, i; + if (quic_varint_from_bytes(largest_ack, &p, &len) != 1 + || quic_varint_from_bytes(&ack_delay, &p, &len) != 1 + || quic_varint_from_bytes(&ack_range_count, &p, &len) != 1 + || quic_varint_from_bytes(&first_ack_range, &p, &len) != 1) { + error_print(); + return -1; + } + for (i = 0; i < ack_range_count; i++) { + uint64_t gap, ack_range; + if (quic_varint_from_bytes(&gap, &p, &len) != 1 || quic_varint_from_bytes(&ack_range, &p, &len) != 1) { + error_print(); + return -1; + } + } + if (type == QUIC_frame_ack_ecn) { + uint64_t ect0, ect1, ce; + if (quic_varint_from_bytes(&ect0, &p, &len) != 1 + || quic_varint_from_bytes(&ect1, &p, &len) != 1 + || quic_varint_from_bytes(&ce, &p, &len) != 1) { + error_print(); + return -1; + } + } + } else if (type == QUIC_frame_crypto) { + uint64_t off, data_len; + if (quic_varint_from_bytes(&off, &p, &len) != 1 + || quic_varint_from_bytes(&data_len, &p, &len) != 1 + || data_len > len || off + data_len > crypto_max) { + error_print(); + return -1; + } + memcpy(crypto + off, p, (size_t)data_len); + if (*crypto_len < off + data_len) { + *crypto_len = (size_t)(off + data_len); + } + p += data_len; + len -= data_len; + } else { + error_print(); + return -1; + } + } + return 1; +} + +static int quic_tls_handshake_is_complete(const uint8_t *handshake, size_t handshake_len) +{ + size_t len; + + if (!handshake || handshake_len < TLS_HANDSHAKE_HEADER_SIZE) { + return 0; + } + len = ((size_t)handshake[1] << 16) | ((size_t)handshake[2] << 8) | handshake[3]; + return handshake_len >= TLS_HANDSHAKE_HEADER_SIZE + len; +} + +static int quic_server_client_hello_process(TLS_CONNECT *conn, const uint8_t *client_hello, size_t client_hello_len) +{ + uint8_t record[TLS_MAX_RECORD_SIZE]; + size_t record_len; + int legacy_version; + const uint8_t *random; + const uint8_t *legacy_session_id; + size_t legacy_session_id_len; + const uint8_t *cipher_suites; + size_t cipher_suites_len; + const uint8_t *exts; + size_t extslen; + const uint8_t *supported_versions = NULL; + size_t supported_versions_len = 0; + const uint8_t *supported_groups = NULL; + size_t supported_groups_len = 0; + const uint8_t *signature_algorithms = NULL; + size_t signature_algorithms_len = 0; + const uint8_t *key_share = NULL; + size_t key_share_len = 0; + const uint8_t *server_name = NULL; + size_t server_name_len = 0; + const uint8_t *alpn = NULL; + size_t alpn_len = 0; + int common_versions[4]; + size_t common_versions_cnt = 0; + int common_groups[4]; + size_t common_groups_cnt = 0; + int common_sig_algs[8]; + size_t common_sig_algs_cnt = 0; + const uint8_t *host_name = NULL; + size_t host_name_len = 0; + int group = 0; + const uint8_t *key_exchange = NULL; + size_t key_exchange_len = 0; + int ret; + + if (!conn || !client_hello || !client_hello_len) { + error_print(); + return -1; + } + if (quic_tls_record_from_handshake(client_hello, client_hello_len, record, &record_len) != 1 + || tls_record_get_handshake_client_hello(record, &legacy_version, &random, &legacy_session_id, + &legacy_session_id_len, &cipher_suites, &cipher_suites_len, &exts, &extslen) != 1) { + error_print(); + return -1; + } + if (legacy_version != TLS_protocol_tls12) { + error_print(); + return -1; + } + memcpy(conn->client_random, random, 32); + if (legacy_session_id_len > sizeof(conn->session_id)) { + error_print(); + return -1; + } + memcpy(conn->session_id, legacy_session_id, legacy_session_id_len); + conn->session_id_len = legacy_session_id_len; + + while (extslen) { + int ext_type; + const uint8_t *ext_data; + size_t ext_datalen; + + if (tls_ext_from_bytes(&ext_type, &ext_data, &ext_datalen, &exts, &extslen) != 1) { + error_print(); + return -1; + } + switch (ext_type) { + case TLS_extension_supported_versions: + supported_versions = ext_data; + supported_versions_len = ext_datalen; + break; + case TLS_extension_supported_groups: + supported_groups = ext_data; + supported_groups_len = ext_datalen; + break; + case TLS_extension_signature_algorithms: + signature_algorithms = ext_data; + signature_algorithms_len = ext_datalen; + break; + case TLS_extension_key_share: + key_share = ext_data; + key_share_len = ext_datalen; + break; + case TLS_extension_server_name: + server_name = ext_data; + server_name_len = ext_datalen; + break; + case TLS_extension_application_layer_protocol_negotiation: + alpn = ext_data; + alpn_len = ext_datalen; + break; + default: + break; + } + } + if (!supported_versions || !supported_groups || !signature_algorithms || !key_share || !alpn) { + error_print(); + return -1; + } + if ((ret = tls13_process_client_supported_versions(supported_versions, supported_versions_len, + conn->ctx->supported_versions, conn->ctx->supported_versions_cnt, + common_versions, &common_versions_cnt, sizeof(common_versions)/sizeof(common_versions[0]))) != 1 + || common_versions[0] != TLS_protocol_tls13) { + error_print(); + return -1; + } + conn->protocol = TLS_protocol_tls13; + if ((ret = tls_process_supported_groups(supported_groups, supported_groups_len, + conn->ctx->supported_groups, conn->ctx->supported_groups_cnt, + common_groups, &common_groups_cnt, sizeof(common_groups)/sizeof(common_groups[0]))) != 1) { + error_print(); + return -1; + } + if ((ret = tls_process_signature_algorithms(signature_algorithms, signature_algorithms_len, + conn->ctx->signature_algorithms, conn->ctx->signature_algorithms_cnt, + common_sig_algs, &common_sig_algs_cnt, sizeof(common_sig_algs)/sizeof(common_sig_algs[0]))) != 1) { + error_print(); + return -1; + } + if (server_name + && tls_server_name_from_bytes(&host_name, &host_name_len, server_name, server_name_len) != 1) { + error_print(); + return -1; + } + if ((ret = tls_application_layer_protocol_negotiation_select(alpn, alpn_len, + conn->ctx->alpn_protocols, conn->ctx->alpn_protocols_cnt, &conn->alpn_selected)) != 1) { + error_print(); + return -1; + } + if ((ret = tls13_cert_chains_select(conn->ctx->cert_chains, conn->ctx->cert_chains_len, + common_sig_algs, common_sig_algs_cnt, NULL, 0, NULL, 0, NULL, 0, host_name, host_name_len, + &conn->cert_chain, &conn->cert_chain_len, &conn->cert_chain_idx, &conn->sig_alg)) != 1) { + error_print(); + return -1; + } + if (tls_cipher_suites_select(cipher_suites, cipher_suites_len, + conn->ctx->cipher_suites, conn->ctx->cipher_suites_cnt, &conn->cipher_suite) != 1 + || tls_cipher_suite_get(conn->cipher_suite, &conn->cipher, &conn->digest) != 1) { + error_print(); + return -1; + } + if (tls13_process_key_share_client_hello(key_share, key_share_len, common_groups, common_groups_cnt, + &group, &key_exchange, &key_exchange_len) != 1 || key_exchange_len != 65) { + error_print(); + return -1; + } + conn->key_exchange_group = group; + memcpy(conn->peer_key_exchange, key_exchange, key_exchange_len); + conn->peer_key_exchange_len = key_exchange_len; + conn->key_exchange_modes = TLS_KE_CERT_DHE; + if (digest_init(&conn->dgst_ctx, conn->digest) != 1 + || digest_update(&conn->dgst_ctx, client_hello, client_hello_len) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_record_handshake_append(TLS_CONNECT *conn, const uint8_t *record, size_t record_len, uint8_t **out, size_t *outlen) +{ + const uint8_t *handshake; + size_t handshake_len; + + if (!conn || !record || record_len < TLS_RECORD_HEADER_SIZE || !outlen + || tls_record_type(record) != TLS_record_handshake || tls_record_length(record) != record_len) { + error_print(); + return -1; + } + handshake = record + TLS_RECORD_HEADER_SIZE; + handshake_len = record_len - TLS_RECORD_HEADER_SIZE; + if (out && *out) { + memcpy(*out, handshake, handshake_len); + *out += handshake_len; + } + *outlen += handshake_len; + if (digest_update(&conn->dgst_ctx, handshake, handshake_len) != 1) { + error_print(); + return -1; + } + return 1; +} + +static void quic_tls_record_header_init(uint8_t *record) +{ + record[0] = TLS_record_handshake; + record[1] = 0x03; + record[2] = 0x03; +} + +static int quic_server_encrypted_extensions_to_bytes(TLS_CONNECT *conn, const QUIC_TRANSPORT_PARAMS *params, uint8_t **out, size_t *outlen) +{ + uint8_t exts[512]; + uint8_t *pexts = exts; + size_t extslen = 0; + uint8_t transport_params[QUIC_TRANSPORT_PARAM_MAX_SIZE]; + uint8_t *ptransport_params = transport_params; + size_t transport_params_len = 0; + uint8_t record[TLS_MAX_RECORD_SIZE]; + size_t record_len = 0; + + if (!conn || !params || !outlen) { + error_print(); + return -1; + } + quic_tls_record_header_init(record); + if (tls_application_layer_protocol_negotiation_selected_ext_to_bytes(conn->alpn_selected, &pexts, &extslen) != 1 + || quic_transport_params_to_bytes(params, &ptransport_params, &transport_params_len) != 1 + || tls_ext_to_bytes(TLS_extension_quic_transport_parameters, transport_params, transport_params_len, &pexts, &extslen) != 1 + || tls13_record_set_handshake_encrypted_extensions(record, &record_len, exts, extslen) != 1 + || quic_record_handshake_append(conn, record, record_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_server_certificate_to_bytes(TLS_CONNECT *conn, uint8_t **out, size_t *outlen) +{ + uint8_t record[TLS_MAX_RECORD_SIZE]; + size_t record_len = 0; + + if (!conn || !outlen) { + error_print(); + return -1; + } + quic_tls_record_header_init(record); + if (tls13_record_set_handshake_certificate(record, &record_len, NULL, 0, + conn->cert_chain, conn->cert_chain_len, NULL, 0, NULL, 0) != 1 + || quic_record_handshake_append(conn, record, record_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_server_certificate_verify_to_bytes(TLS_CONNECT *conn, uint8_t **out, size_t *outlen) +{ + X509_KEY *sign_key; + uint8_t sig[SM2_MAX_SIGNATURE_SIZE]; + size_t siglen; + uint8_t record[TLS_MAX_RECORD_SIZE]; + size_t record_len = 0; + + if (!conn || !outlen || !conn->cert_chain_idx) { + error_print(); + return -1; + } + quic_tls_record_header_init(record); + sign_key = &conn->ctx->x509_keys[conn->cert_chain_idx - 1]; + if (tls13_sign_certificate_verify(TLS_server_mode, conn->sig_alg, sign_key, &conn->dgst_ctx, sig, &siglen) != 1 + || tls13_record_set_handshake_certificate_verify(record, &record_len, conn->sig_alg, sig, siglen) != 1 + || quic_record_handshake_append(conn, record, record_len, out, outlen) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_server_finished_to_bytes(TLS_CONNECT *conn, uint8_t **out, size_t *outlen) +{ + uint8_t verify_data[64]; + size_t verify_data_len; + uint8_t record[TLS_MAX_RECORD_SIZE]; + size_t record_len = 0; + const uint8_t *handshake; + size_t handshake_len; + + if (!conn || !outlen) { + error_print(); + return -1; + } + quic_tls_record_header_init(record); + if (tls13_compute_verify_data(conn->server_handshake_traffic_secret, &conn->dgst_ctx, verify_data, &verify_data_len) != 1 + || tls13_record_set_handshake_finished(record, &record_len, verify_data, verify_data_len) != 1) { + error_print(); + return -1; + } + handshake = record + TLS_RECORD_HEADER_SIZE; + handshake_len = record_len - TLS_RECORD_HEADER_SIZE; + if (out && *out) { + memcpy(*out, handshake, handshake_len); + *out += handshake_len; + } + *outlen += handshake_len; + if (digest_update(&conn->dgst_ctx, handshake, handshake_len) != 1 + || tls13_generate_application_secrets(conn) != 1 + || tls13_generate_server_application_keys(conn) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_server_client_finished_process(TLS_CONNECT *conn, const uint8_t *handshake, size_t handshake_len) +{ + uint8_t record[TLS_MAX_RECORD_SIZE]; + size_t record_len; + const uint8_t *client_verify_data; + size_t client_verify_data_len; + uint8_t verify_data[64]; + size_t verify_data_len; + + if (!conn || !handshake || !handshake_len) { + error_print(); + return -1; + } + if (quic_tls_record_from_handshake(handshake, handshake_len, record, &record_len) != 1 + || tls13_compute_verify_data(conn->client_handshake_traffic_secret, &conn->dgst_ctx, verify_data, &verify_data_len) != 1 + || tls13_record_get_handshake_finished(record, &client_verify_data, &client_verify_data_len) != 1 + || client_verify_data_len != verify_data_len + || memcmp(client_verify_data, verify_data, verify_data_len) != 0 + || digest_update(&conn->dgst_ctx, handshake, handshake_len) != 1 + || tls13_generate_client_application_keys(conn) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_http3_response_to_frames(uint64_t request_stream_id, const uint8_t *body, size_t body_len, uint64_t ack_pn, uint8_t *frames, size_t *frames_len) +{ + uint8_t control_stream[] = { 0x00, 0x04, 0x00 }; + uint8_t qpack_encoder_stream[] = { 0x02 }; + uint8_t qpack_decoder_stream[] = { 0x03 }; + uint8_t headers[] = { 0x01, 0x03, 0x00, 0x00, 0xd9 }; + uint8_t data[512]; + uint8_t *p = frames; + uint8_t *q = data; + size_t data_len = 0; + + if (!body || !frames || !frames_len || body_len > 255) { + error_print(); + return -1; + } + *frames_len = 0; + if (quic_varint_to_bytes(0x00, &q, &data_len) != 1 + || quic_varint_to_bytes(body_len, &q, &data_len) != 1) { + error_print(); + return -1; + } + memcpy(q, body, body_len); + data_len += body_len; + if (quic_ack_frame_to_bytes(ack_pn, &p, frames_len) != 1 + || quic_varint_to_bytes(QUIC_frame_handshake_done, &p, frames_len) != 1 + || quic_stream_frame_to_bytes(3, 0, 0, control_stream, sizeof(control_stream), &p, frames_len) != 1 + || quic_stream_frame_to_bytes(7, 0, 0, qpack_encoder_stream, sizeof(qpack_encoder_stream), &p, frames_len) != 1 + || quic_stream_frame_to_bytes(11, 0, 0, qpack_decoder_stream, sizeof(qpack_decoder_stream), &p, frames_len) != 1 + || quic_stream_frame_to_bytes(request_stream_id, 0, 0, headers, sizeof(headers), &p, frames_len) != 1 + || quic_stream_frame_to_bytes(request_stream_id, sizeof(headers), 1, data, data_len, &p, frames_len) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int quic_application_request_stream_id(const uint8_t *frames, size_t frames_len, uint64_t *packet_number, uint64_t *stream_id) +{ + const uint8_t *p = frames; + size_t len = frames_len; + + if (!frames || !stream_id) { + error_print(); + return -1; + } + *stream_id = 0; + while (len) { + uint64_t type; + if (quic_varint_from_bytes(&type, &p, &len) != 1) return -1; + if (type == QUIC_frame_padding) { + while (len && *p == 0) { p++; len--; } + } else if (type == QUIC_frame_ack || type == QUIC_frame_ack_ecn) { + uint64_t val, range_count, i; + if (quic_varint_from_bytes(&val, &p, &len) != 1 + || quic_varint_from_bytes(&val, &p, &len) != 1 + || quic_varint_from_bytes(&range_count, &p, &len) != 1 + || quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + for (i = 0; i < range_count; i++) { + if (quic_varint_from_bytes(&val, &p, &len) != 1 || quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + } + if (type == QUIC_frame_ack_ecn) { + if (quic_varint_from_bytes(&val, &p, &len) != 1 + || quic_varint_from_bytes(&val, &p, &len) != 1 + || quic_varint_from_bytes(&val, &p, &len) != 1) return -1; + } + } else if ((type & 0xf8) == QUIC_frame_stream_base) { + uint64_t off = 0, data_len; + if (quic_varint_from_bytes(stream_id, &p, &len) != 1) return -1; + if (type & 0x04) { + if (quic_varint_from_bytes(&off, &p, &len) != 1) return -1; + } + if (type & 0x02) { + if (quic_varint_from_bytes(&data_len, &p, &len) != 1 || data_len > len) return -1; + } else { + data_len = len; + } + (void)off; + p += data_len; + len -= data_len; + if ((*stream_id & 0x03) == 0) { + if (packet_number) *packet_number = 0; + return 1; + } + } else if (type == QUIC_frame_new_connection_id) { + uint64_t seq, retire, cid_len; + if (quic_varint_from_bytes(&seq, &p, &len) != 1 + || quic_varint_from_bytes(&retire, &p, &len) != 1 + || quic_varint_from_bytes(&cid_len, &p, &len) != 1 + || cid_len > 20 || cid_len + 16 > len) return -1; + p += cid_len + 16; + len -= cid_len + 16; + } else { + return 0; + } + } + return 0; +} + +int quic_server_main(int argc, char **argv) +{ + char *prog = argv[0]; + int ret = 1; + int port = 443; + char *certfile = NULL; + char *keyfile = NULL; + char *pass = NULL; + int verbose = 0; + TLS_CTX ctx; + TLS_CONNECT conn; + int cipher_suites[TLS_MAX_CIPHER_SUITES] = { TLS_cipher_aes_128_gcm_sha256, TLS_cipher_aes_128_ccm_sha256 }; + size_t cipher_suites_cnt = 2; + int supported_group = TLS_curve_secp256r1; + int sig_alg = TLS_sig_ecdsa_secp256r1_sha256; + char *alpn = "h3"; + tls_socket_t sock = tls_socket_invalid(); + struct sockaddr_in server_addr; + struct sockaddr_in client_addr; + tls_socklen_t client_addr_len; + uint8_t buf[4096]; + uint8_t sendbuf[4096]; + uint8_t initial_crypto[TLS_MAX_RECORD_SIZE]; + size_t initial_crypto_len = 0; + uint8_t handshake_crypto[TLS_MAX_RECORD_SIZE]; + size_t handshake_crypto_len = 0; + uint8_t server_initial_crypto[TLS_MAX_RECORD_SIZE]; + uint8_t server_handshake_crypto[TLS_MAX_RECORD_SIZE]; + uint8_t frames[TLS_MAX_RECORD_SIZE]; + uint8_t packet[1500]; + uint8_t server_dcid[20]; + size_t server_dcid_len = 0; + uint8_t server_scid[20] = { + 0x51, 0x72, 0x9a, 0x41, 0x3e, 0x55, 0x12, 0x3f, + 0x7b, 0x20, 0x65, 0x94, 0xac, 0x37, 0xd1, 0xe8, + }; + size_t server_scid_len = 16; + size_t server_initial_crypto_len = 0; + size_t server_handshake_crypto_len = 0; + size_t frames_len = 0; + size_t packet_len = 0; + uint8_t *p; + QUIC_INITIAL_SECRETS initial_secrets; + QUIC_INITIAL_KEYS initial_client_keys0; + QUIC_INITIAL_KEYS initial_server_keys0; + QUIC_PACKET_KEYS initial_client_keys; + QUIC_PACKET_KEYS initial_server_keys; + QUIC_PACKET_KEYS client_handshake_keys; + QUIC_PACKET_KEYS server_handshake_keys; + QUIC_PACKET_KEYS client_application_keys; + QUIC_PACKET_KEYS server_application_keys; + QUIC_DECRYPTED_PACKET decrypted; + QUIC_TRANSPORT_PARAMS params; + uint64_t largest_ack = 0; + uint64_t request_stream_id = 0; + tls_ret_t n; + + argc--; + argv++; + if (argc < 1) { + fprintf(stderr, "usage: %s %s\n", prog, options); + return 1; + } + 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; + port = atoi(*(++argv)); + } else if (!strcmp(*argv, "-cert")) { + if (--argc < 1) goto bad; + certfile = *(++argv); + } else if (!strcmp(*argv, "-key")) { + if (--argc < 1) goto bad; + keyfile = *(++argv); + } else if (!strcmp(*argv, "-pass")) { + if (--argc < 1) goto bad; + pass = *(++argv); + } else if (!strcmp(*argv, "-cipher_suite")) { + int cipher_suite; + if (--argc < 1) goto bad; + if ((cipher_suite = tls_cipher_suite_from_name(*(++argv))) == 0) { + fprintf(stderr, "%s: invalid cipher suite '%s'\n", prog, *argv); + return 1; + } + if (cipher_suites_cnt == 2 && cipher_suites[0] == TLS_cipher_aes_128_gcm_sha256 && cipher_suites[1] == TLS_cipher_aes_128_ccm_sha256) { + cipher_suites_cnt = 0; + } + if (cipher_suites_cnt >= sizeof(cipher_suites)/sizeof(cipher_suites[0])) { + fprintf(stderr, "%s: too many -cipher_suite options\n", prog); + return 1; + } + cipher_suites[cipher_suites_cnt++] = cipher_suite; + } else if (!strcmp(*argv, "-supported_group")) { + if (--argc < 1) goto bad; + if ((supported_group = tls_named_curve_from_name(*(++argv))) == 0) { + fprintf(stderr, "%s: invalid supported group '%s'\n", prog, *argv); + return 1; + } + } else if (!strcmp(*argv, "-sig_alg")) { + if (--argc < 1) goto bad; + if ((sig_alg = tls_signature_scheme_from_name(*(++argv))) == 0) { + fprintf(stderr, "%s: invalid signature algorithm '%s'\n", prog, *argv); + return 1; + } + } else if (!strcmp(*argv, "-verbose")) { + verbose = 1; + } else { + fprintf(stderr, "%s: invalid option '%s'\n", prog, *argv); + return 1; +bad: + fprintf(stderr, "%s: option '%s' argument required\n", prog, *argv); + return 1; + } + argc--; + argv++; + } + if (!certfile || !keyfile) { + fprintf(stderr, "%s: -cert and -key required\n", prog); + return 1; + } + + memset(&ctx, 0, sizeof(ctx)); + memset(&conn, 0, sizeof(conn)); + if (tls_socket_lib_init() != 1 + || tls_ctx_init(&ctx, TLS_protocol_tls13, TLS_server_mode) != 1 + || tls_ctx_set_cipher_suites(&ctx, cipher_suites, cipher_suites_cnt) != 1 + || tls_ctx_set_supported_groups(&ctx, &supported_group, 1) != 1 + || tls_ctx_set_signature_algorithms(&ctx, &sig_alg, 1) != 1 + || tls_ctx_set_application_layer_protocol_negotiation(&ctx, &alpn, 1) != 1 + || tls_ctx_add_certificate_chain_and_key(&ctx, certfile, keyfile, pass ? pass : "") != 1 + || tls_init(&conn, &ctx) != 1) { + error_print(); + goto end; + } + conn.verbose = verbose ? TLS_verbose : 0; + + if (tls_socket_create(&sock, AF_INET, SOCK_DGRAM, 0) != 1) { + error_print(); + goto end; + } + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(port); + if (tls_socket_bind(sock, &server_addr) != 1) { + fprintf(stderr, "%s: socket bind error\n", prog); + goto end; + } + + client_addr_len = sizeof(client_addr); + n = recvfrom(sock, (char *)buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len); + if (n <= 0) { + fprintf(stderr, "%s: recvfrom error\n", prog); + goto end; + } + if (verbose) { + fprintf(stderr, "recv UDP datagram: %ld bytes\n", (long)n); + quic_packet_print(stderr, 0, 0, buf, (size_t)n); + } + + { + const uint8_t *odcid; + const uint8_t *client_scid; + size_t odcid_len; + size_t client_scid_len; + if (quic_long_packet_connection_ids_get(buf, (size_t)n, &odcid, &odcid_len, &client_scid, &client_scid_len) != 1 + || odcid_len > sizeof(server_dcid) || client_scid_len > sizeof(server_dcid)) { + error_print(); + goto end; + } + memcpy(server_dcid, client_scid, client_scid_len); + server_dcid_len = client_scid_len; + if (quic_derive_initial_secrets(odcid, odcid_len, &initial_secrets) != 1 + || quic_derive_initial_client_keys(&initial_secrets, &initial_client_keys0) != 1 + || quic_derive_initial_server_keys(&initial_secrets, &initial_server_keys0) != 1 + || quic_packet_keys_from_initial(&initial_client_keys0, &initial_client_keys) != 1 + || quic_packet_keys_from_initial(&initial_server_keys0, &initial_server_keys) != 1 + || quic_long_packet_decrypt(&initial_client_keys, buf, (size_t)n, QUIC_packet_initial, &decrypted) != 1) { + error_print(); + goto end; + } + } + if (verbose) { + format_print(stderr, 0, 0, "Decrypted Client Initial Packet\n"); + format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)decrypted.packet_number); + quic_frames_print(stderr, 0, 4, QUIC_encryption_initial, decrypted.plaintext, decrypted.plaintext_len); + } + if (quic_frames_crypto_collect(decrypted.plaintext, decrypted.plaintext_len, + initial_crypto, &initial_crypto_len, sizeof(initial_crypto), &largest_ack) != 1) { + error_print(); + goto end; + } + while (!quic_tls_handshake_is_complete(initial_crypto, initial_crypto_len)) { + n = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len); + if (n <= 0) { + error_print(); + goto end; + } + if (verbose) { + fprintf(stderr, "recv UDP datagram: %ld bytes\n", (long)n); + quic_packet_print(stderr, 0, 0, buf, (size_t)n); + } + if (quic_long_packet_decrypt(&initial_client_keys, buf, (size_t)n, QUIC_packet_initial, &decrypted) != 1) { + error_print(); + goto end; + } + if (verbose) { + format_print(stderr, 0, 0, "Decrypted Client Initial Packet\n"); + format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)decrypted.packet_number); + quic_frames_print(stderr, 0, 4, QUIC_encryption_initial, decrypted.plaintext, decrypted.plaintext_len); + } + if (quic_frames_crypto_collect(decrypted.plaintext, decrypted.plaintext_len, + initial_crypto, &initial_crypto_len, sizeof(initial_crypto), &largest_ack) != 1) { + error_print(); + goto end; + } + } + if (quic_server_client_hello_process(&conn, initial_crypto, initial_crypto_len) != 1) { + error_print(); + goto end; + } + + quic_transport_params_init(¶ms); + if (quic_transport_params_add(¶ms, QUIC_transport_param_original_destination_connection_id, decrypted.dcid, decrypted.dcid_len) != 1 + || quic_transport_params_add(¶ms, QUIC_transport_param_initial_source_connection_id, server_scid, server_scid_len) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_max_udp_payload_size, 1200) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_data, 1048576) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_stream_data_bidi_remote, 262144) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_stream_data_uni, 262144) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_streams_bidi, 100) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_initial_max_streams_uni, 3) != 1 + || quic_transport_params_add_varint(¶ms, QUIC_transport_param_active_connection_id_limit, 2) != 1) { + error_print(); + goto end; + } + + p = server_initial_crypto; + if (quic_server_hello_to_bytes(&conn, &p, &server_initial_crypto_len) != 1 + || quic_packet_keys_derive(conn.digest, conn.client_handshake_traffic_secret, conn.cipher_suite, &client_handshake_keys) != 1 + || quic_packet_keys_derive(conn.digest, conn.server_handshake_traffic_secret, conn.cipher_suite, &server_handshake_keys) != 1) { + error_print(); + goto end; + } + p = server_handshake_crypto; + if (quic_server_encrypted_extensions_to_bytes(&conn, ¶ms, &p, &server_handshake_crypto_len) != 1 + || quic_server_certificate_to_bytes(&conn, &p, &server_handshake_crypto_len) != 1 + || quic_server_certificate_verify_to_bytes(&conn, &p, &server_handshake_crypto_len) != 1 + || quic_server_finished_to_bytes(&conn, &p, &server_handshake_crypto_len) != 1 + || quic_packet_keys_derive(conn.digest, conn.server_application_traffic_secret, conn.cipher_suite, &server_application_keys) != 1) { + error_print(); + goto end; + } + + p = frames; + frames_len = 0; + if (quic_ack_frame_to_bytes(decrypted.packet_number, &p, &frames_len) != 1 + || quic_crypto_frame_to_bytes(0, server_initial_crypto, server_initial_crypto_len, &p, &frames_len) != 1 + || quic_long_packet_encrypt(&initial_server_keys, QUIC_packet_initial, server_dcid, server_dcid_len, + server_scid, server_scid_len, 0, frames, frames_len, packet, &packet_len) != 1) { + error_print(); + goto end; + } + memcpy(sendbuf, packet, packet_len); + { + size_t sendbuf_len = packet_len; + p = frames; + frames_len = 0; + if (quic_crypto_frame_to_bytes(0, server_handshake_crypto, server_handshake_crypto_len, &p, &frames_len) != 1 + || quic_long_packet_encrypt(&server_handshake_keys, QUIC_packet_handshake, server_dcid, server_dcid_len, + server_scid, server_scid_len, 0, frames, frames_len, packet, &packet_len) != 1) { + error_print(); + goto end; + } + memcpy(sendbuf + sendbuf_len, packet, packet_len); + sendbuf_len += packet_len; + if (verbose) { + fprintf(stderr, "send QUIC Initial+Handshake flight: %zu bytes\n", sendbuf_len); + } + if (sendto(sock, (const char *)sendbuf, sendbuf_len, 0, (struct sockaddr *)&client_addr, client_addr_len) != (tls_ret_t)sendbuf_len) { + fprintf(stderr, "%s: sendto handshake flight error\n", prog); + goto end; + } + } + + for (;;) { + size_t off = 0; + client_addr_len = sizeof(client_addr); + n = recvfrom(sock, (char *)buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &client_addr_len); + if (n <= 0) { + fprintf(stderr, "%s: recvfrom error\n", prog); + goto end; + } + if (verbose) { + fprintf(stderr, "recv UDP datagram: %ld bytes\n", (long)n); + quic_packet_print(stderr, 0, 0, buf, (size_t)n); + } + while (off < (size_t)n) { + size_t current_len; + if (quic_packet_total_length(buf + off, (size_t)n - off, ¤t_len) != 1 || !current_len) { + break; + } + if ((buf[off] & 0x80) && ((buf[off] >> 4) & 0x03) == QUIC_packet_handshake) { + if (quic_long_packet_decrypt(&client_handshake_keys, buf + off, current_len, QUIC_packet_handshake, &decrypted) == 1) { + handshake_crypto_len = 0; + largest_ack = 0; + if (quic_frames_crypto_collect(decrypted.plaintext, decrypted.plaintext_len, + handshake_crypto, &handshake_crypto_len, sizeof(handshake_crypto), &largest_ack) != 1 + || quic_server_client_finished_process(&conn, handshake_crypto, handshake_crypto_len) != 1 + || quic_packet_keys_derive(conn.digest, conn.client_application_traffic_secret, conn.cipher_suite, &client_application_keys) != 1) { + error_print(); + goto end; + } + if (verbose) { + fprintf(stderr, "QUIC TLS handshake completed\n"); + } + } + } else if (!(buf[off] & 0x80)) { + if (quic_short_packet_decrypt(&client_application_keys, buf + off, current_len, + server_scid, server_scid_len, &decrypted) == 1) { + if (verbose) { + format_print(stderr, 0, 0, "Decrypted 1-RTT Packet\n"); + format_print(stderr, 0, 4, "Packet Number: %llu\n", (unsigned long long)decrypted.packet_number); + quic_frames_print(stderr, 0, 4, QUIC_encryption_application, decrypted.plaintext, decrypted.plaintext_len); + } + if (quic_application_request_stream_id(decrypted.plaintext, decrypted.plaintext_len, NULL, &request_stream_id) == 1) { + const uint8_t body[] = "hello from gmssl quic server\n"; + if (quic_http3_response_to_frames(request_stream_id, body, sizeof(body) - 1, + decrypted.packet_number, frames, &frames_len) != 1 + || quic_short_packet_encrypt(&server_application_keys, server_dcid, server_dcid_len, + 0, frames, frames_len, packet, &packet_len) != 1) { + error_print(); + goto end; + } + if (verbose) { + fprintf(stderr, "send QUIC 1-RTT HTTP/3 response: %zu bytes\n", packet_len); + quic_frames_print(stderr, 0, 4, QUIC_encryption_application, frames, frames_len); + } + if (sendto(sock, (const char *)packet, packet_len, 0, (struct sockaddr *)&client_addr, client_addr_len) != (tls_ret_t)packet_len) { + fprintf(stderr, "%s: sendto response error\n", prog); + goto end; + } + ret = 0; + goto end; + } + } + } + off += current_len; + } + } + +end: + if (tls_socket_is_valid(sock)) { + tls_socket_close(sock); + } + tls_ctx_cleanup(&ctx); + tls_socket_lib_cleanup(); + return ret; +} diff --git a/tools/sm2exch.c b/tools/sm2exch.c new file mode 100644 index 00000000..a59e4a00 --- /dev/null +++ b/tools/sm2exch.c @@ -0,0 +1,929 @@ +/* + * 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 +#include +#include + + +#define SM2EXCH_RA_SIZE 65 +#define SM2EXCH_RB_SIZE 65 +#define SM2EXCH_CONFIRM_SIZE 32 +#define SM2EXCH_RB_SB_SIZE (SM2EXCH_RB_SIZE + SM2EXCH_CONFIRM_SIZE) +#define SM2EXCH_SECRET_STATE_SIZE 65 +#define SM2EXCH_MAX_SHARED_KEY_SIZE 1024 +#define PEM_SM2_EXCH_PEER_RA "SM2 EXCHANGE PEER RA" + +enum { + SM2EXCH_FMT_BIN = 0, + SM2EXCH_FMT_HEX = 1, +}; + +static const char *usage = + "-stage init|respond|confirm|finish [options]"; + +static const char *options = +"\n" +"Options\n" +"\n" +" -stage init|respond|confirm|finish\n" +" SM2 key exchange stage\n" +" -cert pem Optional local SM2 certificate for checking against -key\n" +" -pubkey pem Optional local SM2 public key for checking against -key\n" +" -key pem Local SM2 private key in PEM format\n" +" -pass str Password to open local private key\n" +" -peer_cert pem Peer SM2 key exchange/encryption certificate in PEM format\n" +" -peer_pubkey pem Peer SM2 public key in PEM format\n" +" -id str Local SM2 identity, '1234567812345678' by default\n" +" -id_hex hex Local SM2 identity in hex\n" +" -peer_id str Peer SM2 identity, '1234567812345678' by default\n" +" -peer_id_hex hex Peer SM2 identity in hex\n" +" -in file | stdin Input data for this stage\n" +" respond: RA, 65 bytes\n" +" confirm: RB || SB, 97 bytes\n" +" finish: SA, 32 bytes\n" +" -out file | stdout Output data to send to peer\n" +" init: RA, 65 bytes\n" +" respond: RB || SB, 97 bytes\n" +" confirm: SA, 32 bytes\n" +" -exch_keyout pem Output local ephemeral private key\n" +" -exch_key pem Input local ephemeral private key\n" +" -exch_pass str Password for local ephemeral private key\n" +" -secret_state_out file\n" +" Output 65-byte secret_state point\n" +" -secret_state file Input 65-byte secret_state point\n" +" -keylen num Shared key length in bytes, 32 by default\n" +" -keyout file Output shared key\n" +" -hex Encode input/output data in hex, default\n" +" -bin Encode input/output data in binary\n" +"\n" +"Examples\n" +"\n" +"Public-key file workflow:\n" +"\n" +" gmssl sm2keygen -pass P@ssw0rd -out alice.pem -pubout alicepub.pem\n" +" gmssl sm2keygen -pass P@ssw0rd -out bob.pem -pubout bobpub.pem\n" +" gmssl sm2exch -stage init \\\n" +" -exch_keyout alice_ra.pem -exch_pass P@ssw0rd -out ra.hex\n" +" gmssl sm2exch -stage respond \\\n" +" -key bob.pem -pass P@ssw0rd -id Bob \\\n" +" -peer_pubkey alicepub.pem -peer_id Alice -in ra.hex \\\n" +" -exch_keyout bob_rb.pem -exch_pass P@ssw0rd \\\n" +" -secret_state_out bob_secret_state.hex -out rb_sb.hex\n" +" gmssl sm2exch -stage confirm \\\n" +" -key alice.pem -pass P@ssw0rd -id Alice \\\n" +" -peer_pubkey bobpub.pem -peer_id Bob \\\n" +" -exch_key alice_ra.pem -exch_pass P@ssw0rd -in rb_sb.hex \\\n" +" -keylen 48 -keyout alice_shared_key.hex -out sa.hex\n" +" gmssl sm2exch -stage finish \\\n" +" -key bob.pem -pass P@ssw0rd -id Bob \\\n" +" -peer_pubkey alicepub.pem -peer_id Alice \\\n" +" -exch_key bob_rb.pem -exch_pass P@ssw0rd \\\n" +" -secret_state bob_secret_state.hex -in sa.hex \\\n" +" -keylen 48 -keyout bob_shared_key.hex\n" +"\n" +"Certificate workflow:\n" +"\n" +" gmssl sm2exch -stage respond \\\n" +" -key bob_enc_key.pem -pass P@ssw0rd -id Bob \\\n" +" -peer_cert alice_enc_cert.pem -peer_id Alice -in ra.hex \\\n" +" -exch_keyout bob_rb.pem -exch_pass P@ssw0rd \\\n" +" -secret_state_out bob_secret_state.hex -out rb_sb.hex\n" +" gmssl sm2exch -stage confirm \\\n" +" -key alice_enc_key.pem -pass P@ssw0rd -id Alice \\\n" +" -peer_cert bob_enc_cert.pem -peer_id Bob \\\n" +" -exch_key alice_ra.pem -exch_pass P@ssw0rd -in rb_sb.hex \\\n" +" -keyout alice_shared_key.hex -out sa.hex\n" +"\n" +"Notes\n" +"\n" +" -cert and -peer_cert read only the first certificate from the PEM file.\n" +" If KeyUsage is present, the certificate must allow keyEncipherment or keyAgreement.\n" +" RA, RB and secret_state are fixed 65-byte uncompressed SM2 points.\n" +" The respond output is RB || SB, fixed 97 bytes.\n" +" -hex and -bin affect RA, RB||SB, SA, secret_state and shared key files.\n" +"\n"; + +static int read_file(const char *file, uint8_t *buf, size_t *len, size_t maxlen) +{ + FILE *fp = stdin; + size_t n; + + if (!buf || !len) { + return -1; + } + if (file && !(fp = fopen(file, "rb"))) { + return -1; + } + n = fread(buf, 1, maxlen + 1, fp); + if (ferror(fp)) { + if (file) fclose(fp); + return -1; + } + if (file) fclose(fp); + if (n > maxlen) { + return -1; + } + *len = n; + return 1; +} + +static int write_file(const char *file, const uint8_t *buf, size_t len) +{ + FILE *fp = stdout; + int ret = -1; + + if (!buf || !len) { + return -1; + } + if (file && !(fp = fopen(file, "wb"))) { + return -1; + } + if (fwrite(buf, 1, len, fp) == len) { + ret = 1; + } + if (file) fclose(fp); + return ret; +} + +static int sm2exch_read_exact(const char *file, uint8_t *buf, size_t len); + +static int sm2exch_write_data(const char *file, const uint8_t *buf, size_t len, int format) +{ + uint8_t *hexbuf = NULL; + size_t i; + int ret; + + if (format == SM2EXCH_FMT_BIN) { + return write_file(file, buf, len); + } + if (!(hexbuf = malloc(len * 2))) { + return -1; + } + for (i = 0; i < len; i++) { + static const char *hex = "0123456789abcdef"; + hexbuf[i * 2] = (uint8_t)hex[buf[i] >> 4]; + hexbuf[i * 2 + 1] = (uint8_t)hex[buf[i] & 0x0f]; + } + ret = write_file(file, hexbuf, len * 2); + gmssl_secure_clear(hexbuf, len * 2); + free(hexbuf); + return ret; +} + +static int sm2exch_read_data(const char *file, uint8_t *buf, size_t len, int format) +{ + uint8_t *in = NULL; + uint8_t *hex = NULL; + size_t inlen; + size_t hexlen = 0; + size_t outlen; + size_t i; + int ret = -1; + + if (format == SM2EXCH_FMT_BIN) { + return sm2exch_read_exact(file, buf, len); + } + if (!(in = malloc(len * 2 + 64)) || !(hex = malloc(len * 2))) { + goto end; + } + if (read_file(file, in, &inlen, len * 2 + 63) != 1) { + goto end; + } + for (i = 0; i < inlen; i++) { + if (in[i] == ' ' || in[i] == '\t' || in[i] == '\r' || in[i] == '\n') { + continue; + } + if (hexlen >= len * 2) { + goto end; + } + hex[hexlen++] = in[i]; + } + if (hexlen != len * 2 + || hex_to_bytes((char *)hex, hexlen, buf, &outlen) != 1 + || outlen != len) { + goto end; + } + ret = 1; +end: + if (in) { + gmssl_secure_clear(in, len * 2 + 64); + free(in); + } + if (hex) { + gmssl_secure_clear(hex, len * 2); + free(hex); + } + return ret; +} + +static int sm2exch_read_point(const char *file, uint8_t point[65], int format) +{ + if (sm2exch_read_data(file, point, 65, format) != 1) { + return -1; + } + if (point[0] != 0x04) { + return -1; + } + return 1; +} + +static int sm2exch_read_exact(const char *file, uint8_t *buf, size_t len) +{ + size_t inlen; + + if (read_file(file, buf, &inlen, len) != 1 || inlen != len) { + return -1; + } + return 1; +} + +static int sm2exch_cert_check_key_usage(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) { + return -1; + } + if ((ret = x509_cert_get_exts(cert, certlen, &exts, &extslen)) < 0) { + return -1; + } + if (ret == 0) { + return 1; + } + if ((ret = x509_exts_get_ext_by_oid(exts, extslen, OID_ce_key_usage, + &critical, &val, &vlen)) < 0) { + return -1; + } + if (ret == 0) { + return 1; + } + if (x509_key_usage_from_der(&bits, &val, &vlen) != 1 + || asn1_length_is_zero(vlen) != 1) { + return -1; + } + if ((bits & (X509_KU_KEY_ENCIPHERMENT | X509_KU_KEY_AGREEMENT)) == 0) { + return 0; + } + return 1; +} + +static int sm2exch_load_public_key(SM2_KEY *pub_key, const char *pubkeyfile, + const char *certfile, const char *prog, const char *label) +{ + FILE *fp = NULL; + uint8_t cert[4096]; + size_t certlen; + X509_KEY x509_key; + int ret; + + if (!pub_key || !prog || !label) { + return -1; + } + if (pubkeyfile && certfile) { + if (label[0]) { + fprintf(stderr, "gmssl %s: options '-%s_pubkey' and '-%s_cert' conflict\n", + prog, label, label); + } else { + fprintf(stderr, "gmssl %s: options '-pubkey' and '-cert' conflict\n", prog); + } + return -1; + } + if (pubkeyfile) { + if (!(fp = fopen(pubkeyfile, "rb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, pubkeyfile, strerror(errno)); + return -1; + } + ret = sm2_public_key_info_from_pem(pub_key, fp); + fclose(fp); + if (ret != 1) { + fprintf(stderr, "gmssl %s: parse public key failed\n", prog); + return -1; + } + return 1; + } + if (certfile) { + if (!(fp = fopen(certfile, "rb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, certfile, strerror(errno)); + return -1; + } + ret = x509_cert_from_pem(cert, &certlen, sizeof(cert), fp); + fclose(fp); + if (ret != 1 + || x509_cert_get_subject_public_key(cert, certlen, &x509_key) != 1) { + fprintf(stderr, "gmssl %s: parse certificate failed\n", prog); + return -1; + } + if (x509_key.algor != OID_ec_public_key || x509_key.algor_param != OID_sm2) { + fprintf(stderr, "gmssl %s: certificate public key is not SM2\n", prog); + return -1; + } + ret = sm2exch_cert_check_key_usage(cert, certlen); + if (ret < 0) { + fprintf(stderr, "gmssl %s: certificate KeyUsage parse failure\n", prog); + return -1; + } + if (ret == 0) { + fprintf(stderr, "gmssl %s: certificate KeyUsage does not allow key exchange/encryption\n", prog); + return -1; + } + *pub_key = x509_key.u.sm2_key; + return 1; + } + + if (label[0]) { + fprintf(stderr, "gmssl %s: '-%s_pubkey' or '-%s_cert' option required\n", + prog, label, label); + } else { + fprintf(stderr, "gmssl %s: '-pubkey' or '-cert' option required\n", prog); + } + return -1; +} + +static int sm2exch_load_private_key(SM2_KEY *key, const char *keyfile, + const char *pass, const char *prog) +{ + FILE *fp; + int ret; + + if (!keyfile) { + fprintf(stderr, "gmssl %s: '-key' option required\n", prog); + return -1; + } + if (!pass) { + fprintf(stderr, "gmssl %s: '-pass' option required\n", prog); + return -1; + } + if (!(fp = fopen(keyfile, "rb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, keyfile, strerror(errno)); + return -1; + } + ret = sm2_private_key_info_decrypt_from_pem(key, pass, fp); + fclose(fp); + if (ret != 1) { + fprintf(stderr, "gmssl %s: private key decryption failure\n", prog); + return -1; + } + return 1; +} + +static int sm2exch_load_exch_key(SM2_KEY *key, uint8_t peer_ra[65], + const char *keyfile, const char *pass, const char *prog) +{ + FILE *fp; + int ret; + size_t len; + + if (!keyfile) { + fprintf(stderr, "gmssl %s: '-exch_key' option required\n", prog); + return -1; + } + if (!pass) { + fprintf(stderr, "gmssl %s: '-exch_pass' option required\n", prog); + return -1; + } + if (!(fp = fopen(keyfile, "rb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, keyfile, strerror(errno)); + return -1; + } + ret = sm2_private_key_info_decrypt_from_pem(key, pass, fp); + if (ret != 1) { + fprintf(stderr, "gmssl %s: ephemeral private key decryption failure\n", prog); + goto end; + } + if (peer_ra) { + if (pem_read(fp, PEM_SM2_EXCH_PEER_RA, peer_ra, &len, 65) != 1 + || len != 65 || peer_ra[0] != 0x04) { + fprintf(stderr, "gmssl %s: peer RA not found in exchange key\n", prog); + ret = -1; + goto end; + } + } + ret = 1; +end: + fclose(fp); + return ret; +} + +static int sm2exch_save_exch_key(const SM2_KEY *key, const char *keyfile, + const char *pass, const uint8_t peer_ra[65], const char *prog) +{ + FILE *fp; + int ret; + + if (!keyfile) { + fprintf(stderr, "gmssl %s: '-exch_keyout' option required\n", prog); + return -1; + } + if (!pass) { + fprintf(stderr, "gmssl %s: '-exch_pass' option required\n", prog); + return -1; + } + if (!(fp = fopen(keyfile, "wb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, keyfile, strerror(errno)); + return -1; + } + ret = sm2_private_key_info_encrypt_to_pem(key, pass, fp); + if (ret == 1 && peer_ra) { + ret = pem_write(fp, PEM_SM2_EXCH_PEER_RA, peer_ra, 65); + } + fclose(fp); + if (ret != 1) { + fprintf(stderr, "gmssl %s: output ephemeral private key failed\n", prog); + return -1; + } + return 1; +} + +static int sm2exch_load_local_keys(SM2_KEY *key, const char *keyfile, const char *pass, + const char *pubkeyfile, const char *certfile, const char *prog) +{ + SM2_KEY pub_key; + + if (sm2exch_load_private_key(key, keyfile, pass, prog) != 1) { + return -1; + } + if (pubkeyfile || certfile) { + if (sm2exch_load_public_key(&pub_key, pubkeyfile, certfile, prog, "") != 1) { + return -1; + } + if (sm2_public_key_equ(key, &pub_key) != 1) { + fprintf(stderr, "gmssl %s: private key does not match local public key/certificate\n", prog); + return -1; + } + } + return 1; +} + +static int sm2exch_derive_key_from_secret_state(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 uint8_t secret_state[65], uint8_t *shared_key, size_t shared_key_len) +{ + SM2_Z256_POINT point; + uint8_t za[32]; + uint8_t zb[32]; + uint8_t kdf_input[128]; + int ret = -1; + + if (!key || !id || !peer_public_key || !peer_id + || !secret_state || !shared_key || !shared_key_len) { + return -1; + } + if (sm2_z256_point_from_octets(&point, secret_state, 65) != 1) { + return -1; + } + 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) { + 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) { + goto end; + } + } + sm2_z256_point_to_bytes(&point, kdf_input); + memcpy(kdf_input + 64, za, sizeof(za)); + memcpy(kdf_input + 96, zb, sizeof(zb)); + if (sm2_kdf(kdf_input, sizeof(kdf_input), shared_key_len, shared_key) != 1 + || mem_is_zero(shared_key, shared_key_len)) { + goto end; + } + ret = 1; +end: + gmssl_secure_clear(&point, sizeof(point)); + gmssl_secure_clear(za, sizeof(za)); + gmssl_secure_clear(zb, sizeof(zb)); + gmssl_secure_clear(kdf_input, sizeof(kdf_input)); + return ret; +} + +static int sm2exch_stage_init(const char *exch_keyoutfile, const char *exch_pass, + const char *outfile, int format, const char *prog) +{ + SM2_KEY exch_key; + uint8_t ra[65]; + int ret = -1; + + if (sm2_key_generate(&exch_key) != 1 + || sm2_z256_point_to_uncompressed_octets(&exch_key.public_key, ra) != 1 + || sm2exch_save_exch_key(&exch_key, exch_keyoutfile, exch_pass, NULL, prog) != 1 + || sm2exch_write_data(outfile, ra, sizeof(ra), format) != 1) { + fprintf(stderr, "gmssl %s: init stage failure\n", prog); + goto end; + } + ret = 0; +end: + gmssl_secure_clear(&exch_key, sizeof(exch_key)); + return ret; +} + +static int sm2exch_stage_respond(const char *keyfile, const char *pass, + const char *pubkeyfile, const char *certfile, + const char *peer_pubkeyfile, const char *peer_certfile, + const char *id, size_t idlen, const char *peer_id, size_t peer_idlen, + const char *infile, const char *exch_keyoutfile, const char *exch_pass, + const char *secret_stateoutfile, const char *outfile, size_t keylen, + int format, const char *prog) +{ + SM2_KEY key; + SM2_KEY peer_public_key; + SM2_KEY exch_key; + uint8_t ra[65]; + uint8_t rb[65]; + uint8_t secret_state[65]; + uint8_t shared_key[SM2EXCH_MAX_SHARED_KEY_SIZE]; + uint8_t sb[32]; + uint8_t rb_sb[97]; + int ret = -1; + + if (!secret_stateoutfile) { + fprintf(stderr, "gmssl %s: '-secret_state_out' option required\n", prog); + return -1; + } + if (sm2exch_load_local_keys(&key, keyfile, pass, pubkeyfile, certfile, prog) != 1 + || sm2exch_load_public_key(&peer_public_key, peer_pubkeyfile, peer_certfile, prog, "peer") != 1 + || sm2exch_read_point(infile, ra, format) != 1 + || sm2_key_generate(&exch_key) != 1 + || sm2_z256_point_to_uncompressed_octets(&exch_key.public_key, rb) != 1) { + fprintf(stderr, "gmssl %s: respond stage input failure\n", prog); + goto end; + } + if (sm2_key_exchange(0, &key, id, idlen, &peer_public_key, peer_id, peer_idlen, + &exch_key, ra, secret_state, keylen, shared_key) != 1 + || sm2_key_exchange_compute_confirm(0, &key, id, idlen, + &peer_public_key, peer_id, peer_idlen, + &exch_key, ra, secret_state, sb) != 1 + || sm2exch_save_exch_key(&exch_key, exch_keyoutfile, exch_pass, ra, prog) != 1 + || sm2exch_write_data(secret_stateoutfile, secret_state, sizeof(secret_state), format) != 1) { + fprintf(stderr, "gmssl %s: respond stage failure\n", prog); + goto end; + } + memcpy(rb_sb, rb, sizeof(rb)); + memcpy(rb_sb + sizeof(rb), sb, sizeof(sb)); + if (sm2exch_write_data(outfile, rb_sb, sizeof(rb_sb), format) != 1) { + fprintf(stderr, "gmssl %s: output RB||SB failed : %s\n", prog, strerror(errno)); + goto end; + } + ret = 0; +end: + gmssl_secure_clear(&key, sizeof(key)); + gmssl_secure_clear(&peer_public_key, sizeof(peer_public_key)); + gmssl_secure_clear(&exch_key, sizeof(exch_key)); + gmssl_secure_clear(secret_state, sizeof(secret_state)); + gmssl_secure_clear(shared_key, sizeof(shared_key)); + gmssl_secure_clear(sb, sizeof(sb)); + return ret; +} + +static int sm2exch_stage_confirm(const char *keyfile, const char *pass, + const char *pubkeyfile, const char *certfile, + const char *peer_pubkeyfile, const char *peer_certfile, + const char *id, size_t idlen, const char *peer_id, size_t peer_idlen, + const char *exch_keyfile, const char *exch_pass, const char *infile, + const char *secret_stateoutfile, const char *keyoutfile, const char *outfile, + size_t keylen, int format, const char *prog) +{ + SM2_KEY key; + SM2_KEY peer_public_key; + SM2_KEY exch_key; + uint8_t rb_sb[97]; + uint8_t secret_state[65]; + uint8_t shared_key[SM2EXCH_MAX_SHARED_KEY_SIZE]; + uint8_t sa[32]; + int vr; + int ret = -1; + + if (!keyoutfile) { + fprintf(stderr, "gmssl %s: '-keyout' option required\n", prog); + return -1; + } + if (sm2exch_load_local_keys(&key, keyfile, pass, pubkeyfile, certfile, prog) != 1 + || sm2exch_load_public_key(&peer_public_key, peer_pubkeyfile, peer_certfile, prog, "peer") != 1 + || sm2exch_load_exch_key(&exch_key, NULL, exch_keyfile, exch_pass, prog) != 1 + || sm2exch_read_data(infile, rb_sb, sizeof(rb_sb), format) != 1) { + fprintf(stderr, "gmssl %s: confirm stage input failure\n", prog); + goto end; + } + if (sm2_key_exchange(1, &key, id, idlen, &peer_public_key, peer_id, peer_idlen, + &exch_key, rb_sb, secret_state, keylen, shared_key) != 1) { + fprintf(stderr, "gmssl %s: key exchange failure\n", prog); + goto end; + } + if ((vr = sm2_key_exchange_verify_confirm(1, &key, id, idlen, + &peer_public_key, peer_id, peer_idlen, &exch_key, + rb_sb, secret_state, rb_sb + SM2EXCH_RB_SIZE)) != 1) { + fprintf(stderr, "gmssl %s: SB verification %s\n", prog, vr < 0 ? "failure" : "failed"); + goto end; + } + if (sm2_key_exchange_compute_confirm(1, &key, id, idlen, + &peer_public_key, peer_id, peer_idlen, &exch_key, + rb_sb, secret_state, sa) != 1 + || sm2exch_write_data(keyoutfile, shared_key, keylen, format) != 1 + || sm2exch_write_data(outfile, sa, sizeof(sa), format) != 1) { + fprintf(stderr, "gmssl %s: confirm stage failure\n", prog); + goto end; + } + if (secret_stateoutfile + && sm2exch_write_data(secret_stateoutfile, secret_state, sizeof(secret_state), format) != 1) { + fprintf(stderr, "gmssl %s: output secret_state failed : %s\n", prog, strerror(errno)); + goto end; + } + ret = 0; +end: + gmssl_secure_clear(&key, sizeof(key)); + gmssl_secure_clear(&peer_public_key, sizeof(peer_public_key)); + gmssl_secure_clear(&exch_key, sizeof(exch_key)); + gmssl_secure_clear(secret_state, sizeof(secret_state)); + gmssl_secure_clear(shared_key, sizeof(shared_key)); + gmssl_secure_clear(sa, sizeof(sa)); + return ret; +} + +static int sm2exch_stage_finish(const char *keyfile, const char *pass, + const char *pubkeyfile, const char *certfile, + const char *peer_pubkeyfile, const char *peer_certfile, + const char *id, size_t idlen, const char *peer_id, size_t peer_idlen, + const char *exch_keyfile, const char *exch_pass, const char *secret_statefile, + const char *infile, const char *keyoutfile, size_t keylen, int format, + const char *prog) +{ + SM2_KEY key; + SM2_KEY peer_public_key; + SM2_KEY exch_key; + uint8_t ra[65]; + uint8_t secret_state[65]; + uint8_t sa[32]; + uint8_t shared_key[SM2EXCH_MAX_SHARED_KEY_SIZE]; + int vr; + int ret = -1; + + if (!secret_statefile) { + fprintf(stderr, "gmssl %s: '-secret_state' option required\n", prog); + return -1; + } + if (!keyoutfile) { + fprintf(stderr, "gmssl %s: '-keyout' option required\n", prog); + return -1; + } + if (sm2exch_load_local_keys(&key, keyfile, pass, pubkeyfile, certfile, prog) != 1 + || sm2exch_load_public_key(&peer_public_key, peer_pubkeyfile, peer_certfile, prog, "peer") != 1 + || sm2exch_load_exch_key(&exch_key, ra, exch_keyfile, exch_pass, prog) != 1 + || sm2exch_read_point(secret_statefile, secret_state, format) != 1 + || sm2exch_read_data(infile, sa, sizeof(sa), format) != 1) { + fprintf(stderr, "gmssl %s: finish stage input failure\n", prog); + goto end; + } + if ((vr = sm2_key_exchange_verify_confirm(0, &key, id, idlen, + &peer_public_key, peer_id, peer_idlen, &exch_key, + ra, secret_state, sa)) != 1) { + fprintf(stderr, "gmssl %s: SA verification %s\n", prog, vr < 0 ? "failure" : "failed"); + goto end; + } + if (sm2exch_derive_key_from_secret_state(0, &key, id, idlen, + &peer_public_key, peer_id, peer_idlen, + secret_state, shared_key, keylen) != 1 + || sm2exch_write_data(keyoutfile, shared_key, keylen, format) != 1) { + fprintf(stderr, "gmssl %s: finish stage failure\n", prog); + goto end; + } + ret = 0; +end: + gmssl_secure_clear(&key, sizeof(key)); + gmssl_secure_clear(&peer_public_key, sizeof(peer_public_key)); + gmssl_secure_clear(&exch_key, sizeof(exch_key)); + gmssl_secure_clear(secret_state, sizeof(secret_state)); + gmssl_secure_clear(shared_key, sizeof(shared_key)); + return ret; +} + +int sm2exch_main(int argc, char **argv) +{ + int ret = 1; + char *prog = argv[0]; + char *stage = NULL; + char *certfile = NULL; + char *pubkeyfile = NULL; + char *keyfile = NULL; + char *pass = NULL; + char *peer_certfile = NULL; + char *peer_pubkeyfile = NULL; + char *id = NULL; + char *peer_id = NULL; + char *id_hex = NULL; + char *peer_id_hex = NULL; + char id_buf[SM2_MAX_ID_LENGTH]; + size_t id_len = 0; + char peer_id_buf[SM2_MAX_ID_LENGTH]; + size_t peer_id_len = 0; + char *infile = NULL; + char *outfile = NULL; + char *exch_keyfile = NULL; + char *exch_keyoutfile = NULL; + char *exch_pass = NULL; + char *secret_statefile = NULL; + char *secret_stateoutfile = NULL; + char *keyoutfile = NULL; + size_t keylen = 32; + int format = SM2EXCH_FMT_HEX; + + argc--; + argv++; + + if (argc < 1) { + fprintf(stderr, "usage: gmssl %s %s\n", prog, usage); + return 1; + } + + while (argc > 0) { + if (!strcmp(*argv, "-help")) { + printf("usage: gmssl %s %s\n", prog, usage); + printf("%s\n", options); + return 0; + } else if (!strcmp(*argv, "-stage")) { + if (--argc < 1) goto bad; + stage = *(++argv); + } else if (!strcmp(*argv, "-cert")) { + if (pubkeyfile) { + fprintf(stderr, "gmssl %s: options '-cert' and '-pubkey' conflict\n", prog); + goto end; + } + if (--argc < 1) goto bad; + certfile = *(++argv); + } else if (!strcmp(*argv, "-pubkey")) { + if (certfile) { + fprintf(stderr, "gmssl %s: options '-cert' and '-pubkey' conflict\n", prog); + goto end; + } + if (--argc < 1) goto bad; + pubkeyfile = *(++argv); + } else if (!strcmp(*argv, "-key")) { + if (--argc < 1) goto bad; + keyfile = *(++argv); + } else if (!strcmp(*argv, "-pass")) { + if (--argc < 1) goto bad; + pass = *(++argv); + } else if (!strcmp(*argv, "-peer_cert")) { + if (peer_pubkeyfile) { + fprintf(stderr, "gmssl %s: options '-peer_cert' and '-peer_pubkey' conflict\n", prog); + goto end; + } + if (--argc < 1) goto bad; + peer_certfile = *(++argv); + } else if (!strcmp(*argv, "-peer_pubkey")) { + if (peer_certfile) { + fprintf(stderr, "gmssl %s: options '-peer_cert' and '-peer_pubkey' conflict\n", prog); + goto end; + } + if (--argc < 1) goto bad; + peer_pubkeyfile = *(++argv); + } else if (!strcmp(*argv, "-id")) { + if (id_hex) { + fprintf(stderr, "gmssl %s: '-id' and '-id_hex' should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + id = *(++argv); + id_len = strlen(id); + } else if (!strcmp(*argv, "-id_hex")) { + if (id) { + fprintf(stderr, "gmssl %s: '-id' and '-id_hex' should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + id_hex = *(++argv); + if (strlen(id_hex) > sizeof(id_buf) * 2 + || hex_to_bytes(id_hex, strlen(id_hex), (uint8_t *)id_buf, &id_len) != 1) { + fprintf(stderr, "gmssl %s: invalid '-id_hex' value\n", prog); + goto end; + } + id = id_buf; + } else if (!strcmp(*argv, "-peer_id")) { + if (peer_id_hex) { + fprintf(stderr, "gmssl %s: '-peer_id' and '-peer_id_hex' should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + peer_id = *(++argv); + peer_id_len = strlen(peer_id); + } else if (!strcmp(*argv, "-peer_id_hex")) { + if (peer_id) { + fprintf(stderr, "gmssl %s: '-peer_id' and '-peer_id_hex' should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + peer_id_hex = *(++argv); + if (strlen(peer_id_hex) > sizeof(peer_id_buf) * 2 + || hex_to_bytes(peer_id_hex, strlen(peer_id_hex), (uint8_t *)peer_id_buf, &peer_id_len) != 1) { + fprintf(stderr, "gmssl %s: invalid '-peer_id_hex' value\n", prog); + goto end; + } + peer_id = peer_id_buf; + } else if (!strcmp(*argv, "-in")) { + if (--argc < 1) goto bad; + infile = *(++argv); + } else if (!strcmp(*argv, "-out")) { + if (--argc < 1) goto bad; + outfile = *(++argv); + } else if (!strcmp(*argv, "-exch_keyout")) { + if (--argc < 1) goto bad; + exch_keyoutfile = *(++argv); + } else if (!strcmp(*argv, "-exch_key")) { + if (--argc < 1) goto bad; + exch_keyfile = *(++argv); + } else if (!strcmp(*argv, "-exch_pass")) { + if (--argc < 1) goto bad; + exch_pass = *(++argv); + } else if (!strcmp(*argv, "-secret_state_out")) { + if (--argc < 1) goto bad; + secret_stateoutfile = *(++argv); + } else if (!strcmp(*argv, "-secret_state")) { + if (--argc < 1) goto bad; + secret_statefile = *(++argv); + } else if (!strcmp(*argv, "-keylen")) { + if (--argc < 1) goto bad; + keylen = (size_t)atoi(*(++argv)); + if (keylen < 1 || keylen > SM2EXCH_MAX_SHARED_KEY_SIZE) { + fprintf(stderr, "gmssl %s: invalid '-keylen' value\n", prog); + goto end; + } + } else if (!strcmp(*argv, "-keyout")) { + if (--argc < 1) goto bad; + keyoutfile = *(++argv); + } else if (!strcmp(*argv, "-hex")) { + format = SM2EXCH_FMT_HEX; + } else if (!strcmp(*argv, "-bin")) { + format = SM2EXCH_FMT_BIN; + } else { + fprintf(stderr, "gmssl %s: illegal option '%s'\n", prog, *argv); + goto end; +bad: + fprintf(stderr, "gmssl %s: '%s' option value missing\n", prog, *argv); + goto end; + } + + argc--; + argv++; + } + + if (!stage) { + fprintf(stderr, "gmssl %s: '-stage' option required\n", prog); + goto end; + } + if (!id) { + id = SM2_DEFAULT_ID; + id_len = SM2_DEFAULT_ID_LENGTH; + } + if (!peer_id) { + peer_id = SM2_DEFAULT_ID; + peer_id_len = SM2_DEFAULT_ID_LENGTH; + } + + if (!strcmp(stage, "init")) { + ret = sm2exch_stage_init(exch_keyoutfile, exch_pass, outfile, format, prog); + } else if (!strcmp(stage, "respond")) { + ret = sm2exch_stage_respond(keyfile, pass, pubkeyfile, certfile, + peer_pubkeyfile, peer_certfile, id, id_len, peer_id, peer_id_len, + infile, exch_keyoutfile, exch_pass, secret_stateoutfile, + outfile, keylen, format, prog); + } else if (!strcmp(stage, "confirm")) { + ret = sm2exch_stage_confirm(keyfile, pass, pubkeyfile, certfile, + peer_pubkeyfile, peer_certfile, id, id_len, peer_id, peer_id_len, + exch_keyfile, exch_pass, infile, secret_stateoutfile, + keyoutfile, outfile, keylen, format, prog); + } else if (!strcmp(stage, "finish")) { + ret = sm2exch_stage_finish(keyfile, pass, pubkeyfile, certfile, + peer_pubkeyfile, peer_certfile, id, id_len, peer_id, peer_id_len, + exch_keyfile, exch_pass, secret_statefile, infile, + keyoutfile, keylen, format, prog); + } else { + fprintf(stderr, "gmssl %s: invalid '-stage' value\n", prog); + goto end; + } + +end: + return ret == 0 ? 0 : 1; +} diff --git a/tools/sm4.c b/tools/sm4.c index e0f19042..7f41743f 100755 --- a/tools/sm4.c +++ b/tools/sm4.c @@ -27,8 +27,6 @@ enum { SM4_MODE_XTS, SM4_MODE_CCM, SM4_MODE_GCM, - SM4_MODE_CBC_SM3_HMAC, - SM4_MODE_CTR_SM3_HMAC, }; static uint8_t *read_content(FILE *infp, size_t *outlen, const char *prog) @@ -188,8 +186,6 @@ static const char *options = " -ctr CTR mode, need 16-byte key and 16-byte iv\n" " -ccm CCM mode, need 16-byte key and any iv length\n" " -gcm GCM mode, need 16-byte key and any iv length\n" -" -cbc_sm3_hmac CBC mode with padding and HMAC-SM3 (encrypt-then-mac), need 48-byte key and 16-byte iv\n" -" -ctr_sm3_hmac CTR mode with HMAC-SM3 (entrypt-then-mac), need 48-byte key and 16-byte iv\n" " -xts XTS mode\n" "\n" " -encrypt Encrypt\n" @@ -205,13 +201,6 @@ static const char *options = "\n" " echo \"hello\" | gmssl sm4 -gcm -encrypt -key 11223344556677881122334455667788 -iv 112233445566778811223344 -out ciphertext.bin\n" " gmssl sm4 -gcm -decrypt -key 11223344556677881122334455667788 -iv 112233445566778811223344 -in ciphertext.bin\n" -"\n" -" echo \"hello\" | gmssl sm4 -cbc_sm3_hmac -encrypt \\\n" -" -key 112233445566778811223344556677881122334455667788112233445566778811223344556677881122334455667788 \\\n" -" -iv 11223344556677881122334455667788 -out ciphertext.bin\n" -" gmssl sm4 -cbc_sm3_hmac -decrypt \\\n" -" -key 112233445566778811223344556677881122334455667788112233445566778811223344556677881122334455667788 \\\n" -" -iv 11223344556677881122334455667788 -in ciphertext.bin\n" "\n"; int sm4_main(int argc, char **argv) @@ -322,12 +311,6 @@ int sm4_main(int argc, char **argv) } else if (!strcmp(*argv, "-ctr")) { if (mode) goto bad; mode = SM4_MODE_CTR; - } else if (!strcmp(*argv, "-cbc_sm3_hmac")) { - if (mode) goto bad; - mode = SM4_MODE_CBC_SM3_HMAC; - } else if (!strcmp(*argv, "-ctr_sm3_hmac")) { - if (mode) goto bad; - mode = SM4_MODE_CTR_SM3_HMAC; } else if (!strcmp(*argv, "-gcm")) { if (mode) goto bad; mode = SM4_MODE_GCM; @@ -449,9 +432,6 @@ bad: #ifdef ENABLE_SM4_XTS case SM4_MODE_XTS: #endif - case SM4_MODE_CBC_SM3_HMAC: - case SM4_MODE_CTR_SM3_HMAC: - break; default: fprintf(stderr, "%s: mode is not supported\n", prog); goto end; @@ -477,13 +457,6 @@ bad: goto end; } break; - case SM4_MODE_CBC_SM3_HMAC: - case SM4_MODE_CTR_SM3_HMAC: - if (keylen != 48) { - fprintf(stderr, "%s: invalid key length, should be 96 hex digits\n", prog); - goto end; - } - break; } // check iv length @@ -498,8 +471,6 @@ bad: case SM4_MODE_CFB: case SM4_MODE_OFB: case SM4_MODE_CTR: - case SM4_MODE_CBC_SM3_HMAC: - case SM4_MODE_CTR_SM3_HMAC: if (ivlen != 16) { fprintf(stderr, "%s: invalid IV length, should be 32 hex digits\n", prog); goto end; @@ -613,7 +584,6 @@ bad: goto end; } } - switch (mode) { #ifdef ENABLE_SM4_ECB case SM4_MODE_ECB: rv = sm4_ecb_encrypt_finish(&sm4_ctx.ecb, outbuf, &outlen); break; @@ -688,7 +658,6 @@ bad: goto end; } } - switch (mode) { #ifdef ENABLE_SM4_ECB case SM4_MODE_ECB: rv = sm4_ecb_decrypt_finish(&sm4_ctx.ecb, outbuf, &outlen); break; diff --git a/tools/sm9exch.c b/tools/sm9exch.c new file mode 100644 index 00000000..a4d95826 --- /dev/null +++ b/tools/sm9exch.c @@ -0,0 +1,771 @@ +/* + * 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 + + +#define SM9EXCH_R_SIZE 32 +#define SM9EXCH_RA_SIZE 65 +#define SM9EXCH_RB_SIZE 65 +#define SM9EXCH_CONFIRM_SIZE 32 +#define SM9EXCH_RB_SB_SIZE (SM9EXCH_RB_SIZE + SM9EXCH_CONFIRM_SIZE) +#define SM9EXCH_EXCH_KEY_SIZE (SM9EXCH_R_SIZE + SM9EXCH_RB_SIZE) +#define SM9EXCH_EXCH_KEY_WITH_PEER_R_SIZE (SM9EXCH_EXCH_KEY_SIZE + SM9EXCH_RA_SIZE) +#define SM9EXCH_MAX_SHARED_KEY_SIZE 1024 +#define PEM_SM9_EXCH_KEY "SM9 EXCHANGE PRIVATE KEY" + +enum { + SM9EXCH_FMT_BIN = 0, + SM9EXCH_FMT_HEX = 1, +}; + +static const char *usage = + "-stage init|respond|confirm|finish [options]"; + +static const char *options = +"\n" +"Options\n" +"\n" +" -stage init|respond|confirm|finish\n" +" SM9 key exchange stage\n" +" -pubmaster pem SM9 exchange master public key in PEM format\n" +" -key pem Local SM9 exchange private key in PEM format\n" +" -pass str Password to open local private key\n" +" -id str Local SM9 identity\n" +" -id_hex hex Local SM9 identity in hex\n" +" -peer_id str Peer SM9 identity\n" +" -peer_id_hex hex Peer SM9 identity in hex\n" +" -in file | stdin Input data for this stage\n" +" respond: RA, 65 bytes\n" +" confirm: RB || SB, 97 bytes\n" +" finish: SA, 32 bytes\n" +" -out file | stdout Output data to send to peer\n" +" init: RA, 65 bytes\n" +" respond: RB || SB, 97 bytes\n" +" confirm: SA, 32 bytes\n" +" -exch_keyout pem Output local ephemeral exchange state r||R in PEM format\n" +" -exch_key pem Input local ephemeral exchange state r||R in PEM format\n" +" -keylen num Shared key length in bytes, 32 by default\n" +" -keyout file Output shared key\n" +" -hex Encode input/output data in hex, default\n" +" -bin Encode input/output data in binary\n" +"\n" +"Examples\n" +"\n" +" gmssl sm9setup -alg sm9encrypt -pass P@ssw0rd \\\n" +" -out sm9_msk.pem -pubout sm9_mpk.pem\n" +" gmssl sm9keygen -alg sm9keyagreement -in sm9_msk.pem -inpass P@ssw0rd \\\n" +" -id Alice -out alice.pem -outpass 123456\n" +" gmssl sm9keygen -alg sm9keyagreement -in sm9_msk.pem -inpass P@ssw0rd \\\n" +" -id Bob -out bob.pem -outpass 123456\n" +" gmssl sm9exch -stage init \\\n" +" -pubmaster sm9_mpk.pem -peer_id Bob \\\n" +" -exch_keyout alice_ra.pem -out ra.hex\n" +" gmssl sm9exch -stage respond \\\n" +" -pubmaster sm9_mpk.pem -key bob.pem -pass 123456 -id Bob \\\n" +" -peer_id Alice -in ra.hex \\\n" +" -exch_keyout bob_rb.pem -out rb_sb.hex\n" +" gmssl sm9exch -stage confirm \\\n" +" -pubmaster sm9_mpk.pem -key alice.pem -pass 123456 -id Alice \\\n" +" -peer_id Bob -exch_key alice_ra.pem -in rb_sb.hex \\\n" +" -keylen 48 -keyout alice_shared_key.hex -out sa.hex\n" +" gmssl sm9exch -stage finish \\\n" +" -pubmaster sm9_mpk.pem -key bob.pem -pass 123456 -id Bob \\\n" +" -peer_id Alice -exch_key bob_rb.pem \\\n" +" -in sa.hex -keylen 48 -keyout bob_shared_key.hex\n" +"\n" +"Notes\n" +"\n" +" RA and RB are fixed 65-byte uncompressed SM9 curve points.\n" +" The respond output is RB || SB, fixed 97 bytes.\n" +" The ephemeral exchange state contains secret scalar r, public point R and optional peer point.\n" +" -hex and -bin affect RA, RB||SB, SA and shared key files.\n" +"\n"; + +static int read_file(const char *file, uint8_t *buf, size_t *len, size_t maxlen) +{ + FILE *fp = stdin; + size_t n; + + if (!buf || !len) { + return -1; + } + if (file && !(fp = fopen(file, "rb"))) { + return -1; + } + n = fread(buf, 1, maxlen + 1, fp); + if (ferror(fp)) { + if (file) fclose(fp); + return -1; + } + if (file) fclose(fp); + if (n > maxlen) { + return -1; + } + *len = n; + return 1; +} + +static int write_file(const char *file, const uint8_t *buf, size_t len) +{ + FILE *fp = stdout; + int ret = -1; + + if (!buf || !len) { + return -1; + } + if (file && !(fp = fopen(file, "wb"))) { + return -1; + } + if (fwrite(buf, 1, len, fp) == len) { + ret = 1; + } + if (file) fclose(fp); + return ret; +} + +static int sm9exch_read_exact(const char *file, uint8_t *buf, size_t len); + +static int sm9exch_write_data(const char *file, const uint8_t *buf, size_t len, int format) +{ + uint8_t *hexbuf = NULL; + size_t i; + int ret; + + if (format == SM9EXCH_FMT_BIN) { + return write_file(file, buf, len); + } + if (!(hexbuf = malloc(len * 2))) { + return -1; + } + for (i = 0; i < len; i++) { + static const char *hex = "0123456789abcdef"; + hexbuf[i * 2] = (uint8_t)hex[buf[i] >> 4]; + hexbuf[i * 2 + 1] = (uint8_t)hex[buf[i] & 0x0f]; + } + ret = write_file(file, hexbuf, len * 2); + gmssl_secure_clear(hexbuf, len * 2); + free(hexbuf); + return ret; +} + +static int sm9exch_read_data(const char *file, uint8_t *buf, size_t len, int format) +{ + uint8_t *in = NULL; + uint8_t *hex = NULL; + size_t inlen; + size_t hexlen = 0; + size_t outlen; + size_t i; + int ret = -1; + + if (format == SM9EXCH_FMT_BIN) { + return sm9exch_read_exact(file, buf, len); + } + if (!(in = malloc(len * 2 + 64)) || !(hex = malloc(len * 2))) { + goto end; + } + if (read_file(file, in, &inlen, len * 2 + 63) != 1) { + goto end; + } + for (i = 0; i < inlen; i++) { + if (in[i] == ' ' || in[i] == '\t' || in[i] == '\r' || in[i] == '\n') { + continue; + } + if (hexlen >= len * 2) { + goto end; + } + hex[hexlen++] = in[i]; + } + if (hexlen != len * 2 + || hex_to_bytes((char *)hex, hexlen, buf, &outlen) != 1 + || outlen != len) { + goto end; + } + ret = 1; +end: + if (in) { + gmssl_secure_clear(in, len * 2 + 64); + free(in); + } + if (hex) { + gmssl_secure_clear(hex, len * 2); + free(hex); + } + return ret; +} + +static int sm9exch_read_exact(const char *file, uint8_t *buf, size_t len) +{ + size_t inlen; + + if (read_file(file, buf, &inlen, len) != 1 || inlen != len) { + return -1; + } + return 1; +} + +static int sm9exch_read_point_file(const char *file, SM9_Z256_POINT *point, int format) +{ + uint8_t buf[65]; + + if (sm9exch_read_data(file, buf, sizeof(buf), format) != 1 + || sm9_z256_point_from_uncompressed_octets(point, buf) != 1 + || !sm9_z256_point_is_on_curve(point)) { + return -1; + } + return 1; +} + +static int sm9exch_load_master_public_key(SM9_EXCH_MASTER_KEY *mpk, + const char *mpkfile, const char *prog) +{ + FILE *fp = NULL; + int ret = -1; + + if (!mpkfile) { + fprintf(stderr, "gmssl %s: '-pubmaster' option required\n", prog); + return -1; + } + if (!(fp = fopen(mpkfile, "rb"))) { + fprintf(stderr, "gmssl %s: open master public key failed : %s\n", prog, strerror(errno)); + goto end; + } + if (sm9_enc_master_public_key_from_pem(mpk, fp) != 1) { + fprintf(stderr, "gmssl %s: parse master public key failed\n", prog); + goto end; + } + ret = 1; +end: + if (fp) fclose(fp); + return ret; +} + +static int sm9exch_load_private_key(SM9_EXCH_KEY *key, const char *keyfile, + const char *pass, const SM9_EXCH_MASTER_KEY *mpk, const char *prog) +{ + FILE *fp = NULL; + int ret = -1; + + if (!keyfile) { + fprintf(stderr, "gmssl %s: '-key' option required\n", prog); + return -1; + } + if (!pass) { + fprintf(stderr, "gmssl %s: '-pass' option required\n", prog); + return -1; + } + if (!(fp = fopen(keyfile, "rb"))) { + fprintf(stderr, "gmssl %s: open private key failed : %s\n", prog, strerror(errno)); + goto end; + } + if (sm9_enc_key_info_decrypt_from_pem(key, pass, fp) != 1) { + fprintf(stderr, "gmssl %s: parse private key failed\n", prog); + goto end; + } + if (mpk && sm9_z256_point_equ(&key->Ppube, &mpk->Ppube) != 1) { + fprintf(stderr, "gmssl %s: private key does not match master public key\n", prog); + goto end; + } + ret = 1; +end: + if (fp) fclose(fp); + return ret; +} + +static int sm9exch_generate_R(const SM9_EXCH_MASTER_KEY *mpk, + const char *peer_id, size_t peer_idlen, sm9_z256_t r, SM9_Z256_POINT *R) +{ + if (!mpk || !peer_id || !peer_idlen || !r || !R) { + return -1; + } + if (peer_idlen > SM9_MAX_ID_SIZE) { + return -1; + } + sm9_z256_hash1(r, peer_id, peer_idlen, SM9_HID_EXCH); + sm9_z256_point_mul(R, r, sm9_z256_generator()); + sm9_z256_point_add(R, R, &mpk->Ppube); + + do { + if (sm9_z256_rand_range(r, sm9_z256_order()) != 1) { + return -1; + } + } while (sm9_z256_is_zero(r)); + + sm9_z256_point_mul(R, r, R); + return 1; +} + +static int sm9exch_save_exch_key(const sm9_z256_t r, const SM9_Z256_POINT *R, + const SM9_Z256_POINT *peer_R, const char *keyfile, const char *prog) +{ + FILE *fp = NULL; + uint8_t buf[SM9EXCH_EXCH_KEY_WITH_PEER_R_SIZE]; + size_t len = SM9EXCH_EXCH_KEY_SIZE; + int ret = -1; + + if (!keyfile) { + fprintf(stderr, "gmssl %s: '-exch_keyout' option required\n", prog); + return -1; + } + sm9_z256_to_bytes(r, buf); + if (sm9_z256_point_to_uncompressed_octets(R, buf + SM9EXCH_R_SIZE) != 1) { + goto end; + } + if (peer_R) { + if (sm9_z256_point_to_uncompressed_octets(peer_R, buf + SM9EXCH_EXCH_KEY_SIZE) != 1) { + goto end; + } + len = SM9EXCH_EXCH_KEY_WITH_PEER_R_SIZE; + } + if (!(fp = fopen(keyfile, "wb"))) { + fprintf(stderr, "gmssl %s: open output exchange key failed : %s\n", prog, strerror(errno)); + goto end; + } + if (pem_write(fp, PEM_SM9_EXCH_KEY, buf, len) != 1) { + fprintf(stderr, "gmssl %s: output exchange key failed\n", prog); + goto end; + } + ret = 1; +end: + gmssl_secure_clear(buf, sizeof(buf)); + if (fp) fclose(fp); + return ret; +} + +static int sm9exch_load_exch_key(sm9_z256_t r, SM9_Z256_POINT *R, + SM9_Z256_POINT *peer_R, const char *keyfile, const char *prog) +{ + FILE *fp = NULL; + uint8_t buf[SM9EXCH_EXCH_KEY_WITH_PEER_R_SIZE]; + size_t len; + int ret = -1; + + if (!keyfile) { + fprintf(stderr, "gmssl %s: '-exch_key' option required\n", prog); + return -1; + } + if (!(fp = fopen(keyfile, "rb"))) { + fprintf(stderr, "gmssl %s: open exchange key failed : %s\n", prog, strerror(errno)); + goto end; + } + if (pem_read(fp, PEM_SM9_EXCH_KEY, buf, &len, sizeof(buf)) != 1 + || (len != SM9EXCH_EXCH_KEY_SIZE && len != SM9EXCH_EXCH_KEY_WITH_PEER_R_SIZE)) { + fprintf(stderr, "gmssl %s: parse exchange key failed\n", prog); + goto end; + } + sm9_z256_from_bytes(r, buf); + if (sm9_z256_is_zero(r) + || sm9_z256_point_from_uncompressed_octets(R, buf + SM9EXCH_R_SIZE) != 1 + || !sm9_z256_point_is_on_curve(R)) { + fprintf(stderr, "gmssl %s: invalid exchange key\n", prog); + goto end; + } + if (peer_R) { + if (len != SM9EXCH_EXCH_KEY_WITH_PEER_R_SIZE + || sm9_z256_point_from_uncompressed_octets(peer_R, buf + SM9EXCH_EXCH_KEY_SIZE) != 1 + || !sm9_z256_point_is_on_curve(peer_R)) { + fprintf(stderr, "gmssl %s: peer exchange point not found in exchange key\n", prog); + goto end; + } + } + ret = 1; +end: + gmssl_secure_clear(buf, sizeof(buf)); + if (fp) fclose(fp); + return ret; +} + +static int sm9exch_stage_init(const char *mpkfile, const char *peer_id, + size_t peer_idlen, const char *exch_keyoutfile, const char *outfile, + int format, const char *prog) +{ + SM9_EXCH_MASTER_KEY mpk; + SM9_Z256_POINT R; + sm9_z256_t r; + uint8_t ra[65]; + int ret = -1; + + if (!peer_id) { + fprintf(stderr, "gmssl %s: '-peer_id' option required\n", prog); + return -1; + } + if (sm9exch_load_master_public_key(&mpk, mpkfile, prog) != 1 + || sm9exch_generate_R(&mpk, peer_id, peer_idlen, r, &R) != 1 + || sm9_z256_point_to_uncompressed_octets(&R, ra) != 1 + || sm9exch_save_exch_key(r, &R, NULL, exch_keyoutfile, prog) != 1 + || sm9exch_write_data(outfile, ra, sizeof(ra), format) != 1) { + fprintf(stderr, "gmssl %s: init stage failure\n", prog); + goto end; + } + ret = 0; +end: + gmssl_secure_clear(&mpk, sizeof(mpk)); + gmssl_secure_clear(&R, sizeof(R)); + gmssl_secure_clear(r, sizeof(r)); + return ret; +} + +static int sm9exch_stage_respond(const char *mpkfile, const char *keyfile, + const char *pass, const char *id, size_t idlen, + const char *peer_id, size_t peer_idlen, const char *infile, + const char *exch_keyoutfile, const char *outfile, size_t keylen, + int format, const char *prog) +{ + SM9_EXCH_MASTER_KEY mpk; + SM9_EXCH_KEY key; + SM9_Z256_POINT ra; + SM9_Z256_POINT rb; + sm9_z256_t r; + uint8_t rb_octets[65]; + uint8_t shared_key[SM9EXCH_MAX_SHARED_KEY_SIZE]; + uint8_t sb[32]; + uint8_t rb_sb[97]; + int ret = -1; + + if (!id) { + fprintf(stderr, "gmssl %s: '-id' option required\n", prog); + return -1; + } + if (!peer_id) { + fprintf(stderr, "gmssl %s: '-peer_id' option required\n", prog); + return -1; + } + if (sm9exch_load_master_public_key(&mpk, mpkfile, prog) != 1 + || sm9exch_load_private_key(&key, keyfile, pass, &mpk, prog) != 1 + || sm9exch_read_point_file(infile, &ra, format) != 1 + || sm9exch_generate_R(&mpk, peer_id, peer_idlen, r, &rb) != 1 + || sm9_z256_point_to_uncompressed_octets(&rb, rb_octets) != 1) { + fprintf(stderr, "gmssl %s: respond stage input failure\n", prog); + goto end; + } + if (sm9_key_exchange(0, &mpk, &key, id, idlen, peer_id, peer_idlen, + r, &rb, &ra, keylen, shared_key) != 1 + || sm9_key_exchange_compute_confirm(0, &mpk, &key, id, idlen, + peer_id, peer_idlen, r, &rb, &ra, sb) != 1 + || sm9exch_save_exch_key(r, &rb, &ra, exch_keyoutfile, prog) != 1) { + fprintf(stderr, "gmssl %s: respond stage failure\n", prog); + goto end; + } + memcpy(rb_sb, rb_octets, sizeof(rb_octets)); + memcpy(rb_sb + sizeof(rb_octets), sb, sizeof(sb)); + if (sm9exch_write_data(outfile, rb_sb, sizeof(rb_sb), format) != 1) { + fprintf(stderr, "gmssl %s: output RB||SB failed : %s\n", prog, strerror(errno)); + goto end; + } + ret = 0; +end: + gmssl_secure_clear(&mpk, sizeof(mpk)); + gmssl_secure_clear(&key, sizeof(key)); + gmssl_secure_clear(&ra, sizeof(ra)); + gmssl_secure_clear(&rb, sizeof(rb)); + gmssl_secure_clear(r, sizeof(r)); + gmssl_secure_clear(shared_key, sizeof(shared_key)); + gmssl_secure_clear(sb, sizeof(sb)); + return ret; +} + +static int sm9exch_stage_confirm(const char *mpkfile, const char *keyfile, + const char *pass, const char *id, size_t idlen, + const char *peer_id, size_t peer_idlen, const char *exch_keyfile, + const char *infile, const char *keyoutfile, const char *outfile, + size_t keylen, int format, const char *prog) +{ + SM9_EXCH_MASTER_KEY mpk; + SM9_EXCH_KEY key; + SM9_Z256_POINT ra; + SM9_Z256_POINT rb; + sm9_z256_t r; + uint8_t rb_sb[97]; + uint8_t shared_key[SM9EXCH_MAX_SHARED_KEY_SIZE]; + uint8_t sa[32]; + int vr; + int ret = -1; + + if (!id) { + fprintf(stderr, "gmssl %s: '-id' option required\n", prog); + return -1; + } + if (!peer_id) { + fprintf(stderr, "gmssl %s: '-peer_id' option required\n", prog); + return -1; + } + if (!keyoutfile) { + fprintf(stderr, "gmssl %s: '-keyout' option required\n", prog); + return -1; + } + if (sm9exch_load_master_public_key(&mpk, mpkfile, prog) != 1 + || sm9exch_load_private_key(&key, keyfile, pass, &mpk, prog) != 1 + || sm9exch_load_exch_key(r, &ra, NULL, exch_keyfile, prog) != 1 + || sm9exch_read_data(infile, rb_sb, sizeof(rb_sb), format) != 1 + || sm9_z256_point_from_uncompressed_octets(&rb, rb_sb) != 1 + || !sm9_z256_point_is_on_curve(&rb)) { + fprintf(stderr, "gmssl %s: confirm stage input failure\n", prog); + goto end; + } + if (sm9_key_exchange(1, &mpk, &key, id, idlen, peer_id, peer_idlen, + r, &ra, &rb, keylen, shared_key) != 1) { + fprintf(stderr, "gmssl %s: key exchange failure\n", prog); + goto end; + } + if ((vr = sm9_key_exchange_verify_confirm(1, &mpk, &key, id, idlen, + peer_id, peer_idlen, r, &ra, &rb, rb_sb + SM9EXCH_RB_SIZE)) != 1) { + fprintf(stderr, "gmssl %s: SB verification %s\n", prog, vr < 0 ? "failure" : "failed"); + goto end; + } + if (sm9_key_exchange_compute_confirm(1, &mpk, &key, id, idlen, + peer_id, peer_idlen, r, &ra, &rb, sa) != 1 + || sm9exch_write_data(keyoutfile, shared_key, keylen, format) != 1 + || sm9exch_write_data(outfile, sa, sizeof(sa), format) != 1) { + fprintf(stderr, "gmssl %s: confirm stage failure\n", prog); + goto end; + } + ret = 0; +end: + gmssl_secure_clear(&mpk, sizeof(mpk)); + gmssl_secure_clear(&key, sizeof(key)); + gmssl_secure_clear(&ra, sizeof(ra)); + gmssl_secure_clear(&rb, sizeof(rb)); + gmssl_secure_clear(r, sizeof(r)); + gmssl_secure_clear(shared_key, sizeof(shared_key)); + gmssl_secure_clear(sa, sizeof(sa)); + return ret; +} + +static int sm9exch_stage_finish(const char *mpkfile, const char *keyfile, + const char *pass, const char *id, size_t idlen, + const char *peer_id, size_t peer_idlen, const char *exch_keyfile, + const char *infile, const char *keyoutfile, size_t keylen, int format, + const char *prog) +{ + SM9_EXCH_MASTER_KEY mpk; + SM9_EXCH_KEY key; + SM9_Z256_POINT ra; + SM9_Z256_POINT rb; + sm9_z256_t r; + uint8_t sa[32]; + uint8_t shared_key[SM9EXCH_MAX_SHARED_KEY_SIZE]; + int vr; + int ret = -1; + + if (!id) { + fprintf(stderr, "gmssl %s: '-id' option required\n", prog); + return -1; + } + if (!peer_id) { + fprintf(stderr, "gmssl %s: '-peer_id' option required\n", prog); + return -1; + } + if (!keyoutfile) { + fprintf(stderr, "gmssl %s: '-keyout' option required\n", prog); + return -1; + } + if (sm9exch_load_master_public_key(&mpk, mpkfile, prog) != 1 + || sm9exch_load_private_key(&key, keyfile, pass, &mpk, prog) != 1 + || sm9exch_load_exch_key(r, &rb, &ra, exch_keyfile, prog) != 1 + || sm9exch_read_data(infile, sa, sizeof(sa), format) != 1) { + fprintf(stderr, "gmssl %s: finish stage input failure\n", prog); + goto end; + } + if ((vr = sm9_key_exchange_verify_confirm(0, &mpk, &key, id, idlen, + peer_id, peer_idlen, r, &rb, &ra, sa)) != 1) { + fprintf(stderr, "gmssl %s: SA verification %s\n", prog, vr < 0 ? "failure" : "failed"); + goto end; + } + if (sm9_key_exchange(0, &mpk, &key, id, idlen, peer_id, peer_idlen, + r, &rb, &ra, keylen, shared_key) != 1 + || sm9exch_write_data(keyoutfile, shared_key, keylen, format) != 1) { + fprintf(stderr, "gmssl %s: finish stage failure\n", prog); + goto end; + } + ret = 0; +end: + gmssl_secure_clear(&mpk, sizeof(mpk)); + gmssl_secure_clear(&key, sizeof(key)); + gmssl_secure_clear(&ra, sizeof(ra)); + gmssl_secure_clear(&rb, sizeof(rb)); + gmssl_secure_clear(r, sizeof(r)); + gmssl_secure_clear(shared_key, sizeof(shared_key)); + return ret; +} + +int sm9exch_main(int argc, char **argv) +{ + int ret = 1; + char *prog = argv[0]; + char *stage = NULL; + char *mpkfile = NULL; + char *keyfile = NULL; + char *pass = NULL; + char *id = NULL; + char *peer_id = NULL; + char *id_hex = NULL; + char *peer_id_hex = NULL; + char id_buf[SM9_MAX_ID_SIZE]; + size_t id_len = 0; + char peer_id_buf[SM9_MAX_ID_SIZE]; + size_t peer_id_len = 0; + char *infile = NULL; + char *outfile = NULL; + char *exch_keyfile = NULL; + char *exch_keyoutfile = NULL; + char *keyoutfile = NULL; + size_t keylen = 32; + int format = SM9EXCH_FMT_HEX; + + argc--; + argv++; + + if (argc < 1) { + fprintf(stderr, "usage: gmssl %s %s\n", prog, usage); + return 1; + } + + while (argc > 0) { + if (!strcmp(*argv, "-help")) { + printf("usage: gmssl %s %s\n", prog, usage); + printf("%s\n", options); + return 0; + } else if (!strcmp(*argv, "-stage")) { + if (--argc < 1) goto bad; + stage = *(++argv); + } else if (!strcmp(*argv, "-pubmaster")) { + if (--argc < 1) goto bad; + mpkfile = *(++argv); + } else if (!strcmp(*argv, "-key")) { + if (--argc < 1) goto bad; + keyfile = *(++argv); + } else if (!strcmp(*argv, "-pass")) { + if (--argc < 1) goto bad; + pass = *(++argv); + } else if (!strcmp(*argv, "-id")) { + if (id_hex) { + fprintf(stderr, "gmssl %s: '-id' and '-id_hex' should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + id = *(++argv); + id_len = strlen(id); + } else if (!strcmp(*argv, "-id_hex")) { + if (id) { + fprintf(stderr, "gmssl %s: '-id' and '-id_hex' should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + id_hex = *(++argv); + if (strlen(id_hex) > sizeof(id_buf) * 2 + || hex_to_bytes(id_hex, strlen(id_hex), (uint8_t *)id_buf, &id_len) != 1) { + fprintf(stderr, "gmssl %s: invalid '-id_hex' value\n", prog); + goto end; + } + id = id_buf; + } else if (!strcmp(*argv, "-peer_id")) { + if (peer_id_hex) { + fprintf(stderr, "gmssl %s: '-peer_id' and '-peer_id_hex' should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + peer_id = *(++argv); + peer_id_len = strlen(peer_id); + } else if (!strcmp(*argv, "-peer_id_hex")) { + if (peer_id) { + fprintf(stderr, "gmssl %s: '-peer_id' and '-peer_id_hex' should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + peer_id_hex = *(++argv); + if (strlen(peer_id_hex) > sizeof(peer_id_buf) * 2 + || hex_to_bytes(peer_id_hex, strlen(peer_id_hex), (uint8_t *)peer_id_buf, &peer_id_len) != 1) { + fprintf(stderr, "gmssl %s: invalid '-peer_id_hex' value\n", prog); + goto end; + } + peer_id = peer_id_buf; + } else if (!strcmp(*argv, "-in")) { + if (--argc < 1) goto bad; + infile = *(++argv); + } else if (!strcmp(*argv, "-out")) { + if (--argc < 1) goto bad; + outfile = *(++argv); + } else if (!strcmp(*argv, "-exch_keyout")) { + if (--argc < 1) goto bad; + exch_keyoutfile = *(++argv); + } else if (!strcmp(*argv, "-exch_key")) { + if (--argc < 1) goto bad; + exch_keyfile = *(++argv); + } else if (!strcmp(*argv, "-keylen")) { + if (--argc < 1) goto bad; + keylen = (size_t)atoi(*(++argv)); + if (keylen < 1 || keylen > SM9EXCH_MAX_SHARED_KEY_SIZE) { + fprintf(stderr, "gmssl %s: invalid '-keylen' value\n", prog); + goto end; + } + } else if (!strcmp(*argv, "-keyout")) { + if (--argc < 1) goto bad; + keyoutfile = *(++argv); + } else if (!strcmp(*argv, "-hex")) { + format = SM9EXCH_FMT_HEX; + } else if (!strcmp(*argv, "-bin")) { + format = SM9EXCH_FMT_BIN; + } else { + fprintf(stderr, "gmssl %s: illegal option '%s'\n", prog, *argv); + goto end; +bad: + fprintf(stderr, "gmssl %s: '%s' option value missing\n", prog, *argv); + goto end; + } + + argc--; + argv++; + } + + if (!stage) { + fprintf(stderr, "gmssl %s: '-stage' option required\n", prog); + goto end; + } + if (id && id_len > SM9_MAX_ID_SIZE) { + fprintf(stderr, "gmssl %s: local identity too long\n", prog); + goto end; + } + if (peer_id && peer_id_len > SM9_MAX_ID_SIZE) { + fprintf(stderr, "gmssl %s: peer identity too long\n", prog); + goto end; + } + + if (!strcmp(stage, "init")) { + ret = sm9exch_stage_init(mpkfile, peer_id, peer_id_len, + exch_keyoutfile, outfile, format, prog); + } else if (!strcmp(stage, "respond")) { + ret = sm9exch_stage_respond(mpkfile, keyfile, pass, id, id_len, + peer_id, peer_id_len, infile, exch_keyoutfile, + outfile, keylen, format, prog); + } else if (!strcmp(stage, "confirm")) { + ret = sm9exch_stage_confirm(mpkfile, keyfile, pass, id, id_len, + peer_id, peer_id_len, exch_keyfile, infile, + keyoutfile, outfile, keylen, format, prog); + } else if (!strcmp(stage, "finish")) { + ret = sm9exch_stage_finish(mpkfile, keyfile, pass, id, id_len, + peer_id, peer_id_len, exch_keyfile, infile, + keyoutfile, keylen, format, prog); + } else { + fprintf(stderr, "gmssl %s: invalid '-stage' value\n", prog); + goto end; + } + +end: + return ret == 0 ? 0 : 1; +} diff --git a/tools/sm9keygen.c b/tools/sm9keygen.c index ab90eea5..295329f0 100644 --- a/tools/sm9keygen.c +++ b/tools/sm9keygen.c @@ -17,12 +17,13 @@ #include -static const char *usage = "-alg (sm9sign|sm9encrypt) -in master_key.pem -inpass str -id str [-out pem] -outpass str"; +static const char *usage = "-alg (sm9sign|sm9encrypt|sm9keyagreement) -in master_key.pem -inpass str -id str [-out pem] -outpass str"; static const char *options = "Options\n" "\n" -" -alg sm9sign|sm9encrypt Generate maeter key for sm9sign or sm9encrypt\n" +" -alg sm9sign|sm9encrypt|sm9keyagreement\n" +" Generate user's private key for sm9sign, sm9encrypt or sm9keyagreement\n" " -in pem SM9 master private key in PEM format\n" " -inpass pass Password to decrypt the master private key\n" " -id str User's identity\n" @@ -36,6 +37,7 @@ static const char *options = "\n" " $ gmssl sm9setup -alg sm9encrypt -pass P@ssw0rd -out sm9enc_msk.pem\n" " $ gmssl sm9keygen -alg sm9encrypt -in sm9enc_msk.pem -inpass P@ssw0rd -id Alice -out sm9enc.pem -outpass 123456\n" +" $ gmssl sm9keygen -alg sm9keyagreement -in sm9enc_msk.pem -inpass P@ssw0rd -id Alice -out sm9exch.pem -outpass 123456\n" "\n"; int sm9keygen_main(int argc, char **argv) @@ -60,19 +62,20 @@ int sm9keygen_main(int argc, char **argv) argv++; if (argc < 1) { - fprintf(stderr, "usage: %s %s\n", prog, options); + fprintf(stderr, "usage: gmssl %s %s\n", prog, usage); return 1; } while (argc > 0) { if (!strcmp(*argv, "-help")) { - fprintf(stdout, "usage: %s %s\n", prog, options); + fprintf(stdout, "usage: gmssl %s %s\n", prog, usage); + fprintf(stdout, "%s\n", options); return 0; } else if (!strcmp(*argv, "-alg")) { if (--argc < 1) goto bad; alg = *(++argv); if ((oid = sm9_oid_from_name(alg)) < 1) { - fprintf(stdout, "%s: invalid alg '%s', should be sm9sign or sm9encrypt\n", prog, alg); + fprintf(stdout, "%s: invalid alg '%s', should be sm9sign, sm9encrypt or sm9keyagreement\n", prog, alg); goto end; } } else if (!strcmp(*argv, "-in")) { @@ -135,6 +138,14 @@ bad: goto end; } break; + case OID_sm9keyagreement: + if (sm9_enc_master_key_info_decrypt_from_pem(&enc_msk, inpass, infp) != 1 + || sm9_exch_master_key_extract_key(&enc_msk, id, strlen(id), &enc_key) != 1 + || sm9_enc_key_info_encrypt_to_pem(&enc_key, outpass, outfp) != 1) { + error_print(); + goto end; + } + break; default: error_print(); goto end; diff --git a/tools/zuc256.c b/tools/zuc256.c new file mode 100644 index 00000000..6ef8ed93 --- /dev/null +++ b/tools/zuc256.c @@ -0,0 +1,150 @@ +/* + * 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 + + +static const char *usage = "-key hex -iv hex [-in file] [-out file]"; + +static const char *help = +"Options\n" +"\n" +" -key hex ZUC-256 key in HEX format, 32 bytes\n" +" -iv hex ZUC-256 IV in HEX format, 23 bytes\n" +" -in file | stdin Input data\n" +" -out file | stdout Output data\n" +"\n" +"Examples\n" +"\n" +" gmssl zuc256 -key 0000000000000000000000000000000000000000000000000000000000000000 \\\n" +" -iv 0000000000000000000000000000000000000000000000 -in plaintext.bin -out ciphertext.bin\n" +"\n"; + +int zuc256_main(int argc, char **argv) +{ + int ret = 1; + char *prog = argv[0]; + char *keyhex = NULL; + char *ivhex = NULL; + char *infile = NULL; + char *outfile = NULL; + uint8_t key[ZUC256_KEY_SIZE]; + uint8_t iv[ZUC256_IV_SIZE]; + size_t keylen; + size_t ivlen; + FILE *infp = stdin; + FILE *outfp = stdout; + ZUC_STATE zuc_state; + uint8_t inbuf[4096]; + uint8_t outbuf[4096]; + size_t inlen; + + argc--; + argv++; + + if (argc < 1) { + fprintf(stderr, "usage: gmssl %s %s\n", prog, usage); + return 1; + } + + while (argc > 0) { + if (!strcmp(*argv, "-help")) { + printf("usage: gmssl %s %s\n", prog, usage); + printf("%s\n", help); + ret = 0; + goto end; + } else if (!strcmp(*argv, "-key")) { + if (--argc < 1) goto bad; + keyhex = *(++argv); + if (strlen(keyhex) != sizeof(key) * 2) { + fprintf(stderr, "gmssl %s: key should be 32 bytes\n", prog); + goto end; + } + if (hex_to_bytes(keyhex, strlen(keyhex), key, &keylen) != 1) { + fprintf(stderr, "gmssl %s: invalid key hex digits\n", prog); + goto end; + } + } else if (!strcmp(*argv, "-iv")) { + if (--argc < 1) goto bad; + ivhex = *(++argv); + if (strlen(ivhex) != sizeof(iv) * 2) { + fprintf(stderr, "gmssl %s: IV should be 23 bytes\n", prog); + goto end; + } + if (hex_to_bytes(ivhex, strlen(ivhex), iv, &ivlen) != 1) { + fprintf(stderr, "gmssl %s: invalid IV hex digits\n", prog); + goto end; + } + } else if (!strcmp(*argv, "-in")) { + if (--argc < 1) goto bad; + infile = *(++argv); + if (!(infp = fopen(infile, "rb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, infile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-out")) { + if (--argc < 1) goto bad; + outfile = *(++argv); + if (!(outfp = fopen(outfile, "wb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, outfile, strerror(errno)); + goto end; + } + } else { + fprintf(stderr, "gmssl %s: illegal option '%s'\n", prog, *argv); + goto end; +bad: + fprintf(stderr, "gmssl %s: '%s' option value missing\n", prog, *argv); + goto end; + } + + argc--; + argv++; + } + + if (!keyhex) { + fprintf(stderr, "gmssl %s: option '-key' required\n", prog); + goto end; + } + if (!ivhex) { + fprintf(stderr, "gmssl %s: option '-iv' required\n", prog); + goto end; + } + + zuc256_init(&zuc_state, key, iv); + while ((inlen = fread(inbuf, 1, sizeof(inbuf), infp)) > 0) { + zuc_encrypt(&zuc_state, inbuf, inlen, outbuf); + if (fwrite(outbuf, 1, inlen, outfp) != inlen) { + fprintf(stderr, "gmssl %s: output failure : %s\n", prog, strerror(errno)); + goto end; + } + } + if (ferror(infp)) { + fprintf(stderr, "gmssl %s: read failure : %s\n", prog, strerror(errno)); + goto end; + } + + ret = 0; + +end: + gmssl_secure_clear(&zuc_state, sizeof(zuc_state)); + gmssl_secure_clear(key, sizeof(key)); + gmssl_secure_clear(iv, sizeof(iv)); + gmssl_secure_clear(inbuf, sizeof(inbuf)); + gmssl_secure_clear(outbuf, sizeof(outbuf)); + if (infile && infp) fclose(infp); + if (outfile && outfp) fclose(outfp); + return ret; +} diff --git a/tools/zuc_128_eea3.c b/tools/zuc_128_eea3.c new file mode 100644 index 00000000..26c4b7b3 --- /dev/null +++ b/tools/zuc_128_eea3.c @@ -0,0 +1,310 @@ +/* + * 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 +#include + + +static const char *usage = + "-key hex -count num -bearer num -direction num [-in file|-in_hex hex] [-out file]"; + +static const char *help = +"Options\n" +"\n" +" -key hex 128-EEA3 confidentiality key, 16 bytes\n" +" -count num COUNT parameter, 32-bit integer, decimal or 0x-prefixed hex\n" +" -bearer num BEARER parameter, 5-bit integer in [0, 31]\n" +" -direction num DIRECTION parameter, 0 or 1\n" +" -in_hex hex Input bytes in HEX format\n" +" -in file | stdin Input file path\n" +" `-in_hex` and `-in` should not be used together\n" +" If neither `-in_hex` nor `-in` specified, read from stdin\n" +" -out file | stdout Output ciphertext bytes. If not specified, output to stdout\n" +"\n" +"Examples\n" +"\n" +" gmssl zuc_128_eea3 -key 173d14ba5003731d7a60049470f00a29 \\\n" +" -count 0x66035492 -bearer 15 -direction 0 \\\n" +" -in_hex 6cf65340735552ab0c9752fa6f9025fe0bd675d9005875b2 -out ciphertext.bin\n" +"\n"; + +static int parse_uint64(const char *s, uint64_t max, uint64_t *out) +{ + char *end = NULL; + unsigned long long v; + + if (!s || !*s) { + return -1; + } + errno = 0; + v = strtoull(s, &end, 0); + if (errno || *end || v > max) { + return -1; + } + *out = (uint64_t)v; + return 1; +} + +static uint8_t *read_content(FILE *infp, size_t *outlen, const char *prog) +{ + const size_t initial_size = 4096; + const size_t max_size = 512 * 1024 * 1024; + uint8_t *buf = NULL; + size_t bufsiz = initial_size; + size_t len = 0; + + if (!(buf = (uint8_t *)malloc(bufsiz))) { + fprintf(stderr, "gmssl %s: malloc failure\n", prog); + return NULL; + } + for (;;) { + size_t n; + + if (len == bufsiz) { + uint8_t *tmp; + + if (bufsiz >= max_size) { + fprintf(stderr, "gmssl %s: input too long, should be less than %zu\n", prog, max_size); + free(buf); + return NULL; + } + bufsiz *= 2; + if (bufsiz > max_size) { + bufsiz = max_size; + } + if (!(tmp = (uint8_t *)realloc(buf, bufsiz))) { + fprintf(stderr, "gmssl %s: realloc failure\n", prog); + free(buf); + return NULL; + } + buf = tmp; + } + + n = fread(buf + len, 1, bufsiz - len, infp); + len += n; + + if (feof(infp)) { + break; + } + if (ferror(infp)) { + fprintf(stderr, "gmssl %s: read failure : %s\n", prog, strerror(errno)); + free(buf); + return NULL; + } + } + + *outlen = len; + return buf; +} + +int zuc_128_eea3_main(int argc, char **argv) +{ + int ret = 1; + char *prog = argv[0]; + char *keyhex = NULL; + char *inhex = NULL; + char *infile = NULL; + char *outfile = NULL; + uint8_t key[ZUC_KEY_SIZE]; + size_t keylen; + uint8_t *in = NULL; + size_t inlen = 0; + uint8_t *padded = NULL; + ZUC_UINT32 *inwords = NULL; + ZUC_UINT32 *outwords = NULL; + uint8_t *out = NULL; + size_t nbits; + size_t nbytes = 0; + size_t nwords = 0; + size_t i; + uint64_t v; + ZUC_UINT32 count = 0; + ZUC_UINT5 bearer = 0; + ZUC_BIT direction = 0; + int count_set = 0; + int bearer_set = 0; + int direction_set = 0; + FILE *infp = stdin; + FILE *outfp = stdout; + + argc--; + argv++; + + if (argc < 1) { + fprintf(stderr, "usage: gmssl %s %s\n", prog, usage); + return 1; + } + + while (argc > 0) { + if (!strcmp(*argv, "-help")) { + printf("usage: gmssl %s %s\n", prog, usage); + printf("%s\n", help); + ret = 0; + goto end; + } else if (!strcmp(*argv, "-key")) { + if (--argc < 1) goto bad; + keyhex = *(++argv); + if (strlen(keyhex) != sizeof(key) * 2) { + fprintf(stderr, "gmssl %s: key should be 16 bytes\n", prog); + goto end; + } + if (hex_to_bytes(keyhex, strlen(keyhex), key, &keylen) != 1) { + fprintf(stderr, "gmssl %s: invalid key hex digits\n", prog); + goto end; + } + } else if (!strcmp(*argv, "-count")) { + if (--argc < 1) goto bad; + if (parse_uint64(*(++argv), UINT32_MAX, &v) != 1) { + fprintf(stderr, "gmssl %s: invalid COUNT value\n", prog); + goto end; + } + count = (ZUC_UINT32)v; + count_set = 1; + } else if (!strcmp(*argv, "-bearer")) { + if (--argc < 1) goto bad; + if (parse_uint64(*(++argv), 31, &v) != 1) { + fprintf(stderr, "gmssl %s: invalid BEARER value\n", prog); + goto end; + } + bearer = (ZUC_UINT5)v; + bearer_set = 1; + } else if (!strcmp(*argv, "-direction")) { + if (--argc < 1) goto bad; + if (parse_uint64(*(++argv), 1, &v) != 1) { + fprintf(stderr, "gmssl %s: invalid DIRECTION value\n", prog); + goto end; + } + direction = (ZUC_BIT)v; + direction_set = 1; + } else if (!strcmp(*argv, "-in_hex")) { + if (infile) { + fprintf(stderr, "gmssl %s: `-in` and `-in_hex` should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + inhex = *(++argv); + } else if (!strcmp(*argv, "-in")) { + if (inhex) { + fprintf(stderr, "gmssl %s: `-in` and `-in_hex` should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + infile = *(++argv); + if (!(infp = fopen(infile, "rb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, infile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-out")) { + if (--argc < 1) goto bad; + outfile = *(++argv); + if (!(outfp = fopen(outfile, "wb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, outfile, strerror(errno)); + goto end; + } + } else { + fprintf(stderr, "gmssl %s: illegal option '%s'\n", prog, *argv); + goto end; +bad: + fprintf(stderr, "gmssl %s: '%s' option value missing\n", prog, *argv); + goto end; + } + + argc--; + argv++; + } + + if (!keyhex) { + fprintf(stderr, "gmssl %s: option '-key' required\n", prog); + goto end; + } + if (!count_set || !bearer_set || !direction_set) { + fprintf(stderr, "gmssl %s: options '-count', '-bearer' and '-direction' are required\n", prog); + goto end; + } + + if (inhex) { + if (strlen(inhex) % 2) { + fprintf(stderr, "gmssl %s: invalid input hex length\n", prog); + goto end; + } + nbytes = strlen(inhex) / 2; + if (!(in = (uint8_t *)malloc(nbytes ? nbytes : 1))) { + fprintf(stderr, "gmssl %s: malloc failure\n", prog); + goto end; + } + if (hex_to_bytes(inhex, strlen(inhex), in, &inlen) != 1) { + fprintf(stderr, "gmssl %s: invalid input hex digits\n", prog); + goto end; + } + } else if (!(in = read_content(infp, &inlen, prog))) { + goto end; + } + nbytes = inlen; + nbits = inlen * 8; + nwords = (nbits + 31) / 32; + + if (!(padded = (uint8_t *)calloc(nwords ? nwords : 1, sizeof(uint32_t))) + || !(inwords = (ZUC_UINT32 *)calloc(nwords ? nwords : 1, sizeof(uint32_t))) + || !(outwords = (ZUC_UINT32 *)calloc(nwords ? nwords : 1, sizeof(uint32_t))) + || !(out = (uint8_t *)calloc(nwords ? nwords : 1, sizeof(uint32_t)))) { + fprintf(stderr, "gmssl %s: malloc failure\n", prog); + goto end; + } + memcpy(padded, in, inlen); + for (i = 0; i < nwords; i++) { + inwords[i] = GETU32(padded + i * 4); + } + + zuc_eea_encrypt(inwords, outwords, nbits, key, count, bearer, direction); + for (i = 0; i < nwords; i++) { + PUTU32(out + i * 4, outwords[i]); + } + + if (nbytes && fwrite(out, 1, nbytes, outfp) != nbytes) { + fprintf(stderr, "gmssl %s: output failure : %s\n", prog, strerror(errno)); + goto end; + } + + ret = 0; + +end: + gmssl_secure_clear(key, sizeof(key)); + if (in) { + gmssl_secure_clear(in, inlen); + free(in); + } + if (padded) { + gmssl_secure_clear(padded, (nwords ? nwords : 1) * sizeof(uint32_t)); + free(padded); + } + if (inwords) { + gmssl_secure_clear(inwords, (nwords ? nwords : 1) * sizeof(uint32_t)); + free(inwords); + } + if (outwords) { + gmssl_secure_clear(outwords, (nwords ? nwords : 1) * sizeof(uint32_t)); + free(outwords); + } + if (out) { + gmssl_secure_clear(out, (nwords ? nwords : 1) * sizeof(uint32_t)); + free(out); + } + if (infile && infp) fclose(infp); + if (outfile && outfp) fclose(outfp); + return ret; +} diff --git a/tools/zuc_128_eia3.c b/tools/zuc_128_eia3.c new file mode 100644 index 00000000..e54b9a77 --- /dev/null +++ b/tools/zuc_128_eia3.c @@ -0,0 +1,299 @@ +/* + * 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 +#include + + +static const char *usage = + "-key hex -count num -bearer num -direction num [-in file|-in_hex hex] [-hex|-bin] [-out file]"; + +static const char *help = +"Options\n" +"\n" +" -key hex 128-EIA3 integrity key, 16 bytes\n" +" -count num COUNT parameter, 32-bit integer, decimal or 0x-prefixed hex\n" +" -bearer num BEARER parameter, 5-bit integer in [0, 31]\n" +" -direction num DIRECTION parameter, 0 or 1\n" +" -in_hex hex Input message bytes in HEX format\n" +" -in file | stdin Input file path\n" +" `-in_hex` and `-in` should not be used together\n" +" If neither `-in_hex` nor `-in` specified, read from stdin\n" +" -hex Output MAC as hex string (by default)\n" +" -bin Output MAC as binary\n" +" `-hex` and `-bin` should not be used together\n" +" -out file | stdout Output file path. If not specified, output to stdout\n" +"\n" +"Examples\n" +"\n" +" gmssl zuc_128_eia3 -key 00000000000000000000000000000000 \\\n" +" -count 0 -bearer 0 -direction 0 -in_hex 00\n" +"\n"; + +static int parse_uint64(const char *s, uint64_t max, uint64_t *out) +{ + char *end = NULL; + unsigned long long v; + + if (!s || !*s) { + return -1; + } + errno = 0; + v = strtoull(s, &end, 0); + if (errno || *end || v > max) { + return -1; + } + *out = (uint64_t)v; + return 1; +} + +static uint8_t *read_content(FILE *infp, size_t *outlen, const char *prog) +{ + const size_t initial_size = 4096; + const size_t max_size = 512 * 1024 * 1024; + uint8_t *buf = NULL; + size_t bufsiz = initial_size; + size_t len = 0; + + if (!(buf = (uint8_t *)malloc(bufsiz))) { + fprintf(stderr, "gmssl %s: malloc failure\n", prog); + return NULL; + } + for (;;) { + size_t n; + + if (len == bufsiz) { + uint8_t *tmp; + + if (bufsiz >= max_size) { + fprintf(stderr, "gmssl %s: input too long, should be less than %zu\n", prog, max_size); + free(buf); + return NULL; + } + bufsiz *= 2; + if (bufsiz > max_size) { + bufsiz = max_size; + } + if (!(tmp = (uint8_t *)realloc(buf, bufsiz))) { + fprintf(stderr, "gmssl %s: realloc failure\n", prog); + free(buf); + return NULL; + } + buf = tmp; + } + + n = fread(buf + len, 1, bufsiz - len, infp); + len += n; + + if (feof(infp)) { + break; + } + if (ferror(infp)) { + fprintf(stderr, "gmssl %s: read failure : %s\n", prog, strerror(errno)); + free(buf); + return NULL; + } + } + + *outlen = len; + return buf; +} + +int zuc_128_eia3_main(int argc, char **argv) +{ + int ret = 1; + char *prog = argv[0]; + char *keyhex = NULL; + char *inhex = NULL; + char *infile = NULL; + char *outfile = NULL; + int outformat = 0; + uint8_t key[ZUC_KEY_SIZE]; + size_t keylen; + uint8_t *in = NULL; + size_t inlen = 0; + size_t nbits; + size_t nbytes = 0; + uint64_t v; + ZUC_UINT32 count = 0; + ZUC_UINT5 bearer = 0; + ZUC_BIT direction = 0; + ZUC_UINT32 macword; + uint8_t mac[ZUC_MAC_SIZE]; + int count_set = 0; + int bearer_set = 0; + int direction_set = 0; + FILE *infp = stdin; + FILE *outfp = stdout; + size_t i; + + argc--; + argv++; + + if (argc < 1) { + fprintf(stderr, "usage: gmssl %s %s\n", prog, usage); + return 1; + } + + while (argc > 0) { + if (!strcmp(*argv, "-help")) { + printf("usage: gmssl %s %s\n", prog, usage); + printf("%s\n", help); + ret = 0; + goto end; + } else if (!strcmp(*argv, "-key")) { + if (--argc < 1) goto bad; + keyhex = *(++argv); + if (strlen(keyhex) != sizeof(key) * 2) { + fprintf(stderr, "gmssl %s: key should be 16 bytes\n", prog); + goto end; + } + if (hex_to_bytes(keyhex, strlen(keyhex), key, &keylen) != 1) { + fprintf(stderr, "gmssl %s: invalid key hex digits\n", prog); + goto end; + } + } else if (!strcmp(*argv, "-count")) { + if (--argc < 1) goto bad; + if (parse_uint64(*(++argv), UINT32_MAX, &v) != 1) { + fprintf(stderr, "gmssl %s: invalid COUNT value\n", prog); + goto end; + } + count = (ZUC_UINT32)v; + count_set = 1; + } else if (!strcmp(*argv, "-bearer")) { + if (--argc < 1) goto bad; + if (parse_uint64(*(++argv), 31, &v) != 1) { + fprintf(stderr, "gmssl %s: invalid BEARER value\n", prog); + goto end; + } + bearer = (ZUC_UINT5)v; + bearer_set = 1; + } else if (!strcmp(*argv, "-direction")) { + if (--argc < 1) goto bad; + if (parse_uint64(*(++argv), 1, &v) != 1) { + fprintf(stderr, "gmssl %s: invalid DIRECTION value\n", prog); + goto end; + } + direction = (ZUC_BIT)v; + direction_set = 1; + } else if (!strcmp(*argv, "-in_hex")) { + if (infile) { + fprintf(stderr, "gmssl %s: `-in` and `-in_hex` should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + inhex = *(++argv); + } else if (!strcmp(*argv, "-in")) { + if (inhex) { + fprintf(stderr, "gmssl %s: `-in` and `-in_hex` should not be used together\n", prog); + goto end; + } + if (--argc < 1) goto bad; + infile = *(++argv); + if (!(infp = fopen(infile, "rb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, infile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-hex")) { + if (outformat == 2) { + fprintf(stderr, "gmssl %s: `-hex` and `-bin` should not be used together\n", prog); + goto end; + } + outformat = 1; + } else if (!strcmp(*argv, "-bin")) { + if (outformat == 1) { + fprintf(stderr, "gmssl %s: `-hex` and `-bin` should not be used together\n", prog); + goto end; + } + outformat = 2; + } else if (!strcmp(*argv, "-out")) { + if (--argc < 1) goto bad; + outfile = *(++argv); + if (!(outfp = fopen(outfile, "wb"))) { + fprintf(stderr, "gmssl %s: open '%s' failure : %s\n", prog, outfile, strerror(errno)); + goto end; + } + } else { + fprintf(stderr, "gmssl %s: illegal option '%s'\n", prog, *argv); + goto end; +bad: + fprintf(stderr, "gmssl %s: '%s' option value missing\n", prog, *argv); + goto end; + } + + argc--; + argv++; + } + + if (!keyhex) { + fprintf(stderr, "gmssl %s: option '-key' required\n", prog); + goto end; + } + if (!count_set || !bearer_set || !direction_set) { + fprintf(stderr, "gmssl %s: options '-count', '-bearer' and '-direction' are required\n", prog); + goto end; + } + + if (inhex) { + if (strlen(inhex) % 2) { + fprintf(stderr, "gmssl %s: invalid input hex length\n", prog); + goto end; + } + nbytes = strlen(inhex) / 2; + if (!(in = (uint8_t *)malloc(nbytes ? nbytes : 1))) { + fprintf(stderr, "gmssl %s: malloc failure\n", prog); + goto end; + } + if (hex_to_bytes(inhex, strlen(inhex), in, &inlen) != 1) { + fprintf(stderr, "gmssl %s: invalid input hex digits\n", prog); + goto end; + } + } else if (!(in = read_content(infp, &inlen, prog))) { + goto end; + } + nbytes = inlen; + nbits = inlen * 8; + + macword = zuc_eia_generate_mac((ZUC_UINT32 *)in, nbits, key, count, bearer, direction); + PUTU32(mac, macword); + + if (outformat == 2) { + if (fwrite(mac, 1, sizeof(mac), outfp) != sizeof(mac)) { + fprintf(stderr, "gmssl %s: output failure : %s\n", prog, strerror(errno)); + goto end; + } + } else { + for (i = 0; i < sizeof(mac); i++) { + fprintf(outfp, "%02x", mac[i]); + } + fprintf(outfp, "\n"); + } + + ret = 0; + +end: + gmssl_secure_clear(key, sizeof(key)); + gmssl_secure_clear(mac, sizeof(mac)); + if (in) { + gmssl_secure_clear(in, inlen); + free(in); + } + if (infile && infp) fclose(infp); + if (outfile && outfp) fclose(outfp); + return ret; +}