From: Eugene Mironenko Date: Wed, 20 Aug 2025 16:15:29 +0000 (+0300) Subject: Implement provider interface to support GOST TLS 1.3 X-Git-Url: http://www.wagner.pp.ru/gitweb/?a=commitdiff_plain;h=refs%2Fpull%2F482%2Fhead;p=openssl-gost%2Fengine.git Implement provider interface to support GOST TLS 1.3 Source id: 9d91e6da264b7ae075a83c5281964fb74d8832fe --- diff --git a/.github/before_script.sh b/.github/before_script.sh index 2164d7f..d77ba17 100755 --- a/.github/before_script.sh +++ b/.github/before_script.sh @@ -12,6 +12,9 @@ if [ "${APT_INSTALL-}" ]; then fi git clone --depth 1 -b $OPENSSL_BRANCH https://github.com/openssl/openssl.git +if [ "${PATCH_OPENSSL}" == "1" ]; then + git apply patches/openssl-tls1.3.patch +fi cd openssl git describe --always --long diff --git a/.github/script.sh b/.github/script.sh index f8b053c..51154ee 100755 --- a/.github/script.sh +++ b/.github/script.sh @@ -5,9 +5,10 @@ PATH=$PREFIX/bin:$PATH mkdir build cd build -cmake -DOPENSSL_ROOT_DIR=$PREFIX -DOPENSSL_ENGINES_DIR=$PREFIX/engines ${ASAN-} .. +cmake -DTLS13_PATCHED_OPENSSL=$PATCH_OPENSSL -DOPENSSL_ROOT_DIR=$PREFIX -DOPENSSL_ENGINES_DIR=$PREFIX/engines ${ASAN-} .. make make test CTEST_OUTPUT_ON_FAILURE=1 if [ -z "${ASAN-}" ]; then - make tcl_tests + make tcl_tests_engine + make tcl_tests_provider fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b046b0..bcb5e95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,12 +2,16 @@ name: CI on: [push, pull_request] env: - OPENSSL_BRANCH: openssl-3.0 + OPENSSL_BRANCH: openssl-3.4.2 USE_RPATH: yes + PATCH_OPENSSL: 0 + GOST_PROVIDER_ENABLE_ONLINE_TESTS: 1 jobs: gcc-openssl-stable: runs-on: ubuntu-latest + env: + PATCH_OPENSSL: 1 steps: - uses: actions/checkout@v2 with: @@ -19,6 +23,7 @@ jobs: runs-on: ubuntu-latest env: CC: clang + PATCH_OPENSSL: 1 steps: - uses: actions/checkout@v2 with: @@ -30,6 +35,8 @@ jobs: runs-on: macos-latest env: USE_RPATH: + PATCH_OPENSSL: 1 + GOST_PROVIDER_ENABLE_ONLINE_TESTS: 0 # macOS runner has no network access to infotecs TLS1.3 server steps: - uses: actions/checkout@v2 with: @@ -92,6 +99,7 @@ jobs: LDFLAGS: -m32 SETARCH: "setarch i386" APT_INSTALL: gcc-multilib + PATCH_OPENSSL: 1 steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 194a08d..73e59bf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,7 +1,7 @@ name: "CodeQL" env: - OPENSSL_BRANCH: openssl-3.0 + OPENSSL_BRANCH: openssl-3.4.2 #RPATH: "-Wl,-rpath=${PREFIX}/lib" #PREFIX: ${HOME}/opt #PATH: ${PREFIX}/bin:${PATH} diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 191a834..984c6e1 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -10,8 +10,10 @@ jobs: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'gost-engine' || github.event_name == 'workflow_dispatch' }} env: - OPENSSL_BRANCH: openssl-3.0 + OPENSSL_BRANCH: openssl-3.4.2 USE_RPATH: yes + PATCH_OPENSSL: 1 + GOST_PROVIDER_ENABLE_ONLINE_TESTS: 0 steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 5581a8a..728a308 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -7,20 +7,27 @@ jobs: outputs: openssl-head: ${{ steps.openssl.outputs.head }} steps: + - uses: actions/checkout@v2 - uses: actions/checkout@v2 with: repository: openssl/openssl + path: openssl + ref: openssl-3.4.2 fetch-depth: 0 - - run: echo "::set-output name=head::$(git describe --always --long)" + - run: echo "::set-output name=head::$(git -C openssl describe --always --long)" id: openssl - uses: actions/cache@v4 id: cache with: - path: _dest + path: openssl/_dest key: ${{ runner.os }}-openssl-${{ steps.openssl.outputs.head }} + - name: Apply patches + run: | + git apply patches/openssl-tls1.3.patch - uses: ilammy/msvc-dev-cmd@v1 - name: Build OpenSSL if: steps.cache.outputs.cache-hit != 'true' + working-directory: openssl run: | perl Configure no-makedepend no-tests no-asm VC-WIN64A perl configdata.pm --dump @@ -36,13 +43,13 @@ jobs: submodules: true - uses: actions/cache@v4 with: - path: _dest + path: openssl/_dest key: ${{ runner.os }}-openssl-${{ needs.msvc-openssl.outputs.openssl-head }} - - run: cmake -DOPENSSL_ROOT_DIR="_dest\Program Files\OpenSSL" -DOPENSSL_ENGINES_DIR=bin . + - run: cmake -DOPENSSL_ROOT_DIR="openssl\_dest\Program Files\OpenSSL" -DOPENSSL_ENGINES_DIR=bin . - run: cmake --build . - name: Run tests run: | - $env:PATH = "$env:PATH;$pwd\_dest\Program Files\OpenSSL\bin" + $env:PATH = "$pwd\openssl\_dest\Program Files\OpenSSL\bin;$env:PATH" $env:OPENSSL_ENGINES = "$pwd\bin\Debug" $env:OPENSSL_MODULES = "$pwd\bin\Debug" ctest -C Debug --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index cae27c3..91e9377 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(CheckCSourceRuns) enable_testing() -find_package(OpenSSL 3.0 REQUIRED) +find_package(OpenSSL 3.4 REQUIRED) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") @@ -29,12 +29,12 @@ if (CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS_RELEASE -O2) set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -ggdb") - add_compile_options(-Werror -Wall -Wno-unused-parameter -Wno-unused-function -Wno-missing-braces -Qunused-arguments -Wno-deprecated-declarations) + add_compile_options(-Werror -Wall -Wno-unused-parameter -Wno-unused-function -Wno-missing-braces -Qunused-arguments -Wno-deprecated-declarations -Wno-error=\#warnings) elseif(CMAKE_C_COMPILER_ID MATCHES "GNU") set(CMAKE_C_FLAGS_RELEASE -O2) set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -ggdb") - add_compile_options(-Werror -Wall -Wno-unused-parameter -Wno-unused-function -Wno-missing-braces -Wno-error=unknown-pragmas -Wno-error=pragmas -Wno-deprecated-declarations) + add_compile_options(-Werror -Wall -Wno-unused-parameter -Wno-unused-function -Wno-missing-braces -Wno-error=unknown-pragmas -Wno-error=pragmas -Wno-deprecated-declarations -Wno-error=cpp) elseif(CMAKE_C_COMPILER_ID MATCHES "MSVC") add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_CRT_DEPRECATED_NO_WARNINGS) @@ -204,15 +204,25 @@ set(GOST_ENGINE_SOURCE_FILES set(GOST_PROV_SOURCE_FILES gost_prov.c + gost_prov.h gost_prov_cipher.c gost_prov_digest.c gost_prov_mac.c + gost_prov_keymgmt.c + gost_prov_encoder.c + gost_prov_signature.c + gost_prov_decoder.c + gost_prov_keyexch.c + gost_prov_tls.c + gost_prov_tls.h ) set(TEST_ENVIRONMENT_COMMON + TLS13_PATCHED_OPENSSL=${TLS13_PATCHED_OPENSSL} CMAKE_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} PERL5LIB=${CMAKE_CURRENT_SOURCE_DIR}/test OPENSSL_PROGRAM=${OPENSSL_PROGRAM} + OPENSSL_MODULES=${CMAKE_RUNTIME_OUTPUT_DIRECTORY} OPENSSL_CRYPTO_LIBRARY=${OPENSSL_CRYPTO_LIBRARY} ) @@ -247,6 +257,13 @@ set_tests_properties(ciphers-with-provider PROPERTIES ENVIRONMENT "${TEST_ENVIRONMENT_PROVIDER}") # test_curves is an internals testing program, it doesn't need a test env + +add_executable(test_ecdhe test_ecdhe.c) +target_link_libraries(test_ecdhe gost_core gost_err) +add_test(NAME ecdhe COMMAND test_ecdhe) +set_tests_properties(ecdhe + PROPERTIES ENVIRONMENT "${TEST_ENVIRONMENT_ENGINE}") + add_executable(test_curves test_curves.c) target_link_libraries(test_curves gost_core gost_err) add_test(NAME curves COMMAND test_curves) @@ -295,14 +312,17 @@ add_executable(test_gost89 test_gost89.c) target_link_libraries(test_gost89 gost_core gost_err) add_test(NAME gost89 COMMAND test_gost89) -add_executable(test_mgm test_mgm.c) -target_link_libraries(test_mgm OpenSSL::Crypto) -add_test(NAME mgm-with-engine COMMAND test_mgm) -set_tests_properties(mgm-with-engine - PROPERTIES ENVIRONMENT "${TEST_ENVIRONMENT_ENGINE}") -add_test(NAME mgm-with-provider COMMAND test_mgm) -set_tests_properties(mgm-with-provider - PROPERTIES ENVIRONMENT "${TEST_ENVIRONMENT_PROVIDER}") +if(TLS13_PATCHED_OPENSSL) + add_executable(test_mgm test_mgm.c) + target_link_libraries(test_mgm OpenSSL::Crypto) + add_test(NAME mgm-with-engine COMMAND test_mgm) + set_tests_properties(mgm-with-engine + PROPERTIES ENVIRONMENT "${TEST_ENVIRONMENT_ENGINE}") + add_test(NAME mgm-with-provider COMMAND test_mgm) + set_tests_properties(mgm-with-provider + PROPERTIES ENVIRONMENT "${TEST_ENVIRONMENT_PROVIDER}") + set_property(TARGET test_mgm APPEND PROPERTY COMPILE_DEFINITIONS ENGINE_DIR="${OUTPUT_DIRECTORY}") +endif() if(NOT SKIP_PERL_TESTS) execute_process(COMMAND perl -MTest2::V0 -e "" @@ -330,6 +350,7 @@ set(BINARY_TESTS_TARGETS test_digest test_ciphers test_curves + test_ecdhe test_params test_derive test_sign @@ -337,7 +358,6 @@ set(BINARY_TESTS_TARGETS test_keyexpimp test_gost89 test_tls - test_mgm ) set_property(TARGET ${BINARY_TESTS_TARGETS} APPEND PROPERTY COMPILE_DEFINITIONS ENGINE_DIR="${OUTPUT_DIRECTORY}") @@ -410,12 +430,23 @@ add_custom_target(tags COMMAND ctags -R . ${OPENSSL_ROOT_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) -add_custom_target(tcl_tests +add_custom_target(tcl_tests_provider + COMMAND OPENSSL_LIBCRYPTO=${OPENSSL_CRYPTO_LIBRARY} + OPENSSL_APP=${OPENSSL_PROGRAM} + TESTSRC=${CMAKE_SOURCE_DIR}/tcl_tests + TESTDIR=${CMAKE_BINARY_DIR}/tcl_tests_provider + OPENSSL_MODULES_DIR=${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + OPENSSL_CONF=${CMAKE_SOURCE_DIR}/tcl_tests/openssl-gost-provider.cnf + sh ./runtest.sh + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tcl_tests) + +add_custom_target(tcl_tests_engine COMMAND OPENSSL_LIBCRYPTO=${OPENSSL_CRYPTO_LIBRARY} OPENSSL_APP=${OPENSSL_PROGRAM} TESTSRC=${CMAKE_SOURCE_DIR}/tcl_tests TESTDIR=${CMAKE_BINARY_DIR}/tcl_tests ENGINE_DIR=${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + OPENSSL_CONF=${CMAKE_SOURCE_DIR}/tcl_tests/openssl-gost-engine.cnf sh ./runtest.sh WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tcl_tests) diff --git a/README.prov.md b/README.prov.md index 0749104..d2a3e5e 100644 --- a/README.prov.md +++ b/README.prov.md @@ -37,21 +37,49 @@ MACs: - kuznyechik-mac - kuznyechik-ctr-acpkm-omac -## TODO, not requiring additional OpenSSL support +Keymgmt: -- Basic support for GOST keys, i.e. implementations of KEYMGMT - (including key generation), DECODER and DECODER. +- id-GostR3410-2001 ("GOST R 34.10-2001", "1.2.643.2.2.19") +- id-GostR3410-2001DH ("GOST R 34.10-2001 DH", "1.2.643.2.2.98") +- gost2012_256 ("GOST R 34.10-2012 with 256 bit modulus", "1.2.643.7.1.1.1.1") +- gost2012_512 ("GOST R 34.10-2012 with 512 bit modulus", "1.2.643.7.1.1.1.2") -- Support for these operations using GOST keys: +Encoder: +- id-GostR3410-2001 ("GOST R 34.10-2001", "1.2.643.2.2.19") with structure format = pem/der/text +- id-GostR3410-2001DH ("GOST R 34.10-2001 DH", "1.2.643.2.2.98") with structure format = pem/der/text +- gost2012_256 ("GOST R 34.10-2012 with 256 bit modulus", "1.2.643.7.1.1.1.1") with structure format = pem/der/text +- gost2012_512 ("GOST R 34.10-2012 with 512 bit modulus", "1.2.643.7.1.1.1.2") with structure format = pem/der/text + +PrivateKeyInfo can only be saved in pkcs8 format without encryption. + +Decoder: +- id-GostR3410-2001 ("GOST R 34.10-2001", "1.2.643.2.2.19") with structure format = der +- id-GostR3410-2001DH ("GOST R 34.10-2001 DH", "1.2.643.2.2.98") with structure format = der +- gost2012_256 ("GOST R 34.10-2012 with 256 bit modulus", "1.2.643.7.1.1.1.1") with structure format = der +- gost2012_512 ("GOST R 34.10-2012 with 512 bit modulus", "1.2.643.7.1.1.1.2") with structure format = der + +pem2der decoder already implemented by OpenSSL default provider. + +PrivateKeyInfo can only be loaded in pkcs8 format without decryption. + +Signature: +- SN_id_GostR3410_2001, "id-GostR3411-94-with-GostR3410-2001", "GOST R 34.11-94 with GOST R 34.10-2001", "1.2.643.2.2.3" +- SN_id_GostR3410_2012_256, "id-tc26-signwithdigest-gost3410-2012-256", "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)", "1.2.643.7.1.1.3.2" +- gost2012_256, "id-tc26-signwithdigest-gost3410-2012-512", "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)", "1.2.643.7.1.1.3.3" +Keyexchange: +- ECDHE + +TLS1.3: +- OpenSSL patch has been implemented that allows to connect TLS1.3 using a provider. + +## TODO, not requiring additional OpenSSL support + +- Support for these operations using GOST keys: - ASYM_CIPHER (encryption and decryption using GOST keys) - - SIGNATURE (signing and verifying using GOST keys) ## TODO, which requires additional OpenSSL support -- TLSTREE support. This may require additional changes in libssl. - Needs investigation. - - PKCS7 and CMS support. This requires OpenSSL PKCS7 and CMS code to change for better interfacing with providers. diff --git a/gost_ameth.c b/gost_ameth.c index d7e7d63..dd116f7 100644 --- a/gost_ameth.c +++ b/gost_ameth.c @@ -36,12 +36,9 @@ int store_bignum(const BIGNUM *bn, unsigned char *buf, int len) return 1; } -static int pkey_bits_gost(const EVP_PKEY *pk) +static int internal_pkey_bits(int key_type) { - if (!pk) - return -1; - - switch (EVP_PKEY_base_id(pk)) { + switch (key_type) { case NID_id_GostR3410_2001: case NID_id_GostR3410_2001DH: case NID_id_GostR3410_2012_256: @@ -53,19 +50,28 @@ static int pkey_bits_gost(const EVP_PKEY *pk) return -1; } -static ASN1_STRING *encode_gost_algor_params(const EVP_PKEY *key) +static int pkey_bits_gost(const EVP_PKEY *pk) +{ + int key_type = (pk == NULL) ? NID_undef : EVP_PKEY_base_id(pk); + + return internal_pkey_bits(key_type); +} + +static ASN1_STRING *internal_encode_algor_params(EC_KEY *key_ptr, + int key_type) { ASN1_STRING *params = ASN1_STRING_new(); GOST_KEY_PARAMS *gkp = GOST_KEY_PARAMS_new(); int pkey_param_nid = NID_undef; - void *key_ptr = EVP_PKEY_get0((EVP_PKEY *)key); int result = 0; + if (!key_ptr) + goto err; if (!params || !gkp) { GOSTerr(GOST_F_ENCODE_GOST_ALGOR_PARAMS, ERR_R_MALLOC_FAILURE); goto err; } - switch (EVP_PKEY_base_id(key)) { + switch (key_type) { case NID_id_GostR3410_2012_256: pkey_param_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(key_ptr)); switch (pkey_param_nid) { @@ -121,25 +127,25 @@ static ASN1_STRING *encode_gost_algor_params(const EVP_PKEY *key) return params; } -static int gost_decode_nid_params(EVP_PKEY *pkey, int pkey_nid, int param_nid) +static ASN1_STRING *encode_gost_algor_params(const EVP_PKEY *key) { - void *key_ptr = EVP_PKEY_get0(pkey); + EC_KEY *ec = EVP_PKEY_get0((EVP_PKEY *)key); + int key_type = (key == NULL) ? NID_undef : EVP_PKEY_base_id(key); + + if (!ec) + return 0; + return internal_encode_algor_params(ec, key_type); +} +static int internal_is_gost_pkey_nid(int pkey_nid) +{ switch (pkey_nid) { case NID_id_GostR3410_2012_256: case NID_id_GostR3410_2012_512: case NID_id_GostR3410_2001: case NID_id_GostR3410_2001DH: - if (!key_ptr) { - key_ptr = EC_KEY_new(); - if (!EVP_PKEY_assign(pkey, pkey_nid, key_ptr)) { - EC_KEY_free(key_ptr); - break; - } - } - return fill_GOST_EC_params(key_ptr, param_nid); + return 1; } - return 0; } @@ -147,7 +153,7 @@ static int gost_decode_nid_params(EVP_PKEY *pkey, int pkey_nid, int param_nid) * Parses GOST algorithm parameters from X509_ALGOR and modifies pkey setting * NID and parameters */ -static int decode_gost_algor_params(EVP_PKEY *pkey, +static int decode_gost_algor_params(EC_KEY *ec, int *key_type, const X509_ALGOR *palg) { const ASN1_OBJECT *palg_obj = NULL; @@ -157,7 +163,7 @@ static int decode_gost_algor_params(EVP_PKEY *pkey, const unsigned char *p; GOST_KEY_PARAMS *gkp = NULL; - if (!pkey || !palg) + if (!ec || !palg || !key_type) return 0; X509_ALGOR_get0(&palg_obj, &ptype, (const void **)&pval, palg); if (ptype != V_ASN1_SEQUENCE) { @@ -167,7 +173,9 @@ static int decode_gost_algor_params(EVP_PKEY *pkey, } p = pval->data; pkey_nid = OBJ_obj2nid(palg_obj); - + if (!internal_is_gost_pkey_nid(pkey_nid)) + return 0; + *key_type = pkey_nid; gkp = d2i_GOST_KEY_PARAMS(NULL, &p, pval->length); if (!gkp) { GOSTerr(GOST_F_DECODE_GOST_ALGOR_PARAMS, @@ -176,30 +184,24 @@ static int decode_gost_algor_params(EVP_PKEY *pkey, } param_nid = OBJ_obj2nid(gkp->key_params); GOST_KEY_PARAMS_free(gkp); - if (!EVP_PKEY_set_type(pkey, pkey_nid)) { - GOSTerr(GOST_F_DECODE_GOST_ALGOR_PARAMS, ERR_R_INTERNAL_ERROR); - return 0; - } - return gost_decode_nid_params(pkey, pkey_nid, param_nid); + + return fill_GOST_EC_params(ec, param_nid); } -static int gost_set_priv_key(EVP_PKEY *pkey, BIGNUM *priv) +static int gost_set_priv_key(EC_KEY *ec, BIGNUM *priv, int key_type) { - switch (EVP_PKEY_base_id(pkey)) { + switch (key_type) { case NID_id_GostR3410_2012_512: case NID_id_GostR3410_2012_256: case NID_id_GostR3410_2001: case NID_id_GostR3410_2001DH: { - EC_KEY *ec = EVP_PKEY_get0(pkey); - if (!ec) { - ec = EC_KEY_new(); - EVP_PKEY_assign(pkey, EVP_PKEY_base_id(pkey), ec); - } if (!EC_KEY_set_private_key(ec, priv)) return 0; - if (!EVP_PKEY_missing_parameters(pkey)) - return gost_ec_compute_public(ec); + if (!gost_ec_compute_public(ec)) + return 0; + if (!EC_KEY_check_key(ec)) + return 0; break; } default: @@ -692,12 +694,10 @@ static void pkey_free_gost_ec(EVP_PKEY *key) } /* ------------------ private key functions -----------------------------*/ - -static BIGNUM *unmask_priv_key(EVP_PKEY *pk, - const unsigned char *buf, int len, int num_masks) +static BIGNUM *internal_unmask_priv_key(const EC_KEY *key_ptr, + const unsigned char *buf, int len, int num_masks) { BIGNUM *pknum_masked = NULL, *q = NULL; - const EC_KEY *key_ptr = (pk) ? EVP_PKEY_get0(pk) : NULL; const EC_GROUP *group = (key_ptr) ? EC_KEY_get0_group(key_ptr) : NULL; pknum_masked = BN_lebin2bn(buf, len, BN_secure_new()); @@ -734,8 +734,8 @@ static BIGNUM *unmask_priv_key(EVP_PKEY *pk, return pknum_masked; } -static int priv_decode_gost(EVP_PKEY *pk, - const PKCS8_PRIV_KEY_INFO *p8inf) +int internal_priv_decode(EC_KEY *ec, int *key_type, + const PKCS8_PRIV_KEY_INFO *p8inf) { const unsigned char *pkey_buf = NULL, *p = NULL; int priv_len = 0; @@ -746,14 +746,14 @@ static int priv_decode_gost(EVP_PKEY *pk, ASN1_INTEGER *priv_key = NULL; int expected_key_len; + if (!key_type || !ec) + return 0; if (!PKCS8_pkey_get0(&palg_obj, &pkey_buf, &priv_len, &palg, p8inf)) return 0; p = pkey_buf; - if (!decode_gost_algor_params(pk, palg)) { + if (!decode_gost_algor_params(ec, key_type, palg)) return 0; - } - - expected_key_len = pkey_bits_gost(pk) > 0 ? pkey_bits_gost(pk) / 8 : 0; + expected_key_len = internal_pkey_bits(*key_type) > 0 ? internal_pkey_bits(*key_type) / 8 : 0; if (expected_key_len == 0) { GOSTerr(GOST_F_PRIV_DECODE_GOST, EVP_R_DECODE_ERROR); return 0; @@ -761,8 +761,8 @@ static int priv_decode_gost(EVP_PKEY *pk, if (priv_len % expected_key_len == 0) { /* Key is not wrapped but masked */ - pk_num = unmask_priv_key(pk, pkey_buf, expected_key_len, - priv_len / expected_key_len - 1); + pk_num = internal_unmask_priv_key(ec, pkey_buf, expected_key_len, + priv_len / expected_key_len - 1); } else if (V_ASN1_OCTET_STRING == *p) { /* New format - Little endian octet string */ ASN1_OCTET_STRING *s = d2i_ASN1_OCTET_STRING(NULL, &p, priv_len); @@ -796,9 +796,9 @@ static int priv_decode_gost(EVP_PKEY *pk, return 0; } - pk_num = unmask_priv_key(pk, mgk->masked_priv_key->data, - expected_key_len, - priv_len / expected_key_len - 1); + pk_num = internal_unmask_priv_key(ec, mgk->masked_priv_key->data, + expected_key_len, + priv_len / expected_key_len - 1); MASKED_GOST_KEY_free(mgk); } else { GOSTerr(GOST_F_PRIV_DECODE_GOST, EVP_R_DECODE_ERROR); @@ -810,32 +810,55 @@ static int priv_decode_gost(EVP_PKEY *pk, return 0; } - ret = gost_set_priv_key(pk, pk_num); + ret = gost_set_priv_key(ec, pk_num, *key_type); BN_free(pk_num); return ret; } -/* ----------------------------------------------------------------------*/ -static int priv_encode_gost(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pk) +static int priv_decode_gost(EVP_PKEY *pk, const PKCS8_PRIV_KEY_INFO *p8inf) +{ + int ret = 0; + int key_type = NID_undef; + EC_KEY *ec = NULL; + + ec = EC_KEY_new(); + if (!ec) + goto exit; + if (!internal_priv_decode(ec, &key_type, p8inf)) + goto exit; + if (!EVP_PKEY_assign(pk, key_type, ec)) + goto exit; + ret = 1; +exit: + if (!ret) + EC_KEY_free(ec); + return ret; +} + +/* ---------------------------------------------------------------------- */ +int internal_priv_encode(PKCS8_PRIV_KEY_INFO *p8, EC_KEY *ec, int key_type) { - ASN1_OBJECT *algobj = OBJ_nid2obj(EVP_PKEY_base_id(pk)); + ASN1_OBJECT *algobj = OBJ_nid2obj(key_type); ASN1_STRING *params = NULL; unsigned char *buf = NULL; - int key_len = pkey_bits_gost(pk), i = 0; + int key_len = internal_pkey_bits(key_type), i = 0; /* unmasked private key */ const char *pk_format = get_gost_engine_param(GOST_PARAM_PK_FORMAT); + if (!ec || !internal_is_gost_pkey_nid(key_type)) + return 0; + key_len = (key_len < 0) ? 0 : key_len / 8; if (key_len == 0 || !(buf = OPENSSL_secure_malloc(key_len))) { return 0; } - if (!store_bignum(gost_get0_priv_key(pk), buf, key_len)) { + if (!store_bignum(EC_KEY_get0_private_key(ec), buf, key_len)) { OPENSSL_secure_free(buf); return 0; } - params = encode_gost_algor_params(pk); + params = internal_encode_algor_params(ec, key_type); if (!params) { OPENSSL_secure_free(buf); return 0; @@ -870,76 +893,87 @@ static int priv_encode_gost(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pk) buf, key_len); } -/* --------- printing keys --------------------------------*/ -static int print_gost_priv(BIO *out, const EVP_PKEY *pkey, int indent) +static int priv_encode_gost(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pk) +{ + int key_type = (pk == NULL) ? NID_undef : EVP_PKEY_base_id(pk); + EC_KEY *ec = EVP_PKEY_get0((EVP_PKEY *)pk); + + return internal_priv_encode(p8, ec, key_type); +} + +/* --------- printing keys -------------------------------- */ +int internal_print_gost_priv(BIO *out, const EC_KEY *ec, int indent, int pkey_nid) { - BIGNUM *key; + const BIGNUM *key; if (!BIO_indent(out, indent, 128)) return 0; + BIO_printf(out, "Private key: "); - key = gost_get0_priv_key(pkey); - if (!key) + key = ec ? EC_KEY_get0_private_key(ec) : NULL; + if (!key || !internal_is_gost_pkey_nid(pkey_nid)) BIO_printf(out, ""); else BN_print(out, key); - BIO_printf(out, "\n"); + BIO_printf(out, "\n"); return 1; } -static int print_gost_ec_pub(BIO *out, const EVP_PKEY *pkey, int indent) +int internal_print_gost_ec_pub(BIO *out, const EC_KEY *ec, int indent, int pkey_nid) { - BN_CTX *ctx; - BIGNUM *X, *Y; - const EC_POINT *pubkey; - const EC_GROUP *group; - EC_KEY *key = (EC_KEY *)EVP_PKEY_get0((EVP_PKEY *)pkey); - int ok = 0; - - ctx = BN_CTX_new(); - if (!ctx) { - GOSTerr(GOST_F_PRINT_GOST_EC_PUB, ERR_R_MALLOC_FAILURE); + if (!ec) return 0; - } + if (!internal_is_gost_pkey_nid(pkey_nid)) + return 0; + + BN_CTX *ctx = BN_CTX_new(); + BIGNUM *X = NULL, *Y = NULL; + const EC_POINT *pubkey = EC_KEY_get0_public_key(ec); + const EC_GROUP *group = EC_KEY_get0_group(ec); + int ret = 0; + + if (!ctx || !pubkey || !group) + goto err; BN_CTX_start(ctx); X = BN_CTX_get(ctx); Y = BN_CTX_get(ctx); - pubkey = (key) ? EC_KEY_get0_public_key(key) : NULL; - group = (key) ? EC_KEY_get0_group(key) : NULL; - if (!pubkey || !group) + if (!X || !Y) goto err; - if (!EC_POINT_get_affine_coordinates(group, pubkey, X, Y, ctx)) { - GOSTerr(GOST_F_PRINT_GOST_EC_PUB, ERR_R_EC_LIB); + if (!EC_POINT_get_affine_coordinates(group, pubkey, X, Y, ctx)) goto err; - } + if (!BIO_indent(out, indent, 128)) goto err; BIO_printf(out, "Public key:\n"); + if (!BIO_indent(out, indent + 3, 128)) goto err; BIO_printf(out, "X:"); BN_print(out, X); BIO_printf(out, "\n"); + if (!BIO_indent(out, indent + 3, 128)) goto err; BIO_printf(out, "Y:"); BN_print(out, Y); BIO_printf(out, "\n"); - ok = 1; - err: + + ret = 1; +err: BN_CTX_end(ctx); BN_CTX_free(ctx); - - return ok; + return ret; } -static int print_gost_ec_param(BIO *out, const EVP_PKEY *pkey, int indent) +int internal_print_gost_ec_param(BIO *out, const EC_KEY *ec, int indent) { - EC_KEY *ec = EVP_PKEY_get0((EVP_PKEY *)pkey); - const EC_GROUP *group = (ec) ? EC_KEY_get0_group(ec) : NULL; + if (!ec) + return 0; + + const EC_GROUP *group = EC_KEY_get0_group(ec); int param_nid; if (!group) @@ -948,24 +982,24 @@ static int print_gost_ec_param(BIO *out, const EVP_PKEY *pkey, int indent) param_nid = EC_GROUP_get_curve_name(group); if (!BIO_indent(out, indent, 128)) return 0; - BIO_printf(out, "Parameter set: %s\n", OBJ_nid2ln(param_nid)); + BIO_printf(out, "Parameter set: %s\n", OBJ_nid2ln(param_nid)); return 1; } static int print_gost_ec(BIO *out, const EVP_PKEY *pkey, int indent, ASN1_PCTX *pctx, int type) { - if (type == 2) { - if (print_gost_priv(out, pkey, indent) == 0) - return 0; - } - if (type >= 1) { - if (print_gost_ec_pub(out, pkey, indent) == 0) - return 0; - } + const EC_KEY *ec = EVP_PKEY_get0((EVP_PKEY *)pkey); + int pkey_nid = EVP_PKEY_base_id(pkey); + + if (type == 2 && !internal_print_gost_priv(out, ec, indent, pkey_nid)) + return 0; + + if (type >= 1 && !internal_print_gost_ec_pub(out, ec, indent, pkey_nid)) + return 0; - return print_gost_ec_param(out, pkey, indent); + return internal_print_gost_ec_param(out, ec, indent); } static int param_print_gost_ec(BIO *out, const EVP_PKEY *pkey, int indent, @@ -1051,13 +1085,10 @@ static int param_cmp_gost_ec(const EVP_PKEY *a, const EVP_PKEY *b) } /* ---------- Public key functions * --------------------------------------*/ -static int pub_decode_gost_ec(EVP_PKEY *pk, const X509_PUBKEY *pub) +int internal_pub_decode_ec(EC_KEY *ec, int *key_type, X509_ALGOR *palg, + const unsigned char *pubkey_buf, int pub_len) { - X509_ALGOR *palg = NULL; - const unsigned char *pubkey_buf = NULL; unsigned char *databuf = NULL; - ASN1_OBJECT *palgobj = NULL; - int pub_len; EC_POINT *pub_key = NULL; BIGNUM *X = NULL, *Y = NULL; ASN1_OCTET_STRING *octet = NULL; @@ -1065,12 +1096,13 @@ static int pub_decode_gost_ec(EVP_PKEY *pk, const X509_PUBKEY *pub) const EC_GROUP *group; int retval = 0; - if (!X509_PUBKEY_get0_param(&palgobj, &pubkey_buf, &pub_len, &palg, pub)) + if (!key_type || !ec) goto ret; - EVP_PKEY_assign(pk, OBJ_obj2nid(palgobj), NULL); - if (!decode_gost_algor_params(pk, palg)) + + if (!decode_gost_algor_params(ec, key_type, palg)) goto ret; - group = EC_KEY_get0_group(EVP_PKEY_get0(pk)); + + group = EC_KEY_get0_group(ec); octet = d2i_ASN1_OCTET_STRING(NULL, &pubkey_buf, pub_len); if (!octet) { GOSTerr(GOST_F_PUB_DECODE_GOST_EC, ERR_R_MALLOC_FAILURE); @@ -1097,7 +1129,7 @@ static int pub_decode_gost_ec(EVP_PKEY *pk, const X509_PUBKEY *pub) goto ret; } - retval = EC_KEY_set_public_key(EVP_PKEY_get0(pk), pub_key); + retval = EC_KEY_set_public_key(ec, pub_key); if (!retval) GOSTerr(GOST_F_PUB_DECODE_GOST_EC, ERR_R_EC_LIB); @@ -1110,23 +1142,58 @@ ret: return retval; } -static int pub_encode_gost_ec(X509_PUBKEY *pub, const EVP_PKEY *pk) +static int pub_decode_gost_ec(EVP_PKEY *pk, const X509_PUBKEY *pub) { - ASN1_OBJECT *algobj; + int ret = 0; + int key_type = NID_undef; + EC_KEY *ec = NULL; + ASN1_OBJECT *palgobj = NULL; + const unsigned char *pubkey_buf = NULL; + int pubkey_len = 0; + X509_ALGOR *palg = NULL; + + if (!pub) + goto exit; + + ec = EC_KEY_new(); + if (!ec) + goto exit; + + if (!X509_PUBKEY_get0_param(&palgobj, &pubkey_buf, &pubkey_len, &palg, pub)) + goto exit; + + if (!internal_pub_decode_ec(ec, &key_type, palg, pubkey_buf, pubkey_len)) + goto exit; + + if (!EVP_PKEY_assign(pk, key_type, ec)) + goto exit; + ret = 1; +exit: + if (!ret) + EC_KEY_free(ec); + return ret; +} + +int internal_pub_encode_ec(X509_PUBKEY *pub, EC_KEY *ec, int key_type) +{ + ASN1_OBJECT *algobj = NULL; ASN1_OCTET_STRING *octet = NULL; - void *pval; unsigned char *buf = NULL, *databuf = NULL; - int data_len, ret = -1; + int data_len, buf_len, ret = 0; const EC_POINT *pub_key; - BIGNUM *X = NULL, *Y = NULL, *order; - const EC_KEY *ec = EVP_PKEY_get0((EVP_PKEY *)pk); + BIGNUM *X = NULL, *Y = NULL, *order = NULL; int ptype = V_ASN1_SEQUENCE; - ASN1_STRING *params; + ASN1_STRING *params = NULL; - algobj = OBJ_nid2obj(EVP_PKEY_base_id(pk)); + if (!ec) + goto err; - params = encode_gost_algor_params(pk); - pval = params; + algobj = OBJ_nid2obj(key_type); + params = internal_encode_algor_params(ec, key_type); + if (!params) { + GOSTerr(GOST_F_PUB_ENCODE_GOST_EC, ERR_R_INTERNAL_ERROR); + goto err; + } order = BN_new(); if (order == NULL || EC_GROUP_get_order(EC_KEY_get0_group(ec), order, NULL) == 0) { @@ -1176,8 +1243,23 @@ static int pub_encode_gost_ec(X509_PUBKEY *pub, const EVP_PKEY *pk) goto err; } - ret = i2d_ASN1_OCTET_STRING(octet, &buf); + buf_len = i2d_ASN1_OCTET_STRING(octet, &buf); + if (buf_len < 0) { + GOSTerr(GOST_F_PUB_ENCODE_GOST_EC, ERR_R_INTERNAL_ERROR); + goto err; + } + + ret = X509_PUBKEY_set0_param(pub, algobj, ptype, params, buf, buf_len); + if (!ret) { + GOSTerr(GOST_F_PUB_ENCODE_GOST_EC, ERR_R_INTERNAL_ERROR); + goto err; + } + err: + if (!ret) { + ASN1_STRING_free(params); + OPENSSL_free(buf); + } ASN1_BIT_STRING_free(octet); if (X) BN_free(X); @@ -1188,9 +1270,17 @@ static int pub_encode_gost_ec(X509_PUBKEY *pub, const EVP_PKEY *pk) if (databuf) OPENSSL_free(databuf); - if (ret < 0) + return ret; +} + +static int pub_encode_gost_ec(X509_PUBKEY *pub, const EVP_PKEY *pk) +{ + EC_KEY *ec = EVP_PKEY_get0((EVP_PKEY *)pk); + int key_type = (pk == NULL) ? NID_undef : EVP_PKEY_base_id(pk); + + if (!ec) return 0; - return X509_PUBKEY_set0_param(pub, algobj, ptype, pval, buf, ret); + return internal_pub_encode_ec(pub, ec, key_type); } static int pub_cmp_gost_ec(const EVP_PKEY *a, const EVP_PKEY *b) @@ -1279,26 +1369,54 @@ static int mac_ctrl_grasshopper(EVP_PKEY *pkey, int op, long arg1, void *arg2) return -2; } -static int gost2001_param_encode(const EVP_PKEY *pkey, unsigned char **pder) +int internal_gost2001_param_encode(const EC_KEY *ec, unsigned char **pder) { - int nid = - EC_GROUP_get_curve_name(EC_KEY_get0_group - (EVP_PKEY_get0((EVP_PKEY *)pkey))); + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + return i2d_ASN1_OBJECT(OBJ_nid2obj(nid), pder); } -static int gost2001_param_decode(EVP_PKEY *pkey, const unsigned char **pder, - int derlen) +static int gost2001_param_encode(const EVP_PKEY *pkey, unsigned char **pder) +{ + EC_KEY *ec = EVP_PKEY_get0(pkey); + + if (!ec) + return 0; + return internal_gost2001_param_encode(ec, pder); +} + +int internal_gost2001_param_decode(EC_KEY *ec, const unsigned char **pder, + int derlen) { ASN1_OBJECT *obj = NULL; - int nid; if (d2i_ASN1_OBJECT(&obj, pder, derlen) == NULL) { return 0; } - nid = OBJ_obj2nid(obj); + int nid = OBJ_obj2nid(obj); ASN1_OBJECT_free(obj); - return gost_decode_nid_params(pkey, NID_id_GostR3410_2001, nid); + return fill_GOST_EC_params(ec, nid); +} + +static int gost2001_param_decode(EVP_PKEY *pkey, const unsigned char **pder, + int derlen) +{ + int ret = 0; + int key_type = NID_id_GostR3410_2001; + EC_KEY *ec = NULL; + + ec = EC_KEY_new(); + if (!ec) + goto exit; + if (!internal_gost2001_param_decode(ec, pder, derlen)) + goto exit; + if (!EVP_PKEY_assign(pkey, key_type, ec)) + goto exit; + ret = 1; +exit: + if (!ret) + EC_KEY_free(ec); + return ret; } /* ----------------------------------------------------------------------*/ diff --git a/gost_crypt.c b/gost_crypt.c index be44595..fdd38bb 100644 --- a/gost_crypt.c +++ b/gost_crypt.c @@ -630,6 +630,7 @@ static int gost_magma_cipher_init_mgm(EVP_CIPHER_CTX *ctx, const unsigned char * if (!gost_cipher_set_param(&mctx->ks.g_ks, NID_id_tc26_gost_28147_param_Z)) return 0; magma_key(&(mctx->ks.g_ks.cctx), key); + magma_master_key(&(mctx->ks.g_ks.cctx), key); gost_mgm128_init(&mctx->mgm, &mctx->ks, (block128_f) gost_magma_encrypt_wrap, gf64_mul, bl); @@ -1109,6 +1110,7 @@ static int gost_magma_mgm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr) mctx->ivlen = ivlen; mctx->iv = iv; mctx->taglen = -1; + mctx->tlstree_mode = TLSTREE_MODE_NONE; return 1; case EVP_CTRL_GET_IVLEN: @@ -1148,6 +1150,30 @@ static int gost_magma_mgm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr) memcpy(ptr, buf, arg); return 1; + case EVP_CTRL_SET_TLSTREE_PARAMS: + if (strcmp((char *)ptr, "strong") == 0) + mctx->tlstree_mode = TLSTREE_MODE_S; + else if (strcmp((char *)ptr, "light") == 0) + mctx->tlstree_mode = TLSTREE_MODE_L; + else { + // TODO: set err + return 0; + } + return 1; + + case EVP_CTRL_TLSTREE: + { + unsigned char newkey[32]; + if (gost_tlstree(NID_magma_mgm, + (const unsigned char *)mctx->ks.g_ks.cctx.master_key, + newkey, (const unsigned char *)ptr, mctx->tlstree_mode) + > 0) { + magma_key(&mctx->ks.g_ks.cctx, newkey); + OPENSSL_cleanse(newkey, sizeof(newkey)); + } + } + return 1; + default: return -1; } @@ -1311,7 +1337,7 @@ static int magma_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr) } if (gost_tlstree(NID_magma_cbc, (const unsigned char *)c->master_key, newkey, - (const unsigned char *)seq) > 0) { + (const unsigned char *)seq, TLSTREE_MODE_NONE) > 0) { memset(adjusted_iv, 0, 8); memcpy(adjusted_iv, EVP_CIPHER_CTX_original_iv(ctx), 4); for (j = 3, carry = 0; j >= 0; j--) diff --git a/gost_ec_keyx.c b/gost_ec_keyx.c index 8839c4a..bc65899 100644 --- a/gost_ec_keyx.c +++ b/gost_ec_keyx.c @@ -23,104 +23,113 @@ #include "gost_keywrap.h" #include "gost_lcl.h" -/* Implementation of CryptoPro VKO 34.10-2001/2012 algorithm */ -int VKO_compute_key(unsigned char *shared_key, - const EC_POINT *pub_key, const EC_KEY *priv_key, - const unsigned char *ukm, const size_t ukm_size, - const int vko_dgst_nid) +int internal_compute_ecdh(unsigned char *out, size_t *out_len, + const unsigned char *ukm, size_t ukm_size, + const EC_POINT *pub_key, const EC_KEY *priv_key) { - unsigned char *databuf = NULL; - BIGNUM *scalar = NULL, *X = NULL, *Y = NULL; + BIGNUM *X = NULL, *Y = NULL; const EC_GROUP *grp = NULL; EC_POINT *pnt = NULL; BN_CTX *ctx = NULL; - EVP_MD_CTX *mdctx = NULL; - const EVP_MD *md = NULL; - int buf_len, half_len; int ret = 0; + int half_len; - if ((ctx = BN_CTX_secure_new()) == NULL) { - GOSTerr(GOST_F_VKO_COMPUTE_KEY, ERR_R_MALLOC_FAILURE); - return 0; + grp = EC_KEY_get0_group(priv_key); + half_len = BN_num_bytes(EC_GROUP_get0_field(grp)); + if (out == NULL) { + *out_len = 2 * half_len; + ret = 1; + goto exit; } - BN_CTX_start(ctx); - md = EVP_get_digestbynid(vko_dgst_nid); - if (!md) { - GOSTerr(GOST_F_VKO_COMPUTE_KEY, GOST_R_INVALID_DIGEST_TYPE); - goto err; - } + if (*out_len < 2 * half_len) + goto exit; - grp = EC_KEY_get0_group(priv_key); - scalar = BN_CTX_get(ctx); + if ((ctx = BN_CTX_secure_new()) == NULL) + goto exit; + + BN_CTX_start(ctx); X = BN_CTX_get(ctx); + Y = BN_CTX_get(ctx); + if (X == NULL || Y == NULL || (pnt = EC_POINT_new(grp)) == NULL) + goto exit; - if ((Y = BN_CTX_get(ctx)) == NULL - || (pnt = EC_POINT_new(grp)) == NULL - || BN_lebin2bn(ukm, ukm_size, scalar) == NULL - || !BN_mod_mul(scalar, scalar, EC_KEY_get0_private_key(priv_key), - EC_GROUP_get0_order(grp), ctx)) - goto err; + if (BN_lebin2bn(ukm, ukm_size, X) == NULL) + goto exit; -#if 0 - /*- - * These two curves have cofactor 4; the rest have cofactor 1. - * But currently gost_ec_point_mul takes care of the cofactor clearing, - * hence this code is not needed. - */ - switch (EC_GROUP_get_curve_name(grp)) { - case NID_id_tc26_gost_3410_2012_256_paramSetA: - case NID_id_tc26_gost_3410_2012_512_paramSetC: - if (!BN_lshift(scalar, scalar, 2)) - goto err; - break; - } -#endif + if (!BN_mod_mul(X, X, EC_KEY_get0_private_key(priv_key), EC_GROUP_get0_order(grp), ctx)) + goto exit; - if (!gost_ec_point_mul(grp, pnt, NULL, pub_key, scalar, ctx)) { + if (!gost_ec_point_mul(grp, pnt, NULL, pub_key, X, ctx)) { GOSTerr(GOST_F_VKO_COMPUTE_KEY, GOST_R_ERROR_POINT_MUL); - goto err; + goto exit; } if (!EC_POINT_get_affine_coordinates(grp, pnt, X, Y, ctx)) { GOSTerr(GOST_F_VKO_COMPUTE_KEY, ERR_R_EC_LIB); - goto err; + goto exit; } + if ((BN_bn2lebinpad(X, out, half_len) == -1) + || (BN_bn2lebinpad(Y, out + half_len, half_len) == -1)) + goto exit; - half_len = BN_num_bytes(EC_GROUP_get0_field(grp)); - buf_len = 2 * half_len; - if ((databuf = OPENSSL_malloc(buf_len)) == NULL) { + *out_len = 2 * half_len; + ret = 1; +exit: + BN_CTX_end(ctx); + BN_CTX_free(ctx); + EC_POINT_free(pnt); + return ret; +} + +/* Implementation of CryptoPro VKO 34.10-2001/2012 algorithm */ + +int VKO_compute_key(unsigned char *shared_key, + const EC_POINT *pub_key, const EC_KEY *priv_key, + const unsigned char *ukm, const size_t ukm_size, + const int vko_dgst_nid) +{ + unsigned char *ecdh_secret = NULL; + size_t secret_len = 0; + EVP_MD_CTX *mdctx = NULL; + const EVP_MD *md = NULL; + int ret = 0; + + md = EVP_get_digestbynid(vko_dgst_nid); + if (!md) { + GOSTerr(GOST_F_VKO_COMPUTE_KEY, GOST_R_INVALID_DIGEST_TYPE); + goto exit; + } + + if (internal_compute_ecdh(NULL, &secret_len, ukm, ukm_size, + pub_key, priv_key) == 0) + goto exit; + + if ((ecdh_secret = OPENSSL_malloc(secret_len)) == NULL) { GOSTerr(GOST_F_VKO_COMPUTE_KEY, ERR_R_MALLOC_FAILURE); - goto err; + goto exit; } - /* - * Serialize elliptic curve point same way as we do it when saving key - */ - if (BN_bn2lebinpad(X, databuf, half_len) != half_len - || BN_bn2lebinpad(Y, databuf + half_len, half_len) != half_len) - goto err; + if (internal_compute_ecdh(ecdh_secret, &secret_len, ukm, ukm_size, + pub_key, priv_key) == 0) + goto exit; if ((mdctx = EVP_MD_CTX_new()) == NULL) { GOSTerr(GOST_F_VKO_COMPUTE_KEY, ERR_R_MALLOC_FAILURE); - goto err; + goto exit; } - if (EVP_MD_CTX_init(mdctx) == 0 - || EVP_DigestInit_ex(mdctx, md, NULL) == 0 - || EVP_DigestUpdate(mdctx, databuf, buf_len) == 0 + if (EVP_DigestInit_ex(mdctx, md, NULL) == 0 + || EVP_DigestUpdate(mdctx, ecdh_secret, secret_len) == 0 || EVP_DigestFinal_ex(mdctx, shared_key, NULL) == 0) { GOSTerr(GOST_F_VKO_COMPUTE_KEY, ERR_R_EVP_LIB); - goto err; + goto exit; } ret = (EVP_MD_size(md) > 0) ? EVP_MD_size(md) : 0; +exit: - err: - BN_CTX_end(ctx); - BN_CTX_free(ctx); - EC_POINT_free(pnt); EVP_MD_CTX_free(mdctx); - OPENSSL_free(databuf); + OPENSSL_free(ecdh_secret); return ret; } diff --git a/gost_eng.c b/gost_eng.c index 4df83e8..ddb59d4 100644 --- a/gost_eng.c +++ b/gost_eng.c @@ -17,6 +17,7 @@ #include "e_gost_err.h" #include "gost_lcl.h" #include "gost-engine.h" +#include #include "gost_grasshopper_cipher.h" @@ -430,6 +431,16 @@ int populate_gost_engine(ENGINE* e) { if (!register_pmeth_gost(minfo->nid, minfo->pmeth, 0)) goto end; } + int i; + + for (i = 0; i < OSSL_NELEM(gost_digest_array); i++) { + const EVP_MD *md = GOST_init_digest(gost_digest_array[i]); + + if (!EVP_add_digest(md)) + goto end; + + assert(EVP_get_digestbynid(gost_digest_array[i]->nid) != NULL); + } ret = 1; end: diff --git a/gost_grasshopper_cipher.c b/gost_grasshopper_cipher.c index a213ac4..8f4cce6 100644 --- a/gost_grasshopper_cipher.c +++ b/gost_grasshopper_cipher.c @@ -485,6 +485,7 @@ gost_grasshopper_cipher_init_mgm(EVP_CIPHER_CTX *ctx, const unsigned char *key, if (key) { bl = EVP_CIPHER_CTX_iv_length(ctx); gost_grasshopper_cipher_key(&mctx->ks.gh_ks, key); + gost_grasshopper_master_key(&mctx->ks.gh_ks, key); gost_mgm128_init(&mctx->mgm, &mctx->ks, (block128_f) gost_grasshopper_encrypt_wrap, gf128_mul_uint64, bl); @@ -1050,6 +1051,7 @@ static int gost_grasshopper_mgm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void mctx->ivlen = ivlen; mctx->iv = iv; mctx->taglen = -1; + mctx->tlstree_mode = TLSTREE_MODE_NONE; return 1; case EVP_CTRL_GET_IVLEN: @@ -1089,6 +1091,30 @@ static int gost_grasshopper_mgm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void memcpy(ptr, buf, arg); return 1; + case EVP_CTRL_SET_TLSTREE_PARAMS: + if (strcmp((char *)ptr, "strong") == 0) + mctx->tlstree_mode = TLSTREE_MODE_S; + else if (strcmp((char *)ptr, "light") == 0) + mctx->tlstree_mode = TLSTREE_MODE_L; + else { + // TODO: set err + return 0; + } + return 1; + + case EVP_CTRL_TLSTREE: + { + unsigned char newkey[32]; + if (gost_tlstree(NID_kuznyechik_mgm, + mctx->ks.gh_ks.master_key.k.b, newkey, + (const unsigned char *)ptr, mctx->tlstree_mode) + > 0) { + gost_grasshopper_cipher_key(&mctx->ks.gh_ks, newkey); + OPENSSL_cleanse(newkey, sizeof(newkey)); + } + } + return 1; + default: return -1; } @@ -1149,7 +1175,7 @@ static int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg, v } if (gost_tlstree(NID_grasshopper_cbc, c->master_key.k.b, newkey, - (const unsigned char *)seq) > 0) { + (const unsigned char *)seq, TLSTREE_MODE_NONE) > 0) { memset(adjusted_iv, 0, 16); memcpy(adjusted_iv, EVP_CIPHER_CTX_original_iv(ctx), 8); for(j=7,carry=0; j>=0; j--) diff --git a/gost_keyexpimp.c b/gost_keyexpimp.c index e9da9b5..8f66d60 100644 --- a/gost_keyexpimp.c +++ b/gost_keyexpimp.c @@ -259,12 +259,8 @@ int gost_kdftree2012_256(unsigned char *keyout, size_t keyout_len, } int gost_tlstree(int cipher_nid, const unsigned char *in, unsigned char *out, - const unsigned char *tlsseq) + const unsigned char *tlsseq, int mode) { - uint64_t gh_c1 = 0x00000000FFFFFFFF, gh_c2 = 0x0000F8FFFFFFFFFF, - gh_c3 = 0xC0FFFFFFFFFFFFFF; - uint64_t mg_c1 = 0x00000000C0FFFFFF, mg_c2 = 0x000000FEFFFFFFFF, - mg_c3 = 0x00F0FFFFFFFFFFFF; uint64_t c1, c2, c3; uint64_t seed1, seed2, seed3; uint64_t seq; @@ -273,14 +269,46 @@ int gost_tlstree(int cipher_nid, const unsigned char *in, unsigned char *out, switch (cipher_nid) { case NID_magma_cbc: - c1 = mg_c1; - c2 = mg_c2; - c3 = mg_c3; + c1 = 0x00000000C0FFFFFF; + c2 = 0x000000FEFFFFFFFF; + c3 = 0x00F0FFFFFFFFFFFF; break; case NID_grasshopper_cbc: - c1 = gh_c1; - c2 = gh_c2; - c3 = gh_c3; + c1 = 0x00000000FFFFFFFF; + c2 = 0x0000F8FFFFFFFFFF; + c3 = 0xC0FFFFFFFFFFFFFF; + break; + case NID_magma_mgm: + switch (mode) { + case TLSTREE_MODE_S: // TLS_GOSTR341112_256_WITH_MAGMA_MGM_S + c1 = 0x000000fcffffffff; + c2 = 0x00e0ffffffffffff; + c3 = 0xffffffffffffffff; + break; + case TLSTREE_MODE_L: // TLS_GOSTR341112_256_WITH_MAGMA_MGM_L + c1 = 0x000000000000e0ff; + c2 = 0x000000c0ffffffff; + c3 = 0x80ffffffffffffff; + break; + default: + return 0; + } + break; + case NID_kuznyechik_mgm: + switch (mode) { + case TLSTREE_MODE_S: // TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S + c1 = 0x000000e0ffffffff; + c2 = 0x0000ffffffffffff; + c3 = 0xf8ffffffffffffff; + break; + case TLSTREE_MODE_L: // TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L + c1 = 0x00000000000000f8; + c2 = 0x00000000f0ffffff; + c3 = 0x00e0ffffffffffff; + break; + default: + return 0; + } break; default: return 0; diff --git a/gost_lcl.h b/gost_lcl.h index 21a5197..44621fd 100644 --- a/gost_lcl.h +++ b/gost_lcl.h @@ -20,6 +20,49 @@ # include # include "gost89.h" # include "gosthash.h" + +/* + * This definitions are added in the patch to OpenSSL 3.4.2 version to support + * GOST TLS 1.3. Definitions below must be removed when the patch is added to + * OpenSSL upstream. + */ +# ifndef EVP_CTRL_SET_TLSTREE_PARAMS +# if defined(_MSC_VER) +# pragma message("Gost-engine is built against not fully supported version of OpenSSL. \ +EVP_CTRL_SET_TLSTREE_PARAMS definition in OpenSSL is expected.") +# else +# warning "Gost-engine is built against not fully supported version of OpenSSL. \ +EVP_CTRL_SET_TLSTREE_PARAMS definition in OpenSSL is expected." +# endif +# define EVP_CTRL_SET_TLSTREE_PARAMS 0xFF +# endif + +# ifndef NID_magma_mgm +# if defined(_MSC_VER) +# pragma message("Gost-engine is built against not fully supported version of OpenSSL. \ +NID_magma_mgm definition in OpenSSL is expected. No magma mgm functionality is \ +guaranteed.") +# else +# warning "Gost-engine is built against not fully supported version of OpenSSL. \ +NID_magma_mgm definition in OpenSSL is expected. No magma mgm functionality is \ +guaranteed." +# endif +# define NID_magma_mgm ((int)(INT_MAX - 1)) +# endif + +# ifndef NID_kuznyechik_mgm +# if defined(_MSC_VER) +# pragma message("Gost-engine is built against not fully supported version of OpenSSL. \ +NID_kuznyechik_mgm definition in OpenSSL is expected. No magma mgm functionality is \ +guaranteed.") +# else +# warning "Gost-engine is built against not fully supported version of OpenSSL. \ +NID_kuznyechik_mgm definition in OpenSSL is expected. No kuznyechik mgm functionality is \ +guaranteed." +# endif +# define NID_kuznyechik_mgm ((int)(INT_MAX - 2)) +# endif + /* Control commands */ # define GOST_PARAM_CRYPT_PARAMS 0 # define GOST_PARAM_PBE_PARAMS 1 @@ -53,11 +96,6 @@ int gost_set_default_param(int param, const char *value); void gost_param_free(void); /* method registration */ - -/* Provider implementation data */ -extern const OSSL_ALGORITHM GOST_prov_macs[]; -void GOST_prov_deinit_mac_digests(void); - int register_ameth_gost(int nid, EVP_PKEY_ASN1_METHOD **ameth, const char *pemstr, const char *info); int register_pmeth_gost(int id, EVP_PKEY_METHOD **pmeth, int flags); @@ -75,6 +113,9 @@ int register_pmeth_gost(int id, EVP_PKEY_METHOD **pmeth, int flags); # define EVP_PKEY_CTRL_GOST_MAC_HEXKEY (EVP_PKEY_ALG_CTRL+3) # define EVP_PKEY_CTRL_MAC_LEN (EVP_PKEY_ALG_CTRL+5) # define EVP_PKEY_CTRL_SET_VKO (EVP_PKEY_ALG_CTRL+11) +# define TLSTREE_MODE_NONE 0 +# define TLSTREE_MODE_S 1 +# define TLSTREE_MODE_L 2 /* Pmeth internal representation */ struct gost_pmeth_data { int sign_param_nid; /* Should be set whenever parameters are @@ -290,7 +331,7 @@ int gost_kdftree2012_256(unsigned char *keyout, size_t keyout_len, const size_t representation); int gost_tlstree(int cipher_nid, const unsigned char *in, unsigned char *out, - const unsigned char *tlsseq); + const unsigned char *tlsseq, int mode); /* KExp/KImp */ int gost_kexp15(const unsigned char *shared_key, const int shared_len, int cipher_nid, const unsigned char *cipher_key, @@ -366,9 +407,6 @@ extern GOST_cipher grasshopper_ctr_acpkm_omac_cipher; extern GOST_cipher magma_kexp15_cipher; extern GOST_cipher kuznyechik_kexp15_cipher; -/* Provider implementation data */ -extern const OSSL_ALGORITHM GOST_prov_ciphers[]; -void GOST_prov_deinit_ciphers(void); struct gost_digest_st { struct gost_digest_st *template; @@ -391,6 +429,41 @@ typedef struct gost_digest_st GOST_digest; EVP_MD *GOST_init_digest(GOST_digest *d); void GOST_deinit_digest(GOST_digest *d); +/* Internal functions */ +EC_KEY * internal_ec_paramgen(int sign_param_nid); +int internal_ec_ctrl(struct gost_pmeth_data *pctx, int pkey_nid, + int type, int p1, void *p2); +int internal_ec_ctrl_str_common(struct gost_pmeth_data *ctx, int key_type, + const char *type, const char *value); +int internal_ec_ctrl_str_256(struct gost_pmeth_data *ctx, int key_type, + const char *type, const char *value); +int internal_ec_ctrl_str_512(struct gost_pmeth_data *ctx, int key_type, + const char *type, const char *value); +int internal_priv_decode(EC_KEY *ec, int *key_type, + const PKCS8_PRIV_KEY_INFO *p8inf); +int internal_priv_encode(PKCS8_PRIV_KEY_INFO *p8, + EC_KEY *ec, int key_type); +int internal_pub_decode_ec(EC_KEY *ec, int *key_type, X509_ALGOR *palg, + const unsigned char *pubkey_buf, int pub_len); +int internal_pub_encode_ec(X509_PUBKEY *pub, EC_KEY *ec, int key_type); +int internal_gost2001_param_decode(EC_KEY *ec, const unsigned char **pder, + int derlen); +int internal_gost2001_param_encode(const EC_KEY *ec, unsigned char **pder); +int internal_pkey_ec_cp_sign(EC_KEY *ec, int key_type, unsigned char *sig, + size_t *siglen, const unsigned char *tbs, + size_t tbs_len); +int internal_pkey_ec_cp_verify(EC_KEY *ec, const unsigned char *sig, + size_t siglen, const unsigned char *tbs, + size_t tbs_len); +int internal_param_str_to_nid_256(const char *value, int *param_nid_ptr); +int internal_param_str_to_nid_512(const char *value, int *param_nid_ptr); +int internal_compute_ecdh(unsigned char *out, size_t *out_len, + const unsigned char *ukm, const size_t ukm_size, + const EC_POINT *pub_key, const EC_KEY *priv_key); +int internal_print_gost_priv(BIO *out, const EC_KEY *ec, int indent, int pkey_nid); +int internal_print_gost_ec_pub(BIO *out, const EC_KEY *ec, int indent, int pkey_nid); +int internal_print_gost_ec_param(BIO *out, const EC_KEY *ec, int indent); + /* ENGINE implementation data */ extern GOST_digest GostR3411_94_digest; extern GOST_digest Gost28147_89_MAC_digest; @@ -402,9 +475,6 @@ extern GOST_digest grasshopper_mac_digest; extern GOST_digest kuznyechik_ctracpkm_omac_digest; extern GOST_digest magma_ctracpkm_omac_digest; -/* Provider implementation data */ -extern const OSSL_ALGORITHM GOST_prov_digests[]; -void GOST_prov_deinit_digests(void); /* job to initialize a missing NID */ struct gost_nid_job { diff --git a/gost_omac.c b/gost_omac.c index f97adec..370c979 100644 --- a/gost_omac.c +++ b/gost_omac.c @@ -243,7 +243,7 @@ int omac_imit_ctrl(EVP_MD_CTX *ctx, int type, int arg, void *ptr) int ret = 0; if (gost_tlstree(OBJ_txt2nid(c->cipher_name), c->key, diversed_key, - (const unsigned char *)ptr)) { + (const unsigned char *)ptr, TLSTREE_MODE_NONE)) { EVP_CIPHER *cipher; if ((cipher = (EVP_CIPHER *)EVP_get_cipherbyname(c->cipher_name)) || (cipher = EVP_CIPHER_fetch(NULL, c->cipher_name, NULL))) diff --git a/gost_pmeth.c b/gost_pmeth.c index bf7528c..8f8c057 100644 --- a/gost_pmeth.c +++ b/gost_pmeth.c @@ -86,20 +86,13 @@ static void pkey_gost_cleanup(EVP_PKEY_CTX *ctx) OPENSSL_free(data); } -/* --------------------- control functions ------------------------------*/ -static int pkey_gost_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) +/* --------------------- control functions internal ------------------------------ */ +int internal_ec_ctrl(struct gost_pmeth_data *pctx, int pkey_nid, + int type, int p1, void *p2) { - struct gost_pmeth_data *pctx = - (struct gost_pmeth_data *)EVP_PKEY_CTX_get_data(ctx); - if (pctx == NULL) - return 0; - switch (type) { case EVP_PKEY_CTRL_MD: { - EVP_PKEY *key = EVP_PKEY_CTX_get0_pkey(ctx); - int pkey_nid = (key == NULL) ? NID_undef : EVP_PKEY_base_id(key); - OPENSSL_assert(p2 != NULL); switch (EVP_MD_type((const EVP_MD *)p2)) { @@ -199,134 +192,154 @@ static int pkey_gost_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) return -2; } -static int pkey_gost_ec_ctrl_str_common(EVP_PKEY_CTX *ctx, - const char *type, const char *value) +int internal_ec_ctrl_str_common(struct gost_pmeth_data *ctx, int key_type, + const char *type, const char *value) { - if (0 == strcmp(type, ukm_ctrl_string)) { - unsigned char ukm_buf[32], *tmp = NULL; - long len = 0; - tmp = OPENSSL_hexstr2buf(value, &len); - if (tmp == NULL) - return 0; - - if (len > 32) { - OPENSSL_free(tmp); - GOSTerr(GOST_F_PKEY_GOST_EC_CTRL_STR_COMMON, GOST_R_CTRL_CALL_FAILED); - return 0; + if (strcmp(type, ukm_ctrl_string) == 0) { + unsigned char ukm_buf[32], *tmp = NULL; + long len = 0; + + tmp = OPENSSL_hexstr2buf(value, &len); + if (tmp == NULL) + return 0; + if (len > 32) { + OPENSSL_free(tmp); + GOSTerr(GOST_F_PKEY_GOST_EC_CTRL_STR_COMMON, GOST_R_CTRL_CALL_FAILED); + return 0; + } + memcpy(ukm_buf, tmp, len); + OPENSSL_free(tmp); + return internal_ec_ctrl(ctx, key_type, EVP_PKEY_CTRL_SET_IV, len, ukm_buf); + } else if (strcmp(type, vko_ctrl_string) == 0) { + int bits = atoi(value); + int vko_dgst_nid = 0; + + if (bits == 256) { + vko_dgst_nid = NID_id_GostR3411_2012_256; + } else if (bits == 512) { + vko_dgst_nid = NID_id_GostR3411_2012_512; + } else if (bits != 0) { + GOSTerr(GOST_F_PKEY_GOST_EC_CTRL_STR_COMMON, GOST_R_INVALID_DIGEST_TYPE); + return 0; + } + return internal_ec_ctrl(ctx, key_type, EVP_PKEY_CTRL_SET_VKO, vko_dgst_nid, NULL); } - memcpy(ukm_buf, tmp, len); - OPENSSL_free(tmp); - - return pkey_gost_ctrl(ctx, EVP_PKEY_CTRL_SET_IV, len, ukm_buf); - } else if (strcmp(type, vko_ctrl_string) == 0) { - int bits = atoi(value); - int vko_dgst_nid = 0; - - if (bits == 256) - vko_dgst_nid = NID_id_GostR3411_2012_256; - else if (bits == 512) - vko_dgst_nid = NID_id_GostR3411_2012_512; - else if (bits != 0) { - GOSTerr(GOST_F_PKEY_GOST_EC_CTRL_STR_COMMON, GOST_R_INVALID_DIGEST_TYPE); - return 0; - } - return pkey_gost_ctrl(ctx, EVP_PKEY_CTRL_SET_VKO, vko_dgst_nid, NULL); - } - return -2; + return -2; } -static int pkey_gost_ec_ctrl_str_256(EVP_PKEY_CTX *ctx, - const char *type, const char *value) +int internal_ec_ctrl_str_256(struct gost_pmeth_data *ctx, int key_type, + const char *type, const char *value) { - if (strcmp(type, param_ctrl_string) == 0) { - int param_nid = 0; + if (strcmp(type, param_ctrl_string)) + return internal_ec_ctrl_str_common(ctx, key_type, type, value); + + int param_nid = NID_undef; - if (!value) { + if (!internal_param_str_to_nid_256(value, ¶m_nid)) + return 0; + + return internal_ec_ctrl(ctx, key_type, EVP_PKEY_CTRL_GOST_PARAMSET, + param_nid, NULL); +} + +int internal_param_str_to_nid_256(const char *value, int *param_nid_ptr) +{ + if (!value) + return 0; + + int param_nid = NID_undef; + + if (strlen(value) == 1) { + switch (toupper((unsigned char)value[0])) { + case 'A': + param_nid = NID_id_GostR3410_2001_CryptoPro_A_ParamSet; + break; + case 'B': + param_nid = NID_id_GostR3410_2001_CryptoPro_B_ParamSet; + break; + case 'C': + param_nid = NID_id_GostR3410_2001_CryptoPro_C_ParamSet; + break; + case '0': + param_nid = NID_id_GostR3410_2001_TestParamSet; + break; + default: + return 0; + } + } else if ((strlen(value) == 2) + && (toupper((unsigned char)value[0]) == 'X')) { + switch (toupper((unsigned char)value[1])) { + case 'A': + param_nid = NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet; + break; + case 'B': + param_nid = NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet; + break; + default: return 0; } - if (strlen(value) == 1) { - switch (toupper((unsigned char)value[0])) { - case 'A': - param_nid = NID_id_GostR3410_2001_CryptoPro_A_ParamSet; - break; - case 'B': - param_nid = NID_id_GostR3410_2001_CryptoPro_B_ParamSet; - break; - case 'C': - param_nid = NID_id_GostR3410_2001_CryptoPro_C_ParamSet; - break; - case '0': - param_nid = NID_id_GostR3410_2001_TestParamSet; - break; - default: - return 0; - } - } else if ((strlen(value) == 2) - && (toupper((unsigned char)value[0]) == 'X')) { - switch (toupper((unsigned char)value[1])) { - case 'A': - param_nid = NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet; - break; - case 'B': - param_nid = NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet; - break; - default: - return 0; - } } else if ((strlen(value) == 3) && (toupper((unsigned char)value[0]) == 'T') && (toupper((unsigned char)value[1]) == 'C')) { - switch (toupper((unsigned char)value[2])) { - case 'A': - param_nid = NID_id_tc26_gost_3410_2012_256_paramSetA; - break; - case 'B': - param_nid = NID_id_tc26_gost_3410_2012_256_paramSetB; - break; - case 'C': - param_nid = NID_id_tc26_gost_3410_2012_256_paramSetC; - break; - case 'D': - param_nid = NID_id_tc26_gost_3410_2012_256_paramSetD; - break; - default: - return 0; - } - } else { - R3410_ec_params *p = R3410_2001_paramset; - param_nid = OBJ_txt2nid(value); - if (param_nid == NID_undef) { - return 0; - } - for (; p->nid != NID_undef; p++) { - if (p->nid == param_nid) - break; - } - if (p->nid == NID_undef) { - GOSTerr(GOST_F_PKEY_GOST_EC_CTRL_STR_256, - GOST_R_INVALID_PARAMSET); - return 0; - } + switch (toupper((unsigned char)value[2])) { + case 'A': + param_nid = NID_id_tc26_gost_3410_2012_256_paramSetA; + break; + case 'B': + param_nid = NID_id_tc26_gost_3410_2012_256_paramSetB; + break; + case 'C': + param_nid = NID_id_tc26_gost_3410_2012_256_paramSetC; + break; + case 'D': + param_nid = NID_id_tc26_gost_3410_2012_256_paramSetD; + break; + default: + return 0; } + } else { + R3410_ec_params *p = R3410_2001_paramset; - return pkey_gost_ctrl(ctx, EVP_PKEY_CTRL_GOST_PARAMSET, - param_nid, NULL); + param_nid = OBJ_txt2nid(value); + if (param_nid == NID_undef) + return 0; + + for (; p->nid != NID_undef; p++) { + if (p->nid == param_nid) + break; + } + if (p->nid == NID_undef) { + GOSTerr(GOST_F_PKEY_GOST_EC_CTRL_STR_256, + GOST_R_INVALID_PARAMSET); + return 0; + } } - return pkey_gost_ec_ctrl_str_common(ctx, type, value); + *param_nid_ptr = param_nid; + + return 1; } -static int pkey_gost_ec_ctrl_str_512(EVP_PKEY_CTX *ctx, - const char *type, const char *value) +int internal_ec_ctrl_str_512(struct gost_pmeth_data *ctx, int key_type, + const char *type, const char *value) { + if (strcmp(type, param_ctrl_string)) + return internal_ec_ctrl_str_common(ctx, key_type, type, value); + int param_nid = NID_undef; + if (!internal_param_str_to_nid_512(value, ¶m_nid)) + return 0; - if (strcmp(type, param_ctrl_string)) - return pkey_gost_ec_ctrl_str_common(ctx, type, value); + return internal_ec_ctrl(ctx, key_type, EVP_PKEY_CTRL_GOST_PARAMSET, param_nid, NULL); +} +int internal_param_str_to_nid_512(const char *value, int *param_nid_ptr) +{ if (!value) return 0; + int param_nid = NID_undef; + if (strlen(value) == 1) { switch (toupper((unsigned char)value[0])) { case 'A': @@ -360,7 +373,48 @@ static int pkey_gost_ec_ctrl_str_512(EVP_PKEY_CTX *ctx, } } - return pkey_gost_ctrl(ctx, EVP_PKEY_CTRL_GOST_PARAMSET, param_nid, NULL); + *param_nid_ptr = param_nid; + + return 1; +} + +/* --------------------- control functions pkey ------------------------------ */ +static int pkey_gost_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) +{ + struct gost_pmeth_data *pctx = + (struct gost_pmeth_data *)EVP_PKEY_CTX_get_data(ctx); + + if (pctx == NULL) + return 0; + EVP_PKEY *key = EVP_PKEY_CTX_get0_pkey(ctx); + int key_type = (key == NULL) ? NID_undef : EVP_PKEY_base_id(key); + return internal_ec_ctrl(pctx, key_type, type, p1, p2); +} + +static int pkey_gost_ec_ctrl_str_256(EVP_PKEY_CTX *ctx, + const char *type, const char *value) +{ + struct gost_pmeth_data *pctx = + (struct gost_pmeth_data *)EVP_PKEY_CTX_get_data(ctx); + + if (pctx == NULL) + return 0; + EVP_PKEY *key = EVP_PKEY_CTX_get0_pkey(ctx); + int key_type = (key == NULL) ? NID_undef : EVP_PKEY_base_id(key); + return internal_ec_ctrl_str_256(pctx, key_type, type, value); +} + +static int pkey_gost_ec_ctrl_str_512(EVP_PKEY_CTX *ctx, + const char *type, const char *value) +{ + struct gost_pmeth_data *pctx = + (struct gost_pmeth_data *)EVP_PKEY_CTX_get_data(ctx); + + if (pctx == NULL) + return 0; + EVP_PKEY *key = EVP_PKEY_CTX_get0_pkey(ctx); + int key_type = (key == NULL) ? NID_undef : EVP_PKEY_base_id(key); + return internal_ec_ctrl_str_512(pctx, key_type, type, value); } /* --------------------- key generation --------------------------------*/ @@ -370,6 +424,18 @@ static int pkey_gost_paramgen_init(EVP_PKEY_CTX *ctx) return 1; } +EC_KEY *internal_ec_paramgen(int sign_param_nid) +{ + EC_KEY *ec = EC_KEY_new(); + + if (!fill_GOST_EC_params(ec, sign_param_nid)) { + EC_KEY_free(ec); + return NULL; + } + + return ec; +} + static int pkey_gost2001_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) { struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx); @@ -380,9 +446,12 @@ static int pkey_gost2001_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) return 0; } - ec = EC_KEY_new(); - if (!fill_GOST_EC_params(ec, data->sign_param_nid) - || !EVP_PKEY_assign(pkey, NID_id_GostR3410_2001, ec)) { + ec = internal_ec_paramgen(data->sign_param_nid); + + if (!ec) + return 0; + + if (!EVP_PKEY_assign(pkey, NID_id_GostR3410_2001, ec)) { EC_KEY_free(ec); return 0; } @@ -400,11 +469,10 @@ static int pkey_gost2012_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) return 0; } - ec = EC_KEY_new(); - if (!fill_GOST_EC_params(ec, data->sign_param_nid)) { - EC_KEY_free(ec); + ec = internal_ec_paramgen(data->sign_param_nid); + + if (!ec) return 0; - } switch (data->sign_param_nid) { case NID_id_tc26_gost_3410_2012_512_paramSetA: @@ -478,20 +546,17 @@ int pack_sign_cp(ECDSA_SIG *s, int order, unsigned char *sig, size_t *siglen) return 1; } -static int pkey_gost_ec_cp_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, - size_t *siglen, const unsigned char *tbs, - size_t tbs_len) +int internal_pkey_ec_cp_sign(EC_KEY *ec, int key_type, unsigned char *sig, + size_t *siglen, const unsigned char *tbs, + size_t tbs_len) { ECDSA_SIG *unpacked_sig = NULL; - EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx); int order = 0; - if (!siglen) - return 0; - if (!pkey) + if (!siglen || !ec) return 0; - switch (EVP_PKEY_base_id(pkey)) { + switch (key_type) { case NID_id_GostR3410_2001: case NID_id_GostR3410_2001DH: case NID_id_GostR3410_2012_256: @@ -508,14 +573,35 @@ static int pkey_gost_ec_cp_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, *siglen = order; return 1; } - unpacked_sig = gost_ec_sign(tbs, tbs_len, EVP_PKEY_get0(pkey)); + + if (*siglen < order) + return 0; + + unpacked_sig = gost_ec_sign(tbs, tbs_len, ec); if (!unpacked_sig) { return 0; } return pack_sign_cp(unpacked_sig, order / 2, sig, siglen); } -/* ------------------- verify callbacks ---------------------------*/ +static int pkey_gost_ec_cp_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, + size_t *siglen, const unsigned char *tbs, + size_t tbs_len) +{ + EVP_PKEY *pkey = NULL; + EC_KEY *ec = NULL; + int key_type = NID_undef; + + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + ec = EVP_PKEY_get0(pkey); + key_type = (pkey == NULL) ? NID_undef : EVP_PKEY_base_id(pkey); + if (!pkey || !ec) + return 0; + + return internal_pkey_ec_cp_sign(ec, key_type, sig, siglen, tbs, tbs_len); +} + +/* ------------------- verify callbacks --------------------------- */ /* Unpack signature according to cryptopro rules */ ECDSA_SIG *unpack_cp_signature(const unsigned char *sigbuf, size_t siglen) { @@ -533,15 +619,20 @@ ECDSA_SIG *unpack_cp_signature(const unsigned char *sigbuf, size_t siglen) return sig; } -static int pkey_gost_ec_cp_verify(EVP_PKEY_CTX *ctx, const unsigned char *sig, - size_t siglen, const unsigned char *tbs, - size_t tbs_len) +int internal_pkey_ec_cp_verify(EC_KEY *ec, const unsigned char *sig, + size_t siglen, const unsigned char *tbs, + size_t tbs_len) { int ok = 0; - EVP_PKEY *pub_key = EVP_PKEY_CTX_get0_pkey(ctx); - ECDSA_SIG *s = (sig) ? unpack_cp_signature(sig, siglen) : NULL; + ECDSA_SIG *s = NULL; + + if (!ec) + return 0; + + s = (sig) ? unpack_cp_signature(sig, siglen) : NULL; if (!s) return 0; + #ifdef DEBUG_SIGN fprintf(stderr, "R="); BN_print_fp(stderr, ECDSA_SIG_get0_r(s)); @@ -549,13 +640,27 @@ static int pkey_gost_ec_cp_verify(EVP_PKEY_CTX *ctx, const unsigned char *sig, BN_print_fp(stderr, ECDSA_SIG_get0_s(s)); fprintf(stderr, "\n"); #endif - if (pub_key) - ok = gost_ec_verify(tbs, tbs_len, s, EVP_PKEY_get0(pub_key)); + ok = gost_ec_verify(tbs, tbs_len, s, ec); ECDSA_SIG_free(s); return ok; } -/* ------------- encrypt init -------------------------------------*/ +static int pkey_gost_ec_cp_verify(EVP_PKEY_CTX *ctx, const unsigned char *sig, + size_t siglen, const unsigned char *tbs, + size_t tbs_len) +{ + EVP_PKEY *pkey = NULL; + EC_KEY *ec = NULL; + + pkey = EVP_PKEY_CTX_get0_pkey(ctx); + ec = EVP_PKEY_get0(pkey); + if (!pkey || !ec) + return 0; + + return internal_pkey_ec_cp_verify(ec, sig, siglen, tbs, tbs_len); +} + +/* ------------- encrypt init ------------------------------------- */ /* Generates ephermeral key */ static int pkey_gost_encrypt_init(EVP_PKEY_CTX *ctx) { diff --git a/gost_prov.c b/gost_prov.c index eab5918..4ae77a1 100644 --- a/gost_prov.c +++ b/gost_prov.c @@ -10,6 +10,7 @@ #include #include #include "gost_prov.h" +#include "gost_prov_tls.h" #include "gost_lcl.h" #include "prov/err.h" /* libprov err functions */ @@ -60,7 +61,7 @@ static PROV_CTX *provider_ctx_new(const OSSL_CORE_HANDLE *core, if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL && (ctx->proverr_handle = proverr_new_handle(core, in)) != NULL - && (ctx->libctx = OSSL_LIB_CTX_new()) != NULL + && (ctx->libctx = OSSL_LIB_CTX_new_child(core, in)) != NULL && (ctx->e = ENGINE_new()) != NULL && populate_gost_engine(ctx->e)) { ctx->core_handle = core; @@ -94,6 +95,16 @@ static const OSSL_ALGORITHM *gost_operation(void *vprovctx, return GOST_prov_digests; case OSSL_OP_MAC: return GOST_prov_macs; + case OSSL_OP_KEYMGMT: + return GOST_prov_keymgmt; + case OSSL_OP_ENCODER: + return GOST_prov_encoder; + case OSSL_OP_SIGNATURE: + return GOST_prov_signature; + case OSSL_OP_DECODER: + return GOST_prov_decoder; + case OSSL_OP_KEYEXCH: + return GOST_prov_keyexch; } return NULL; } @@ -129,20 +140,31 @@ static void gost_teardown(void *vprovctx) provider_ctx_free(vprovctx); } +int gost_prov_get_capabilities(void *provctx, const char *capability, + OSSL_CALLBACK *cb, void *arg) +{ + if (!cb) + return 0; + + if (OPENSSL_strcasecmp(capability, "TLS-GROUP") == 0) + return gost_prov_get_tls_group_capability(cb, arg); + + if (OPENSSL_strcasecmp(capability, "TLS-SIGALG") == 0) + return gost_prov_get_tls_sigalg_capability(cb, arg); + + return 0; +} + /* The base dispatch table */ static const OSSL_DISPATCH provider_functions[] = { { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (fptr_t)gost_operation }, { OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, (fptr_t)gost_get_reason_strings }, { OSSL_FUNC_PROVIDER_GET_PARAMS, (fptr_t)gost_get_params }, { OSSL_FUNC_PROVIDER_TEARDOWN, (fptr_t)gost_teardown }, + { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, (fptr_t)gost_prov_get_capabilities }, { 0, NULL } }; -struct prov_ctx_st { - void *core_handle; - struct proverr_functions_st *err_handle; -}; - #ifdef BUILDING_PROVIDER_AS_LIBRARY /* * This allows the provider to be built in library form. In this case, the diff --git a/gost_prov.h b/gost_prov.h index c7f6e66..b86c509 100644 --- a/gost_prov.h +++ b/gost_prov.h @@ -1,3 +1,5 @@ +#pragma once + /********************************************************************** * gost_prov.h - The provider itself * * * @@ -10,6 +12,44 @@ #include #include +/* OID constants for GOST algorithms */ +#define OID_id_GostR3410_2001 "1.2.643.2.2.19" +#define OID_id_GostR3410_2001DH "1.2.643.2.2.98" +#define OID_id_GostR3410_2012_256 "1.2.643.7.1.1.1.1" +#define OID_id_GostR3410_2012_512 "1.2.643.7.1.1.1.2" +#define OID_id_GostR3411_94_with_GostR3410_2001 "1.2.643.2.2.3" +#define OID_id_tc26_signwithdigest_gost3410_2012_256 "1.2.643.7.1.1.3.2" +#define OID_id_tc26_signwithdigest_gost3410_2012_512 "1.2.643.7.1.1.3.3" + +/* Algorithm name constants for initializing OSSL_ALGORITHM */ +#define ALG_NAME_GOST2001 \ + SN_id_GostR3410_2001 ":" \ + LN_id_GostR3410_2001 ":" \ + OID_id_GostR3410_2001 + +#define ALG_NAME_GOST2001DH \ + SN_id_GostR3410_2001DH ":" \ + LN_id_GostR3410_2001DH ":" \ + OID_id_GostR3410_2001DH + +#define ALG_NAME_GOST2012_256 \ + SN_id_GostR3410_2012_256 ":" \ + LN_id_GostR3410_2012_256 ":" \ + OID_id_GostR3410_2012_256 + +#define ALG_NAME_GOST2012_512 \ + SN_id_GostR3410_2012_512 ":" \ + LN_id_GostR3410_2012_512 ":" \ + OID_id_GostR3410_2012_512 + +/* Utilities for checking and working with bit flags */ +#define FLAGS_CONTAIN(flags, subset) (((flags)&(subset)) == (subset)) +#define FLAGS_INTERSECT(flags, subset) (((flags)&(subset)) != 0) + +OSSL_FUNC_keymgmt_dup_fn keymgmt_dup; +OSSL_FUNC_keymgmt_free_fn keymgmt_free; +OSSL_FUNC_keymgmt_match_fn keymgmt_match; + struct provider_ctx_st { OSSL_LIB_CTX *libctx; const OSSL_CORE_HANDLE *core_handle; @@ -25,3 +65,26 @@ struct provider_ctx_st { ENGINE *e; }; typedef struct provider_ctx_st PROV_CTX; + +typedef struct gost_key_data_st +{ + EC_KEY *ec; + int type; + int param_nid; +} GOST_KEY_DATA; + +int gost_get_max_keyexch_size(const GOST_KEY_DATA *); +int gost_get_max_signature_size(const GOST_KEY_DATA *); + +void GOST_prov_deinit_mac_digests(void); +void GOST_prov_deinit_ciphers(void); +void GOST_prov_deinit_digests(void); + +extern const OSSL_ALGORITHM GOST_prov_macs[]; +extern const OSSL_ALGORITHM GOST_prov_ciphers[]; +extern const OSSL_ALGORITHM GOST_prov_digests[]; +extern const OSSL_ALGORITHM GOST_prov_keymgmt[]; +extern const OSSL_ALGORITHM GOST_prov_encoder[]; +extern const OSSL_ALGORITHM GOST_prov_signature[]; +extern const OSSL_ALGORITHM GOST_prov_decoder[]; +extern const OSSL_ALGORITHM GOST_prov_keyexch[]; diff --git a/gost_prov_cipher.c b/gost_prov_cipher.c index ce9665e..ce688df 100644 --- a/gost_prov_cipher.c +++ b/gost_prov_cipher.c @@ -14,6 +14,35 @@ #include "gost_prov.h" #include "gost_lcl.h" +/* + * This definitions are added in the patch to OpenSSL 3.4.2 version to support + * GOST TLS 1.3. Definitions below must be removed when the patch is added to + * OpenSSL upstream. + */ +#ifndef OSSL_CIPHER_PARAM_TLSTREE +# if defined(_MSC_VER) +# pragma message("Gost-engine is built against not fully supported version of OpenSSL. \ +OSSL_CIPHER_PARAM_TLSTREE definition in OpenSSL is expected.") +# else +# warning "Gost-engine is built against not fully supported version of OpenSSL. \ +NID_kuznyechik_mgm definition in OpenSSL is expected. No kuznyechik mgm functionality is \ +guaranteed." +# endif +# define OSSL_CIPHER_PARAM_TLSTREE "tlstree" +#endif + +#ifndef OSSL_CIPHER_PARAM_TLSTREE_MODE +# if defined(_MSC_VER) +# pragma message("Gost-engine is built against not fully supported version of OpenSSL. \ +OSSL_CIPHER_PARAM_TLSTREE_MODE definition in OpenSSL is expected.") +# else +# warning "Gost-engine is built against not fully supported version of OpenSSL. \ +NID_kuznyechik_mgm definition in OpenSSL is expected. No kuznyechik mgm functionality is \ +guaranteed." +# endif +# define OSSL_CIPHER_PARAM_TLSTREE_MODE "tlstree_mode" +#endif + /* * Forward declarations of all generic OSSL_DISPATCH functions, to make sure * they are correctly defined further down. For the algorithm specific ones @@ -106,7 +135,11 @@ static int cipher_get_params(EVP_CIPHER *c, OSSL_PARAM params[]) || ((p = OSSL_PARAM_locate(params, "keylen")) != NULL && !OSSL_PARAM_set_size_t(p, EVP_CIPHER_key_length(c))) || ((p = OSSL_PARAM_locate(params, "mode")) != NULL - && !OSSL_PARAM_set_size_t(p, EVP_CIPHER_flags(c)))) + && !OSSL_PARAM_set_size_t(p, EVP_CIPHER_flags(c))) + || ((p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD)) != NULL + && (strcmp(EVP_CIPHER_name(c), "magma-mgm") == 0 + || strcmp(EVP_CIPHER_name(c), "kuznyechik-mgm") == 0) + && !OSSL_PARAM_set_size_t(p, 1))) return 0; return 1; } @@ -204,6 +237,24 @@ static int cipher_set_ctx_params(void *vgctx, const OSSL_PARAM params[]) taglen, &tag) <= 0) return 0; } + if ((p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLSTREE)) != NULL) { + const void *val = NULL; + size_t arg = 0; + + if (!OSSL_PARAM_get_octet_string_ptr(p, &val, &arg) + || EVP_CIPHER_CTX_ctrl(gctx->cctx, EVP_CTRL_TLSTREE, + arg, (void *)val) <= 0) + return 0; + } + if ((p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLSTREE_MODE)) != NULL) { + const void *val = NULL; + size_t arg = 0; + + if (!OSSL_PARAM_get_octet_string_ptr(p, &val, &arg) + || EVP_CIPHER_CTX_ctrl(gctx->cctx, EVP_CTRL_SET_TLSTREE_PARAMS, + arg, (void *)val) <= 0) + return 0; + } return 1; } diff --git a/gost_prov_decoder.c b/gost_prov_decoder.c new file mode 100644 index 0000000..963761f --- /dev/null +++ b/gost_prov_decoder.c @@ -0,0 +1,317 @@ +#include +#include +#include "gost_prov.h" +#include "gost_lcl.h" +#include +#include + +/* + * Forward declarations of all generic OSSL_DISPATCH functions, to make sure + * they are correctly defined further down. For the structure specific ones + * MAKE_DECODER_FUNCTIONS() does it for us. + */ +static OSSL_FUNC_decoder_newctx_fn decoder_newctx; +static OSSL_FUNC_decoder_freectx_fn decoder_freectx; + +typedef struct { + PROV_CTX *provctx; +} GOST_DECODER_CTX; + +typedef int (st2ec_fn)(EC_KEY *ec, int *key_type, const void *st); +typedef void *(bio2st_fn)(BIO *bio, void **st); +typedef void (st_free_fn)(void *st); + +typedef struct { + X509_ALGOR *algor; + ASN1_BIT_STRING *public_key; +} x509_pubkey_st; + +typedef struct { + int key_type; + unsigned char *data; + long int data_size; +} params_st; + +ASN1_NDEF_SEQUENCE(x509_pubkey_st) = { + ASN1_SIMPLE(x509_pubkey_st, algor, X509_ALGOR), + ASN1_SIMPLE(x509_pubkey_st, public_key, ASN1_BIT_STRING) +} ASN1_NDEF_SEQUENCE_END(x509_pubkey_st) + +IMPLEMENT_ASN1_FUNCTIONS(x509_pubkey_st) + +static st2ec_fn pkcs8_decode_wrapper; +static bio2st_fn pkcs8_read_bio_der_wrapper; +static st_free_fn pkcs8_free_wrapper; + +static st2ec_fn x509_pub_decode_wrapper; +static bio2st_fn x509_pub_read_bio_der_wrapper; +static st_free_fn x509_pub_free_wrapper; + +static st2ec_fn param_decode_wrapper; +static bio2st_fn param_read_bio_pem_wrapper; +static st_free_fn param_free_wrapper; + +static int pkcs8_decode_wrapper(EC_KEY *ec, int *key_type, const void *st) +{ + return internal_priv_decode(ec, key_type, (PKCS8_PRIV_KEY_INFO *)st); +} +static void *pkcs8_read_bio_der_wrapper(BIO *bio, void **st) +{ + return d2i_PKCS8_PRIV_KEY_INFO_bio(bio, (PKCS8_PRIV_KEY_INFO **)st); +} +static void pkcs8_free_wrapper(void *st) +{ + PKCS8_PRIV_KEY_INFO_free((PKCS8_PRIV_KEY_INFO *)st); +} + +static int x509_pub_decode_wrapper(EC_KEY *ec, int *key_type, const void *st) +{ + return internal_pub_decode_ec(ec, key_type, + ((x509_pubkey_st *)st)->algor, + ((x509_pubkey_st *)st)->public_key->data, + ((x509_pubkey_st *)st)->public_key->length); +} +static void *x509_pub_read_bio_der_wrapper(BIO *bio, void **st) +{ + unsigned char *data = NULL; + size_t dlen; + + dlen = BIO_get_mem_data(bio, &data); + if (!dlen) + return 0; + return d2i_x509_pubkey_st((x509_pubkey_st **)st, (const unsigned char **) &data, dlen); +} +static void x509_pub_free_wrapper(void *st) +{ + x509_pubkey_st_free((x509_pubkey_st *)st); +} + +static int param_decode_wrapper(EC_KEY *ec, int *key_type, const void *st) +{ + const params_st *params = (const params_st *)st; + const unsigned char *pdata = params->data; + + if (!internal_gost2001_param_decode(ec, &pdata, params->data_size)) + return 0; + + *key_type = params->key_type; + + return 1; +} + +static int get_key_type_from_pem_name(char *pem_name) +{ + if (!pem_name) + return NID_undef; + + char *space_pos = strchr(pem_name, ' '); + if (space_pos) + *space_pos = '\0'; + + size_t i; + for (i = 0; pem_name[i]; ++i) + pem_name[i] = tolower((unsigned char)pem_name[i]); + + return OBJ_sn2nid(pem_name); +} + +static void *param_read_bio_pem_wrapper(BIO *bio, void **st) +{ + params_st *params = NULL; + char *name = NULL; + char *header = NULL; + + params = OPENSSL_zalloc(sizeof(params_st)); + if (!params) + goto exit; + + int len = PEM_read_bio(bio, &name, &header, + ¶ms->data, + ¶ms->data_size); + + if (len <= 0 || name == NULL) + goto exit; + + params->key_type = get_key_type_from_pem_name(name); + if (params->key_type == NID_undef) + goto exit; +exit: + OPENSSL_free(name); + OPENSSL_free(header); + + *st = params; + return *st; +} + +static void param_free_wrapper(void *st) +{ + params_st *params = (params_st *)st; + + if (!params) + return; + + OPENSSL_free(params->data); + OPENSSL_free(params); +} + +static void *decoder_newctx(void *provctx) +{ + if (!provctx) + return NULL; + + GOST_DECODER_CTX *ctx = OPENSSL_zalloc(sizeof(GOST_DECODER_CTX)); + if (!ctx) + return NULL; + + ctx->provctx = provctx; + return ctx; +} + +static void decoder_freectx(void *ctx) +{ + GOST_DECODER_CTX *ectx = ctx; + + OPENSSL_free(ectx); +} + +static int decoder_does_selection(int selection, int selection_mask) +{ + return FLAGS_INTERSECT(selection, selection_mask); +} + +static int decoder_decode(void *ctx, OSSL_CORE_BIO *cbio, int selection, + int selection_mask, OSSL_CALLBACK *data_cb, + void *data_cdarg, bio2st_fn *bio2st, + st2ec_fn *st2ec, st_free_fn *st_free) +{ + GOST_DECODER_CTX *dctx = NULL; + GOST_KEY_DATA *key_data = NULL; + BIO *bio = NULL; + void *st = NULL; + int ret = 1; + + if (!FLAGS_INTERSECT(selection, selection_mask)) + goto exit; + + if (!cbio || !data_cb) + goto exit; + + dctx = ctx; + if (!dctx || !dctx->provctx || !dctx->provctx->libctx) + goto exit; + + bio = BIO_new_from_core_bio(dctx->provctx->libctx, cbio); + if (!bio) + goto exit; + + if (!bio2st(bio, &st)) + goto exit; + + key_data = OPENSSL_zalloc(sizeof(GOST_KEY_DATA)); + if (!key_data) { + ret = 0; + goto exit; + } + + key_data->ec = EC_KEY_new(); + if (!key_data->ec) { + ret = 0; + goto exit; + } + + if (!st2ec(key_data->ec, &key_data->type, st)) + goto exit; + + key_data->param_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(key_data->ec)); + if (key_data->param_nid == NID_undef) { + ret = 0; + goto exit; + } + + OSSL_PARAM params[4]; + int object_type = OSSL_OBJECT_PKEY; + params[0] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + (char *)OBJ_nid2sn(key_data->type), 0); + params[2] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE, + &key_data, sizeof(key_data)); + params[3] = OSSL_PARAM_construct_end(); + + ret = data_cb(params, data_cdarg); + +exit: + + keymgmt_free(key_data); + BIO_free(bio); + st_free(st); + return ret; +} + +typedef void (*fptr_t)(void); +#define MAKE_DECODER_FUNCTIONS(input, structure, bio2st, st2ec, st_free, selection_mask) \ + static OSSL_FUNC_decoder_decode_fn input##_##structure##_decoder_decode; \ + static int input##_##structure##_decoder_decode(void *ctx, OSSL_CORE_BIO *cbio, \ + int selection, \ + OSSL_CALLBACK *data_cb, \ + void *data_cdarg, \ + OSSL_PASSPHRASE_CALLBACK *cb, \ + void *cbarg) \ + { \ + return decoder_decode(ctx, cbio, selection, selection_mask, data_cb, data_cdarg, \ + bio2st, st2ec, st_free); \ + } \ + static OSSL_FUNC_decoder_does_selection_fn \ + input##_##structure##_decoder_does_selection; \ + static int input##_##structure##_decoder_does_selection(void *ctx, \ + int selection) \ + { \ + return decoder_does_selection(selection, selection_mask); \ + } \ + static const OSSL_DISPATCH id_##input##_##structure##_decoder_functions[] = { \ + { OSSL_FUNC_DECODER_NEWCTX, (fptr_t)decoder_newctx }, \ + { OSSL_FUNC_DECODER_FREECTX, (fptr_t)decoder_freectx }, \ + { OSSL_FUNC_DECODER_DOES_SELECTION, \ + (fptr_t)input##_##structure##_decoder_does_selection }, \ + { OSSL_FUNC_DECODER_DECODE, (fptr_t)input##_##structure##_decoder_decode }, \ + { 0, NULL } \ + }; + +#define DECODER(decoder_name, input, structure) \ + { \ + decoder_name, \ + "provider=gostprov,input=" #input ",structure=" #structure, \ + (id_##input##_##structure##_decoder_functions) \ + } + +MAKE_DECODER_FUNCTIONS(der, PrivateKeyInfo, pkcs8_read_bio_der_wrapper, + pkcs8_decode_wrapper, pkcs8_free_wrapper, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + +MAKE_DECODER_FUNCTIONS(der, SubjectPublicKeyInfo, x509_pub_read_bio_der_wrapper, + x509_pub_decode_wrapper, x509_pub_free_wrapper, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) +MAKE_DECODER_FUNCTIONS(pem, type_specific, param_read_bio_pem_wrapper, + param_decode_wrapper, param_free_wrapper, + OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) + +/* + * Each algorithm of PRIVATE KEYS (PrivateKeyInfo) and PUBLIC KEYS (SubjectPublicKeyInfo) + * is registered separately because OpenSSL extracts the algorithm OID from ASN.1 + * structure and directly maps it to the specific decoder (no iteration needed). + * + * Decoding of the keys from PEM to DER happens in default provider, while the key + * parameters (type_specific) are decoded directly from PEM since the PEM header + * is crucial for the algorithm identification. + */ +const OSSL_ALGORITHM GOST_prov_decoder[] = { + DECODER(ALG_NAME_GOST2001, der, SubjectPublicKeyInfo), + DECODER(ALG_NAME_GOST2001, pem, type_specific), /* Decode domain parameters */ + DECODER(ALG_NAME_GOST2001DH, der, SubjectPublicKeyInfo), + DECODER(ALG_NAME_GOST2012_256, der, SubjectPublicKeyInfo), + DECODER(ALG_NAME_GOST2012_512, der, SubjectPublicKeyInfo), + DECODER(ALG_NAME_GOST2001, der, PrivateKeyInfo), + DECODER(ALG_NAME_GOST2001DH, der, PrivateKeyInfo), + DECODER(ALG_NAME_GOST2012_256, der, PrivateKeyInfo), + DECODER(ALG_NAME_GOST2012_512, der, PrivateKeyInfo), + { NULL, NULL, NULL } +}; diff --git a/gost_prov_digest.c b/gost_prov_digest.c index 79eb5a3..a79c09f 100644 --- a/gost_prov_digest.c +++ b/gost_prov_digest.c @@ -173,15 +173,15 @@ const OSSL_ALGORITHM GOST_prov_digests[] = { * https://www.ietf.org/archive/id/draft-deremin-rfc4491-bis-06.txt * (is there not an RFC namming these?) */ - { "id-tc26-gost3411-12-256:md_gost12_256:1.2.643.7.1.1.2.2", NULL, + { SN_id_GostR3411_2012_256":id-tc26-gost3411-12-256:1.2.643.7.1.1.2.2", NULL, GostR3411_2012_256_digest_functions, "GOST R 34.11-2012 with 256 bit hash" }, - { "id-tc26-gost3411-12-512:md_gost12_512:1.2.643.7.1.1.2.3", NULL, + { SN_id_GostR3411_2012_512":id-tc26-gost3411-12-512:1.2.643.7.1.1.2.3", NULL, GostR3411_2012_512_digest_functions, "GOST R 34.11-2012 with 512 bit hash" }, /* Described in RFC 5831, first name from RFC 4357, section 10.4 */ - { "id-GostR3411-94:md_gost94:1.2.643.2.2.9", NULL, + { SN_id_GostR3411_94":id-GostR3411-94:1.2.643.2.2.9", NULL, GostR3411_94_digest_functions, "GOST R 34.11-94" }, { NULL , NULL, NULL } }; diff --git a/gost_prov_encoder.c b/gost_prov_encoder.c new file mode 100644 index 0000000..a323803 --- /dev/null +++ b/gost_prov_encoder.c @@ -0,0 +1,435 @@ +#include +#include "gost_prov.h" +#include "gost_lcl.h" + +/* + * Forward declarations of all generic OSSL_DISPATCH functions, to make sure + * they are correctly defined further down. For the algorithm and structure specific ones + * MAKE_ENCODER_FUNCTIONS() and MAKE_ENCODER_TEXT_FUNCTIONS() does it for us. + */ +static OSSL_FUNC_encoder_freectx_fn encoder_freectx; +static OSSL_FUNC_encoder_newctx_fn encoder_newctx; + +typedef void * (st_new_fn)(void); +typedef int (ec2st_encode_fn)(void *st, const EC_KEY *key, int key_type); +typedef int (st2bio_fn)(BIO *bio, void *st); +typedef void (st_free_fn)(void *st); + +static st_new_fn pkcs8_new_wrapper; +static ec2st_encode_fn pkcs8_priv_encode_wrapper; +static st2bio_fn pkcs8_write_bio_pem; +static st2bio_fn pkcs8_write_bio_der; +static st_free_fn pkcs8_free_wrapper; +static st_new_fn x509_pub_new_wrapper; +static ec2st_encode_fn x509_pub_encode_wrapper; +static st2bio_fn x509_write_bio_pem; +static st2bio_fn x509_write_bio_der; +static st_free_fn x509_pub_free_wrapper; + +static void *pkcs8_new_wrapper(void) +{ + return PKCS8_PRIV_KEY_INFO_new(); +} + +static int pkcs8_priv_encode_wrapper(void *st, const EC_KEY *key, int key_type) +{ + return internal_priv_encode((PKCS8_PRIV_KEY_INFO *)st, (EC_KEY *)key, key_type); +} + +static int pkcs8_write_bio_pem(BIO *bio, void *st) +{ + return PEM_write_bio_PKCS8_PRIV_KEY_INFO(bio, (PKCS8_PRIV_KEY_INFO *)st); +} + +static int pkcs8_write_bio_der(BIO *bio, void *st) +{ + return i2d_PKCS8_PRIV_KEY_INFO_bio(bio, (PKCS8_PRIV_KEY_INFO *)st); +} + +static void pkcs8_free_wrapper(void *st) +{ + PKCS8_PRIV_KEY_INFO_free((PKCS8_PRIV_KEY_INFO *)st); +} + +static void *x509_pub_new_wrapper(void) +{ + return X509_PUBKEY_new(); +} + +static int x509_pub_encode_wrapper(void *st, const EC_KEY *key, int key_type) +{ + return internal_pub_encode_ec((X509_PUBKEY *)st, (EC_KEY *)key, key_type); +} + +static int x509_write_bio_pem(BIO *bio, void *st) +{ + return PEM_write_bio_X509_PUBKEY(bio, (X509_PUBKEY *)st); +} + +static int x509_write_bio_der(BIO *bio, void *st) +{ + return i2d_X509_PUBKEY_bio(bio, (X509_PUBKEY *)st); +} + +static void x509_pub_free_wrapper(void *st) +{ + X509_PUBKEY_free((X509_PUBKEY *)st); +} + +typedef struct { + PROV_CTX *provctx; +} GOST_ENCODER_CTX; + +static void encoder_freectx(void *ctx) +{ + GOST_ENCODER_CTX *ectx = ctx; + + OPENSSL_free(ectx); +} + +static void *encoder_newctx(void *provctx) +{ + if (!provctx) + return NULL; + + GOST_ENCODER_CTX *ctx = OPENSSL_zalloc(sizeof(GOST_ENCODER_CTX)); + if (!ctx) + return NULL; + + ctx->provctx = provctx; + return ctx; +} + +static int encoder_does_selection(int selection, int selection_mask) +{ + return FLAGS_INTERSECT(selection, selection_mask); +} + +static int encoder_encode( + void *ctx, + OSSL_CORE_BIO *cbio, + const void *key, + const OSSL_PARAM key_params[], + int selection, + int selection_mask, + st_new_fn *st_new, + ec2st_encode_fn *ec2st_encode, + st2bio_fn *st2bio, + st_free_fn *st_free) +{ + int ok = 0; + BIO *out = NULL; + void *key_st = NULL; + GOST_ENCODER_CTX *ectx = NULL; + const GOST_KEY_DATA *key_data = NULL; + + if (!ctx || !cbio || !key) + goto exit; + + if (key_params != NULL) + goto exit; + + if (!FLAGS_INTERSECT(selection, selection_mask)) + goto exit; + + ectx = ctx; + key_data = key; + + if (!ectx->provctx || !ectx->provctx->libctx) + goto exit; + + out = BIO_new_from_core_bio(ectx->provctx->libctx, cbio); + if (!out) + goto exit; + + key_st = st_new(); + if (!key_st) + goto exit; + + if (!ec2st_encode(key_st, key_data->ec, key_data->type)) + goto exit; + + if (!st2bio(out, key_st)) + goto exit; + + ok = 1; + +exit: + st_free(key_st); + BIO_free(out); + return ok; +} + +static int encoder_text_encode(void *vctx, OSSL_CORE_BIO *cbio, + const void *key, const OSSL_PARAM key_params[], + int selection, int selection_mask) +{ + GOST_ENCODER_CTX *ctx = vctx; + const GOST_KEY_DATA *key_data = key; + BIO *out = NULL; + int ok = 0; + + if (!ctx || !cbio || !key) + goto exit; + + if (!FLAGS_INTERSECT(selection, selection_mask)) + goto exit; + + out = BIO_new_from_core_bio(ctx->provctx->libctx, cbio); + if (!out) + goto exit; + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + && !internal_print_gost_priv(out, key_data->ec, 0, key_data->type)) + goto exit; + + if (FLAGS_INTERSECT(selection, OSSL_KEYMGMT_SELECT_KEYPAIR) + && !internal_print_gost_ec_pub(out, key_data->ec, 0, key_data->type)) + goto exit; + + if (!internal_print_gost_ec_param(out, key_data->ec, 0)) + goto exit; + + ok = 1; + +exit: + BIO_free(out); + return ok; +} + +typedef void (*fptr_t)(void); +#define MAKE_ENCODER_FUNCTIONS(alg, output, structure, st_new_fn, ec2st_encode, write_key_st, \ + key_st_free, selection_mask) \ + static OSSL_FUNC_encoder_encode_fn alg##_##output##_##structure##_encoder_encode; \ + static int alg##_##output##_##structure##_encoder_encode( \ + void *ctx, \ + OSSL_CORE_BIO *cbio, \ + const void *key, \ + const OSSL_PARAM key_params[], \ + int selection, \ + OSSL_PASSPHRASE_CALLBACK *cb, \ + void *cbarg) \ + { \ + return encoder_encode( \ + ctx, cbio, key, key_params, \ + selection, selection_mask, \ + st_new_fn, ec2st_encode, write_key_st, key_st_free); \ + } \ + static OSSL_FUNC_encoder_does_selection_fn \ + alg##_##output##_##structure##_encoder_does_selection; \ + static int alg##_##output##_##structure##_encoder_does_selection(void *ctx, int selection) \ + { \ + return encoder_does_selection(selection, selection_mask); \ + } \ + static const OSSL_DISPATCH id_##alg##_##output##_##structure##_encoder_functions[] = \ + { \ + { OSSL_FUNC_ENCODER_NEWCTX, (fptr_t)encoder_newctx }, \ + { OSSL_FUNC_ENCODER_FREECTX, (fptr_t)encoder_freectx }, \ + { OSSL_FUNC_ENCODER_DOES_SELECTION, \ + (fptr_t)alg##_##output##_##structure##_encoder_does_selection }, \ + { OSSL_FUNC_ENCODER_ENCODE, (fptr_t)alg##_##output##_##structure##_encoder_encode }, \ + { 0, NULL } \ + }; \ + +#define MAKE_ENCODER_TEXT_FUNCTIONS(alg, output, selection_mask) \ + static OSSL_FUNC_encoder_encode_fn alg##_##output##_encoder_text_encode; \ + static int alg##_##output##_encoder_text_encode( \ + void *vctx, \ + OSSL_CORE_BIO *cbio, \ + const void *key, \ + const OSSL_PARAM key_params[], \ + int selection, \ + OSSL_PASSPHRASE_CALLBACK *cb, \ + void *cbarg) \ + { \ + if (key_params != NULL) { \ + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \ + return 0; \ + } \ + return encoder_text_encode( \ + vctx, cbio, key, key_params, \ + selection, selection_mask); \ + } \ + static OSSL_FUNC_encoder_does_selection_fn \ + alg##_##output##_encoder_does_selection; \ + static int alg##_##output##_encoder_does_selection(void *ctx, int selection) \ + { \ + return encoder_does_selection(selection, selection_mask); \ + } \ + static const OSSL_DISPATCH id_##alg##_##output##_encoder_text_functions[] = \ + { \ + { OSSL_FUNC_ENCODER_NEWCTX, (fptr_t)encoder_newctx }, \ + { OSSL_FUNC_ENCODER_FREECTX, (fptr_t)encoder_freectx }, \ + { OSSL_FUNC_ENCODER_DOES_SELECTION, \ + (fptr_t)alg##_##output##_encoder_does_selection }, \ + { OSSL_FUNC_ENCODER_ENCODE, \ + (fptr_t)alg##_##output##_encoder_text_encode }, \ + { 0, NULL } \ + }; + +#define ENCODER(sn_name, name, output, structure) \ + { \ + sn_name, \ + "provider=gostprov,output=" #output \ + ",structure=" #structure, \ + (id_##name##_##output##_##structure##_encoder_functions) \ + } + +#define ENCODER_TEXT(sn_name, name, output) \ + { \ + sn_name, \ + "provider=gostprov,output=" #output, \ + (id_##name##_##output##_encoder_text_functions) \ + } + +MAKE_ENCODER_FUNCTIONS(gost2001, pem, PrivateKeyInfo, + pkcs8_new_wrapper, + pkcs8_priv_encode_wrapper, + pkcs8_write_bio_pem, + pkcs8_free_wrapper, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2001dh, pem, PrivateKeyInfo, + pkcs8_new_wrapper, + pkcs8_priv_encode_wrapper, + pkcs8_write_bio_pem, + pkcs8_free_wrapper, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2012_256, pem, PrivateKeyInfo, + pkcs8_new_wrapper, + pkcs8_priv_encode_wrapper, + pkcs8_write_bio_pem, + pkcs8_free_wrapper, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2012_512, pem, PrivateKeyInfo, + pkcs8_new_wrapper, + pkcs8_priv_encode_wrapper, + pkcs8_write_bio_pem, + pkcs8_free_wrapper, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2001, der, PrivateKeyInfo, + pkcs8_new_wrapper, + pkcs8_priv_encode_wrapper, + pkcs8_write_bio_der, + pkcs8_free_wrapper, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2001dh, der, PrivateKeyInfo, + pkcs8_new_wrapper, + pkcs8_priv_encode_wrapper, + pkcs8_write_bio_der, + pkcs8_free_wrapper, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2012_256, der, PrivateKeyInfo, + pkcs8_new_wrapper, + pkcs8_priv_encode_wrapper, + pkcs8_write_bio_der, + pkcs8_free_wrapper, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2012_512, der, PrivateKeyInfo, + pkcs8_new_wrapper, + pkcs8_priv_encode_wrapper, + pkcs8_write_bio_der, + pkcs8_free_wrapper, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2001, pem, SubjectPublicKeyInfo, + x509_pub_new_wrapper, + x509_pub_encode_wrapper, + x509_write_bio_pem, + x509_pub_free_wrapper, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2001dh, pem, SubjectPublicKeyInfo, + x509_pub_new_wrapper, + x509_pub_encode_wrapper, + x509_write_bio_pem, + x509_pub_free_wrapper, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2012_256, pem, SubjectPublicKeyInfo, + x509_pub_new_wrapper, + x509_pub_encode_wrapper, + x509_write_bio_pem, + x509_pub_free_wrapper, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2012_512, pem, SubjectPublicKeyInfo, + x509_pub_new_wrapper, + x509_pub_encode_wrapper, + x509_write_bio_pem, + x509_pub_free_wrapper, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2001, der, SubjectPublicKeyInfo, + x509_pub_new_wrapper, + x509_pub_encode_wrapper, + x509_write_bio_der, + x509_pub_free_wrapper, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2001dh, der, SubjectPublicKeyInfo, + x509_pub_new_wrapper, + x509_pub_encode_wrapper, + x509_write_bio_der, + x509_pub_free_wrapper, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2012_256, der, SubjectPublicKeyInfo, + x509_pub_new_wrapper, + x509_pub_encode_wrapper, + x509_write_bio_der, + x509_pub_free_wrapper, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + +MAKE_ENCODER_FUNCTIONS(gost2012_512, der, SubjectPublicKeyInfo, + x509_pub_new_wrapper, + x509_pub_encode_wrapper, + x509_write_bio_der, + x509_pub_free_wrapper, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + +MAKE_ENCODER_TEXT_FUNCTIONS(gost2001, text, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY | OSSL_KEYMGMT_SELECT_PUBLIC_KEY + | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) + +MAKE_ENCODER_TEXT_FUNCTIONS(gost2001dh, text, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY | OSSL_KEYMGMT_SELECT_PUBLIC_KEY + | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) + +MAKE_ENCODER_TEXT_FUNCTIONS(gost2012_256, text, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY | OSSL_KEYMGMT_SELECT_PUBLIC_KEY + | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) + +MAKE_ENCODER_TEXT_FUNCTIONS(gost2012_512, text, + OSSL_KEYMGMT_SELECT_PRIVATE_KEY | OSSL_KEYMGMT_SELECT_PUBLIC_KEY + | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) + +const OSSL_ALGORITHM GOST_prov_encoder[] = { + ENCODER(ALG_NAME_GOST2001, gost2001, pem, PrivateKeyInfo), + ENCODER(ALG_NAME_GOST2001, gost2001, der, PrivateKeyInfo), + ENCODER(ALG_NAME_GOST2001, gost2001, pem, SubjectPublicKeyInfo), + ENCODER(ALG_NAME_GOST2001, gost2001, der, SubjectPublicKeyInfo), + ENCODER_TEXT(ALG_NAME_GOST2001, gost2001, text), + ENCODER(ALG_NAME_GOST2001DH, gost2001dh, pem, PrivateKeyInfo), + ENCODER(ALG_NAME_GOST2001DH, gost2001dh, der, PrivateKeyInfo), + ENCODER(ALG_NAME_GOST2001DH, gost2001dh, pem, SubjectPublicKeyInfo), + ENCODER(ALG_NAME_GOST2001DH, gost2001dh, der, SubjectPublicKeyInfo), + ENCODER_TEXT(ALG_NAME_GOST2001DH, gost2001dh, text), + ENCODER(ALG_NAME_GOST2012_256, gost2012_256, pem, PrivateKeyInfo), + ENCODER(ALG_NAME_GOST2012_256, gost2012_256, der, PrivateKeyInfo), + ENCODER(ALG_NAME_GOST2012_256, gost2012_256, pem, SubjectPublicKeyInfo), + ENCODER(ALG_NAME_GOST2012_256, gost2012_256, der, SubjectPublicKeyInfo), + ENCODER_TEXT(ALG_NAME_GOST2012_256, gost2012_256, text), + ENCODER(ALG_NAME_GOST2012_512, gost2012_512, pem, PrivateKeyInfo), + ENCODER(ALG_NAME_GOST2012_512, gost2012_512, der, PrivateKeyInfo), + ENCODER(ALG_NAME_GOST2012_512, gost2012_512, pem, SubjectPublicKeyInfo), + ENCODER(ALG_NAME_GOST2012_512, gost2012_512, der, SubjectPublicKeyInfo), + ENCODER_TEXT(ALG_NAME_GOST2012_512, gost2012_512, text), + { NULL, NULL, NULL } +}; \ No newline at end of file diff --git a/gost_prov_keyexch.c b/gost_prov_keyexch.c new file mode 100644 index 0000000..7ae94c9 --- /dev/null +++ b/gost_prov_keyexch.c @@ -0,0 +1,139 @@ +#include +#include +#include "gost_prov.h" +#include "gost_lcl.h" + +#define GOST_MAX_ECDH_LEN 128 + +int gost_get_max_keyexch_size(const GOST_KEY_DATA *key_data) +{ + /* You should modify this function when add new derive algorithm */ + return GOST_MAX_ECDH_LEN / 2; +} + +/* + * Forward declarations of all generic OSSL_DISPATCH functions, to make sure + * they are correctly defined further down. + */ +static OSSL_FUNC_keyexch_newctx_fn ecdhe_newctx; +static OSSL_FUNC_keyexch_init_fn ecdhe_init; +static OSSL_FUNC_keyexch_set_peer_fn ecdhe_set_peer; +static OSSL_FUNC_keyexch_derive_fn ecdhe_derive; +static OSSL_FUNC_keyexch_freectx_fn ecdhe_freectx; + +typedef struct { + GOST_KEY_DATA *key_data; + GOST_KEY_DATA *peer_key_data; +} GOST_ECDHE_CTX; + +static void *ecdhe_newctx(void *provctx) +{ + GOST_ECDHE_CTX *ctx; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + return ctx; +} + +static int ecdhe_init(void *vctx, void *vkey_data, const OSSL_PARAM params[]) +{ + GOST_ECDHE_CTX *ctx = vctx; + GOST_KEY_DATA *key_data = vkey_data; + + if (!ctx || !key_data || !key_data->ec) + return 0; + + ctx->key_data = keymgmt_dup(key_data, (OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS + | OSSL_KEYMGMT_SELECT_PRIVATE_KEY)); + if (!ctx->key_data) + return 0; + + return 1; +} + +static int ecdhe_set_peer(void *vctx, void *vpeer_key_data) +{ + GOST_ECDHE_CTX *ctx = vctx; + GOST_KEY_DATA *peer_key_data = vpeer_key_data; + + if (!ctx || !ctx->key_data || !ctx->key_data->ec || !peer_key_data || !peer_key_data->ec) + return 0; + + if (!EC_KEY_get0_public_key(peer_key_data->ec)) + return 0; + + if (!keymgmt_match(ctx->key_data, peer_key_data, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) + return 0; + + keymgmt_free(ctx->peer_key_data); + ctx->peer_key_data = keymgmt_dup(peer_key_data, (OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS + | OSSL_KEYMGMT_SELECT_PUBLIC_KEY)); + if (!ctx->peer_key_data) + return 0; + + return 1; +} + +static void ecdhe_freectx(void *vctx) +{ + GOST_ECDHE_CTX *ctx = (GOST_ECDHE_CTX *)vctx; + + if (!ctx) + return; + + keymgmt_free(ctx->key_data); + keymgmt_free(ctx->peer_key_data); + OPENSSL_free(ctx); +} + +static int ecdhe_derive(void *vctx, unsigned char *secret, + size_t *psecretlen, size_t outlen) +{ + GOST_ECDHE_CTX *ctx = vctx; + size_t ecdh_result_len; + unsigned char ecdh_result[GOST_MAX_ECDH_LEN]; + const unsigned char ukm[] = {1}; + + if (!psecretlen + || !ctx + || !ctx->key_data + || !ctx->peer_key_data + || !ctx->peer_key_data->ec + || !internal_compute_ecdh(NULL, &ecdh_result_len, ukm, sizeof(ukm), + EC_KEY_get0_public_key(ctx->peer_key_data->ec), + ctx->key_data->ec)) + return 0; + + assert(ecdh_result_len <= sizeof(ecdh_result)); + + /* Return only X coordinate */ + *psecretlen = ecdh_result_len >> 1; + + if (!secret) + return 1; + + if (outlen < *psecretlen) + return 0; + + if (!internal_compute_ecdh(ecdh_result, &ecdh_result_len, ukm, sizeof(ukm), + EC_KEY_get0_public_key(ctx->peer_key_data->ec), ctx->key_data->ec)) + return 0; + + memcpy(secret, ecdh_result, *psecretlen); + OPENSSL_cleanse(ecdh_result, sizeof(ecdh_result)); + + return 1; +} + +static const OSSL_DISPATCH ecdh_keyexch_functions[] = { + { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ecdhe_newctx }, + { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ecdhe_init }, + { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecdhe_derive }, + { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecdhe_set_peer }, + { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecdhe_freectx }, + OSSL_DISPATCH_END +}; + +const OSSL_ALGORITHM GOST_prov_keyexch[] = { + { "ECDHE", NULL, ecdh_keyexch_functions }, + { NULL, NULL, NULL } +}; \ No newline at end of file diff --git a/gost_prov_keymgmt.c b/gost_prov_keymgmt.c new file mode 100644 index 0000000..f8ef3d0 --- /dev/null +++ b/gost_prov_keymgmt.c @@ -0,0 +1,805 @@ +#include +#include +#include "gost_prov.h" +#include "gost_lcl.h" + +#define PARAMSET_NID "paramset_nid" + +#define GOST_MAX(X, Y) ((X) > (Y) ? (X) : (Y)) + +/* + * Forward declarations of all generic OSSL_DISPATCH functions, to make sure + * they are correctly defined further down. For the algorithm specific ones + * MAKE_FUNCTIONS() does it for us. + */ +static OSSL_FUNC_keymgmt_set_params_fn keymgmt_set_params; +static OSSL_FUNC_keymgmt_settable_params_fn keymgmt_settable_params; +static OSSL_FUNC_keymgmt_gen_set_params_fn keymgmt_gen_set_params; +static OSSL_FUNC_keymgmt_gen_fn keymgmt_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn keymgmt_gen_cleanup; +static OSSL_FUNC_keymgmt_has_fn keymgmt_has; +static OSSL_FUNC_keymgmt_get_params_fn keymgmt_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn keymgmt_gettable_params; +static OSSL_FUNC_keymgmt_gen_get_params_fn keymgmt_gen_get_params; +static OSSL_FUNC_keymgmt_gen_gettable_params_fn keymgmt_gen_gettable_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn keymgmt_gen_settable_params; +static OSSL_FUNC_keymgmt_load_fn keymgmt_load; +static OSSL_FUNC_keymgmt_query_operation_name_fn keymgmt_gost2001_operation_name; +static OSSL_FUNC_keymgmt_query_operation_name_fn keymgmt_gost2012_256_operation_name; +static OSSL_FUNC_keymgmt_query_operation_name_fn keymgmt_gost2012_512_operation_name; +static OSSL_FUNC_keymgmt_validate_fn keymgmt_validate; + +typedef struct gost_gen_ctx_st { + int type; + int sign_param_nid; + int selection; +} GOST_GEN_CTX; + +static const char *keymgmt_gost2012_256_operation_name(int operation_id) +{ + switch (operation_id) { + case OSSL_OP_SIGNATURE: + return SN_id_GostR3410_2012_256; + case OSSL_OP_KEYEXCH: + return "ECDHE"; + default: + return NULL; + } +} + +static const char *keymgmt_gost2012_512_operation_name(int operation_id) +{ + switch (operation_id) { + case OSSL_OP_SIGNATURE: + return SN_id_GostR3410_2012_512; + case OSSL_OP_KEYEXCH: + return "ECDHE"; + default: + return NULL; + } +} + +static const char *keymgmt_gost2001_operation_name(int operation_id) +{ + if (operation_id == OSSL_OP_SIGNATURE) + return SN_id_GostR3410_2001; + return NULL; +} + +static void *keymgmt_new(void *vprovctx, int type) +{ + GOST_KEY_DATA *key_data = NULL; + + key_data = OPENSSL_zalloc(sizeof(GOST_KEY_DATA)); + if (!key_data) + return NULL; + + key_data->type = type; + key_data->param_nid = NID_undef; + key_data->ec = EC_KEY_new(); + if (!key_data->ec) { + OPENSSL_free(key_data); + return NULL; + } + + return key_data; +} + +void keymgmt_free(void *vkctx) +{ + GOST_KEY_DATA *key_data = vkctx; + + if (!key_data) + return; + EC_KEY_free(key_data->ec); + OPENSSL_free(key_data); +} + +static int keymgmt_has(const void *vkctx, int selection) +{ + const GOST_KEY_DATA *key_data = vkctx; + int ok = 1; + + if (!vkctx) + return !ok; + + if (!FLAGS_INTERSECT(selection, OSSL_KEYMGMT_SELECT_ALL)) + return ok; + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) + ok = ok && (EC_KEY_get0_public_key(key_data->ec) != NULL); + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) + ok = ok && (EC_KEY_get0_private_key(key_data->ec) != NULL); + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) + ok = ok && (EC_KEY_get0_group(key_data->ec) != NULL); + + return ok; +} + +static void *keymgmt_load(const void *reference, size_t reference_sz) +{ + GOST_KEY_DATA *key_data = NULL; + + if (!reference) + return NULL; + + if (reference_sz != sizeof(key_data)) + return NULL; + + key_data = *(GOST_KEY_DATA **)reference; + + /* Questionable hack of changing the constant value by pointer */ + *(GOST_KEY_DATA **)reference = NULL; + + return key_data; +} + +static void keymgmt_gen_cleanup(void *genctx) +{ + if (!genctx) + return; + + GOST_GEN_CTX *gctx = genctx; + OPENSSL_free(gctx); +} + +void *keymgmt_gen_init(int selection, const OSSL_PARAM params[], int type) +{ + if (!FLAGS_INTERSECT(selection, OSSL_KEYMGMT_SELECT_ALL)) + return NULL; + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + && !FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) + return NULL; + + GOST_GEN_CTX *gctx = OPENSSL_zalloc(sizeof(GOST_GEN_CTX)); + if (!gctx) + return NULL; + + gctx->type = type; + gctx->selection = selection; + gctx->sign_param_nid = NID_undef; + + if (params && !keymgmt_gen_set_params(gctx, params)) { + keymgmt_gen_cleanup(gctx); + return NULL; + } + + return gctx; +} + +/* + * The callback arguments (osslcb & cbarg) are not used by EC_KEY generation + */ +static void *keymgmt_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + GOST_GEN_CTX *gctx = genctx; + GOST_KEY_DATA *key_data = NULL; + + if (!gctx) + goto end; + + if (gctx->sign_param_nid == NID_undef) + goto end; + + key_data = OPENSSL_zalloc(sizeof(GOST_KEY_DATA)); + if (!key_data) + goto end; + + key_data->type = gctx->type; + key_data->param_nid = gctx->sign_param_nid; + + key_data->ec = internal_ec_paramgen(key_data->param_nid); + if (!key_data->ec) + goto end; + + if (FLAGS_CONTAIN(gctx->selection, OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + && !gost_ec_keygen(key_data->ec)) + goto end; + + return key_data; + +end: + keymgmt_free(key_data); + return NULL; +} + +static const OSSL_PARAM known_settable_params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *keymgmt_settable_params(void *provctx) +{ + return known_settable_params; +} + +static int set_encoded_key(GOST_KEY_DATA *key_data, const OSSL_PARAM *p) +{ + int ret = 0; + BN_CTX *ctx = NULL; + BIGNUM *x = NULL, *y = NULL; + EC_POINT *point = NULL; + const EC_GROUP *group = EC_KEY_get0_group(key_data->ec); + const unsigned char *pub_key_buf = NULL; + size_t pub_key_buflen = 0; + + if (!group) + goto end; + + if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&pub_key_buf, &pub_key_buflen)) + goto end; + + size_t coord_len = pub_key_buflen / 2; + if (pub_key_buflen % 2 != 0 || coord_len == 0) + goto end; + + ctx = BN_CTX_new(); + if (!ctx) + goto end; + BN_CTX_start(ctx); + + x = BN_CTX_get(ctx); + y = BN_CTX_get(ctx); + if (!x || !y) + goto end; + + if (!BN_lebin2bn(pub_key_buf, coord_len, x)) + goto end; + if (!BN_lebin2bn(pub_key_buf + coord_len, coord_len, y)) + goto end; + + point = EC_POINT_new(group); + if (!point) + goto end; + + if (!EC_POINT_set_affine_coordinates(group, point, x, y, ctx)) + goto end; + if (!EC_KEY_set_public_key(key_data->ec, point)) + goto end; + + ret = 1; + +end: + EC_POINT_free(point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; +} + +static int keymgmt_set_params(void *key, const OSSL_PARAM params[]) +{ + if (key == NULL) + return 0; + if (params == NULL) + return 1; + + GOST_KEY_DATA *key_data = key; + const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + + if (p != NULL && !set_encoded_key(key_data, p)) + return 0; + + return 1; +} + +const OSSL_PARAM *keymgmt_gen_settable_params(void *genctx, void *provctx) +{ + static const OSSL_PARAM settable_params[] = { + OSSL_PARAM_utf8_string(param_ctrl_string, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_END + }; + + return settable_params; +} + +static int keygmgmt_gen_set_paramset_param(GOST_GEN_CTX *genctx, const OSSL_PARAM *param) +{ + int result = 0; + const char *paramset = NULL; + + if (!OSSL_PARAM_get_utf8_string_ptr(param, ¶mset)) + goto exit; + + int sign_param_nid = NID_undef; + + switch (genctx->type) { + case NID_id_GostR3410_2001: + case NID_id_GostR3410_2001DH: + case NID_id_GostR3410_2012_256: + result = internal_param_str_to_nid_256(paramset, &sign_param_nid); + break; + case NID_id_GostR3410_2012_512: + result = internal_param_str_to_nid_512(paramset, &sign_param_nid); + break; + } + + if (!result) + goto exit; + + genctx->sign_param_nid = sign_param_nid; + +exit: + return result; +} + +int keymgmt_gen_set_params(void *p0, const OSSL_PARAM params[]) +{ + GOST_GEN_CTX *gctx = p0; + + if (!gctx) + return 0; + if (!params) + return 1; + + const OSSL_PARAM *p = OSSL_PARAM_locate_const(params, param_ctrl_string); + if (p != NULL && !keygmgmt_gen_set_paramset_param(gctx, p)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME); + if (p != NULL && !keygmgmt_gen_set_paramset_param(gctx, p)) + return 0; + + return 1; +} + +static const OSSL_PARAM known_gettable_params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *keymgmt_gettable_params(void *provctx) +{ + return known_gettable_params; +} + +static int get_encoded_key(const GOST_KEY_DATA *key_data, OSSL_PARAM *p) +{ + BN_CTX *ctx = NULL; + BIGNUM *x = NULL, *y = NULL; + unsigned char *buf = NULL; + int ret = 0; + const EC_POINT *pub = EC_KEY_get0_public_key(key_data->ec); + const EC_GROUP *group = EC_KEY_get0_group(key_data->ec); + + if (!pub || !group) + goto end; + + ctx = BN_CTX_new(); + if (!ctx) + goto end; + + BN_CTX_start(ctx); + x = BN_CTX_get(ctx); + y = BN_CTX_get(ctx); + if (!x || !y) + goto end; + + if (!EC_POINT_get_affine_coordinates(group, pub, x, y, ctx)) + goto end; + + int field_size = (EC_GROUP_get_degree(group) + 7) / 8; + buf = OPENSSL_zalloc(2 * field_size); + if (!buf) + goto end; + + if (BN_bn2lebinpad(x, buf, field_size) != field_size || + BN_bn2lebinpad(y, buf + field_size, field_size) != field_size) + goto end; + + if (!OSSL_PARAM_set_octet_string(p, buf, 2 * field_size)) + goto end; + + ret = 1; + +end: + OPENSSL_free(buf); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; +} + +static int get_bits(GOST_KEY_DATA *key_data, OSSL_PARAM *p) +{ + const EC_GROUP *group = EC_KEY_get0_group(key_data->ec); + + if (!group) + return 0; + return OSSL_PARAM_set_int(p, EC_GROUP_get_degree(group)); +} + +static int get_security_bits(GOST_KEY_DATA *key_data, OSSL_PARAM *p) +{ + const EC_GROUP *group = EC_KEY_get0_group(key_data->ec); + + if (!group) + return 0; + + int sec_bits = EC_GROUP_get_degree(group) >> 1; + return OSSL_PARAM_set_int(p, sec_bits); +} + +static int get_default_digest_name(const GOST_KEY_DATA *key_data, OSSL_PARAM *p) +{ + const char *digest = NULL; + + switch (key_data->type) { + case NID_id_GostR3410_2001: + case NID_id_GostR3410_2001DH: + digest = SN_id_GostR3411_94; + break; + + case NID_id_GostR3410_2012_256: + digest = SN_id_GostR3411_2012_256; + break; + + case NID_id_GostR3410_2012_512: + digest = SN_id_GostR3411_2012_512; + break; + + default: + return 0; + } + + return OSSL_PARAM_set_utf8_string(p, digest); +} + +static int get_max_size(const GOST_KEY_DATA *key_data, OSSL_PARAM *p) +{ + int max_signature_size = gost_get_max_signature_size(key_data); + + if (max_signature_size == -1) + return 0; + + int size = GOST_MAX(max_signature_size, gost_get_max_keyexch_size(key_data)); + + return OSSL_PARAM_set_int(p, size); +} + +static int keymgmt_get_params(void *key, OSSL_PARAM params[]) +{ + if (key == NULL) + return 0; + if (params == NULL) + return 1; + + GOST_KEY_DATA *key_data = key; + OSSL_PARAM *p = NULL; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE); + if (p && !get_max_size(key_data, p)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p && !get_encoded_key(key_data, p)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS); + if (p && !get_bits(key_data, p)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS); + if (p && !get_security_bits(key_data, p)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MANDATORY_DIGEST); + if (p && !get_default_digest_name(key_data, p)) + return 0; + + return 1; +} + +static int keymgmt_gen_get_params(void *genctx, OSSL_PARAM params[]) +{ + int ret = 0; + + if (!genctx || !params) + goto end; + + GOST_GEN_CTX *gctx = genctx; + OSSL_PARAM *p = OSSL_PARAM_locate(params, PARAMSET_NID); + if (p != NULL && !OSSL_PARAM_set_int(p, gctx->sign_param_nid)) + goto end; + + ret = 1; + +end: + return ret; +} + +const OSSL_PARAM *keymgmt_gen_gettable_params(void *provctx, void *unused) +{ + static const OSSL_PARAM gettable_params[] = { + OSSL_PARAM_int(PARAMSET_NID, NULL), + OSSL_PARAM_END + }; + return gettable_params; +} + +void *keymgmt_dup(const void *src, int selection) +{ + const GOST_KEY_DATA *src_data = src; + GOST_KEY_DATA *dst = NULL; + + if (!src_data || !src_data->ec) + goto err; + if (!FLAGS_INTERSECT(selection, OSSL_KEYMGMT_SELECT_ALL)) + goto err; + dst = OPENSSL_zalloc(sizeof(GOST_KEY_DATA)); + if (!dst) + goto err; + + dst->type = src_data->type; + dst->param_nid = src_data->param_nid; + + dst->ec = EC_KEY_new(); + if (!dst->ec) + goto err; + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) { + const EC_GROUP *group = EC_KEY_get0_group(src_data->ec); + + if (!group || !EC_KEY_set_group(dst->ec, group)) + goto err; + } + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) { + const EC_POINT *pub = EC_KEY_get0_public_key(src_data->ec); + const EC_GROUP *group = EC_KEY_get0_group(dst->ec); + + if (!pub || !group || !EC_KEY_set_public_key(dst->ec, pub)) + goto err; + } + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) { + const BIGNUM *priv = EC_KEY_get0_private_key(src_data->ec); + const EC_GROUP *group = EC_KEY_get0_group(dst->ec); + + if (!priv || !group || !EC_KEY_set_private_key(dst->ec, priv)) + goto err; + } + + return dst; + +err: + keymgmt_free(dst); + return NULL; +} + +int keymgmt_match(const void *vkctx1, const void *vkctx2, int selection) +{ + GOST_KEY_DATA *key_data1 = (GOST_KEY_DATA *)vkctx1; + GOST_KEY_DATA *key_data2 = (GOST_KEY_DATA *)vkctx2; + int ok = 1; + + if (!key_data1 || !key_data2 || !key_data1->ec || !key_data2->ec) + return 0; + + if (!FLAGS_INTERSECT(selection, OSSL_KEYMGMT_SELECT_ALL)) + return 1; + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) { + const EC_POINT *pub_key1 = EC_KEY_get0_public_key(key_data1->ec); + const EC_POINT *pub_key2 = EC_KEY_get0_public_key(key_data2->ec); + const EC_GROUP *group1 = EC_KEY_get0_group(key_data1->ec); + const EC_GROUP *group2 = EC_KEY_get0_group(key_data2->ec); + + if (!pub_key1 || !pub_key2 || !group1 || !group2) + return 0; + + if (EC_GROUP_cmp(group1, group2, NULL) != 0) + return 0; + + ok = ok && (EC_POINT_cmp(group1, pub_key1, pub_key2, NULL) == 0); + } + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) { + const BIGNUM *priv_key1 = EC_KEY_get0_private_key(key_data1->ec); + const BIGNUM *priv_key2 = EC_KEY_get0_private_key(key_data2->ec); + + if (!priv_key1 || !priv_key2) + return 0; + + ok = ok && (BN_cmp(priv_key1, priv_key2) == 0); + } + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) { + const EC_GROUP *group1 = EC_KEY_get0_group(key_data1->ec); + const EC_GROUP *group2 = EC_KEY_get0_group(key_data2->ec); + + if (!group1 || !group2) + return 0; + + ok = ok && (EC_GROUP_cmp(group1, group2, NULL) == 0); + } + + return ok; +} + +int gost_key_public_check(const EC_POINT *pub_key, const EC_GROUP *group) +{ + BIGNUM *x = NULL; + BIGNUM *y = NULL; + BIGNUM *p = NULL; + BIGNUM *order = NULL; + BN_CTX *ctx = NULL; + int ret = 0; + + ctx = BN_CTX_new(); + if (!ctx) + goto exit; + + BN_CTX_start(ctx); + p = BN_CTX_get(ctx); + x = BN_CTX_get(ctx); + y = BN_CTX_get(ctx); + order = BN_CTX_get(ctx); + + if (!p || !x || !y || !order) + goto exit; + + if (EC_POINT_is_at_infinity(group, pub_key)) + goto exit; + + if (!EC_POINT_get_affine_coordinates(group, pub_key, x, y, NULL)) + goto exit; + + if (!EC_GROUP_get_curve(group, p, NULL, NULL, NULL)) + goto exit; + + if (BN_cmp(x, p) >= 0 || BN_cmp(y, p) >= 0) + goto exit; + + if (EC_POINT_is_on_curve(group, pub_key, NULL) <= 0) + goto exit; + + if (EC_GROUP_get_order(group, order, NULL) == 0) + goto exit; + + if (BN_cmp(order, BN_value_one()) <= 0) + goto exit; + + ret = 1; +exit: + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; +} + +static int keymgmt_validate(const void *vkctx, int selection, int checktype) +{ + GOST_KEY_DATA *key_data = (GOST_KEY_DATA *)vkctx; + + if (!key_data || !key_data->ec) + return 0; + + if (!FLAGS_INTERSECT(selection, OSSL_KEYMGMT_SELECT_ALL)) + return 1; + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) { + const EC_GROUP *group = EC_KEY_get0_group(key_data->ec); + + if (!group || !EC_GROUP_check(group, NULL)) + return 0; + } + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) { + const EC_POINT *pub_key = EC_KEY_get0_public_key(key_data->ec); + const EC_GROUP *group = EC_KEY_get0_group(key_data->ec); + + if (!pub_key || !group || !gost_key_public_check(pub_key, group)) + return 0; + } + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) { + const BIGNUM *priv_key = EC_KEY_get0_private_key(key_data->ec); + const EC_GROUP *group = EC_KEY_get0_group(key_data->ec); + + if (!priv_key || !group) + return 0; + + BIGNUM *order = BN_new(); + if (!order + || !EC_GROUP_get_order(group, order, NULL) + || !((BN_cmp(priv_key, BN_value_one()) >= 0) && (BN_cmp(priv_key, order) < 0))) { + BN_free(order); + return 0; + } + + BN_free(order); + } + + if (FLAGS_CONTAIN(selection, OSSL_KEYMGMT_SELECT_KEYPAIR)) { + const EC_POINT *pub_key = EC_KEY_get0_public_key(key_data->ec); + const BIGNUM *priv_key = EC_KEY_get0_private_key(key_data->ec); + const EC_GROUP *group = EC_KEY_get0_group(key_data->ec); + + if (!pub_key || !priv_key || !group) + return 0; + + EC_POINT *tmp_pub_key = EC_POINT_new(group); + if (!tmp_pub_key + || !EC_POINT_mul(group, tmp_pub_key, + priv_key, NULL, NULL, NULL) + || EC_POINT_cmp(group, pub_key, tmp_pub_key, NULL)) { + EC_POINT_free(tmp_pub_key); + return 0; + } + + EC_POINT_free(tmp_pub_key); + } + + return 1; +} + +static int keymgmt_gen_set_template(void *genctx, void *template) +{ + GOST_GEN_CTX *gctx = genctx; + GOST_KEY_DATA *template_key_data = template; + + if (!genctx || !template) + return 0; + + if (gctx->type != template_key_data->type) + return 0; + + gctx->sign_param_nid = template_key_data->param_nid; + return 1; +} + +typedef void (*fptr_t)(void); +#define MAKE_KEYMGMT_FUNCTIONS(alg, type, operation_name_fn) \ + static OSSL_FUNC_keymgmt_gen_init_fn alg##_gen_init; \ + static void *alg##_gen_init(void *provctx, int selection, const OSSL_PARAM params[]) \ + { \ + return keymgmt_gen_init(selection, params, type); \ + } \ + static OSSL_FUNC_keymgmt_new_fn alg##_new; \ + static void *alg##_new(void *provctx) \ + { \ + return keymgmt_new(provctx, type); \ + } \ + static const OSSL_DISPATCH id_##alg##_keymgmt_functions[] = { \ + { OSSL_FUNC_KEYMGMT_NEW, (fptr_t)alg##_new}, \ + { OSSL_FUNC_KEYMGMT_FREE, (fptr_t)keymgmt_free }, \ + { OSSL_FUNC_KEYMGMT_HAS, (fptr_t)keymgmt_has }, \ + { OSSL_FUNC_KEYMGMT_GEN_INIT, (fptr_t)alg##_gen_init }, \ + { OSSL_FUNC_KEYMGMT_GEN, (fptr_t)keymgmt_gen }, \ + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (fptr_t)keymgmt_gen_cleanup }, \ + { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (fptr_t)keymgmt_gen_set_template}, \ + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (fptr_t)keymgmt_set_params }, \ + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (fptr_t)keymgmt_settable_params }, \ + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (fptr_t)keymgmt_gen_set_params }, \ + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (fptr_t)keymgmt_gen_settable_params }, \ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (fptr_t)keymgmt_get_params}, \ + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (fptr_t)keymgmt_gettable_params}, \ + { OSSL_FUNC_KEYMGMT_GEN_GET_PARAMS, (fptr_t)keymgmt_gen_get_params}, \ + { OSSL_FUNC_KEYMGMT_GEN_GETTABLE_PARAMS, (fptr_t)keymgmt_gen_gettable_params}, \ + { OSSL_FUNC_KEYMGMT_LOAD, (fptr_t)keymgmt_load}, \ + { OSSL_FUNC_KEYMGMT_MATCH, (fptr_t)keymgmt_match}, \ + { OSSL_FUNC_KEYMGMT_VALIDATE, (fptr_t)keymgmt_validate}, \ + { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, (fptr_t)operation_name_fn}, \ + { OSSL_FUNC_KEYMGMT_DUP, (fptr_t)keymgmt_dup}, \ + OSSL_DISPATCH_END \ + }; + +MAKE_KEYMGMT_FUNCTIONS(gost2001, NID_id_GostR3410_2001, keymgmt_gost2001_operation_name); +MAKE_KEYMGMT_FUNCTIONS(gost2001dh, NID_id_GostR3410_2001DH, NULL); +MAKE_KEYMGMT_FUNCTIONS(gost2012_256, NID_id_GostR3410_2012_256, + keymgmt_gost2012_256_operation_name); +MAKE_KEYMGMT_FUNCTIONS(gost2012_512, NID_id_GostR3410_2012_512, + keymgmt_gost2012_512_operation_name); + +/* The OSSL_ALGORITHM for the provider's operation query function */ +const OSSL_ALGORITHM GOST_prov_keymgmt[] = { + { ALG_NAME_GOST2001, NULL, id_gost2001_keymgmt_functions }, + { ALG_NAME_GOST2001DH, NULL, id_gost2001dh_keymgmt_functions }, + { ALG_NAME_GOST2012_256, NULL, id_gost2012_256_keymgmt_functions }, + { ALG_NAME_GOST2012_512, NULL, id_gost2012_512_keymgmt_functions }, + { NULL, NULL, NULL } +}; \ No newline at end of file diff --git a/gost_prov_signature.c b/gost_prov_signature.c new file mode 100644 index 0000000..4d32b95 --- /dev/null +++ b/gost_prov_signature.c @@ -0,0 +1,428 @@ +#include +#include +#include "gost_prov.h" +#include "gost_lcl.h" + +#define GOST_MAX_ALG_NAME_SIZE 50 /* Algorithm name */ +#define GOST_MAX_PROPQUERY_SIZE 256 /* Property query strings */ +#define GOST_NELEM(x) (sizeof(x) / sizeof((x)[0])) + +#define SIGN_OPERATION 0 +#define VERIFY_OPERATION 1 + +int gost_get_max_signature_size(const GOST_KEY_DATA *key_data) +{ + int size = -1; + + switch (key_data->type) { + case NID_id_GostR3410_2001: + case NID_id_GostR3410_2001DH: + case NID_id_GostR3410_2012_256: + size = 64; + break; + case NID_id_GostR3410_2012_512: + size = 128; + break; + default: + assert(!"Invalid key type"); + } + + return size; +} + +/* + * Forward declarations of all generic OSSL_DISPATCH functions, to make sure + * they are correctly defined further down. + */ +static OSSL_FUNC_signature_newctx_fn signature_newctx; +static OSSL_FUNC_signature_freectx_fn signature_free; +static OSSL_FUNC_signature_digest_sign_init_fn signature_digest_sign_init; +static OSSL_FUNC_signature_digest_sign_update_fn signature_digest_sign_update; +static OSSL_FUNC_signature_digest_sign_final_fn signature_digest_sign_final; +static OSSL_FUNC_signature_digest_verify_init_fn signature_digest_verify_init; +static OSSL_FUNC_signature_digest_verify_update_fn signature_digest_verify_update; +static OSSL_FUNC_signature_digest_verify_final_fn signature_digest_verify_final; +static OSSL_FUNC_signature_get_ctx_params_fn signature_get_ctx_params; +static OSSL_FUNC_signature_gettable_ctx_params_fn signature_gettable_ctx_params; + +typedef struct { + PROV_CTX *provctx; + GOST_KEY_DATA *key_data; + char *propq; + EVP_MD_CTX *mdctx; + EVP_MD *md; + int operation; +} GOST_SIGNATURE_CTX; + +typedef struct { + int key_type; + const char *sn; +} GOST_SUPPORTED_HASH; + +static const GOST_SUPPORTED_HASH supported_hash[] = { + {NID_id_GostR3410_2012_256, SN_id_GostR3411_2012_256}, + {NID_id_GostR3410_2012_512, SN_id_GostR3411_2012_512}, + {NID_id_GostR3410_2001, SN_id_GostR3411_94}, +}; + +static void signature_free(void *vctx) +{ + if (!vctx) + return; + + GOST_SIGNATURE_CTX *ctx = vctx; + + EVP_MD_CTX_free(ctx->mdctx); + EVP_MD_free(ctx->md); + OPENSSL_free(ctx->propq); + keymgmt_free(ctx->key_data); + OPENSSL_free(ctx); +} + +static void *signature_newctx(void *vprovctx, const char *propq) +{ + if (!vprovctx) + return NULL; + + GOST_SIGNATURE_CTX *ctx = OPENSSL_zalloc(sizeof(GOST_SIGNATURE_CTX)); + if (!ctx) + return NULL; + + ctx->provctx = vprovctx; + if (propq && (ctx->propq = OPENSSL_strdup(propq)) == NULL) { + OPENSSL_free(ctx); + return NULL; + } + + return ctx; +} + +static int is_digest_supported_for_key(int key_type, const EVP_MD *md) +{ + size_t i; + + for (i = 0; i < GOST_NELEM(supported_hash); ++i) { + if (supported_hash[i].key_type == key_type && EVP_MD_is_a(md, supported_hash[i].sn)) + return 1; + } + + return 0; +} + +static int signature_setup_md(GOST_SIGNATURE_CTX *ctx, const char *mdname, const char *mdprops) +{ + EVP_MD *md = NULL; + + if (mdprops == NULL) + mdprops = ctx->propq; + + if (mdname == NULL) + return 0; + + md = EVP_MD_fetch(ctx->provctx->libctx, mdname, mdprops); + if (md == NULL) + return 0; + + if (!is_digest_supported_for_key(ctx->key_data->type, md)) { + EVP_MD_free(md); + return 0; + } + + EVP_MD_CTX_free(ctx->mdctx); + EVP_MD_free(ctx->md); + + ctx->mdctx = NULL; + ctx->md = md; + + return 1; +} + +static int signature_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + GOST_SIGNATURE_CTX *ctx = vctx; + const OSSL_PARAM *p, *propsp; + char mdname[GOST_MAX_ALG_NAME_SIZE] = "", *pmdname = mdname; + char mdprops[GOST_MAX_PROPQUERY_SIZE] = "", *pmdprops = mdprops; + + if (params == NULL) + return 1; + + propsp = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_PROPERTIES); + if (propsp != NULL + && !OSSL_PARAM_get_utf8_string(propsp, &pmdprops, sizeof(mdprops))) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p != NULL + && !OSSL_PARAM_get_utf8_string(p, &pmdname, sizeof(mdname))) + return 0; + + if ((p != NULL || propsp != NULL) && !signature_setup_md(ctx, mdname, mdprops)) + return 0; + + return 1; +} + +static int signature_get_algorithm_id(GOST_SIGNATURE_CTX *ctx, OSSL_PARAM *p) +{ + int nid; + ASN1_OBJECT *oid = NULL; + X509_ALGOR *algor = NULL; + unsigned char *der = NULL; + int derlen; + int ret = 0; + + if (ctx == NULL || ctx->key_data == NULL) + return 0; + + switch (ctx->key_data->type) { + case NID_id_GostR3410_2001: + nid = NID_id_GostR3411_94_with_GostR3410_2001; + break; + case NID_id_GostR3410_2012_256: + nid = NID_id_tc26_signwithdigest_gost3410_2012_256; + break; + case NID_id_GostR3410_2012_512: + nid = NID_id_tc26_signwithdigest_gost3410_2012_512; + break; + default: + return 0; + } + + oid = OBJ_nid2obj(nid); + if (oid == NULL) + goto cleanup; + + algor = X509_ALGOR_new(); + if (algor == NULL) + goto cleanup; + + X509_ALGOR_set0(algor, oid, V_ASN1_NULL, NULL); + oid = NULL; + + derlen = i2d_X509_ALGOR(algor, &der); + if (derlen <= 0) + goto cleanup; + + if (!OSSL_PARAM_set_octet_string(p, der, derlen)) + goto cleanup; + + ret = 1; + +cleanup: + X509_ALGOR_free(algor); + ASN1_OBJECT_free(oid); + OPENSSL_free(der); + return ret; +} + +static int signature_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + if (vctx == NULL) + return 0; + + GOST_SIGNATURE_CTX *ctx = vctx; + + if (params == NULL) + return 1; + + OSSL_PARAM *p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p != NULL && !signature_get_algorithm_id(ctx, p)) + return 0; + + return 1; +} + +static const OSSL_PARAM signature_gettable_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *signature_gettable_ctx_params(void *vctx, void *provctx) +{ + return signature_gettable_params; +} + +static int signature_signverify_init(GOST_SIGNATURE_CTX *ctx, void *key_data, + const OSSL_PARAM params[], int operation) +{ + ctx->key_data = keymgmt_dup(key_data, + (OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS | + (operation == SIGN_OPERATION ? + OSSL_KEYMGMT_SELECT_PRIVATE_KEY : + OSSL_KEYMGMT_SELECT_PUBLIC_KEY))); + if (ctx->key_data == NULL) + return 0; + + ctx->operation = operation; + + if (!signature_set_ctx_params(ctx, params)) + return 0; + + return 1; +} + +static int signature_digest_signverify_init(GOST_SIGNATURE_CTX *ctx, const char *mdname, + void *key_data, const OSSL_PARAM params[], + int operation) +{ + if (!ctx + || !ctx->provctx + || !ctx->provctx->libctx + || !key_data) + goto error; + + if (!signature_signverify_init(ctx, key_data, params, + operation)) + goto error; + + if ((mdname != NULL) && !signature_setup_md(ctx, mdname, NULL)) + goto error; + + if (!ctx->md) + goto error; + + if (ctx->mdctx == NULL && ((ctx->mdctx = EVP_MD_CTX_new()) == NULL)) + goto error_clean_md; + + if (!EVP_DigestInit_ex2(ctx->mdctx, ctx->md, params)) + goto error_clean_md; + + return 1; +error_clean_md: + EVP_MD_CTX_free(ctx->mdctx); + EVP_MD_free(ctx->md); + ctx->md = NULL; + ctx->mdctx = NULL; +error: + return 0; +} + +static int signature_signverify_message_update(GOST_SIGNATURE_CTX *ctx, + const unsigned char *data, size_t datalen) +{ + if (!ctx->mdctx) + return 0; + + if (!data && datalen) + return 0; + + return EVP_DigestUpdate(ctx->mdctx, data, datalen); +} + +static int signature_digest_sign_init(void *ctx, const char *mdname, + void *provkey, + const OSSL_PARAM params[]) +{ + return signature_digest_signverify_init(ctx, mdname, provkey, params, SIGN_OPERATION); +} + +static int signature_digest_sign_update(void *vctx, const unsigned char *data, + size_t datalen) +{ + GOST_SIGNATURE_CTX *ctx = vctx; + + if (!ctx || ctx->operation != SIGN_OPERATION) + return 0; + + return signature_signverify_message_update(ctx, data, datalen); +} + +static int signature_digest_sign_final(void *vctx, unsigned char *sig, + size_t *siglen, size_t sigsize) +{ + GOST_SIGNATURE_CTX *ctx = vctx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (!ctx + || !ctx->mdctx + || !siglen + || ctx->operation != SIGN_OPERATION) + return 0; + + if (sig != NULL + && !EVP_DigestFinal_ex(ctx->mdctx, digest, &dlen)) + return 0; + + *siglen = sigsize; + return internal_pkey_ec_cp_sign(ctx->key_data->ec, ctx->key_data->type, sig, + siglen, digest, dlen); +} + +static int signature_digest_verify_init(void *ctx, const char *mdname, + void *provkey, + const OSSL_PARAM params[]) +{ + return signature_digest_signverify_init(ctx, mdname, provkey, params, VERIFY_OPERATION); +} + +static int signature_digest_verify_update(void *vctx, const unsigned char *data, + size_t datalen) +{ + GOST_SIGNATURE_CTX *ctx = vctx; + + if (!ctx || ctx->operation != VERIFY_OPERATION) + return 0; + + return signature_signverify_message_update(ctx, data, datalen); +} + +static int signature_digest_verify_final(void *vctx, const unsigned char *sig, + size_t siglen) +{ + GOST_SIGNATURE_CTX *ctx = vctx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (!sig || !ctx || !ctx->mdctx || ctx->operation != VERIFY_OPERATION) + return 0; + + if (!EVP_DigestFinal_ex(ctx->mdctx, digest, &dlen)) + return 0; + + return internal_pkey_ec_cp_verify(ctx->key_data->ec, sig, siglen, digest, dlen); +} + +typedef void (*fptr_t)(void); +static const OSSL_DISPATCH id_signature_functions[] = { + { OSSL_FUNC_SIGNATURE_NEWCTX, (fptr_t)signature_newctx }, + { OSSL_FUNC_SIGNATURE_FREECTX, (fptr_t)signature_free }, + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (fptr_t)signature_get_ctx_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, (fptr_t)signature_gettable_ctx_params}, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, (fptr_t)signature_digest_sign_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, (fptr_t)signature_digest_sign_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, (fptr_t)signature_digest_sign_final }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, (fptr_t)signature_digest_verify_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE, (fptr_t)signature_digest_verify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL, (fptr_t)signature_digest_verify_final}, + { 0, NULL } +}; + +const OSSL_ALGORITHM GOST_prov_signature[] = { + { + SN_id_GostR3410_2001 + ":" SN_id_GostR3411_94_with_GostR3410_2001 + ":" LN_id_GostR3411_94_with_GostR3410_2001 + ":" OID_id_GostR3411_94_with_GostR3410_2001, + NULL, + id_signature_functions + }, + { + SN_id_GostR3410_2012_256 + ":" SN_id_tc26_signwithdigest_gost3410_2012_256 + ":" LN_id_tc26_signwithdigest_gost3410_2012_256 + ":" OID_id_tc26_signwithdigest_gost3410_2012_256, + NULL, + id_signature_functions + }, + { + SN_id_GostR3410_2012_512 + ":" SN_id_tc26_signwithdigest_gost3410_2012_512 + ":" LN_id_tc26_signwithdigest_gost3410_2012_512 + ":" OID_id_tc26_signwithdigest_gost3410_2012_512, + NULL, + id_signature_functions + }, + { NULL, NULL, NULL } +}; diff --git a/gost_prov_tls.c b/gost_prov_tls.c new file mode 100644 index 0000000..b2f2551 --- /dev/null +++ b/gost_prov_tls.c @@ -0,0 +1,145 @@ +#include "gost_prov_tls.h" + +#include +#include +#include +#include + +#define OSSL_TLS_GROUP_ID_gc256A 0x0022 +#define OSSL_TLS_GROUP_ID_gc256B 0x0023 +#define OSSL_TLS_GROUP_ID_gc256C 0x0024 +#define OSSL_TLS_GROUP_ID_gc256D 0x0025 +#define OSSL_TLS_GROUP_ID_gc512A 0x0026 +#define OSSL_TLS_GROUP_ID_gc512B 0x0027 +#define OSSL_TLS_GROUP_ID_gc512C 0x0028 + +typedef struct tls_group_constants_st { + unsigned int group_id; /* Group ID */ + unsigned int secbits; /* Bits of security */ + int mintls; /* Minimum TLS version, -1 unsupported */ + int maxtls; /* Maximum TLS version (or 0 for undefined) */ + int mindtls; /* Minimum DTLS version, -1 unsupported */ + int maxdtls; /* Maximum DTLS version (or 0 for undefined) */ +} TLS_GROUP_CONSTANTS; + +static const TLS_GROUP_CONSTANTS group_list[] = { + { OSSL_TLS_GROUP_ID_gc256A, 128, TLS1_3_VERSION, 0, -1, -1 }, + { OSSL_TLS_GROUP_ID_gc256B, 128, TLS1_3_VERSION, 0, -1, -1 }, + { OSSL_TLS_GROUP_ID_gc256C, 128, TLS1_3_VERSION, 0, -1, -1 }, + { OSSL_TLS_GROUP_ID_gc256D, 128, TLS1_3_VERSION, 0, -1, -1 }, + { OSSL_TLS_GROUP_ID_gc512A, 256, TLS1_3_VERSION, 0, -1, -1 }, + { OSSL_TLS_GROUP_ID_gc512B, 256, TLS1_3_VERSION, 0, -1, -1 }, + { OSSL_TLS_GROUP_ID_gc512C, 256, TLS1_3_VERSION, 0, -1, -1 }, +}; + +#define TLS_GROUP_ENTRY(group_name, name_internal, alg, idx) \ + { \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME, \ + group_name, sizeof(group_name)), \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL, \ + name_internal, sizeof(name_internal)), \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, \ + alg, sizeof(alg)), \ + OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, \ + (unsigned int *)&group_list[idx].group_id), \ + OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS, \ + (unsigned int *)&group_list[idx].secbits), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, \ + (unsigned int *)&group_list[idx].mintls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, \ + (unsigned int *)&group_list[idx].maxtls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, \ + (unsigned int *)&group_list[idx].mindtls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, \ + (unsigned int *)&group_list[idx].maxdtls), \ + OSSL_PARAM_END \ + } + +static const OSSL_PARAM param_group_list[][10] = { + TLS_GROUP_ENTRY("GC256A", "TCA", SN_id_GostR3410_2012_256, 0), + TLS_GROUP_ENTRY("GC256B", "TCB", SN_id_GostR3410_2012_256, 1), + TLS_GROUP_ENTRY("GC256C", "TCC", SN_id_GostR3410_2012_256, 2), + TLS_GROUP_ENTRY("GC256D", "TCD", SN_id_GostR3410_2012_256, 3), + TLS_GROUP_ENTRY("GC512A", "A", SN_id_GostR3410_2012_512, 4), + TLS_GROUP_ENTRY("GC512B", "B", SN_id_GostR3410_2012_512, 5), + TLS_GROUP_ENTRY("GC512C", "C", SN_id_GostR3410_2012_512, 6), +}; + +int gost_prov_get_tls_group_capability(OSSL_CALLBACK *cb, void *arg) +{ + size_t i; + + for (i = 0; i < sizeof(param_group_list) / sizeof(param_group_list[0]); i++) + if (!cb(param_group_list[i], arg)) + return 0; + return 1; +} + +#define TLS_SIGALG_gostr34102012_256a 0x0709 +#define TLS_SIGALG_gostr34102012_256b 0x070A +#define TLS_SIGALG_gostr34102012_256c 0x070B +#define TLS_SIGALG_gostr34102012_256d 0x070C +#define TLS_SIGALG_gostr34102012_512a 0x070D +#define TLS_SIGALG_gostr34102012_512b 0x070E +#define TLS_SIGALG_gostr34102012_512c 0x070F + +typedef struct tls_sigalg_constants_st { + unsigned int code_point; /* SignatureScheme */ + unsigned int secbits; /* Bits of security */ + int mintls; /* Minimum TLS version, -1 unsupported */ + int maxtls; /* Maximum TLS version (or 0 for undefined) */ +} TLS_SIGALG_CONSTANTS; + +static const TLS_SIGALG_CONSTANTS gost_sigalg_constants[] = { + { TLS_SIGALG_gostr34102012_256a, 128, TLS1_3_VERSION, TLS1_3_VERSION }, + { TLS_SIGALG_gostr34102012_256b, 128, TLS1_3_VERSION, TLS1_3_VERSION }, + { TLS_SIGALG_gostr34102012_256c, 128, TLS1_3_VERSION, TLS1_3_VERSION }, + { TLS_SIGALG_gostr34102012_256d, 128, TLS1_3_VERSION, TLS1_3_VERSION }, + { TLS_SIGALG_gostr34102012_512a, 256, TLS1_3_VERSION, TLS1_3_VERSION }, + { TLS_SIGALG_gostr34102012_512b, 256, TLS1_3_VERSION, TLS1_3_VERSION }, + { TLS_SIGALG_gostr34102012_512c, 256, TLS1_3_VERSION, TLS1_3_VERSION }, +}; + +#define TLS_SIGALG_ENTRY(iana_name, sigalg_name, hash_name, idx) \ + { \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_SIGALG_IANA_NAME, iana_name, sizeof(iana_name)), \ + OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_SIGALG_CODE_POINT, \ + (unsigned int *)&gost_sigalg_constants[idx].code_point), \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_SIGALG_NAME, sigalg_name, sizeof(sigalg_name)), \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_SIGALG_HASH_NAME, hash_name, sizeof(hash_name)), \ + OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_SIGALG_SECURITY_BITS, \ + (unsigned int *)&gost_sigalg_constants[idx].secbits), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_SIGALG_MIN_TLS, \ + (int *)&gost_sigalg_constants[idx].mintls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_SIGALG_MAX_TLS, \ + (int *)&gost_sigalg_constants[idx].maxtls), \ + OSSL_PARAM_END \ + } + +static const OSSL_PARAM param_sigalg_list[][8] = { + TLS_SIGALG_ENTRY("gostr34102012_256a", SN_id_GostR3410_2012_256, + SN_id_GostR3411_2012_256, 0), + TLS_SIGALG_ENTRY("gostr34102012_256b", SN_id_GostR3410_2012_256, + SN_id_GostR3411_2012_256, 1), + TLS_SIGALG_ENTRY("gostr34102012_256c", SN_id_GostR3410_2012_256, + SN_id_GostR3411_2012_256, 2), + TLS_SIGALG_ENTRY("gostr34102012_256d", SN_id_GostR3410_2012_256, + SN_id_GostR3411_2012_256, 3), + TLS_SIGALG_ENTRY("gostr34102012_512a", SN_id_GostR3410_2012_512, + SN_id_GostR3411_2012_512, 4), + TLS_SIGALG_ENTRY("gostr34102012_512b", SN_id_GostR3410_2012_512, + SN_id_GostR3411_2012_512, 5), + TLS_SIGALG_ENTRY("gostr34102012_512c", SN_id_GostR3410_2012_512, + SN_id_GostR3411_2012_512, 6) +}; + +int gost_prov_get_tls_sigalg_capability(OSSL_CALLBACK *cb, void *arg) +{ + size_t i; + + for (i = 0; i < sizeof(param_sigalg_list) / sizeof(param_sigalg_list[0]); i++) + if (!cb(param_sigalg_list[i], arg)) + return 0; + + return 1; +} diff --git a/gost_prov_tls.h b/gost_prov_tls.h new file mode 100644 index 0000000..b5f4d7f --- /dev/null +++ b/gost_prov_tls.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +int gost_prov_get_tls_group_capability(OSSL_CALLBACK *cb, void *arg); +int gost_prov_get_tls_sigalg_capability(OSSL_CALLBACK *cb, void *arg); \ No newline at end of file diff --git a/patches/openssl-tls1.3.patch b/patches/openssl-tls1.3.patch new file mode 100644 index 0000000..95cfc4c --- /dev/null +++ b/patches/openssl-tls1.3.patch @@ -0,0 +1,603 @@ +diff --git a/openssl/crypto/evp/evp_enc.c b/openssl/crypto/evp/evp_enc.c +index f96d46f..1c657ce 100644 +--- a/openssl/crypto/evp/evp_enc.c ++++ b/openssl/crypto/evp/evp_enc.c +@@ -1293,6 +1293,14 @@ int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr) + params[0] = OSSL_PARAM_construct_octet_string( + OSSL_CIPHER_PARAM_AEAD_MAC_KEY, ptr, sz); + break; ++ case EVP_CTRL_TLSTREE: ++ params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_TLSTREE, ptr, sz); ++ params[1] = OSSL_PARAM_construct_end(); ++ break; ++ case EVP_CTRL_SET_TLSTREE_PARAMS: ++ params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_TLSTREE_MODE, ptr, sz); ++ params[1] = OSSL_PARAM_construct_end(); ++ break; + } + + if (set_params) +diff --git a/openssl/crypto/objects/obj_dat.h b/openssl/crypto/objects/obj_dat.h +index 4c61e96..8237adf 100644 +--- a/openssl/crypto/objects/obj_dat.h ++++ b/openssl/crypto/objects/obj_dat.h +@@ -1187,7 +1187,7 @@ static const unsigned char so[8504] = { + 0x2B,0x06,0x01,0x04,0x01,0x82,0xE4,0x25,0x01, /* [ 8494] OBJ_id_kp_wisun_fan_device */ + }; + +-#define NUM_NID 1324 ++#define NUM_NID 1326 + static const ASN1_OBJECT nid_objs[NUM_NID] = { + {"UNDEF", "undefined", NID_undef}, + {"rsadsi", "RSA Data Security, Inc.", NID_rsadsi, 6, &so[0]}, +@@ -2513,6 +2513,8 @@ static const ASN1_OBJECT nid_objs[NUM_NID] = { + {"id-on-hardwareModuleName", "Hardware Module Name", NID_id_on_hardwareModuleName, 8, &so[8486]}, + {"id-kp-wisun-fan-device", "Wi-SUN Alliance Field Area Network (FAN)", NID_id_kp_wisun_fan_device, 9, &so[8494]}, + {"NULL", "NULL", NID_ac_auditEntity}, ++ {"magma-mgm", "magma-mgm", NID_magma_mgm}, ++ {"kuznyechik-mgm", "kuznyechik-mgm", NID_kuznyechik_mgm}, + }; + + #define NUM_SN 1315 +diff --git a/openssl/include/openssl/evp.h b/openssl/include/openssl/evp.h +index 5466327..19c3b8e 100644 +--- a/openssl/include/openssl/evp.h ++++ b/openssl/include/openssl/evp.h +@@ -439,6 +439,7 @@ OSSL_DEPRECATEDIN_3_0 int + #define EVP_CTRL_GET_WRAP_CIPHER 0x29 + /* TLSTREE key diversification */ + #define EVP_CTRL_TLSTREE 0x2A ++#define EVP_CTRL_SET_TLSTREE_PARAMS 0x2B + + /* Padding modes */ + #define EVP_PADDING_PKCS7 1 +@@ -477,6 +478,10 @@ typedef struct { + /* Length of CCM8 tag for TLS */ + # define EVP_CCM8_TLS_TAG_LEN 8 + ++/* GOST TLS 1.3 tag lengths */ ++# define EVP_MAGMA_TLS_TAG_LEN 8 ++# define EVP_KUZNYECHIK_TLS_TAG_LEN 16 ++ + /* Length of tag for TLS */ + # define EVP_CHACHAPOLY_TLS_TAG_LEN 16 + +diff --git a/openssl/include/openssl/obj_mac.h b/openssl/include/openssl/obj_mac.h +index ea603c2..ea4af43 100644 +--- a/openssl/include/openssl/obj_mac.h ++++ b/openssl/include/openssl/obj_mac.h +@@ -4880,6 +4880,9 @@ + #define SN_kuznyechik_mac "kuznyechik-mac" + #define NID_kuznyechik_mac 1017 + ++#define SN_kuznyechik_mgm "kuznyechik-mgm" ++#define NID_kuznyechik_mgm 1325 ++ + #define SN_magma_ecb "magma-ecb" + #define NID_magma_ecb 1187 + +@@ -4898,6 +4901,9 @@ + #define SN_magma_mac "magma-mac" + #define NID_magma_mac 1192 + ++#define SN_magma_mgm "magma-mgm" ++#define NID_magma_mgm 1324 ++ + #define SN_camellia_128_cbc "CAMELLIA-128-CBC" + #define LN_camellia_128_cbc "camellia-128-cbc" + #define NID_camellia_128_cbc 751 +diff --git a/openssl/include/openssl/tls1.h b/openssl/include/openssl/tls1.h +index 8e9b110..8cadff9 100644 +--- a/openssl/include/openssl/tls1.h ++++ b/openssl/include/openssl/tls1.h +@@ -658,6 +658,12 @@ int SSL_CTX_set_tlsext_ticket_key_evp_cb + # define TLS1_CK_RSA_PSK_WITH_ARIA_128_GCM_SHA256 0x0300C06E + # define TLS1_CK_RSA_PSK_WITH_ARIA_256_GCM_SHA384 0x0300C06F + ++/* TLS1.3 GOST ciphersuites from RFC9367 */ ++# define TLS1_3_CK_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L 0x0300C103 ++# define TLS1_3_CK_GOSTR341112_256_WITH_MAGMA_MGM_L 0x0300C104 ++# define TLS1_3_CK_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S 0x0300C105 ++# define TLS1_3_CK_GOSTR341112_256_WITH_MAGMA_MGM_S 0x0300C106 ++ + /* a bundle of RFC standard cipher names, generated from ssl3_ciphers[] */ + # define TLS1_RFC_RSA_WITH_AES_128_SHA "TLS_RSA_WITH_AES_128_CBC_SHA" + # define TLS1_RFC_DHE_DSS_WITH_AES_128_SHA "TLS_DHE_DSS_WITH_AES_128_CBC_SHA" +@@ -850,6 +856,10 @@ int SSL_CTX_set_tlsext_ticket_key_evp_cb + # define TLS1_RFC_DHE_PSK_WITH_ARIA_256_GCM_SHA384 "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384" + # define TLS1_RFC_RSA_PSK_WITH_ARIA_128_GCM_SHA256 "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256" + # define TLS1_RFC_RSA_PSK_WITH_ARIA_256_GCM_SHA384 "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384" ++# define TLS1_RFC_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L "TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L" ++# define TLS1_RFC_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S "TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S" ++# define TLS1_RFC_GOSTR341112_256_WITH_MAGMA_MGM_L "TLS_GOSTR341112_256_WITH_MAGMA_MGM_L" ++# define TLS1_RFC_GOSTR341112_256_WITH_MAGMA_MGM_S "TLS_GOSTR341112_256_WITH_MAGMA_MGM_S" + + + /* +diff --git a/openssl/ssl/record/methods/tls13_meth.c b/openssl/ssl/record/methods/tls13_meth.c +index 6bbba84..40cbc0c 100644 +--- a/openssl/ssl/record/methods/tls13_meth.c ++++ b/openssl/ssl/record/methods/tls13_meth.c +@@ -78,6 +78,18 @@ static int tls13_set_crypto_state(OSSL_RECORD_LAYER *rl, int level, + ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); + return OSSL_RECORD_RETURN_FATAL; + } ++ ++ if (!rl->isdtls && rl->tlstree) { ++ int res = 0; ++ ++ if (rl->tlstree & TLS1_TLSTREE_S) ++ res = EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_SET_TLSTREE_PARAMS, 0, "strong"); ++ else if (rl->tlstree & TLS1_TLSTREE_L) ++ res = EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_SET_TLSTREE_PARAMS, 0, "light"); ++ ++ if (res <= 0) ++ return OSSL_RECORD_RETURN_FATAL; ++ } + end: + return OSSL_RECORD_RETURN_SUCCESS; + } +@@ -162,6 +174,11 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs, + for (loop = 0; loop < SEQ_NUM_SIZE; loop++) + nonce[offset + loop] = staticiv[offset + loop] ^ seq[loop]; + ++ if (!rl->isdtls && rl->tlstree && EVP_CIPHER_CTX_ctrl(enc_ctx, EVP_CTRL_TLSTREE, 0, seq) <= 0) { ++ RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); ++ return 0; ++ } ++ + if (!tls_increment_sequence_ctr(rl)) { + /* RLAYERfatal already called */ + return 0; +diff --git a/openssl/ssl/record/rec_layer_s3.c b/openssl/ssl/record/rec_layer_s3.c +index cce236b..48fedcb 100644 +--- a/openssl/ssl/record/rec_layer_s3.c ++++ b/openssl/ssl/record/rec_layer_s3.c +@@ -1310,6 +1310,10 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version, + tlstree = 1; + } + ++ if (SSL_CONNECTION_IS_TLS13(s)) ++ tlstree = (s->s3.tmp.new_cipher->algorithm2 & TLS1_TLSTREE_L) ++ | (s->s3.tmp.new_cipher->algorithm2 & TLS1_TLSTREE_S); ++ + if (use_etm) + *set++ = OSSL_PARAM_construct_int(OSSL_LIBSSL_RECORD_LAYER_PARAM_USE_ETM, + &use_etm); +diff --git a/openssl/ssl/s3_lib.c b/openssl/ssl/s3_lib.c +index 86d8198..19edb89 100644 +--- a/openssl/ssl/s3_lib.c ++++ b/openssl/ssl/s3_lib.c +@@ -146,6 +146,72 @@ static SSL_CIPHER tls13_ciphers[] = { + 384, + }, + #endif ++#ifndef OPENSSL_NO_GOST ++ { ++ 1, ++ TLS1_RFC_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L, ++ TLS1_RFC_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L, ++ TLS1_3_CK_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L, ++ SSL_kANY, ++ SSL_aANY, ++ SSL_KUZNYECHIK_MGM, ++ SSL_AEAD, ++ TLS1_3_VERSION, TLS1_3_VERSION, ++ 0, 0, ++ SSL_HIGH, ++ SSL_HANDSHAKE_MAC_GOST12_256 | TLS1_PRF_GOST12_256 | TLS1_TLSTREE | TLS1_TLSTREE_L, ++ 256, ++ 256, ++ }, ++ { ++ 1, ++ TLS1_RFC_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S, ++ TLS1_RFC_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S, ++ TLS1_3_CK_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S, ++ SSL_kANY, ++ SSL_aANY, ++ SSL_KUZNYECHIK_MGM, ++ SSL_AEAD, ++ TLS1_3_VERSION, TLS1_3_VERSION, ++ 0, 0, ++ SSL_HIGH, ++ SSL_HANDSHAKE_MAC_GOST12_256 | TLS1_PRF_GOST12_256 | TLS1_TLSTREE | TLS1_TLSTREE_S, ++ 256, ++ 256, ++ }, ++ { ++ 1, ++ TLS1_RFC_GOSTR341112_256_WITH_MAGMA_MGM_L, ++ TLS1_RFC_GOSTR341112_256_WITH_MAGMA_MGM_L, ++ TLS1_3_CK_GOSTR341112_256_WITH_MAGMA_MGM_L, ++ SSL_kANY, ++ SSL_aANY, ++ SSL_MAGMA_MGM, ++ SSL_AEAD, ++ TLS1_3_VERSION, TLS1_3_VERSION, ++ 0, 0, ++ SSL_HIGH, ++ SSL_HANDSHAKE_MAC_GOST12_256 | TLS1_PRF_GOST12_256 | TLS1_TLSTREE | TLS1_TLSTREE_L, ++ 256, ++ 256, ++ }, ++ { ++ 1, ++ TLS1_RFC_GOSTR341112_256_WITH_MAGMA_MGM_S, ++ TLS1_RFC_GOSTR341112_256_WITH_MAGMA_MGM_S, ++ TLS1_3_CK_GOSTR341112_256_WITH_MAGMA_MGM_S, ++ SSL_kANY, ++ SSL_aANY, ++ SSL_MAGMA_MGM, ++ SSL_AEAD, ++ TLS1_3_VERSION, TLS1_3_VERSION, ++ 0, 0, ++ SSL_HIGH, ++ SSL_HANDSHAKE_MAC_GOST12_256 | TLS1_PRF_GOST12_256 | TLS1_TLSTREE | TLS1_TLSTREE_S, ++ 256, ++ 256, ++ }, ++#endif + }; + + /* +diff --git a/openssl/ssl/ssl_ciph.c b/openssl/ssl/ssl_ciph.c +index e5d6237..78dd7de 100644 +--- a/openssl/ssl/ssl_ciph.c ++++ b/openssl/ssl/ssl_ciph.c +@@ -54,8 +54,10 @@ static const ssl_cipher_table ssl_cipher_table_cipher[SSL_ENC_NUM_IDX] = { + {SSL_CHACHA20POLY1305, NID_chacha20_poly1305}, /* SSL_ENC_CHACHA_IDX 19 */ + {SSL_ARIA128GCM, NID_aria_128_gcm}, /* SSL_ENC_ARIA128GCM_IDX 20 */ + {SSL_ARIA256GCM, NID_aria_256_gcm}, /* SSL_ENC_ARIA256GCM_IDX 21 */ +- {SSL_MAGMA, NID_magma_ctr_acpkm}, /* SSL_ENC_MAGMA_IDX */ +- {SSL_KUZNYECHIK, NID_kuznyechik_ctr_acpkm}, /* SSL_ENC_KUZNYECHIK_IDX */ ++ {SSL_MAGMA, NID_magma_ctr_acpkm}, /* SSL_ENC_MAGMA_IDX 22 */ ++ {SSL_KUZNYECHIK, NID_kuznyechik_ctr_acpkm}, /* SSL_ENC_KUZNYECHIK_IDX 23 */ ++ {SSL_MAGMA_MGM, NID_magma_mgm}, /* SSL_ENC_MAGMA_MGM_IDX 24 */ ++ {SSL_KUZNYECHIK_MGM, NID_kuznyechik_mgm}, /* SSL_ENC_KUZNYECHIK_MGM_IDX 25 */ + }; + + /* NB: make sure indices in this table matches values above */ +@@ -310,6 +312,23 @@ static int get_optional_pkey_id(const char *pkey_name) + + #endif + ++/* Checks to see if algorithms are fetchable */ ++#define IS_FETCHABLE(type, TYPE) \ ++ static int is_ ## type ## _fetchable(SSL_CTX *ctx, const char *name) \ ++ { \ ++ TYPE *impl; \ ++ \ ++ ERR_set_mark(); \ ++ impl = TYPE ## _fetch(ctx->libctx, name, ctx->propq); \ ++ ERR_pop_to_mark(); \ ++ if (impl == NULL) \ ++ return 0; \ ++ TYPE ## _free(impl); \ ++ return 1; \ ++ } ++ ++IS_FETCHABLE(keymgmt, EVP_KEYMGMT) ++ + int ssl_load_ciphers(SSL_CTX *ctx) + { + size_t i; +@@ -418,11 +437,14 @@ int ssl_load_ciphers(SSL_CTX *ctx) + else + ctx->disabled_mac_mask |= SSL_KUZNYECHIKOMAC; + +- if (!get_optional_pkey_id(SN_id_GostR3410_2001)) ++ if (!get_optional_pkey_id(SN_id_GostR3410_2001) ++ && !is_keymgmt_fetchable(ctx, SN_id_GostR3410_2001)) + ctx->disabled_auth_mask |= SSL_aGOST01 | SSL_aGOST12; +- if (!get_optional_pkey_id(SN_id_GostR3410_2012_256)) ++ if (!get_optional_pkey_id(SN_id_GostR3410_2012_256) ++ && !is_keymgmt_fetchable(ctx, SN_id_GostR3410_2012_256)) + ctx->disabled_auth_mask |= SSL_aGOST12; +- if (!get_optional_pkey_id(SN_id_GostR3410_2012_512)) ++ if (!get_optional_pkey_id(SN_id_GostR3410_2012_512) ++ && !is_keymgmt_fetchable(ctx, SN_id_GostR3410_2012_512)) + ctx->disabled_auth_mask |= SSL_aGOST12; + /* + * Disable GOST key exchange if no GOST signature algs are available * +@@ -1827,9 +1849,15 @@ char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf, int len) + case SSL_MAGMA: + enc = "MAGMA"; + break; ++ case SSL_MAGMA_MGM: ++ enc = "MAGMAMGM"; ++ break; + case SSL_KUZNYECHIK: + enc = "KUZNYECHIK"; + break; ++ case SSL_KUZNYECHIK_MGM: ++ enc = "KUZNYECHIKMGM"; ++ break; + case SSL_CHACHA20POLY1305: + enc = "CHACHA20/POLY1305(256)"; + break; +diff --git a/openssl/ssl/ssl_local.h b/openssl/ssl/ssl_local.h +index 277be30..bf36ac7 100644 +--- a/openssl/ssl/ssl_local.h ++++ b/openssl/ssl/ssl_local.h +@@ -153,6 +153,8 @@ + # define SSL_ARIA256GCM 0x00200000U + # define SSL_MAGMA 0x00400000U + # define SSL_KUZNYECHIK 0x00800000U ++# define SSL_MAGMA_MGM 0x01000000U ++# define SSL_KUZNYECHIK_MGM 0x02000000U + + # define SSL_AESGCM (SSL_AES128GCM | SSL_AES256GCM) + # define SSL_AESCCM (SSL_AES128CCM | SSL_AES256CCM | SSL_AES128CCM8 | SSL_AES256CCM8) +@@ -235,7 +237,9 @@ + * TLSTREE cipher/mac key derivation from draft-smyshlyaev-tls12-gost-suites + * (currently this also goes into algorithm2) + */ +-# define TLS1_TLSTREE 0x20000 ++# define TLS1_TLSTREE 0x20000 ++# define TLS1_TLSTREE_S 0x40000 ++# define TLS1_TLSTREE_L 0x80000 + + /* Ciphersuite supported in QUIC */ + # define SSL_QUIC 0x00040000U +@@ -335,31 +339,33 @@ + # define SSL_PKEY_ED448 8 + # define SSL_PKEY_NUM 9 + +-# define SSL_ENC_DES_IDX 0 +-# define SSL_ENC_3DES_IDX 1 +-# define SSL_ENC_RC4_IDX 2 +-# define SSL_ENC_RC2_IDX 3 +-# define SSL_ENC_IDEA_IDX 4 +-# define SSL_ENC_NULL_IDX 5 +-# define SSL_ENC_AES128_IDX 6 +-# define SSL_ENC_AES256_IDX 7 +-# define SSL_ENC_CAMELLIA128_IDX 8 +-# define SSL_ENC_CAMELLIA256_IDX 9 +-# define SSL_ENC_GOST89_IDX 10 +-# define SSL_ENC_SEED_IDX 11 +-# define SSL_ENC_AES128GCM_IDX 12 +-# define SSL_ENC_AES256GCM_IDX 13 +-# define SSL_ENC_AES128CCM_IDX 14 +-# define SSL_ENC_AES256CCM_IDX 15 +-# define SSL_ENC_AES128CCM8_IDX 16 +-# define SSL_ENC_AES256CCM8_IDX 17 +-# define SSL_ENC_GOST8912_IDX 18 +-# define SSL_ENC_CHACHA_IDX 19 +-# define SSL_ENC_ARIA128GCM_IDX 20 +-# define SSL_ENC_ARIA256GCM_IDX 21 +-# define SSL_ENC_MAGMA_IDX 22 +-# define SSL_ENC_KUZNYECHIK_IDX 23 +-# define SSL_ENC_NUM_IDX 24 ++# define SSL_ENC_DES_IDX 0 ++# define SSL_ENC_3DES_IDX 1 ++# define SSL_ENC_RC4_IDX 2 ++# define SSL_ENC_RC2_IDX 3 ++# define SSL_ENC_IDEA_IDX 4 ++# define SSL_ENC_NULL_IDX 5 ++# define SSL_ENC_AES128_IDX 6 ++# define SSL_ENC_AES256_IDX 7 ++# define SSL_ENC_CAMELLIA128_IDX 8 ++# define SSL_ENC_CAMELLIA256_IDX 9 ++# define SSL_ENC_GOST89_IDX 10 ++# define SSL_ENC_SEED_IDX 11 ++# define SSL_ENC_AES128GCM_IDX 12 ++# define SSL_ENC_AES256GCM_IDX 13 ++# define SSL_ENC_AES128CCM_IDX 14 ++# define SSL_ENC_AES256CCM_IDX 15 ++# define SSL_ENC_AES128CCM8_IDX 16 ++# define SSL_ENC_AES256CCM8_IDX 17 ++# define SSL_ENC_GOST8912_IDX 18 ++# define SSL_ENC_CHACHA_IDX 19 ++# define SSL_ENC_ARIA128GCM_IDX 20 ++# define SSL_ENC_ARIA256GCM_IDX 21 ++# define SSL_ENC_MAGMA_IDX 22 ++# define SSL_ENC_KUZNYECHIK_IDX 23 ++# define SSL_ENC_MAGMA_MGM_IDX 24 ++# define SSL_ENC_KUZNYECHIK_MGM_IDX 25 ++# define SSL_ENC_NUM_IDX 26 + + /*- + * SSL_kRSA <- RSA_ENC +diff --git a/openssl/ssl/statem/statem_lib.c b/openssl/ssl/statem/statem_lib.c +index a52b8af..6437f66 100644 +--- a/openssl/ssl/statem/statem_lib.c ++++ b/openssl/ssl/statem/statem_lib.c +@@ -501,10 +501,10 @@ MSG_PROCESS_RETURN tls_process_cert_verify(SSL_CONNECTION *s, PACKET *pkt) + #ifndef OPENSSL_NO_GOST + if (!SSL_USE_SIGALGS(s) + && ((PACKET_remaining(pkt) == 64 +- && (EVP_PKEY_get_id(pkey) == NID_id_GostR3410_2001 +- || EVP_PKEY_get_id(pkey) == NID_id_GostR3410_2012_256)) ++ && (EVP_PKEY_is_a(pkey, SN_id_GostR3410_2001) ++ || EVP_PKEY_is_a(pkey, SN_id_GostR3410_2012_256))) + || (PACKET_remaining(pkt) == 128 +- && EVP_PKEY_get_id(pkey) == NID_id_GostR3410_2012_512))) { ++ && EVP_PKEY_is_a(pkey, SN_id_GostR3410_2012_512)))) { + len = PACKET_remaining(pkt); + } else + #endif +@@ -539,10 +539,9 @@ MSG_PROCESS_RETURN tls_process_cert_verify(SSL_CONNECTION *s, PACKET *pkt) + } + #ifndef OPENSSL_NO_GOST + { +- int pktype = EVP_PKEY_get_id(pkey); +- if (pktype == NID_id_GostR3410_2001 +- || pktype == NID_id_GostR3410_2012_256 +- || pktype == NID_id_GostR3410_2012_512) { ++ if (EVP_PKEY_is_a(pkey, SN_id_GostR3410_2001) ++ || EVP_PKEY_is_a(pkey, SN_id_GostR3410_2012_256) ++ || EVP_PKEY_is_a(pkey, SN_id_GostR3410_2012_512)) { + if ((gost_data = OPENSSL_malloc(len)) == NULL) + goto err; + BUF_reverse(gost_data, data, len); +@@ -1931,8 +1930,6 @@ static int is_tls13_capable(const SSL_CONNECTION *s) + switch (i) { + case SSL_PKEY_DSA_SIGN: + case SSL_PKEY_GOST01: +- case SSL_PKEY_GOST12_256: +- case SSL_PKEY_GOST12_512: + continue; + default: + break; +@@ -1949,6 +1946,15 @@ static int is_tls13_capable(const SSL_CONNECTION *s) + curve = ssl_get_EC_curve_nid(s->cert->pkeys[SSL_PKEY_ECC].privatekey); + if (tls_check_sigalg_curve(s, curve)) + return 1; ++ ++ /* ++ * TODO: There must be an opportunity to check that the sig algs are ++ * consistent with the keys in case if the sig algs are provider-based. ++ * For example, OSSL_FUNC_keymgmt_get_params could return the list of ++ * sig alg code points supported by the key, and this list could be ++ * checked against the sig algs code points stored in ++ * SSL_CTX.sigalg_lookup_cache ++ */ + } + + return 0; +diff --git a/openssl/ssl/t1_lib.c b/openssl/ssl/t1_lib.c +index bd85167..9b3b821 100644 +--- a/openssl/ssl/t1_lib.c ++++ b/openssl/ssl/t1_lib.c +@@ -1565,14 +1565,19 @@ int ssl_setup_sigalgs(SSL_CTX *ctx) + /* Now complete cache and tls12_sigalgs list with provider sig information */ + cache_idx = OSSL_NELEM(sigalg_lookup_tbl); + for (i = 0; i < ctx->sigalg_list_len; i++) { ++ size_t idx; + TLS_SIGALG_INFO si = ctx->sigalg_list[i]; ++ ++ if (!ssl_cert_lookup_by_nid(OBJ_txt2nid(si.sigalg_name), &idx, ctx)) ++ goto err; ++ + cache[cache_idx].name = si.name; + cache[cache_idx].sigalg = si.code_point; + tls12_sigalgs_list[cache_idx] = si.code_point; + cache[cache_idx].hash = si.hash_name?OBJ_txt2nid(si.hash_name):NID_undef; + cache[cache_idx].hash_idx = ssl_get_md_idx(cache[cache_idx].hash); + cache[cache_idx].sig = OBJ_txt2nid(si.sigalg_name); +- cache[cache_idx].sig_idx = i + SSL_PKEY_NUM; ++ cache[cache_idx].sig_idx = idx; + cache[cache_idx].sigandhash = OBJ_txt2nid(si.sigalg_name); + cache[cache_idx].curve = NID_undef; + /* all provided sigalgs are enabled by load */ +@@ -2601,10 +2606,8 @@ static int tls12_sigalg_allowed(const SSL_CONNECTION *s, int op, + if (ssl_cert_is_disabled(SSL_CONNECTION_GET_CTX(s), lu->sig_idx)) + return 0; + +- if (lu->sig == NID_id_GostR3410_2012_256 +- || lu->sig == NID_id_GostR3410_2012_512 +- || lu->sig == NID_id_GostR3410_2001) { +- /* We never allow GOST sig algs on the server with TLSv1.3 */ ++ if (lu->sig == NID_id_GostR3410_2001) { ++ /* We never allow GOST2001 sig algs on the server with TLSv1.3 */ + if (s->server && SSL_CONNECTION_IS_TLS13(s)) + return 0; + if (!s->server +@@ -2640,6 +2643,18 @@ static int tls12_sigalg_allowed(const SSL_CONNECTION *s, int op, + } + } + ++ /* TLS1.2 GOST sig algs could not be negotiated for the use in TLS1.3 */ ++ if ((lu->sig == NID_id_GostR3410_2012_256 || lu->sig == NID_id_GostR3410_2012_512) ++ && (((s->server && SSL_CONNECTION_IS_TLS13(s))) ++ || (!s->server ++ && SSL_CONNECTION_GET_SSL(s)->method->version == TLS_ANY_VERSION ++ && s->s3.tmp.min_ver >= TLS1_3_VERSION)) ++ && (strcmp(lu->name, TLSEXT_SIGALG_gostr34102012_256_intrinsic_name) == 0 ++ || strcmp(lu->name, TLSEXT_SIGALG_gostr34102012_512_intrinsic_name) == 0 ++ || strcmp(lu->name, TLSEXT_SIGALG_gostr34102012_256_gostr34112012_256_name) == 0 ++ || strcmp(lu->name, TLSEXT_SIGALG_gostr34102012_512_gostr34112012_512_name) == 0)) ++ return 0; ++ + /* Finally see if security callback allows it */ + secbits = sigalg_security_bits(SSL_CONNECTION_GET_CTX(s), lu); + sigalgstr[0] = (lu->sigalg >> 8) & 0xff; +@@ -2994,9 +3009,12 @@ static int sig_cb(const char *elem, int len, void *arg) + if (sarg->ctx != NULL) { + /* Check if a provider supports the sigalg */ + for (i = 0; i < sarg->ctx->sigalg_list_len; i++) { +- if (sarg->ctx->sigalg_list[i].sigalg_name != NULL +- && strcmp(etmp, +- sarg->ctx->sigalg_list[i].sigalg_name) == 0) { ++ if ((sarg->ctx->sigalg_list[i].sigalg_name != NULL ++ && strcmp(etmp, ++ sarg->ctx->sigalg_list[i].sigalg_name) == 0) ++ || (sarg->ctx->sigalg_list[i].name != NULL ++ && strcmp(etmp, ++ sarg->ctx->sigalg_list[i].name) == 0)) { + sarg->sigalgs[sarg->sigalgcnt++] = + sarg->ctx->sigalg_list[i].code_point; + break; +diff --git a/openssl/ssl/tls13_enc.c b/openssl/ssl/tls13_enc.c +index 7846c73..ca81bb3 100644 +--- a/openssl/ssl/tls13_enc.c ++++ b/openssl/ssl/tls13_enc.c +@@ -386,26 +386,26 @@ static int derive_secret_key_and_iv(SSL_CONNECTION *s, const EVP_MD *md, + *ivlen = *taglen = (size_t)mac_mdleni; + *keylen = s->s3.tmp.new_mac_secret_size; + } else { ++ uint32_t algenc; + + *keylen = EVP_CIPHER_get_key_length(ciph); + ++ if (s->s3.tmp.new_cipher != NULL) { ++ algenc = s->s3.tmp.new_cipher->algorithm_enc; ++ } else if (s->session->cipher != NULL) { ++ /* We've not selected a cipher yet - we must be doing early data */ ++ algenc = s->session->cipher->algorithm_enc; ++ } else if (s->psksession != NULL && s->psksession->cipher != NULL) { ++ /* We must be doing early data with out-of-band PSK */ ++ algenc = s->psksession->cipher->algorithm_enc; ++ } else { ++ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); ++ return 0; ++ } ++ + mode = EVP_CIPHER_get_mode(ciph); + if (mode == EVP_CIPH_CCM_MODE) { +- uint32_t algenc; +- + *ivlen = EVP_CCM_TLS_IV_LEN; +- if (s->s3.tmp.new_cipher != NULL) { +- algenc = s->s3.tmp.new_cipher->algorithm_enc; +- } else if (s->session->cipher != NULL) { +- /* We've not selected a cipher yet - we must be doing early data */ +- algenc = s->session->cipher->algorithm_enc; +- } else if (s->psksession != NULL && s->psksession->cipher != NULL) { +- /* We must be doing early data with out-of-band PSK */ +- algenc = s->psksession->cipher->algorithm_enc; +- } else { +- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); +- return 0; +- } + if (algenc & (SSL_AES128CCM8 | SSL_AES256CCM8)) + *taglen = EVP_CCM8_TLS_TAG_LEN; + else +@@ -415,6 +415,12 @@ static int derive_secret_key_and_iv(SSL_CONNECTION *s, const EVP_MD *md, + + if (mode == EVP_CIPH_GCM_MODE) { + *taglen = EVP_GCM_TLS_TAG_LEN; ++#ifndef OPENSSL_NO_GOST ++ } else if (algenc & SSL_MAGMA_MGM) { ++ *taglen = EVP_MAGMA_TLS_TAG_LEN; ++ } else if (algenc & SSL_KUZNYECHIK_MGM) { ++ *taglen = EVP_KUZNYECHIK_TLS_TAG_LEN; ++#endif + } else { + /* CHACHA20P-POLY1305 */ + *taglen = EVP_CHACHAPOLY_TLS_TAG_LEN; +diff --git a/openssl/util/perl/OpenSSL/paramnames.pm b/openssl/util/perl/OpenSSL/paramnames.pm +index 163a61d..dafcf6c 100644 +--- a/openssl/util/perl/OpenSSL/paramnames.pm ++++ b/openssl/util/perl/OpenSSL/paramnames.pm +@@ -158,6 +158,9 @@ my %params = ( + 'CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN' => "tls1multi_encin", # octet_string + 'CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN' => "tls1multi_enclen", # size_t + ++ 'CIPHER_PARAM_TLSTREE' => "tlstree", # octet_string ++ 'CIPHER_PARAM_TLSTREE_MODE' => "tlstree_mode", # octet_string ++ + # digest parameters + 'DIGEST_PARAM_XOFLEN' => "xoflen", # size_t + 'DIGEST_PARAM_SSL3_MS' => "ssl3-ms", # octet string diff --git a/tcl_tests/ca.try b/tcl_tests/ca.try index a1ac8ed..ae6dd3e 100644 --- a/tcl_tests/ca.try +++ b/tcl_tests/ca.try @@ -9,7 +9,7 @@ start_tests "Тесты на команду ca" if {[info exists env(ALG_LIST)]} { set alg_pair_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_pair_list {gost2001:A {gost2001:B} gost2012_256:A {gost2012_256:C} gost2012_512:B {gost2012_256:B gost2012_512:A}}} "open" { set alg_pair_list { @@ -20,6 +20,28 @@ if {[info exists env(ALG_LIST)]} { gost2012_512:C {gost2012_256:B gost2012_256:TCB gost2012_512:B gost2012_512:C} } } + "openprov" { + set all_algorithms { + gost2001:A gost2001:B gost2001:C gost2001:XA gost2001:XB + gost2012_256:A gost2012_256:B gost2012_256:C gost2012_256:TCA gost2012_256:TCB gost2012_256:TCC gost2012_256:TCD + gost2012_512:A gost2012_512:B gost2012_512:C + } + + set run_all 0 + if {[info exists env(GOST_TEST_RUN_EXTENDED)]} { + set run_all 1 + } + + if {!$run_all} { + set alg_sample_count 5 + set all_algorithms [get_sample $all_algorithms $alg_sample_count] + } + + set alg_pair_list {} + foreach ca_alg $all_algorithms { + lappend alg_pair_list $ca_alg $all_algorithms + } + } } } diff --git a/tcl_tests/cms.try b/tcl_tests/cms.try index 1cc6d02..3393080 100644 --- a/tcl_tests/cms.try +++ b/tcl_tests/cms.try @@ -18,7 +18,7 @@ test "Creating CA 2012" { if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} } diff --git a/tcl_tests/cms_cs.try b/tcl_tests/cms_cs.try index 2a619b2..abe1992 100644 --- a/tcl_tests/cms_cs.try +++ b/tcl_tests/cms_cs.try @@ -9,7 +9,7 @@ start_tests "Тесты на интероперабельность резуль if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C}} "open" {set alg_list {gost2001:A gost2001:B gost2001:C}} } diff --git a/tcl_tests/cms_io.try b/tcl_tests/cms_io.try index 0283419..d5d6563 100644 --- a/tcl_tests/cms_io.try +++ b/tcl_tests/cms_io.try @@ -9,7 +9,7 @@ start_tests "Тесты на совместтимость cms и smime -sign" if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} } diff --git a/tcl_tests/cmsenc.try b/tcl_tests/cmsenc.try index f9e6666..f9d463c 100644 --- a/tcl_tests/cmsenc.try +++ b/tcl_tests/cmsenc.try @@ -68,7 +68,7 @@ test "Creating CA 2012" { if {[info exist env(ENC_LIST)]} { set enc_list $env(ENC_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} } diff --git a/tcl_tests/cmsenc_cs.try b/tcl_tests/cmsenc_cs.try index 10cb08a..96fd1e6 100644 --- a/tcl_tests/cmsenc_cs.try +++ b/tcl_tests/cmsenc_cs.try @@ -78,7 +78,7 @@ foreach hstname $hosts { if {[info exist env(ENC_LIST)]} { set enc_list $env(ENC_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 }} "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 }} } diff --git a/tcl_tests/cmsenc_io.try b/tcl_tests/cmsenc_io.try index 01eac8a..ea49f98 100644 --- a/tcl_tests/cmsenc_io.try +++ b/tcl_tests/cmsenc_io.try @@ -38,7 +38,7 @@ test -createsfiles io_cms_decrypt_nocert.rsa "RSA User 2 (without cert) can decr if {[info exist env(ENC_LIST)]} { set enc_list $env(ENC_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} } diff --git a/tcl_tests/cmsenc_sc.try b/tcl_tests/cmsenc_sc.try index ab489d0..c7de4c2 100644 --- a/tcl_tests/cmsenc_sc.try +++ b/tcl_tests/cmsenc_sc.try @@ -78,7 +78,7 @@ foreach hstname $hosts { if {[info exist env(ENC_LIST)]} { set enc_list $env(ENC_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 }} "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 }} } diff --git a/tcl_tests/dgst.try b/tcl_tests/dgst.try index 77d6365..41bce98 100644 --- a/tcl_tests/dgst.try +++ b/tcl_tests/dgst.try @@ -4,9 +4,14 @@ package require ossltest cd $::test::dir start_tests "Тесты на команду dgst" -switch -exact [engine_name] { +switch -exact [test_target_name] { "ccore" {set signalg { gost2001:A gost2012_256:A gost2012_512:A}} "open" {set signalg { gost2001:A gost2012_256:A gost2012_512:A}} + "openprov" { + set signalg { gost2001:A gost2001:B gost2001:C gost2001:XA gost2001:XB + gost2012_256:A gost2012_256:B gost2012_256:C gost2012_256:TCA gost2012_256:TCB gost2012_256:TCC gost2012_256:TCD + gost2012_512:A gost2012_512:B gost2012_512:C } + } } if {[info exists env(ALG_LIST)]} { @@ -114,8 +119,8 @@ foreach alg $alg_list { } else { test -skip {![file exists $username/seckey.pem]||![file exists dgst.dat]} -createsfiles "testmd5.bin" "Подпись несовместимого дайджеста $hash_alg c подписью $alg" { - grep Error [openssl "dgst -[hash_short_name $hash_alg] -sign $username/seckey.pem -out testmd5.bin dgst.dat"] - } 1 {invalid digest type} + [openssl "dgst -[hash_short_name $hash_alg] -sign $username/seckey.pem -out testmd5.bin dgst.dat"] + } 1 } } diff --git a/tcl_tests/engine.try b/tcl_tests/engine.try index f42dbf0..1aa80f9 100644 --- a/tcl_tests/engine.try +++ b/tcl_tests/engine.try @@ -4,7 +4,7 @@ package require ossltest cd $::test::dir start_tests "Тесты на команду engine" -switch -exact [engine_name] { +switch -exact [test_target_name] { "ccore" {set list " \[RAND, gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, id-tc26-cipher-gostr3412-2015-magma-ctracpkm, magma-ctr, magma-ofb, magma-ecb, magma-cbc, magma-cfb, grasshopper-ecb, grasshopper-cbc, grasshopper-ofb, grasshopper-cfb, grasshopper-ctr, id-tc26-cipher-gostr3412-2015-kuznyechik-ctracpkm, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, gost2001, id-GostR3410-2001DH, gost-mac, gost2012_256, gost2012_512, gost-mac-12\]\n"} "open" {set list "(gost) Reference implementation of GOST engine\n \[gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, kuznyechik-ecb, kuznyechik-cbc, kuznyechik-cfb, kuznyechik-ofb, kuznyechik-ctr, magma-ecb, kuznyechik-mgm, magma-cbc, magma-ctr, magma-ctr-acpkm, magma-ctr-acpkm-omac, magma-mgm, kuznyechik-ctr-acpkm, kuznyechik-ctr-acpkm-omac, magma-kexp15, kuznyechik-kexp15, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, magma-mac, kuznyechik-mac, kuznyechik-ctr-acpkm-omac, magma-ctr-acpkm-omac, gost2001, id-GostR3410-2001DH, gost-mac, gost2012_256, gost2012_512, gost-mac-12, magma-mac, kuznyechik-mac, magma-ctr-acpkm-omac, kuznyechik-ctr-acpkm-omac]\n"} } @@ -16,10 +16,10 @@ save_env2 {OPENSSL_CONF} set env(OPENSSL_CONF) [file join [pwd] no_engine.cnf] test "Проверяем поддержку российских алгоритмов" { - grep "gost" [openssl "engine -c $env(ENGINE_NAME)"] + grep "gost" [openssl "engine -c $env(TEST_TARGET_NAME)"] } 0 $list -if {[engine_name] == "ccore"} { +if {[test_target_name] == "ccore"} { test "Получение списка конфигурационных параметров" { openssl "engine -v cryptocom" } 0 "(cryptocom) Cryptocom GOST engine diff --git a/tcl_tests/get_test_target_name.tcl b/tcl_tests/get_test_target_name.tcl new file mode 100644 index 0000000..c966ebf --- /dev/null +++ b/tcl_tests/get_test_target_name.tcl @@ -0,0 +1,38 @@ +#!/usr/bin/tclsh +lappend auto_path . +package require ossltest + +proc getConfigLine {var {section ""}} { + global config + if {[string length $section]} { + if {[regexp -indices "\n\\s*\\\[\\s*$section\\s*\\\]\\s*\n" $config start]} { + set start [lindex $start 1] + } else { + return -code error "Section $section is not found" + } + } else { + set start 0 + } + if {[regexp -indices "\n\\s*\\\[\[^\n\]+\\\]\\s*\n" [string range $config $start end] end]} { + set end [expr $start+[lindex $end 0]] + } else { + set end end + } + if {![regexp "\n\\s*$var\\s*=\\s*(\\S\[^\n\]+?)\\s*\n" "\n[string range $config $start $end]" => value]} { + return -code error "No variable $var in section $section" + } + return $value +} + +set config [getConfig] + +set openssl_def [getConfigLine openssl_conf] + +if {[catch {getConfigLine {(?!\s*default)[^#=]+?} [getConfigLine providers $openssl_def]} provider_section] == 0} { + puts [getConfigLine identity $provider_section] + exit 0 +} + +set engine_section [getConfigLine {[^#]+} [getConfigLine engines $openssl_def ]] + +puts [getConfigLine engine_id $engine_section] diff --git a/tcl_tests/getengine.tcl b/tcl_tests/getengine.tcl deleted file mode 100644 index 49176c0..0000000 --- a/tcl_tests/getengine.tcl +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/tclsh -lappend auto_path . -package require ossltest - -proc getConfigLine {var {section ""}} { - global config - if {[string length $section]} { - if {[regexp -indices "\n\\s*\\\[\\s*$section\\s*\\\]\\s*\n" $config start]} { - set start [lindex $start 1] - } else { - return -code error "Section $section is not found" - } - } else { - set start 0 - } - if {[regexp -indices "\n\\s*\\\[\[^\n\]+\\\]\\s*\n" [string range $config $start end] end]} { - set end [expr $start+[lindex $end 0]] - } else { - set end end - } - if {![regexp "\n\\s*$var\\s*=\\s*(\\S\[^\n\]+?)\\s*\n" "\n[string range $config $start $end]" => value]} { - return -code error "No variable $var in section $section" - } - return $value -} - -set config [getConfig] - -set openssl_def [getConfigLine openssl_conf] - -set engine_section [getConfigLine {[^#]+} [getConfigLine engines $openssl_def ]] - -puts [getConfigLine engine_id $engine_section] - - - - diff --git a/tcl_tests/mac.try b/tcl_tests/mac.try index 8305f5d..6e68acb 100644 --- a/tcl_tests/mac.try +++ b/tcl_tests/mac.try @@ -4,6 +4,23 @@ package require ossltest cd $::test::dir start_tests "Тесты на команду dgst с MAC" +proc mac_testcase { alg file mac_hex macopts } { + set opts [split $macopts " "] + + if {[string equal [test_target_name] "openprov"]} { + set cmd [list mac -in $file] + lappend cmd {*}$opts $alg + set expected [string toupper $mac_hex] + } else { + set cmd [list dgst -mac $alg] + lappend cmd {*}$opts $file + set expected "[string toupper $alg]-$alg\($file\)= $mac_hex" + } + + set result [openssl $cmd] + return [string equal [string trim $result] [string trim $expected]] +} + test -createsfiles {dgst.dat dgst0.dat dgst2.dat dgst8.dat dgst63.dat mac-grasshopper.dat mac-magma.dat} "Формирование тестовых данных" { makeFile dgst.dat [string repeat "Test data to digest.\n" 100] binary makeFile dgst0.dat "" binary @@ -31,91 +48,90 @@ test "Вычисление двух HMAC(md_gost94)" { test "Попытка вычислить MAC с ключом неправильной длины" { grep gost-mac [openssl "dgst -mac gost-mac -macopt key:123456789012345678901234567890 dgst.dat"] -} 1 "invalid mac key length" +} 1 "" test "Попытка вычислить MAC с hex ключом неправильной длины" { grep gost-mac [openssl "dgst -mac gost-mac -macopt hexkey:414243444546474849404142434445464748494041424344454647484940 dgst.dat"] -} 1 "invalid mac key length" +} 1 "" test "Вычисление MAC gost89" { - grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 dgst.dat"] -} 0 "GOST-MAC-gost-mac(dgst.dat)= 37f646d2\n" + mac_testcase "gost-mac" "dgst.dat" "37f646d2" "-macopt key:12345678901234567890123456789012" +} 0 1 -test "Вычисление двух MAC gost89" { +test -skip {[string equal [test_target_name] "openprov"]} "Вычисление двух MAC gost89" { grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 dgst.dat dgst.dat"] } 0 "GOST-MAC-gost-mac(dgst.dat)= 37f646d2\nGOST-MAC-gost-mac(dgst.dat)= 37f646d2\n" -test "Вычислиение MAC gost89 с шестнацатиричным ключом" { - grep gost-mac [openssl "dgst -mac gost-mac -macopt hexkey:3132333435363738393031323334353637383930313233343536373839303132 dgst.dat"] -} 0 "GOST-MAC-gost-mac(dgst.dat)= 37f646d2\n" +test "Вычисление MAC gost89 с шестнадцатиричным ключом" { + mac_testcase "gost-mac" "dgst.dat" "37f646d2" "-macopt hexkey:3132333435363738393031323334353637383930313233343536373839303132" +} 0 1 test "Вычисление MAC gost89 от файла нулевой длины" { - grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 dgst0.dat"] -} 0 "GOST-MAC-gost-mac(dgst0.dat)= 00000000\n" + mac_testcase "gost-mac" "dgst0.dat" "00000000" "-macopt key:12345678901234567890123456789012" +} 0 1 test "Вычисление MAC gost89 от файла длины 2" { - grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 dgst2.dat"] -} 0 "GOST-MAC-gost-mac(dgst2.dat)= 87ea321f\n" + mac_testcase "gost-mac" "dgst2.dat" "87ea321f" "-macopt key:12345678901234567890123456789012" +} 0 1 test "Вычисление MAC gost89 от файла длины 8" { - grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 dgst8.dat"] -} 0 "GOST-MAC-gost-mac(dgst8.dat)= ad9aeae0\n" + mac_testcase "gost-mac" "dgst8.dat" "ad9aeae0" "-macopt key:12345678901234567890123456789012" +} 0 1 test "Вычисление MAC gost8912" { - grep gost-mac [openssl "dgst -mac gost-mac-12 -macopt key:12345678901234567890123456789012 dgst8.dat"] -} 0 "GOST-MAC-12-gost-mac-12(dgst8.dat)= be70ba5e\n" + mac_testcase "gost-mac-12" "dgst8.dat" "be70ba5e" "-macopt key:12345678901234567890123456789012" +} 0 1 -test "Вычисление MAC gost89 со сменой параметров на параметры от gost8912 (symbolic)" { +test -skip {[string equal [test_target_name] "openprov"]} "Вычисление MAC gost89 со сменой параметров на параметры от gost8912 (symbolic)" { grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 -macopt paramset:id-tc26-gost-28147-param-Z dgst8.dat"] } 0 "GOST-MAC-gost-mac(dgst8.dat)= be70ba5e\n" -test "Вычисление MAC gost8912 со сменой параметров на параметры от gost89 (OID)" { +test -skip {[string equal [test_target_name] "openprov"]} "Вычисление MAC gost8912 со сменой параметров на параметры от gost89 (OID)" { grep gost-mac [openssl "dgst -mac gost-mac-12 -macopt key:12345678901234567890123456789012 -macopt paramset:1.2.643.2.2.31.1 dgst8.dat"] } 0 "GOST-MAC-12-gost-mac-12(dgst8.dat)= ad9aeae0\n" -test "Вычисление MAC gost89 со сменой параметров на параметры 1.2.643.2.2.31.2" { +test -skip {[string equal [test_target_name] "openprov"]} "Вычисление MAC gost89 со сменой параметров на параметры 1.2.643.2.2.31.2" { grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 -macopt paramset:1.2.643.2.2.31.2 dgst8.dat"] } 0 "GOST-MAC-gost-mac(dgst8.dat)= c7fdc644\n" -test "Вычисление MAC gost8912 со сменой параметров на параметры id-Gost28147-89-CryptoPro-B-ParamSet" { +test -skip {[string equal [test_target_name] "openprov"]} "Вычисление MAC gost8912 со сменой параметров на параметры id-Gost28147-89-CryptoPro-B-ParamSet" { grep gost-mac [openssl "dgst -mac gost-mac-12 -macopt key:12345678901234567890123456789012 -macopt paramset:id-Gost28147-89-CryptoPro-B-ParamSet dgst8.dat"] } 0 "GOST-MAC-12-gost-mac-12(dgst8.dat)= c7fdc644\n" test "Вычисление MAC gost89 с изменение длины имитовставки (8)" { - grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 -sigopt size:8 dgst8.dat"] -} 0 "GOST-MAC-gost-mac(dgst8.dat)= ad9aeae05a7f6f71\n" + mac_testcase "gost-mac" "dgst8.dat" "ad9aeae05a7f6f71" \ + "-macopt key:12345678901234567890123456789012 -macopt size:8" +} 0 1 test "Вычисление MAC gost8912 с изменение длины имитовставки (6)" { - grep gost-mac [openssl "dgst -mac gost-mac-12 -macopt key:12345678901234567890123456789012 -sigopt size:6 dgst8.dat"] -} 0 "GOST-MAC-12-gost-mac-12(dgst8.dat)= be70ba5ed6b0\n" + mac_testcase "gost-mac-12" "dgst8.dat" "be70ba5ed6b0" \ + "-macopt key:12345678901234567890123456789012 -macopt size:6" +} 0 1 test "Вычисление MAC gost89 с изменение длины имитовставки (2)" { - grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 -sigopt size:3 dgst8.dat"] -} 0 "GOST-MAC-gost-mac(dgst8.dat)= ad9aea\n" - -test "Вычисление MAC gost89 с изменение длины имитовставки через macopt" { - grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 -macopt size:3 dgst8.dat"] -} 0 "GOST-MAC-gost-mac(dgst8.dat)= ad9aea\n" + mac_testcase "gost-mac" "dgst8.dat" "ad9a" \ + "-macopt key:12345678901234567890123456789012 -macopt size:2" +} 0 1 -test "Вычисление MAC gost8912 с изменение длины имитовставки через macopt" { - grep gost-mac [openssl "dgst -mac gost-mac-12 -macopt key:12345678901234567890123456789012 -macopt size:6 dgst8.dat"] -} 0 "GOST-MAC-12-gost-mac-12(dgst8.dat)= be70ba5ed6b0\n" +test -skip {[string equal [test_target_name] "openprov"]} "Вычисление MAC gost89 с изменение длины имитовставки через sigopt" { + mac_testcase "gost-mac" "dgst8.dat" "ad9aea" "-macopt key:12345678901234567890123456789012 -sigopt size:3" +} 0 1 -test "Вычисление MAC gost8912 с изменение длины имитовставки:sigopt переписывает macopt " { - grep gost-mac [openssl "dgst -mac gost-mac-12 -macopt key:12345678901234567890123456789012 -macopt size:2 -sigopt size:6 dgst8.dat"] -} 0 "GOST-MAC-12-gost-mac-12(dgst8.dat)= be70ba5ed6b0\n" +test -skip {[string equal [test_target_name] "openprov"]} "Вычисление MAC gost8912 с изменение длины имитовставки через sigopt" { + mac_testcase "gost-mac-12" "dgst8.dat" "be70ba5ed6b0" "-macopt key:12345678901234567890123456789012 -sigopt size:6" +} 0 1 -test "Вычисление MAC gost8912 с изменение длины имитовставки:sigopt переписывает macopt " { +test -skip {[string equal [test_target_name] "openprov"]} "Вычисление MAC gost8912 с изменение длины имитовставки:sigopt переписывает macopt " { grep gost-mac [openssl "dgst -mac gost-mac-12 -macopt key:12345678901234567890123456789012 -macopt size:2 -sigopt size:6 dgst8.dat"] } 0 "GOST-MAC-12-gost-mac-12(dgst8.dat)= be70ba5ed6b0\n" test "Вычисление MAC magma-mac (пример из ГОСТ 2015 34.13)" { - grep magma-mac [openssl "dgst -mac magma-mac -macopt hexkey:ffeeddccbbaa99887766554433221100f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff mac-magma.dat"] -} 0 "MAGMA-MAC-magma-mac(mac-magma.dat)= 154e72102030c5bb\n" + mac_testcase "magma-mac" "mac-magma.dat" "154e72102030c5bb" "-macopt hexkey:ffeeddccbbaa99887766554433221100f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" +} 0 1 #FIXME my regression test "Вычисление MAC grasshopper-mac (пример из ГОСТ 2015 34.13)" { - grep kuznyechik-mac [openssl "dgst -mac kuznyechik-mac -macopt hexkey:8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef mac-grasshopper.dat"] -} 0 "KUZNYECHIK-MAC-kuznyechik-mac(mac-grasshopper.dat)= 336f4d296059fbe34ddeb35b37749c67\n" + mac_testcase "kuznyechik-mac" "mac-grasshopper.dat" "336f4d296059fbe34ddeb35b37749c67" "-macopt hexkey:8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef" +} 0 1 end_tests diff --git a/tcl_tests/ocsp.try b/tcl_tests/ocsp.try index 92687b1..30b9b80 100644 --- a/tcl_tests/ocsp.try +++ b/tcl_tests/ocsp.try @@ -9,7 +9,7 @@ start_tests "Тесты на OCSP-запросы и ответы" if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2012_256:A gost2012_512:B}} "open" {set alg_list {gost2001:A gost2012_256:A gost2012_512:B}} } diff --git a/tcl_tests/openssl-gost-engine.cnf b/tcl_tests/openssl-gost-engine.cnf new file mode 100644 index 0000000..6fd5305 --- /dev/null +++ b/tcl_tests/openssl-gost-engine.cnf @@ -0,0 +1,11 @@ +openssl_conf = openssl_def + +[openssl_def] +engines = engine_section + +[engine_section] +gost = gost_section + +[gost_section] +engine_id = gost +default_algorithms = ALL diff --git a/tcl_tests/openssl-gost-provider.cnf b/tcl_tests/openssl-gost-provider.cnf new file mode 100644 index 0000000..14b1e3a --- /dev/null +++ b/tcl_tests/openssl-gost-provider.cnf @@ -0,0 +1,14 @@ +openssl_conf = openssl_def + +[openssl_def] +providers = provider_sect + +[provider_sect] +default = default_sect +gostprov = gost_sect + +[default_sect] +activate = 1 +[gost_sect] +activate = 1 +identity = gostprov \ No newline at end of file diff --git a/tcl_tests/openssl-gost.cnf b/tcl_tests/openssl-gost.cnf deleted file mode 100644 index 6fd5305..0000000 --- a/tcl_tests/openssl-gost.cnf +++ /dev/null @@ -1,11 +0,0 @@ -openssl_conf = openssl_def - -[openssl_def] -engines = engine_section - -[engine_section] -gost = gost_section - -[gost_section] -engine_id = gost -default_algorithms = ALL diff --git a/tcl_tests/ossltest.tcl b/tcl_tests/ossltest.tcl index ed657d6..0990a3b 100644 --- a/tcl_tests/ossltest.tcl +++ b/tcl_tests/ossltest.tcl @@ -106,7 +106,24 @@ proc check_builtin_engine {} { } return $found } - + +# Получение случайно подвыборки из доступного списка +proc get_sample {list count} { + set result {} + set n [llength $list] + if {$count >= $n} { + return $list + } + unset -nocomplain seen; + while {[llength $result] < $count} { + set idx [expr {int(rand() * $n)}] + if {![info exists seen($idx)]} { + lappend result [lindex $list $idx] + set seen($idx) 1 + } + } + return $result +} # Вызывает команду openssl. # Посылает в лог вывод на stdout и на stderr, возвращает его же. @@ -299,7 +316,7 @@ proc generate_key {params filename} { set exesuffix "" } log "Keyname is $keyname" -# if {[engine_name] eq "open"} { +# if {[test_target_name] eq "open"} { log "Calling openssl cmd to create private key" openssl "genpkey $optname -out $filename" # } elseif {[info exists ::env(OBJ)] && [file executable ../$::env(OBJ)/keytest$exesuffix]&& $alg eq "gost2001"} { @@ -844,7 +861,7 @@ proc open_server {server_args} { global output finished #puts -nonewline stderr "Waiting for server startup..." - while {![regexp "\nACCEPT\n" $output($server)]} { + while {![regexp "ACCEPT" $output($server)]} { vwait output($server) if {[info exists finished($server)]} { #puts stderr "error" @@ -1054,15 +1071,16 @@ proc der_from_pem {pem} { } } -proc engine_name {} { +proc test_target_name {} { global env - if {[info exists env(ENGINE_NAME)]} { - switch -exact $env(ENGINE_NAME) { + if {[info exists env(TEST_TARGET_NAME)]} { + switch -exact $env(TEST_TARGET_NAME) { "open" {return "open"} "gost" {return "open"} + "gostprov" {return "openprov"} "cryptocom" {return "ccore"} "ccore" {return "ccore"} - default {error "Unknown engine '$env(ENGINE_NAME)'"} + default {error "Unknown engine '$env(TEST_TARGET_NAME)'"} } } else { return "ccore" diff --git a/tcl_tests/pkcs12.try b/tcl_tests/pkcs12.try index 40dce89..a9f3c23 100644 --- a/tcl_tests/pkcs12.try +++ b/tcl_tests/pkcs12.try @@ -8,7 +8,7 @@ start_tests "Тесты на команду pkcs12" if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2001:XA gost2001:XB gost2012_256:A gost2012_256:B gost2012_256:C gost2012_256:XA gost2012_256:XB gost2012_512:A gost2012_512:B}} "open" { set alg_list { diff --git a/tcl_tests/pkcs8.try b/tcl_tests/pkcs8.try index d40c612..5f802a1 100644 --- a/tcl_tests/pkcs8.try +++ b/tcl_tests/pkcs8.try @@ -2,6 +2,12 @@ lappend auto_path [file dirname [info script]] package require ossltest cd $::test::dir + +set is_provider 0 +switch -exact [test_target_name] { + "openprov" { set is_provider 1 } +} + start_tests "тесты на команду pkcs8" set key "-----BEGIN PRIVATE KEY----- MEUCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgIgSZ82qYpu6RQj @@ -25,7 +31,7 @@ test "Конвертируем в DER и проверяем, что ключ т save_env2 {CRYPT_PARAMS GOST_PBE_HMAC} -test "Зашифровываем незашифрованный ключ gost2001, параметры CryptoPro-A" { +test -skip {$is_provider} "Зашифровываем незашифрованный ключ gost2001, параметры CryptoPro-A" { makeFile pkcs8-1A.key $key set env(CRYPT_PARAMS) "id-Gost28147-89-CryptoPro-A-ParamSet" set env(GOST_PBE_HMAC) "md_gost94" @@ -34,19 +40,19 @@ test "Зашифровываем незашифрованный ключ gost200 } 0 1 restore_env2 {CRYPT_PARAMS GOST_PBE_HMAC} -test -skip {![file exists encA.key]} "Проверяем OID-ы PBE" { +test -skip {$is_provider || ![file exists encA.key]} "Проверяем OID-ы PBE" { set res [extract_oids encA.key] regexp "HMAC GOST 34\.11-94" $res && regexp "GOST .*89" } 0 1 -test "Расшифровываем зашифрованный ключ gost2001" { +test -skip {$is_provider} "Расшифровываем зашифрованный ключ gost2001" { set unencrypted [openssl [list pkcs8 -passin pass:qwertyu -topk8 -nocrypt -in encA.key]] openssl [list pkey -text -noout << $unencrypted] } 0 $etalon save_env2 {CRYPT_PARAMS GOST_PBE_HMAC} -test "Зашифровываем незашифрованный ключ gost2001, параметры CryptoPro-B" { +test -skip {$is_provider} "Зашифровываем незашифрованный ключ gost2001, параметры CryptoPro-B" { makeFile pkcs8-1B.key $key set env(CRYPT_PARAMS) "id-Gost28147-89-CryptoPro-B-ParamSet" set env(GOST_PBE_HMAC) "md_gost94" @@ -55,20 +61,20 @@ test "Зашифровываем незашифрованный ключ gost200 } 0 1 restore_env2 {CRYPT_PARAMS GOST_PBE_HMAC} -test -skip {![file exists encB.key]} "Проверяем OID-ы PBE" { +test -skip {$is_provider || ![file exists encB.key]} "Проверяем OID-ы PBE" { set res [extract_oids encB.key] regexp "HMAC GOST 34\.11-94" $res && regexp "GOST .*89" } 0 1 -test "Расшифровываем зашифрованный ключ gost2001" { +test -skip {$is_provider} "Расшифровываем зашифрованный ключ gost2001" { set unencrypted [openssl [list pkcs8 -passin pass:qwertyu -topk8 -nocrypt -in encB.key]] openssl [list pkey -text -noout << $unencrypted] } 0 $etalon -test "Расшифровываем ключ, созданный mkkey" { +test -skip {$is_provider} "Расшифровываем ключ, созданный mkkey" { makeFile pkcs8-2.key "-----BEGIN ENCRYPTED PRIVATE KEY----- MIGvMFUGCSqGSIb3DQEFDTBIMCcGCSqGSIb3DQEFDDAaBAjIvbrnGmGbTAIC CAAwCgYGKoUDAgIKBQAwHQYGKoUDAgIVMBMECOtWtCMQo3dzBgcqhQMCAh8B @@ -80,7 +86,7 @@ bxdi6rTNsYqxWm26qUHz6Op5SvCm0y+f8zE9cACQ5KQnFvNlojHvzmjO+Q== openssl [list pkey -text -noout << $unencrypted] } 0 $etalon -test "Расшифровываем ключ, созданный mkkey, русский пароль" { +test -skip {$is_provider} "Расшифровываем ключ, созданный mkkey, русский пароль" { set env(PASS) [encoding convertfrom [encoding convertto utf-8 [rus "йцукенг"]]] makeFile pkcs8-3.key "-----BEGIN ENCRYPTED PRIVATE KEY----- MIGvMFUGCSqGSIb3DQEFDTBIMCcGCSqGSIb3DQEFDDAaBAgSfbLQ+fNe0AIC @@ -114,7 +120,7 @@ test "Конвертируем в DER и проверяем, что ключ т } 0 $etalon save_env2 {CRYPT_PARAMS GOST_PBE_HMAC} -test "Зашифровываем незашифрованный ключ gost2012_256, параметры TK26 (умолчательные)" { +test -skip {$is_provider} "Зашифровываем незашифрованный ключ gost2012_256, параметры TK26 (умолчательные)" { makeFile pkcs8-256.key $key256 catch {unset env(CRYPT_PARAMS)} catch {unset env(GOST_PBE_HMAC)} @@ -123,12 +129,12 @@ test "Зашифровываем незашифрованный ключ gost201 } 0 1 restore_env2 {CRYPT_PARAMS GOST_PBE_HMAC} -test -skip {![file exists enc256.key]} "Проверяем OID-ы PBE" { +test -skip {$is_provider || ![file exists enc256.key]} "Проверяем OID-ы PBE" { set res [extract_oids enc256.key] regexp "HMAC GOST 34\.11-2012" $res && regexp "GOST .*89" } 0 1 -test "Расшифровываем зашифрованный ключ gost2012_256" { +test -skip {$is_provider} "Расшифровываем зашифрованный ключ gost2012_256" { set unencrypted [openssl [list pkcs8 -passin pass:qwertyu -topk8 -nocrypt -in enc256.key]] openssl [list pkey -text -noout << $unencrypted] @@ -156,7 +162,7 @@ test "Конвертируем в DER и проверяем, что ключ т openssl [list pkey -inform DER -text -noout -in pkcs8-512.der] } 0 $etalon -test "Зашифровываем незашифрованный ключ gost2012_512, параметры TK26 (умолчательные)" { +test -skip {$is_provider} "Зашифровываем незашифрованный ключ gost2012_512, параметры TK26 (умолчательные)" { makeFile pkcs8-512.key $key512 catch {unset env(CRYPT_PARAMS)} set env(GOST_PBE_HMAC) "md_gost12_512" @@ -165,12 +171,12 @@ test "Зашифровываем незашифрованный ключ gost201 } 0 1 restore_env2 {CRYPT_PARAMS GOST_PBE_HMAC} -test -skip {![file exists enc512.key]} "Проверяем OID-ы PBE" { +test -skip {$is_provider || ![file exists enc512.key]} "Проверяем OID-ы PBE" { set res [extract_oids enc512.key] regexp "HMAC GOST 34\.11-2012" $res && regexp "GOST .*89" } 0 1 -test "Расшифровываем зашифрованный ключ gost2012 512 bit" { +test -skip {$is_provider} "Расшифровываем зашифрованный ключ gost2012 512 bit" { set unencrypted [openssl [list pkcs8 -passin pass:qwertyu -topk8 -nocrypt -in enc512.key]] openssl [list pkey -text -noout << $unencrypted] diff --git a/tcl_tests/provider.try b/tcl_tests/provider.try new file mode 100644 index 0000000..f496e44 --- /dev/null +++ b/tcl_tests/provider.try @@ -0,0 +1,48 @@ +#!/usr/bin/tclsh + +lappend auto_path [file dirname [info script]] +package require ossltest + +cd $::test::dir +start_tests "Проверка алгоритмов GOST в провайдере" + +test "Проверка наличия алгоритмов GOST в провайдере" { + set output [openssl "list --cipher-algorithms"] + + if {![regexp -all {@ gostprov} $output]} { + error "Алгоритмы GOST не найдены в выводе --cipher-algorithms" + } + + set expected_algs { + "{ 1.2.643.2.2.21, GOST 28147-89, gost89 } @ gostprov" + "{ 1.2.643.7.1.1.5.2.2, kuznyechik-ctr-acpkm-omac } @ gostprov" + "{ 1.2.643.7.1.1.5.2.1, kuznyechik-ctr-acpkm } @ gostprov" + "{ 1.2.643.7.1.1.5.1.1, magma-ctr-acpkm } @ gostprov" + "{ 1.2.643.7.1.1.5.1.2, magma-ctr-acpkm-omac } @ gostprov" + "gost89-cnt @ gostprov" + "gost89-cnt-12 @ gostprov" + "gost89-cbc @ gostprov" + "kuznyechik-ecb @ gostprov" + "kuznyechik-cbc @ gostprov" + "kuznyechik-cfb @ gostprov" + "kuznyechik-ofb @ gostprov" + "kuznyechik-ctr @ gostprov" + "magma-cbc @ gostprov" + "magma-ctr @ gostprov" + "magma-mgm @ gostprov" + "kuznyechik-mgm @ gostprov" + } + + set output_lines {} + foreach line [split $output \n] { + lappend output_lines [string trim $line] + } + + foreach alg $expected_algs { + if {$alg ni $output_lines} { + error "Алгоритм '$alg' отсутствует в выводе --cipher-algorithms" + } + } +} 0 + +end_tests \ No newline at end of file diff --git a/tcl_tests/req-genpkey.try b/tcl_tests/req-genpkey.try index e8b9c41..c23aeb6 100644 --- a/tcl_tests/req-genpkey.try +++ b/tcl_tests/req-genpkey.try @@ -10,9 +10,10 @@ start_tests "Создание ключей и заявок, команда genpk if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2001:XA gost2001:XB gost2012_256:A gost2012_256:B gost2012_256:C gost2012_256:XA gost2012_256:XB gost2012_512:A gost2012_512:B}} "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2001:XA gost2001:XB gost2012_256:0 gost2012_256:A gost2012_256:B gost2012_256:C gost2012_256:XA gost2012_256:XB gost2012_512:A gost2012_512:B}} + "openprov" {set alg_list {gost2001:A gost2001:B gost2001:C gost2001:XA gost2001:XB gost2012_256:A gost2012_256:B gost2012_256:C gost2012_256:XA gost2012_256:XB gost2012_256:0 gost2012_512:A gost2012_512:B}} } } diff --git a/tcl_tests/req-newkey.try b/tcl_tests/req-newkey.try index 634e0df..86056c1 100644 --- a/tcl_tests/req-newkey.try +++ b/tcl_tests/req-newkey.try @@ -8,7 +8,7 @@ package require ossltest package require asn 0.4.1 cd $::test::dir -switch -exact [engine_name] { +switch -exact [test_target_name] { "ccore" { set no_param_set "no public key parameters set" set invalid_paramset "invalid pubic key paramset name" @@ -17,6 +17,10 @@ switch -exact [engine_name] { set no_param_set "no parameters set" set invalid_paramset "parameter error" } + "openprov" { + set no_param_set "Error generating" + set invalid_paramset "parameter error" + } } start_tests "Создание ключей и заявок, команда req -newkey" @@ -27,7 +31,7 @@ gost2001:A "ГОСТ 2001 Криптопро A" 0 1.2.643.2.2.35.1 gost2001:B "ГОСТ 2001 Криптопро B" 0 1.2.643.2.2.35.2 gost2001:C "ГОСТ 2001 Криптопро C" 0 1.2.643.2.2.35.3 gost2001:D "ГОСТ 2001 Криптопро неверный параметр" 1 invalid_paramset -gost2001:test "ГОСТ 2001 Криптопро тестовый" 0 1.2.643.2.2.35.0 +gost2001:id-GostR3410-2001-TestParamSet "ГОСТ 2001 Криптопро тестовый" 0 1.2.643.2.2.35.0 gost2001:XA "ГОСТ 2001 Криптопро XA" 0 1.2.643.2.2.36.0 gost2001:XB "ГОСТ 2001 Криптопро XB" 0 1.2.643.2.2.36.1 gost2001:id-GostR3410-2001-CryptoPro-XchB-ParamSet "ГОСТ 2001 Криптопро XB по имени" 0 1.2.643.2.2.36.1 @@ -80,7 +84,7 @@ gost2012_512:RSAencryption: "Недопустимое имя объекта" 1 i set algparamcmdline "" } - test -skip {{[engine_name] eq "open" && $alg eq "gost2001:test"}} $descr { + test $descr { openssl "req -newkey $algname $algparamcmdline -keyout test.key -out test.req -batch -nodes -config $::test::ca/req.conf" set pkcs8 [readpem test.key] asn::asnGetSequence pkcs8 seq1 diff --git a/tcl_tests/runtest.bat b/tcl_tests/runtest.bat index 68da053..36f4db7 100755 --- a/tcl_tests/runtest.bat +++ b/tcl_tests/runtest.bat @@ -34,24 +34,24 @@ rem выставлять "любой компьютер", либо явно за IF "%OPENSSL_APP%"=="" set OPENSSL_APP=c:\cryptopack3\bin\openssl.exe IF "%TCLSH%"=="" set TCLSH=c:\Tcl\bin\tclsh.exe -%TCLSH% getengine.tcl > engine_name.txt -set /p ENGINE_NAME= < engine_name.txt -del engine_name.txt +%TCLSH% get_test_target_name.tcl > test_target_name.txt +set /p TEST_TARGET_NAME= < test_target_name.txt +del test_target_name.txt hostname > host_name.txt set /p HOST_NAME= < host_name.txt del host_name.txt -set TESTDIR=%HOST_NAME%-bat-%ENGINE_NAME% +set TESTDIR=%HOST_NAME%-bat-%TEST_TARGET_NAME% rmdir /s /q %TESTDIR% mkdir %TESTDIR% copy oidfile %TESTDIR% set OTHER_VERSION=../OtherVersion -IF %ENGINE_NAME%==cryptocom ( +IF %TEST_TARGET_NAME%==cryptocom ( set BASE_TESTS=engine dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io set OTHER_DIR=../%HOST_NAME%-bat-gost ) ELSE ( - IF %ENGINE_NAME%==gost ( + IF %TEST_TARGET_NAME%==gost ( set BASE_TESTS=engine dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io set OTHER_DIR=../%HOST_NAME%-bat-cryptocom ) ELSE ( @@ -75,7 +75,7 @@ FOR %%t IN (%WINCLIENT_TESTS%) DO %TCLSH% wcli.try %%t IF EXIST %TESTDIR%\%OTHER_DIR% %TCLSH% interop.try IF EXIST %TESTDIR%\%OTHER_VERSION% ( set OTHER_DIR=%OTHER_VERSION% - IF %ENGINE_NAME%==cryptocom ( + IF %TEST_TARGET_NAME%==cryptocom ( set ALG_LIST="gost2001:A gost2001:B gost2001:C" set ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" ) ELSE ( diff --git a/tcl_tests/runtest.sh b/tcl_tests/runtest.sh index 9969102..6de4201 100644 --- a/tcl_tests/runtest.sh +++ b/tcl_tests/runtest.sh @@ -77,26 +77,33 @@ fi TCLSH="$TCLSH -encoding utf-8" echo "PWD: $PWD" -: ${OPENSSL_CONF:=$PWD/openssl-gost.cnf} +: ${OPENSSL_CONF:=$PWD/openssl-gost-engine.cnf} echo "OPENSSL_CONF: $OPENSSL_CONF" export OPENSSL_CONF + echo "ENGINE_DIR: $ENGINE_DIR" : ${OPENSSL_ENGINES:=$ENGINE_DIR} echo "OPENSSL_ENGINES: $OPENSSL_ENGINES" export OPENSSL_ENGINES + +echo "PROVIDER_DIR: $OPENSSL_MODULES_DIR" +: ${OPENSSL_MODULES:=$OPENSSL_MODULES_DIR} +echo "OPENSSL_MODULES: $OPENSSL_MODULES" +export OPENSSL_MODULES + APP_SUFFIX=`basename $OPENSSL_APP .exe|sed s/openssl//` [ -n "$OPENSSL_APP" ]&& export OPENSSL_APP -ENGINE_NAME=`$TCLSH getengine.tcl` -export ENGINE_NAME +TEST_TARGET_NAME=`$TCLSH get_test_target_name.tcl` +export TEST_TARGET_NAME [ -z "$TESTDIR" ] && TESTDIR=`pwd` -TESTDIR=${TESTDIR}/`hostname`-$ENGINE_NAME +TESTDIR=${TESTDIR}/`hostname`-$TEST_TARGET_NAME [ -n "$APP_SUFFIX" ] && TESTDIR=${TESTDIR}-${APP_SUFFIX} [ -d ${TESTDIR} ] && rm -rf ${TESTDIR} mkdir -p ${TESTDIR} cp oidfile ${TESTDIR} export TESTDIR -case "$ENGINE_NAME" in +case "$TEST_TARGET_NAME" in gostkc3) BASE_TEST="1" ;; @@ -108,8 +115,11 @@ case "$ENGINE_NAME" in BASE_TESTS="engine dgst mac pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmstc262019 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io" OTHER_DIR=`echo $TESTDIR |sed 's/gost/cryptocom/'` ;; + gostprov) + BASE_TESTS="provider enc ca dgst tls13 pkcs8 req-newkey req-genpkey mac" + ;; *) - echo "No GOST=providing engine found" 1>&2 + echo "No GOST-providing engine/provider found" 1>&2 exit 1; esac if [ -x copy_param ]; then @@ -169,11 +179,11 @@ if [ -n "WINCLIENT_TESTS" ]; then $TCLSH wcli.try $t || fail=9 done fi -if [ -d $OTHER_DIR ]; then +if [ -n "$OTHER_DIR" -a -d "$OTHER_DIR" ]; then OTHER_DIR=../${OTHER_DIR} $TCLSH interop.try fi if [ -d OtherVersion ] ; then - case "$ENGINE_NAME" in + case "$TEST_TARGET_NAME" in gostkc3) ;; cryptocom) @@ -183,7 +193,7 @@ if [ -d OtherVersion ] ; then OTHER_DIR=../OtherVersion ALG_LIST="gost2001:A gost2001:B gost2001:C" ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" $TCLSH interop.try ;; *) - echo "No GOST=providing engine found" 1>&2 + echo "No GOST-providing module found" 1>&2 exit 1; esac fi diff --git a/tcl_tests/runtest1.bat b/tcl_tests/runtest1.bat index 8986104..1623508 100755 --- a/tcl_tests/runtest1.bat +++ b/tcl_tests/runtest1.bat @@ -27,24 +27,24 @@ rem выставлять "любой компьютер", либо явно за IF "%OPENSSL_APP%"=="" set OPENSSL_APP=c:\cryptopack3\bin\openssl.exe IF "%TCLSH%"=="" set TCLSH=c:\Tcl\bin\tclsh.exe -%TCLSH% getengine.tcl > engine_name.txt -set /p ENGINE_NAME= < engine_name.txt -del engine_name.txt +%TCLSH% get_test_target_name.tcl > test_target_name.txt +set /p TEST_TARGET_NAME= < test_target_name.txt +del test_target_name.txt hostname > host_name.txt set /p HOST_NAME= < host_name.txt del host_name.txt -set TESTDIR=%HOST_NAME%-bat-%ENGINE_NAME% +set TESTDIR=%HOST_NAME%-bat-%TEST_TARGET_NAME% rmdir /s /q %TESTDIR% mkdir %TESTDIR% copy oidfile %TESTDIR% set OTHER_VERSION=../OtherVersion -IF %ENGINE_NAME%==cryptocom ( +IF %TEST_TARGET_NAME%==cryptocom ( set BASE_TESTS=engine ssl dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts smime_io cms_io smimeenc_io cmsenc_io set OTHER_DIR=../%HOST_NAME%-bat-gost ) ELSE ( - IF %ENGINE_NAME%==gost ( + IF %TEST_TARGET_NAME%==gost ( set BASE_TESTS=engine dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io set OTHER_DIR=../%HOST_NAME%-bat-cryptocom ) ELSE ( @@ -68,7 +68,7 @@ rem FOR %%t IN (%WINCLIENT_TESTS%) DO %TCLSH% wcli.try %%t IF EXIST %TESTDIR%\%OTHER_DIR% %TCLSH% interop.try IF EXIST %TESTDIR%\%OTHER_VERSION% ( set OTHER_DIR=%OTHER_VERSION% - IF %ENGINE_NAME%==cryptocom ( + IF %TEST_TARGET_NAME%==cryptocom ( set ALG_LIST="gost2001:A gost2001:B gost2001:C" set ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" ) ELSE ( diff --git a/tcl_tests/runtest2.bat b/tcl_tests/runtest2.bat index ab7ccaf..9d52916 100755 --- a/tcl_tests/runtest2.bat +++ b/tcl_tests/runtest2.bat @@ -27,24 +27,24 @@ rem выставлять "любой компьютер", либо явно за IF "%OPENSSL_APP%"=="" set OPENSSL_APP=c:\cryptopack3\bin\openssl.exe IF "%TCLSH%"=="" set TCLSH=c:\Tcl\bin\tclsh.exe -%TCLSH% getengine.tcl > engine_name.txt -set /p ENGINE_NAME= < engine_name.txt -del engine_name.txt +%TCLSH% get_test_target_name.tcl > test_target_name.txt +set /p TEST_TARGET_NAME= < test_target_name.txt +del test_target_name.txt hostname > host_name.txt set /p HOST_NAME= < host_name.txt del host_name.txt -set TESTDIR=%HOST_NAME%-bat-%ENGINE_NAME% +set TESTDIR=%HOST_NAME%-bat-%TEST_TARGET_NAME% rem emdir /s /q %TESTDIR% rem mkdir %TESTDIR% rem copy oidfile %TESTDIR% set OTHER_VERSION=../OtherVersion -IF %ENGINE_NAME%==cryptocom ( +IF %TEST_TARGET_NAME%==cryptocom ( set BASE_TESTS=engine ssl dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts smime_io cms_io smimeenc_io cmsenc_io set OTHER_DIR=../%HOST_NAME%-bat-gost ) ELSE ( - IF %ENGINE_NAME%==gost ( + IF %TEST_TARGET_NAME%==gost ( set BASE_TESTS=engine dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io set OTHER_DIR=../%HOST_NAME%-bat-cryptocom ) ELSE ( @@ -68,7 +68,7 @@ FOR %%t IN (%WINCLIENT_TESTS%) DO %TCLSH% wcli.try %%t IF EXIST %TESTDIR%\%OTHER_DIR% %TCLSH% interop.try IF EXIST %TESTDIR%\%OTHER_VERSION% ( set OTHER_DIR=%OTHER_VERSION% - IF %ENGINE_NAME%==cryptocom ( + IF %TEST_TARGET_NAME%==cryptocom ( set ALG_LIST="gost2001:A gost2001:B gost2001:C" set ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" ) ELSE ( diff --git a/tcl_tests/server.try b/tcl_tests/server.try index 5c4048a..819599e 100644 --- a/tcl_tests/server.try +++ b/tcl_tests/server.try @@ -20,7 +20,7 @@ get_hosts [lindex $argv 0] cd $::test::dir start_tests "TLS-соединение с сервером [lindex $argv 0]" -if {[engine_name] eq "ccore"} { +if {[test_target_name] eq "ccore"} { array unset hosts gost94* } diff --git a/tcl_tests/smime.try b/tcl_tests/smime.try index 3fd722a..613f513 100644 --- a/tcl_tests/smime.try +++ b/tcl_tests/smime.try @@ -18,7 +18,7 @@ test "Creating CA 2012" { if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} } diff --git a/tcl_tests/smime_cs.try b/tcl_tests/smime_cs.try index 3792bc4..34f62d8 100644 --- a/tcl_tests/smime_cs.try +++ b/tcl_tests/smime_cs.try @@ -9,7 +9,7 @@ start_tests "Тесты на восместимость по подписи с if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C}} "open" {set alg_list {gost2001:A gost2001:B gost2001:C}} } diff --git a/tcl_tests/smime_io.try b/tcl_tests/smime_io.try index e0c7235..e945f21 100644 --- a/tcl_tests/smime_io.try +++ b/tcl_tests/smime_io.try @@ -9,7 +9,7 @@ start_tests "Тесты на совместимость smime и cms -sign" if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} } diff --git a/tcl_tests/smimeenc.try b/tcl_tests/smimeenc.try index da0c340..4467f40 100644 --- a/tcl_tests/smimeenc.try +++ b/tcl_tests/smimeenc.try @@ -67,7 +67,7 @@ test "Creating CA 2012" { if {[info exist env(ENC_LIST)]} { set enc_list $env(ENC_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1}} } diff --git a/tcl_tests/smimeenc_io.try b/tcl_tests/smimeenc_io.try index 5e54d94..aeec5bf 100644 --- a/tcl_tests/smimeenc_io.try +++ b/tcl_tests/smimeenc_io.try @@ -37,7 +37,7 @@ test -createsfiles io_decrypt_nocert.rsa "RSA User 2 (without cert) can decrypt if {[info exist env(ENC_LIST)]} { set enc_list $env(ENC_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1}} } diff --git a/tcl_tests/ssl.try b/tcl_tests/ssl.try index 1c5f9e9..07179aa 100644 --- a/tcl_tests/ssl.try +++ b/tcl_tests/ssl.try @@ -35,7 +35,7 @@ start_tests "Тесты на SSL-соединение между s_client и s_s if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {rsa:1024 gost2001:XA gost2012_256:XA gost2012_512:A}} "open" {set alg_list {rsa:1024 gost2001:XA gost2012_256:XA gost2012_512:A}} } diff --git a/tcl_tests/tls13.try b/tcl_tests/tls13.try new file mode 100644 index 0000000..7bcb105 --- /dev/null +++ b/tcl_tests/tls13.try @@ -0,0 +1,221 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest + +array set protos { + TLSv1.3 -tls1_3 +} + +array set groups { + GC256A 256 + GC256B 256 + GC256C 256 + GC256D 256 + GC512A 512 + GC512B 512 + GC512C 512 +} + +cd $::test::dir + +start_tests "TLS 1.3 tests" + +if {![info exists ::env(TLS13_PATCHED_OPENSSL)] || !$::env(TLS13_PATCHED_OPENSSL) == 1} { + log "Test skipped. Use patched openssl to run the test. Set env variable TLS13_PATCHED_OPENSSL to run the test." + end_tests + return +} + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [test_target_name] { + "openprov" {set alg_list { gost2012_256:TCA gost2012_256:TCB gost2012_256:TCC gost2012_256:TCD gost2012_512:A gost2012_512:B gost2012_512:C }} + } +} + +array set alg_to_rfc { + "gost2012_256:TCA" "gostr34102012_256a" + "gost2012_256:TCB" "gostr34102012_256b" + "gost2012_256:TCC" "gostr34102012_256c" + "gost2012_256:TCD" "gostr34102012_256d" + "gost2012_512:A" "gostr34102012_512a" + "gost2012_512:B" "gostr34102012_512b" + "gost2012_512:C" "gostr34102012_512c" +} + +set suites {TLS_GOSTR341112_256_WITH_MAGMA_MGM_L TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L TLS_GOSTR341112_256_WITH_MAGMA_MGM_S TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S} + +set proto_list {"TLSv1.3"} +set expected_proto "TLSv1.3" + +if {![file exists sslCA/cacert.pem]} { + makeCA sslCA gost2012_256:A +} else { + set ::test::ca sslCA +} + +proc sclient_args {ciphersuites_str groups_str {additional_args {}}} { + return [list \ + -connect localhost:4433 -CAfile $::test::ca/cacert.pem -verify_return_error \ + -verify 1 -state -ciphersuites $ciphersuites_str -curves $groups_str {*}$additional_args] +} + +proc sclient_auth_args {cert key ciphersuites_str groups_str {additional_args {}}} { + return [sclient_args $ciphersuites_str $groups_str [list -cert $cert -key $key {*}$additional_args]] +} + +proc sserver_args {cert key ciphersuites_str proto_str {additional_args {}}} { + return [list \ + -no_dhe -www -cert $cert -key $key -ciphersuites $ciphersuites_str $proto_str {*}$additional_args] +} + +set run_all 0 +if {[info exists env(GOST_TEST_RUN_EXTENDED)]} { + set run_all 1 +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + + test -skip {[file exist localhost_$alg_fn/cert.pem]} \ + "Создаем серверный сертификат $alg" { + makeRegisteredUser localhost_$alg_fn $alg CN localhost OU $alg_fn + } 0 1 + + test -skip {[file exists ssl_user_$alg_fn/cert.pem]} \ + "Создаем клиентский сертификат $alg" { + makeRegisteredUser ssl_user_$alg_fn $alg CN ssl_user OU $alg_fn + } 0 1 +} + +foreach suite $suites { + test "Get GOST TLS1.3 ciphers" { + set ciphers_output [openssl [list ciphers -tls1_3 -ciphersuites $suite]] + regexp "\\y$suite\\y" $ciphers_output match + } 0 1 +} + +set full_alg_list $alg_list + +if {!$run_all} { + set proto_list [get_sample $proto_list 1] + set suite_sample_count 2 + set alg_sample_count 5 + set group_sample_count 2 + + set suites [get_sample $suites $suite_sample_count] + set alg_list [get_sample $alg_list $alg_sample_count] + set group_list [get_sample [array names groups] $group_sample_count] +} else { + set group_list [array names groups] +} + +foreach proto $proto_list { + foreach group $group_list { + foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + + foreach suite $suites { + set raw_name [lindex [split $suite @] 0] + set server_cert localhost_$alg_fn/cert.pem + set server_key localhost_$alg_fn/seckey.pem + + test "Handshake $group $suite $proto" { + set list [client_server \ + [sclient_args $suite $group {}] \ + [sserver_args $server_cert $server_key $suite $protos($proto)] \ + {}] + if {[regexp -lineanchor \ + {^Server Temp Key: , (\d+) bits.*^\s*New,\s+(\S+),\s+Cipher\s+is\s+(\S+)\s*$} \ + [lindex $list 0] -> group_name result_proto result_cipher]} { + list [lindex $list 2] $group_name $result_proto $result_cipher + } else { + lindex $list 1 + } + } 0 [list 0 $groups($group) $proto $raw_name] + + test "Get page $group $suite $proto" { + set list [client_server \ + [sclient_args $suite $group {-ign_eof}] \ + [sserver_args $server_cert $server_key $suite $protos($proto)] \ + "GET /\n\n"] + grep "^New," [lindex $list 0] + } 0 "New, $expected_proto, Cipher is $raw_name\nNew, $expected_proto, Cipher is $raw_name\n" + + test "Multi-ciphersuites server $proto, $group client" { + set list [client_server \ + [sclient_args $suite $group] \ + [sserver_args $server_cert $server_key $suite:TLS_AES_256_GCM_SHA384 $protos($proto)] \ + {}] + if {[regexp -lineanchor \ + {^Server Temp Key: , (\d+) bits.*^\s*New,\s+(\S+),\s+Cipher\s+is\s+(\S+)\s*$} \ + [lindex $list 0] -> group_name result_proto result_cipher]} { + list [lindex $list 2] $group_name $result_proto $result_cipher + } else { + lindex $list 1 + } + } 0 [list 0 $groups($group) $proto $suite] + + if {[string match *gost* $alg]} { + set alg_cli_list [list $alg gost2012_256:TCB gost2012_512:B] + } else { + set alg_cli_list $alg + } + + foreach alg_cli $alg_cli_list { + set alg_cli_fn [string map {":" "_"} $alg_cli] + + set user_cert ssl_user_$alg_cli_fn/cert.pem + set user_key ssl_user_$alg_cli_fn/seckey.pem + + test "Server $alg, client certificate $alg_cli $proto $group" { + set list [client_server \ + [sclient_auth_args $user_cert $user_key $suite $group {-ign_eof}]\ + [sserver_args $server_cert $server_key $suite:TLS_AES_256_GCM_SHA384 $protos($proto) \ + {-Verify 3}] \ + "GET /\n"] + list [lindex $list 2] [grep "^New," [lindex $list 0]] + } 0 [list 0 [string repeat "New, $expected_proto, Cipher is $raw_name\n" 2]] + + + # TODO: Fix the test below when the following bug is fixed: + # on client side it is possible to specify key with a paramset + # that does not match sigalg. + + set mismatching_alg "" + foreach other_alg $full_alg_list { + if {[string first ":" $alg] != -1 && [string first ":" $other_alg] != -1} { + set cli_group [lindex [split $alg ":"] 0] + set other_group [lindex [split $other_alg ":"] 0] + + if {$other_alg != $alg && $cli_group == $other_group} { + set mismatching_alg $other_alg + break ; + } + } + } + set mm_alg_fn [string map {":" "_"} $mismatching_alg] + set mm_server_cert localhost_$mm_alg_fn/cert.pem + set mm_server_key localhost_$mm_alg_fn/seckey.pem + set mm_user_cert ssl_user_$mm_alg_fn/cert.pem + set mm_user_key ssl_user_$mm_alg_fn/seckey.pem + + test "Mismatching sigalgs: client sigalgs $alg_to_rfc($alg), key $mismatching_alg, \ + server $mismatching_alg $proto $group" { + set list [client_server \ + [sclient_auth_args $mm_user_cert $mm_user_key $suite $group \ + [list -ign_eof -sigalgs $alg_to_rfc($alg)]] \ + [sserver_args $mm_server_cert $mm_server_key $suite:TLS_AES_256_GCM_SHA384 $protos($proto) \ + {-Verify 3}] \ + "GET /\n"] + list [lindex $list 2] [grep "^New," [lindex $list 0]] + } 0 [list 0 [string repeat "New, $expected_proto, Cipher is $raw_name\n" 2]] + } + } + } + } +} + +end_tests diff --git a/tcl_tests/ts.try b/tcl_tests/ts.try index e3a4bad..c4a6591 100644 --- a/tcl_tests/ts.try +++ b/tcl_tests/ts.try @@ -65,7 +65,7 @@ set serial_num 0 if {[info exists env(ALG_LIST)]} { set alg_list $env(ALG_LIST) } else { - switch -exact [engine_name] { + switch -exact [test_target_name] { "ccore" {set alg_list {gost2001:A gost2012_256:A gost2012_512:A}} "open" {set alg_list {gost2001:A gost2012_256:A gost2012_512:A}} } diff --git a/test/04-pkey.t b/test/04-pkey.t index 4469f86..679e2e9 100644 --- a/test/04-pkey.t +++ b/test/04-pkey.t @@ -1,43 +1,48 @@ #!/usr/bin/perl use Test2::V0; -skip_all('TODO: add pkey support in provider') - unless $ARGV[0] eq 'engine'; +my @valid_target = qw(engine provider); +my $target = $ARGV[0]; + +unless (grep { $_ eq $target } @valid_target) { + skip_all("Unknown test mode '$target' – expected one of @valid_target"); +} plan(2); use Cwd 'abs_path'; -# -# If this variable is set, engine would be loaded via configuration -# file. Otherwise - via command line -# -my $use_config = 1; - # prepare data for - +my $openssl_bin = $ENV{OPENSSL_PROGRAM} || "openssl"; my $engine=$ENV{'ENGINE_NAME'}||"gost"; +my $provider=$ENV{'PROVIDER_NAME'}||"gostprov"; # Reopen STDERR to eliminate extra output open STDERR, ">>","tests.err"; my $F; -my $eng_param; open $F,">","test.cnf"; -if (defined($use_config) && $use_config) { - $eng_param = ""; - open $F,">","test.cnf"; +if ($target eq 'engine') { print $F <","tmp.pem"; print $F $pubkey; close $F; #3. Прочитать публичный и напечать его в виде текста - is(`openssl pkey -pubin -noout -in tmp.pem -text_pub`,$pubtext, + is(`$openssl_bin pkey -pubin -noout -in tmp.pem -text_pub`,$pubtext, "Read and print public key $alg:$paramset"); } #unlink "tmp.pem"; @@ -229,6 +234,7 @@ MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBBCDVwXdvq1zdBBmzVjG1WOBQR/dkwCzF6KSIiVkf -----END PRIVATE KEY-----', 'c019d8939e12740a328625cea86efa3b39170412772b3c110536410bdd58a854', 'e9f7c57547fa0cd3c9942c62f9c74a553626d5f9810975a476825cd6f22a4e86', +'21c41441319adddafa29983dd2d970c1760a1c127c486edd31720ec8151c1055', '-----BEGIN PUBLIC KEY----- MF4wFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBA0MABEB3WS+MEcXnrMCdavPRgF28U5PDlV1atDh1ADUFxoB/f80OjqQ0T7cGQtk/2nWCGDX7uUrBGA8dql8Bnw9Sgn5+ -----END PUBLIC KEY-----'], @@ -241,7 +247,8 @@ MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQECBCDQ6G51VK2+96rvFyG/dRqWOFNJA33jQajAnzra MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQECBCCvvOUfoyljV0zfUrfEj1nOgBbelamj+eXgl0qxDJjDDA== -----END PRIVATE KEY-----', '6f7c5716c08fca79725beb4afaf2a48fd2fa547536d267f2b869b6ced5fddfa4', -'c9b2ad43f1aa70185f94dbc207ab4a147002f8aac5cf2fcec9d771a36f5f7a91'], +'c9b2ad43f1aa70185f94dbc207ab4a147002f8aac5cf2fcec9d771a36f5f7a91', +'21499f455c53ccf80f9a4c24d6370c2aa0c405e266ca9345fa373f13c9c763c5'], 'id-tc26-gost-3410-2012-256-paramSetC'=> ['-----BEGIN PRIVATE KEY----- MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEDBCDq9XGURfLDPrDiMNPUcunrvUwI46FBO2EU+ok8a1DANw== @@ -251,7 +258,8 @@ MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEDBCDq9XGURfLDPrDiMNPUcunrvUwI46FBO2EU+ok8 MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEDBCAWm69+rfnGTDZ24MR29IcjMsuPhjBQT6zxPvUYQBrGLg== -----END PRIVATE KEY-----', '27e3afdcb9f191b0465ae7d28245cee6ca44d537a7c67d938933cf2012ec71a6', -'43c9f321b3659ee5108f0bcd5527f403d445f486c9e492768f46a82359ee0385'], +'43c9f321b3659ee5108f0bcd5527f403d445f486c9e492768f46a82359ee0385', +'40b6d4df162db8c8639fc55fc8e02e70137b8c46b0891d8990f81cb3452b0eb9'], 'id-tc26-gost-3410-2012-256-paramSetD'=> ['-----BEGIN PRIVATE KEY----- MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEEBCBnmzl1MutYiAXBmZa3GW5sK6Kznpt6V5i+xAl36RDhXQ== @@ -261,7 +269,8 @@ MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEEBCBnmzl1MutYiAXBmZa3GW5sK6Kznpt6V5i+xAl3 MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEEBCBpp7anU1gMcaK/BzAQzAbUHXW2kuh6h9t67i67eIfAgQ== -----END PRIVATE KEY-----', '902a174ace21dc8ecf94e6a7e84cde115f902484e2c37d1d2652b1ef0a402dfc', -'3af2a69e68cd444acc269e75edb90dfe01b8f3d9f97fe7c8b36841df9a2771a1'], +'3af2a69e68cd444acc269e75edb90dfe01b8f3d9f97fe7c8b36841df9a2771a1', +'4971dad44d16df7c246daba399039264eec11bb99cb35390455df3736d1b5d4d'], 'id-tc26-gost-3410-2012-512-paramSetA'=> ['-----BEGIN PRIVATE KEY----- MGgCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRAVbz5k/8Zj8XbTEtlv9bK9i8FaIbm+NN9kCp2wCbiaw6AXvdBiQlMj7hSGv7AdW928VRszq9Elwc63VQcYzdnkw== @@ -271,7 +280,8 @@ MGgCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRAVbz5k/8Zj8XbTEtlv9bK9i8F MGgCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRASeoodGB639ETkSEfOLTFkTozKEpMVAlFPgvK6fOlD9u1/ITUXBoERea2R+HG3YNi81wTMqT0Njq9WnbQvgIx6g== -----END PRIVATE KEY-----', 'e88ba18821e6a86787cb225ea9b731821efb9e07bdcfb7b0b8f78c70d4e88c2b', -'4d032ae84928991a48d83fc462da4d21173d8e832a3b30df71a6974f66e377a8'], +'4d032ae84928991a48d83fc462da4d21173d8e832a3b30df71a6974f66e377a8', +'4329d5fbc3d5dd87d2633967d098042549ed9dbf76fe5adb27399a151b4a44d2'], 'id-tc26-gost-3410-2012-512-paramSetB'=> ['-----BEGIN PRIVATE KEY----- MGgCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQICBggqhQMHAQECAwRAvQKu1fl21NUXvdWlYtRs3Bs4ZW9vQlV1rf1D1rfRUdxjuC2A3xdD9RoUupzK6EeNFkhTMbZ+euQTXwPFN6ykbA== @@ -281,7 +291,8 @@ MGgCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQICBggqhQMHAQECAwRAvQKu1fl21NUXvdWlYtRs3Bs4 MGgCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQICBggqhQMHAQECAwRA+I8I9E0Fz0cKG21QHn7VluHB9j348leFmeXLfGUS+jLqllemtCObR7KLW3bkzH+EiqXbLNMm+JLsmeGv4/nvYQ== -----END PRIVATE KEY-----', 'f7071ed951ac98570a5f9d299bf5a61d3dcb8082e8733b1571164ce6b54b2d8f', -'f37881bf843ecee4f0935c4f7653d4cb48b8db6a50394f89792dad899765d7d9'], +'f37881bf843ecee4f0935c4f7653d4cb48b8db6a50394f89792dad899765d7d9', +'1e2b8e4beffe40bd9f94110f624f3d5f07ace14b7952f306b22010b51fec15e3'], 'id-tc26-gost-3410-2012-512-paramSetC'=> ['-----BEGIN PRIVATE KEY----- MF4CAQAwFwYIKoUDBwEBAQIwCwYJKoUDBwECAQIDBEA79FKW7MqF4pQJJvpAhKd9YkwsFXBzcaUhYt3N1KuJV6n5aJ4+kaJfuT3YbhtwWWzNIsIdXUZRaBEGO2cEwysa @@ -292,6 +303,7 @@ MF4CAQAwFwYIKoUDBwEBAQIwCwYJKoUDBwECAQIDBEAiCNNQAMnur4EG8eSDpr5WjJaoHquSsK3wydCr -----END PRIVATE KEY-----', '6e1db0da8832660fbf761119e41d356a1599686a157c9a598b8e18b56cb09791', '2df0dfa8d437689d41fad965f13ea28ce27c29dd84514b376ea6ad9f0c7e3ece', +'cd8deae809dc76bc9f77765e3e73b822832ccb073caded0ae579b41a7da55cdb', '-----BEGIN PUBLIC KEY----- MIGgMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAwOBhAAEgYCPdAER26Ym73DSUXBamTLJcntdV3oZ7RRx/+Ijf13GnF36o36i8tEC13uJqOOmujEkAGPtui6yE4iJNVU0uM6yHmIEM5H0c81Sd/VQD8yXW1hyGAZvTMc+U/6oa30YU9YY7+t759d1CIVznPmq9C+VbAApyDCMFjuYnKD/nChsGA== -----END PUBLIC KEY-----'], @@ -304,7 +316,8 @@ MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBBCD5+u2ebYwQ9iDYWHmif4XeGgj2OijJuq4YsbTN MD4CAQAwFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBBCBmDDZsVa8VwTVme8jfzdgPAAAAAAAAAAAAAAAAAAAAQA== -----END PRIVATE KEY-----', '29132b8efb7b21a15133e51c70599031ea813cca86edb0985e86f331493b3d73', -'7206480037eb130595c0ed350046af8c96b0fc5bfb4030be65dbf3e207a25de2'], +'7206480037eb130595c0ed350046af8c96b0fc5bfb4030be65dbf3e207a25de2', +'6e19ffd60b0fbbfc7657ea4c113b3ffe6aedb789ffbdb25cdb14dfedf400e312'], 'id-tc26-gost-3410-2012-512-paramSetC-rangetest'=> ['-----BEGIN PRIVATE KEY----- MF4CAQAwFwYIKoUDBwEBAQIwCwYJKoUDBwECAQIDBEA79FKW7MqF4pQJJvpAhKd9YkwsFXBzcaUhYt3N1KuJV6n5aJ4+kaJfuT3YbhtwWWzNIsIdXUZRaBEGO2cEwysa @@ -314,37 +327,44 @@ MF4CAQAwFwYIKoUDBwEBAQIwCwYJKoUDBwECAQIDBEA79FKW7MqF4pQJJvpAhKd9YkwsFXBzcaUhYt3N MF4CAQAwFwYIKoUDBwEBAQIwCwYJKoUDBwECAQIDBEDsI/BH7zxilCahaafnqe3ILFBHUf+pM0wAqwZlpNuMyf////////////////////////////////////////8/ -----END PRIVATE KEY-----', 'fbcd6e72572335d291be497b7bfb264138ab7b2ecca00bc7a9fd90ad7557c0cc', -'8e5b7bd8b3680d3dc33627c5bed85fdeb4e1ba67307714eb260412ddbb4bb87e'] +'8e5b7bd8b3680d3dc33627c5bed85fdeb4e1ba67307714eb260412ddbb4bb87e', +'3fbc1dc5b1922f17864871d57bdabd58e342b3e1bfcb39b9cff7b63b680bfcf0'] ); - plan(64); + plan(94); + my $pkeyopt = $target eq 'engine' ? "-pkeyopt ukmhex:0100000000000000" : ""; while(my($id, $v) = each %derives) { - my ($alice,$alicehash,$bob,$bobhash,$secrethash,$malice) = @$v; + my ($alice,$alicehash,$bob,$bobhash,$secrethashvko,$secrethashecdhe,$malice) = @$v; + my $expected_secret_hash = $target eq 'engine' ? $secrethashvko : $secrethashecdhe; # Alice: keygen open $F,">",'alice.prv'; print $F $alice; close $F; - system("openssl pkey -in alice.prv -out alice.pub.der -pubout -outform DER"); - like(`openssl dgst -sha256 -r alice.pub.der`, qr/^$alicehash/, "Compute public key:$id:Alice"); + system("$openssl_bin pkey -in alice.prv -out alice.pub.der -pubout -outform DER"); + like(`$openssl_bin dgst -sha256 -r alice.pub.der`, qr/^$alicehash/, "Compute public key:$id:Alice"); # Bob: keygen open $F,">",'bob.prv'; print $F $bob; close $F; - system("openssl pkey -in bob.prv -out bob.pub.der -pubout -outform DER"); - like(`openssl dgst -sha256 -r bob.pub.der`, qr/^$bobhash/, "Compute public key:$id:Bob"); - # Alice: derive - system("openssl pkeyutl -derive -inkey alice.prv -keyform PEM -peerkey bob.pub.der -peerform DER -pkeyopt ukmhex:0100000000000000 -out secret_a.bin"); - like(`openssl dgst -sha256 -r secret_a.bin`, qr/^$secrethash/, "Compute shared key:$id:Alice:Bob"); - # Bob: derive - system("openssl pkeyutl -derive -inkey bob.prv -keyform PEM -peerkey alice.pub.der -peerform DER -pkeyopt ukmhex:0100000000000000 -out secret_b.bin"); - like(`openssl dgst -sha256 -r secret_b.bin`, qr/^$secrethash/, "Compute shared key:$id:Bob:Alice"); + system("$openssl_bin pkey -in bob.prv -out bob.pub.der -pubout -outform DER"); + like(`$openssl_bin dgst -sha256 -r bob.pub.der`, qr/^$bobhash/, "Compute public key:$id:Bob"); + SKIP: { + skip "Provider doesn't support derive for GOST2001 paramsets", 4 + if $target eq 'provider' && $id =~ m/^id-GostR3410-2001-/; + # Alice: derive + ok(system("$openssl_bin pkeyutl -derive -inkey alice.prv -keyform PEM -peerkey bob.pub.der -peerform DER $pkeyopt -out secret_a.bin") == 0,"Derive succeeded for Alice"); + like(`$openssl_bin dgst -sha256 -r secret_a.bin`,qr/^\Q$expected_secret_hash\E/,"Compute shared key:$id:Alice:Bob"); + # Bob: derive + ok(system("$openssl_bin pkeyutl -derive -inkey bob.prv -keyform PEM -peerkey alice.pub.der -peerform DER $pkeyopt -out secret_b.bin") == 0,"Derive succeeded for Bob"); + like(`$openssl_bin dgst -sha256 -r secret_b.bin`,qr/^\Q$expected_secret_hash\E/,"Compute shared key:$id:Bob:Alice"); + } if (defined $malice && $malice ne "") { # Malice: negative test -- this PEM is in the small subgroup open $F,">",'malice.pub'; print $F $malice; close $F; # NB system should return true on failure, so this is a negative test - ok(system("openssl pkeyutl -derive -inkey alice.prv -keyform PEM -peerkey malice.pub -peerform PEM -pkeyopt ukmhex:0100000000000000 -out secret_m.bin"), "Compute shared key:$id:Alice:Malice"); - ok(system("openssl pkeyutl -derive -inkey bob.prv -keyform PEM -peerkey malice.pub -peerform PEM -pkeyopt ukmhex:0100000000000000 -out secret_m.bin"), "Compute shared key:$id:Bob:Malice"); + ok(system("$openssl_bin pkeyutl -derive -inkey alice.prv -keyform PEM -peerkey malice.pub -peerform PEM $pkeyopt -out secret_m.bin"), "Compute shared key:$id:Alice:Malice"); + ok(system("$openssl_bin pkeyutl -derive -inkey bob.prv -keyform PEM -peerkey malice.pub -peerform PEM $pkeyopt -out secret_m.bin"), "Compute shared key:$id:Bob:Malice"); } } unlink "alice.prv"; diff --git a/test/05-tls13.t b/test/05-tls13.t new file mode 100755 index 0000000..f320d11 --- /dev/null +++ b/test/05-tls13.t @@ -0,0 +1,240 @@ +#!/usr/bin/perl +use strict; +use warnings; +use Test2::V0; +use Cwd 'abs_path'; +use FindBin; +use lib "$FindBin::Bin"; +use File::Temp qw(tempfile); +use JSON::PP; +use ProcessInteractor; +use Utils; + +skip_all('Only for provider') unless $ARGV[0] eq 'provider'; +skip_all('Enable with GOST_PROVIDER_ENABLE_ONLINE_TESTS=1') + unless $ENV{GOST_PROVIDER_ENABLE_ONLINE_TESTS}; + +skip_all('Test skipped. Use patched openssl to run the test. Set env variable TLS13_PATCHED_OPENSSL to run the test.') + unless $ENV{TLS13_PATCHED_OPENSSL}; + +my $openssl_bin = $ENV{OPENSSL_PROGRAM} || "openssl"; +my $run_extended = $ENV{GOST_TEST_RUN_EXTENDED} || 0; + +my $config_dir = abs_path("$FindBin::Bin/tls13-configs"); +my @tests = make_test_plan($config_dir); + +unless ($run_extended) { + note("Set GOST_TEST_RUN_EXTENDED=1 to run all combinations in the test"); + @tests = ($tests[rand @tests]); +} + +plan tests => scalar @tests; + +for my $t (@tests) { + my @cmd = ( + $openssl_bin, "s_client", + "-connect", "$t->{host}:$t->{port}", "-tls1_3", "-no_ign_eof", + "-ciphersuites", $t->{ciphersuite}, + "-curves", $t->{curve}, + "-sigalgs", $t->{sigalg}, + "-CAfile", $t->{ca}, + "-servername", $t->{servername} + ); + + if ($t->{use_auth}) { + push @cmd, + ( + "-cert", $t->{cert}, "-key", $t->{key}, "-client_sigalgs", + $t->{client_sigalg}, + ); + } + + my $cmdline = join(" ", @cmd); + note($cmdline); + + my ($status, $output, $rc, $success) = (ProcessInteractor::STATUS_OK, '', -1, 0); + my $command = "HEAD / HTTP/1.1\r\nHost: $t->{servername}\r\n\r\n"; + if (not $t->{use_auth} or $t->{expect_auth_success}) { + ($status, $output, $rc) = run_sclient($cmdline, $command, 30, 0); + $success = (($status == ProcessInteractor::STATUS_OK) and ($rc == 0)); + } + else { + # Infotecs test certificates expire on 2025-12-09. After that we've got to set + # expect_auth_success=false in the config. + ($status, $output, $rc) = run_sclient($cmdline, $command, 1, 1); + $success = (($status == ProcessInteractor::STATUS_OK) + and ($output =~ /Connection: close/m)); + } + my $info = "TLS1.3 to $t->{host}:$t->{port} ciphersuite=$t->{ciphersuite}" + ." group=$t->{curve} sigalg=$t->{sigalg}: status=@{[ProcessInteractor::status_str($status)]}, rc=$rc"; + is($success, 1, $info) or diag($output); +} + +sub end_on_blank { + my ($response) = @_; + return $response =~ /\r\n\r\n/m; +} + +sub end_on_verify_return_code { + my ($response) = @_; + return $response =~ /Verify return code[^\n]*\n---\n/m; +} + +sub run_sclient { + my ($cmdline, $command, $iterations, $server_closes_connection) = @_; + + my $proc = ProcessInteractor->new( + cmdline => $cmdline, + start_timeout => 30, + read_timeout => 5, + exit_timeout => 1 + ); + my ($status, $out) = $proc->start(\&end_on_verify_return_code); + return ($status, $out, 1) if $status != ProcessInteractor::STATUS_OK; + + my $resp; + for my $i (1 .. $iterations) { + ($status, $resp) = $proc->interact($command, \&end_on_blank); + $out .= $resp; + return ($status, $out, 1) if $status != ProcessInteractor::STATUS_OK; + } + + unless ($server_closes_connection) { + $proc->close_stdin(); + } + + (undef, $resp, my $exitcode) = $proc->wait_for_exit(); + $out .= $resp; + return (ProcessInteractor::STATUS_OK, $out, $exitcode); +} + +sub read_file { + my ($path) = @_; + open my $fh, '<', $path; + local $/; + return <$fh>; +} + +sub write_temp_file { + my ($data, $suffix, $template) = @_; + my ($fh, $filename) = tempfile( + TEMPLATE => ($template || "tls13_$$\_" . time . "_XXXXXXXX"), + DIR => "$FindBin::Bin", + SUFFIX => $suffix, + UNLINK => 1, + ); + binmode($fh, ":utf8"); + print $fh $data; + close $fh; + return $filename; +} + +sub load_endpoint_tests { + my ($conf) = @_; + my @tests; + + if ($conf->{skip} == 1) { + return @tests; + } + + my $host = $conf->{host}; + my $servername = $conf->{servername} // $host; + my $ca_path = write_temp_file(join("\n", @{$conf->{ca}}), + '.pem', "tls13_" . $servername . "_ca_XXXX"); + my @users = @{$conf->{user} // []}; + + for my $user (@users) { + my $user_sigalg = $user->{sigalg}; + my $cert = join("\n", @{$user->{cert}}); + my $key = join("\n", @{$user->{key}}); + $user->{cert_file} = write_temp_file($cert, '.pem', + "tls13_" . $servername . "_usercert_" . $user_sigalg . "_XXXX"); + $user->{key_file} = write_temp_file($key, '.pem', + "tls13_" . $servername . "_userkey_" . $user_sigalg . "_XXXX"); + } + + srand(time ^ $$); + + for my $ep (@{$conf->{endpoints}}) { + my $port = $ep->{port} // 443; + my $auth = $ep->{auth} // $conf->{auth} // 0; + my $join_sigalgs = $ep->{join_sigalgs} // $conf->{join_sigalgs} // 0; + my $expect_auth_success = $ep->{expect_auth_success} // $conf->{expect_auth_success} // $auth; + my @ciphersuites = @{$ep->{ciphersuites} // $conf->{ciphersuites}}; + my @curves = @{$ep->{curves} // $conf->{curves}}; + my @sigalgs = @{$ep->{sigalgs} // $conf->{sigalgs}}; + my @supported_client_sigalgs = @{$ep->{supported_client_sigalgs} // $conf->{supported_client_sigalgs}}; + + if ($join_sigalgs) { + @sigalgs = (join(":", @sigalgs)); + } + + my $it; + if ($auth) { + $it = Utils::cartesian_product_iterator(\@ciphersuites, + \@curves, \@sigalgs, \@users); + } + else { + $it = Utils::cartesian_product_iterator(\@ciphersuites, + \@curves, \@sigalgs); + } + + my @ep_tests; + while (my $combination = $it->()) { + my ($ciphersuite, $curve, $sigalg, $user) = @$combination; + + my ($user_cert, $user_key, $user_sigalg); + if (defined($user)) { + $user_sigalg = $user->{sigalg}; + unless (grep { $_ eq $user_sigalg } @supported_client_sigalgs) { + next; + } + $user_cert = $user->{cert_file}; + $user_key = $user->{key_file}; + } + + push @ep_tests, + { + host => $host, + port => $port, + ciphersuite => $ciphersuite, + curve => $curve, + client_sigalg => $user_sigalg, + sigalg => $sigalg, + servername => $servername, + ca => $ca_path, + cert => $user_cert, + key => $user_key, + use_auth => $auth, + expect_auth_success => $expect_auth_success, + }; + } + + if (@ep_tests) { + push @tests, @ep_tests; + } + } + + return @tests; +} + +sub make_test_plan { + my ($config_dir) = @_; + + opendir(my $dh, $config_dir) + or skip_all("Directory $config_dir is not found"); + my @config_files = grep { /\.json$/ && -f "$config_dir/$_" } readdir($dh); + closedir($dh); + skip_all("Directory $config_dir has no test configs") unless @config_files; + + my @tests; + for my $config_file (@config_files) { + my $data = read_file("$config_dir/$config_file"); + my $config = JSON::PP->new->utf8->decode($data); + for my $server (@$config) { + push @tests, load_endpoint_tests($server); + } + } + + return @tests; +} diff --git a/test/ProcessInteractor.pm b/test/ProcessInteractor.pm new file mode 100644 index 0000000..0bdcd8e --- /dev/null +++ b/test/ProcessInteractor.pm @@ -0,0 +1,158 @@ +# Implemented using ChatGPT +package ProcessInteractor; +use strict; +use warnings; +use IPC::Open3; +use IO::Select; +use POSIX ":sys_wait_h"; +use Symbol 'gensym'; +use feature "switch"; +use constant { + STATUS_OK => 0, + STATUS_TIMEOUT => 1, + STATUS_EOF => 2, +}; + +sub status_str { + my ($status) = @_; + + if ($status == STATUS_OK) { + return "OK"; + } + elsif ($status == STATUS_TIMEOUT) { + return "Timeout"; + } + elsif ($status == STATUS_EOF) { + return "EOF"; + } + else { + return "Unknown"; + } +} + +sub new { + my ($class, %args) = @_; + my $self = { + cmdline => ($args{cmdline} || die "cmdline required"), + start_timeout => ($args{start_timeout} || 1), + read_timeout => ($args{read_timeout} || 1), + exit_timeout => ($args{exit_timeout} || 1), + _pid => undef, + _reader => undef, + _writer => undef, + _err => undef, + _sel => undef, + }; + + return bless $self, $class; +} + +sub _start_process { + my ($self) = @_; + my ($reader, $writer, $err); + $err = gensym; + my $pid = open3($writer, $reader, $err, "sh -c \'$self->{cmdline} 2>&1\' ") + or die "Cannot start process: $!"; + my $sel = IO::Select->new($reader); + $writer->autoflush(1); + close $err; + + $self->{_pid} = $pid; + $self->{_reader} = $reader; + $self->{_writer} = $writer; + $self->{_sel} = $sel; +} + +sub _read_until { + my ($self, $check_sub, $timeout) = @_; + my $reader = $self->{_reader} or die "Reader not set"; + my $sel = $self->{_sel} or die "Selector not set"; + my $output = ''; + + my $buf; + while (1) { + my @ready = $sel->can_read($timeout); + unless (@ready) { + return (STATUS_TIMEOUT, $output); + } + + # Читаем ВСЁ, что доступно прямо сейчас + while ($sel->can_read(0)) { + my $bytes_read = sysread($reader, $buf, 4096); + unless (defined $bytes_read) { + die "Error reading from process: $!"; + } + if ($bytes_read == 0) { + return (STATUS_EOF, $output); + } + $output .= $buf; + } + + if ($check_sub->($output)) { + return (STATUS_OK, $output); + } + } +} + +sub start { + my ($self, $start_check) = @_; + $start_check ||= sub { 1 }; + die "start_check must be a CODE ref" unless ref $start_check eq 'CODE'; + + $self->_start_process(); + my ($status, $output) = + $self->_read_until($start_check, $self->{start_timeout}); + return ($status, $output); +} + +sub interact { + my ($self, $command, $response_check) = @_; + die "Process not started" unless defined $self->{_pid}; + $response_check ||= sub { 1 }; + die "response_check must be a CODE ref" + unless ref $response_check eq 'CODE'; + + my $writer = $self->{_writer}; + print $writer "$command"; + + my ($status, $response) = + $self->_read_until($response_check, $self->{read_timeout}); + return ($status, $response); +} + +sub close_stdin { + my ($self) = @_; + close $self->{_writer}; +} + +sub wait_for_exit { + my ($self) = @_; + die "Process not started" unless defined $self->{_pid}; + + my ($status, $output) = + $self->_read_until(sub { 0 }, $self->{exit_timeout}); + + my $exit_timeout = $self->{exit_timeout}; + my $elapsed = 0; + my $interval = 0.1; + while ($elapsed < $exit_timeout) { + my $res = waitpid($self->{_pid}, WNOHANG); + if ($res > 0) { + my $exitcode = $? >> 8; + return ($status, $output, $exitcode); + } + select(undef, undef, undef, $interval); + $elapsed += $interval; + } + + kill 'TERM', $self->{_pid}; + select(undef, undef, undef, 0.5); + if (waitpid($self->{_pid}, WNOHANG) == 0) { + kill 'KILL', $self->{_pid}; + waitpid($self->{_pid}, 0); + } + my $exitcode = $? >> 8; + return ($status, $output, $exitcode); +} + +1; diff --git a/test/Utils.pm b/test/Utils.pm new file mode 100644 index 0000000..c2828d7 --- /dev/null +++ b/test/Utils.pm @@ -0,0 +1,43 @@ +package Utils; +use strict; +use warnings; + +# # Implemented using ChatGPT +sub cartesian_product_iterator { + my @arrays = @_; + + my $empty_result = sub {return}; + + unless (@arrays) { + return $empty_result; + } + + for my $a (@arrays) { + unless (@$a) { + return $empty_result; + } + } + + my @idx = (0) x @arrays; + my $done = 0; + + return sub { + return if $done; + my @current = map {$arrays[$_]->[$idx[$_]]} 0 .. $#arrays; + + # increment indices + for (my $i = $#idx ; $i >= 0 ; $i--) { + $idx[$i]++; + if ($idx[$i] < @{$arrays[$i]}) { + last; + } + else { + $idx[$i] = 0; + if ($i == 0) {$done = 1} + } + } + return \@current; + }; +} + +1; diff --git a/test/tls13-configs/infotecs.json b/test/tls13-configs/infotecs.json new file mode 100644 index 0000000..2724fb9 --- /dev/null +++ b/test/tls13-configs/infotecs.json @@ -0,0 +1,190 @@ +[ + { + "name": "Infotecs", + "skip": false, + "host": "91.244.183.22", + "ca": [ + "-----BEGIN CERTIFICATE-----", + "MIICcDCCAh2gAwIBAgIQFWZq8KR5sZRF3z1wqYxUlDAKBggqhQMHAQEDAjB5MQsw", + "CQYDVQQGEwJSVTEPMA0GA1UECBMGTW9zY293MREwDwYDVQQHEwhSb29tIDItMzEe", + "MBwGA1UECxMVQ3J5cHRvZ3JhcHlEZXBhcnRtZW50MREwDwYDVQQKEwhJbmZvdGVj", + "czETMBEGA1UEAxMKQ1JZUFRPNC1DQTAgFw0xOTExMDgwMjU4MjBaGA8yMDY5MTEw", + "ODAzMDgwM1oweTELMAkGA1UEBhMCUlUxDzANBgNVBAgTBk1vc2NvdzERMA8GA1UE", + "BxMIUm9vbSAyLTMxHjAcBgNVBAsTFUNyeXB0b2dyYXB5RGVwYXJ0bWVudDERMA8G", + "A1UEChMISW5mb3RlY3MxEzARBgNVBAMTCkNSWVBUTzQtQ0EwZjAfBggqhQMHAQEB", + "ATATBgcqhQMCAiMBBggqhQMHAQECAgNDAARAVCk2ydYf2zuj1dd0gwQiSaxqcTK/", + "o987avmYKyHWzwZRGzPUOpkW/7xHgqRbgEqh77KhXkttvayvevc63pNjW6N4MHYw", + "CwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOOsXFhoPf1/", + "svgUsBqEFya6ezufMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYE", + "FMA4xxW2VZcnLXQdYH4dAypxPLr7MAoGCCqFAwcBAQMCA0EAuQRaslNHZRwLli/U", + "86dH8BtHJ6gPLN2CFahrrgROFyQbNBWAQBsCEgjw6UBgW60U7uzYMwaKmvBaFPEq", + "pluXLg==", + "-----END CERTIFICATE-----" + ], + "curves": [ + "GC256A", + "GC256B", + "GC256C", + "GC256D", + "GC512A", + "GC512B", + "GC512C" + ], + "ciphersuites": [ + "TLS_GOSTR341112_256_WITH_MAGMA_MGM_L", + "TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L", + "TLS_GOSTR341112_256_WITH_MAGMA_MGM_S", + "TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S" + ], + "auth": true, + "expect_auth_success": true, + "supported_client_sigalgs": [ + "gostr34102012_256a", + "gostr34102012_256b", + "gostr34102012_256c", + "gostr34102012_256d", + "gostr34102012_512a", + "gostr34102012_512b", + "gostr34102012_512c" + ], + "endpoints": [ + { + "port": 15003, + "sigalgs": [ + "gostr34102012_256a" + ] + }, + { + "port": 15013, + "sigalgs": [ + "gostr34102012_256b" + ] + }, + { + "port": 15023, + "sigalgs": [ + "gostr34102012_256c" + ] + }, + { + "port": 15033, + "sigalgs": [ + "gostr34102012_256d" + ] + }, + { + "port": 15073, + "sigalgs": [ + "gostr34102012_512a" + ] + }, + { + "port": 15083, + "sigalgs": [ + "gostr34102012_512b" + ] + }, + { + "port": 15093, + "sigalgs": [ + "gostr34102012_512c" + ] + }, + { + "port": 15002, + "sigalgs": [ + "gostr34102012_256a" + ], + "auth": false + }, + { + "port": 15012, + "sigalgs": [ + "gostr34102012_256b" + ], + "auth": false + }, + { + "port": 15022, + "sigalgs": [ + "gostr34102012_256c" + ], + "auth": false + }, + { + "port": 15032, + "sigalgs": [ + "gostr34102012_256d" + ], + "auth": false + }, + { + "port": 15072, + "sigalgs": [ + "gostr34102012_512a" + ], + "auth": false, + "supported_client_sigalgs": [ + "gostr34102012_512a" + ] + }, + { + "port": 15082, + "sigalgs": [ + "gostr34102012_512b" + ], + "auth": false + }, + { + "port": 15092, + "sigalgs": [ + "gostr34102012_512c" + ], + "auth": false + } + ], + "user": [ + { + "sigalg": "gostr34102012_512a", + "key": [ + "-----BEGIN PRIVATE KEY-----", + "MGgCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRAErXiIIvv", + "u2tngjox1+6GflZUoLcNwGdwXATDeKxhnOwg1rb9elc0mbvIocXRnRmwbXGytlLW", + "vpbSqadDor4P1A==", + "-----END PRIVATE KEY-----" + ], + "cert": [ + "-----BEGIN CERTIFICATE-----", + "MIIFCTCCBLagAwIBAgIQAdv7vv6FK+AAAOmsAAUAATAKBggqhQMHAQEDAjCB6jEs", + "MCoGA1UECQwj0YPQuy7QntGC0YDQsNC00L3QsNGPLCAy0JEg0YHRgtGALjExCzAJ", + "BgNVBAYTAlJVMRkwFwYDVQQHDBDQsy4g0JzQvtGB0LrQstCwMTAwLgYDVQQLDCfQ", + "o9C00L7RgdGC0L7QstC10YDRj9GO0YnQuNC5INGG0LXQvdGC0YAxLzAtBgNVBAoM", + "JtCi0LXRgdGC0L7QstGL0Lkg0KPQpiDQmNC90YTQvtCi0LXQmtChMS8wLQYDVQQD", + "DCbQotC10YHRgtC+0LLRi9C5INCj0KYg0JjQvdGE0L7QotC10JrQoTAeFw0yNTA3", + "MjMxMDQ2MDBaFw0yNTEyMDkyMDU5NTlaMBMxETAPBgNVBAMMCE5ldyBVc2VyMIGq", + "MCEGCCqFAwcBAQECMBUGCSqFAwcBAgECAQYIKoUDBwEBAgMDgYQABIGAsTeeRPKQ", + "veMIzP3UKIBfANrihEAOblFCPkxdOm2TEVCMkNcgC945xSnBMx3oEe+OVDqUOZPq", + "hDP6j886saIUWwnkIvuP7WoBe1QEh23y31n7SwCe0JaqGDLg9OVGtk2qUQBFP3Y3", + "LK4Yy+oe3Qj1CG4rnxIOOL5uMmbP09AH/W2BCQAwMDA1MDAwMaOCArUwggKxMB0G", + "A1UdDgQWBBTGivt2al/MErN1WJ0vmHZ5ZWrdCTAdBgNVHSAEFjAUMAgGBiqFA2Rx", + "ATAIBgYqhQNkcQIwGQYFKoUDZG8EEAwOVmlQTmV0IENTUCA0LjQwgbMGBSqFA2Rw", + "BIGpMIGmDA5WaVBOZXQgQ1NQIDQuNAwm0KLQtdGB0YLQvtCy0YvQuSDQo9CmINCY", + "0L3RhNC+0KLQtdCa0KEMNtCX0LDQutC70Y7Rh9C10L3QuNC1IOKEljE0OS8zLzIv", + "MS0yMDU5INC+0YIgMTcuMDguMjAyMgw00JfQsNC60LvRjtGH0LXQvdC40LUg4oSW", + "IDE0OS83LzYtMjUyINC+0YIgMDQuMDcuMjAyMzA+BggrBgEFBQcBAQQyMDAwLgYI", + "KwYBBQUHMAKGImh0dHA6Ly90ZXN0Y2VydC5pbmZvdGVjcy5ydS9DQS5kZXIwNAYD", + "VR0fBC0wKzApoCegJYYjaHR0cDovL3Rlc3RjZXJ0LmluZm90ZWNzLnJ1L1NPUy5j", + "cmwwggEoBgNVHSMEggEfMIIBG4AUZIMCNzuDIxnzogrocUY6fz89Ib+hgfCkge0w", + "geoxLDAqBgNVBAkMI9GD0Lsu0J7RgtGA0LDQtNC90LDRjywgMtCRINGB0YLRgC4x", + "MQswCQYDVQQGEwJSVTEZMBcGA1UEBwwQ0LMuINCc0L7RgdC60LLQsDEwMC4GA1UE", + "Cwwn0KPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMS8wLQYD", + "VQQKDCbQotC10YHRgtC+0LLRi9C5INCj0KYg0JjQvdGE0L7QotC10JrQoTEvMC0G", + "A1UEAwwm0KLQtdGB0YLQvtCy0YvQuSDQo9CmINCY0L3RhNC+0KLQtdCa0KGCEAHb", + "Qj5MiT5wAADNpgAFAAEwCgYIKoUDBwEBAwIDQQDwFJsxJveNXoba8l5PjptqW6Vp", + "+cKOYP0NI1vKIWI91WCp2XxicRHKDoRJgyIGj1zR+D+ddQFMOGn4eeUQSvsE", + "-----END CERTIFICATE-----" + ] + } + ] + } +] \ No newline at end of file diff --git a/test_ecdhe.c b/test_ecdhe.c new file mode 100644 index 0000000..5b75bfa --- /dev/null +++ b/test_ecdhe.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include "gost_lcl.h" + +#define T(e) \ + if (!(e)) { \ + ERR_print_errors_fp(stderr); \ + OpenSSLDie(__FILE__, __LINE__, #e); \ + } + +#define cRED "\033[1;31m" +#define cGREEN "\033[1;32m" +#define cNORM "\033[m" + +static EVP_PKEY *load_private_key(int key_nid, int param_nid, const char *pk, + const char *pub) +{ + EVP_PKEY_CTX *ctx; + + T(ctx = EVP_PKEY_CTX_new_id(key_nid, NULL)); + T(EVP_PKEY_paramgen_init(ctx)); + T(EVP_PKEY_CTX_ctrl(ctx, -1, -1, EVP_PKEY_CTRL_GOST_PARAMSET, param_nid, + NULL)); + EVP_PKEY *key = NULL; + T((EVP_PKEY_paramgen(ctx, &key)) == 1); + EVP_PKEY_CTX_free(ctx); + + EC_KEY *ec; + T(ec = EVP_PKEY_get0(key)); + + const int len = EVP_PKEY_bits(key) / 8; + BN_CTX *bc; + T(bc = BN_CTX_secure_new()); + BN_CTX_start(bc); + const EC_GROUP *group = EC_KEY_get0_group(ec); + EC_POINT *pkey = NULL; + if (pk) { + /* Read private key. */ + BIGNUM *d = NULL; + + T(d = BN_lebin2bn((const unsigned char *)pk, len, NULL)); + T(EC_KEY_set_private_key(ec, d)); + + /* Compute public key. */ + T(pkey = EC_POINT_new(group)); + T(EC_POINT_mul(group, pkey, d, NULL, NULL, bc)); + BN_free(d); + T(EC_KEY_set_public_key(ec, pkey)); + } else { + /* Read public key. */ + BIGNUM *x, *y; + + T(x = BN_lebin2bn((const unsigned char *)pub, len, NULL)); + T(y = BN_lebin2bn((const unsigned char *)pub + len, len, NULL)); + EC_POINT *xy = EC_POINT_new(group); + T(EC_POINT_set_affine_coordinates(group, xy, x, y, bc)); + BN_free(x); + BN_free(y); + T(EC_KEY_set_public_key(ec, xy)); + EC_POINT_free(xy); + } + +#ifdef DEBUG + BIO *bp = BIO_new_fd(1, BIO_NOCLOSE); + if (pk) + PEM_write_bio_PrivateKey(bp, key, NULL, NULL, 0, NULL, NULL); + PEM_write_bio_PUBKEY(bp, key); + BIO_free(bp); +#endif + + /* Verify public key. */ + if (pk && pub) { + BIGNUM *x, *y; + + T(x = BN_lebin2bn((const unsigned char *)pub, len, NULL)); + T(y = BN_lebin2bn((const unsigned char *)pub + len, len, NULL)); + EC_POINT *xy = EC_POINT_new(group); + T(EC_POINT_set_affine_coordinates(group, xy, x, y, bc)); + BN_free(x); + BN_free(y); + if (EC_POINT_cmp(group, pkey, xy, bc) == 0) + printf("Public key %08x matches private key %08x\n", + *(int *)pub, *(int *)pk); + else + { + printf(cRED "Public key mismatch!" cNORM "\n"); + exit(1); + } + EC_POINT_free(xy); + } + EC_POINT_free(pkey); + BN_CTX_end(bc); + BN_CTX_free(bc); + + return key; +} + +int main() +{ + OpenSSL_add_all_algorithms(); + + const char client_private_key[] = + "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04" + "\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04"; + + const char server_private_key[] = + "\xB6\x6B\x18\xAC\xD9\x25\x59\x02\x19\xB8\x8D\xF3\xC4\xE8\xD2\x64" + "\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x84\x04"; + + const char server_public_key[] = + "\xF8\x55\xF9\x3A\xE4\x0B\xC3\xDD\x2B\xCF\xDB\xAC\x99\xD4\xC3\xF9" + "\xD6\xCF\x16\xED\xB8\x1F\x87\xC6\x84\x68\xB6\x1B\xAF\x2E\xF6\xB2" + "\x0C\x18\x4E\xD5\xEC\xB5\x46\x2B\x1E\x18\x7C\x7E\xCB\x84\x40\xAF" + "\x41\xD7\x28\xAA\x45\x84\x2F\xC7\xDB\xD2\xC4\x74\x74\x85\x9F\xD2"; + + const char client_public_key[] = + "\x9E\x44\x41\x7A\x31\x6B\x95\xC5\x4B\xC0\x04\x63\x05\xFA\x60\x9C" + "\x85\xE5\x05\x78\x2D\x26\x1B\xA9\x87\xBF\xF8\xC7\x4B\xEE\x51\xD8" + "\x3B\xF9\xE8\x35\xB9\x33\x18\x2C\x70\xF4\xDE\x50\x04\x75\xB1\x36" + "\xBC\xE4\xD3\x48\xC3\x05\x19\x0A\x60\x8E\xC1\xB1\x28\x70\x56\xEB"; + + EVP_PKEY *client_key = load_private_key(NID_id_GostR3410_2012_256, + NID_id_tc26_gost_3410_2012_256_paramSetA, + client_private_key, client_public_key); + EVP_PKEY *server_key = load_private_key(NID_id_GostR3410_2012_256, + NID_id_tc26_gost_3410_2012_256_paramSetA, + server_private_key, server_public_key); + + unsigned char expected_result[] = {0xD5, 0x56, 0xB0, 0xBC, 0x8F, 0x86, 0xE1, 0x46, + 0x6C, 0xF1, 0x30, 0xD9, 0xE7, 0xDB, 0x80, 0x69, + 0x73, 0xC1, 0x8E, 0xE0, 0x73, 0x8C, 0x33, 0xC6, + 0x73, 0xE0, 0x16, 0x17, 0x70, 0xE3, 0x6B, 0x80}; + + unsigned char *client_result = NULL, *server_result = NULL; + size_t client_result_len = 0, server_result_len = 0; + uint8_t ukm = 1; + int ret = 1; + + if (!internal_compute_ecdh(NULL, &client_result_len, &ukm, 1, + EC_KEY_get0_public_key(EVP_PKEY_get0(server_key)), + EVP_PKEY_get0(client_key))) + goto exit; + + if (!internal_compute_ecdh(NULL, &server_result_len, &ukm, 1, + EC_KEY_get0_public_key(EVP_PKEY_get0(client_key)), + EVP_PKEY_get0(server_key))) + goto exit; + + if ((client_result = OPENSSL_malloc(client_result_len)) == NULL) + goto exit; + + if ((server_result = OPENSSL_malloc(server_result_len)) == NULL) + goto exit; + + if (!internal_compute_ecdh(client_result, &client_result_len, &ukm, 1, + EC_KEY_get0_public_key(EVP_PKEY_get0(server_key)), + EVP_PKEY_get0(client_key))) { + printf(cRED "ECDH compute client key internal error!" cNORM "\n"); + goto exit; + } + + if (!internal_compute_ecdh(server_result, &server_result_len, &ukm, 1, + EC_KEY_get0_public_key(EVP_PKEY_get0(client_key)), + EVP_PKEY_get0(server_key))) { + printf(cRED "ECDH compute server key internal error!" cNORM "\n"); + goto exit; + } + + if (client_result_len != server_result_len + || memcmp(client_result, server_result, client_result_len)) { + printf(cRED "client key and server key mismatch!" cNORM "\n"); + goto exit; + } + + printf(cGREEN "client ECDH and server ECDH match!" cNORM "\n"); + + if (memcmp(client_result, expected_result, client_result_len / 2)) { + printf(cRED "Reference ECDHE and computed ECDHE mismatch!" cNORM "\n"); + goto exit; + } + + printf(cGREEN "Reference ECDHE and computed ECDHE match!" cNORM "\n"); + + ret = 0; +exit: + + EVP_PKEY_free(client_key); + EVP_PKEY_free(server_key); + OPENSSL_free(server_result); + OPENSSL_free(client_result); + + return ret; +} \ No newline at end of file diff --git a/test_keyexpimp.c b/test_keyexpimp.c index 0e61b6f..305b7f9 100644 --- a/test_keyexpimp.c +++ b/test_keyexpimp.c @@ -42,6 +42,43 @@ static void hexdump(FILE *f, const char *title, const unsigned char *s, int l) fprintf(f, "\n"); } +static int expect_eq(const char *test_name, int ret, const unsigned char *result, + const unsigned char *expected, size_t len) +{ + if (ret <= 0) { + ERR_print_errors_fp(stderr); + return 1; + } else { + hexdump(stdout, test_name, result, len); + if (memcmp(result, expected, len) != 0) { + fprintf(stdout, "ERROR! %s test failed\n", test_name); + return 2; + } + } + return 0; +} + +static int initialize_openssl(ENGINE **eng) +{ +#ifdef _MSC_VER + _putenv_s("OPENSSL_ENGINES", ENGINE_DIR); +#else + setenv("OPENSSL_ENGINES", ENGINE_DIR, 0); +#endif + OPENSSL_add_all_algorithms_conf(); + ERR_load_crypto_strings(); + T(*eng = ENGINE_by_id("gost")); + T(ENGINE_init(*eng)); + T(ENGINE_set_default(*eng, ENGINE_METHOD_ALL)); + return 0; +} + +static void cleanup_openssl(ENGINE *eng) +{ + ENGINE_finish(eng); + ENGINE_free(eng); +} + int main(void) { const unsigned char shared_key[] = { @@ -83,8 +120,9 @@ int main(void) }; unsigned char kdf_label[] = { 0x26, 0xBD, 0xB8, 0x78 }; - unsigned char kdf_seed[] = - { 0xAF, 0x21, 0x43, 0x41, 0x45, 0x65, 0x63, 0x78 }; + unsigned char kdf_seed[] = { + 0xAF, 0x21, 0x43, 0x41, 0x45, 0x65, 0x63, 0x78 + }; const unsigned char kdf_etalon[] = { 0x22, 0xB6, 0x83, 0x78, 0x45, 0xC6, 0xBE, 0xF6, 0x5E, 0xA7, 0x16, 0x72, 0xB2, 0x65, 0x83, 0x10, @@ -103,6 +141,34 @@ int main(void) 0x4e, 0x5b, 0xf0, 0xff, 0x64, 0x1a, 0x19, 0xff, }; + const unsigned char kroot_kuzn_s[] = { + 0x58, 0x16, 0x88, 0xD7, 0x6E, 0xFE, 0x12, 0x2B, + 0xB5, 0x5F, 0x62, 0xB3, 0x8E, 0xF0, 0x1B, 0xCC, + 0x8C, 0x88, 0xDB, 0x83, 0xE9, 0xEA, 0x4D, 0x55, + 0xD3, 0x89, 0x8C, 0x53, 0x72, 0x1F, 0xC3, 0x84 + }; + + const unsigned char tlstree_kuzn_s_etalon[] = { + 0xE1, 0xC5, 0x9B, 0x41, 0x69, 0xD8, 0x96, 0x10, + 0x7F, 0x78, 0x45, 0x68, 0x93, 0xA3, 0x75, 0x1E, + 0x15, 0x73, 0x54, 0x3D, 0xAD, 0x8C, 0xB7, 0x40, + 0x69, 0xE6, 0x81, 0x4A, 0x51, 0x3B, 0xBB, 0x1C + }; + + unsigned char kroot_magma_l[] = { + 0xDF, 0x66, 0x60, 0x1E, 0xDD, 0xD6, 0x4E, 0x96, + 0x1D, 0xFC, 0x7D, 0xD0, 0x21, 0x2E, 0xF2, 0x25, + 0xC0, 0x05, 0x33, 0xE6, 0xDA, 0xA4, 0xAD, 0x24, + 0x18, 0x5E, 0xBE, 0xB2, 0x24, 0xB5, 0x46, 0xB8 + }; + + unsigned char tlstree_magma_l_etalon[] = { + 0xBD, 0x00, 0x9F, 0xFC, 0x04, 0xA0, 0x52, 0x9E, + 0x60, 0x78, 0xEB, 0xA5, 0xA0, 0x7A, 0xDE, 0x74, + 0x93, 0x7F, 0xF3, 0xA1, 0xAB, 0x75, 0xF7, 0xAE, + 0x05, 0x19, 0x04, 0x78, 0x51, 0x9B, 0x6D, 0xF3 + }; + unsigned char buf[32 + 16]; int ret = 0, err = 0; int outlen = 40; @@ -112,20 +178,12 @@ int main(void) unsigned char tlsseq[8]; unsigned char out[32]; -#ifdef _MSC_VER - _putenv_s("OPENSSL_ENGINES", ENGINE_DIR); -#else - setenv("OPENSSL_ENGINES", ENGINE_DIR, 0); -#endif - OPENSSL_add_all_algorithms_conf(); - ERR_load_crypto_strings(); ENGINE *eng; - T(eng = ENGINE_by_id("gost")); - T(ENGINE_init(eng)); - T(ENGINE_set_default(eng, ENGINE_METHOD_ALL)); + if (initialize_openssl(&eng) != 0) { + return 1; + } memset(buf, 0, sizeof(buf)); - memset(kroot, 0xFF, 32); memset(tlsseq, 0, 8); tlsseq[7] = 63; @@ -134,60 +192,41 @@ int main(void) ret = gost_kexp15(shared_key, 32, NID_magma_ctr, magma_key, NID_magma_mac, mac_magma_key, magma_iv, 4, buf, &outlen); - - if (ret <= 0) { - ERR_print_errors_fp(stderr); - err = 1; - } else { - hexdump(stdout, "Magma key export", buf, 40); - if (memcmp(buf, magma_export, 40) != 0) { - fprintf(stdout, "ERROR! test failed\n"); - err = 2; - } - } + err = expect_eq("Magma key export", ret, buf, magma_export, 40); + if (err) + goto cleanup; ret = gost_kimp15(magma_export, 40, NID_magma_ctr, magma_key, NID_magma_mac, mac_magma_key, magma_iv, 4, buf); - - if (ret <= 0) { - ERR_print_errors_fp(stderr); - err = 3; - } else { - hexdump(stdout, "Magma key import", buf, 32); - if (memcmp(buf, shared_key, 32) != 0) { - fprintf(stdout, "ERROR! test failed\n"); - err = 4; - } - } + err = expect_eq("Magma key import", ret, buf, shared_key, 32); + if (err) + goto cleanup; ret = gost_kdftree2012_256(kdf_result, 64, kdftree_key, 32, kdf_label, 4, - kdf_seed, 8, 1); - if (ret <= 0) { - ERR_print_errors_fp(stderr); - err = 5; - } else { - hexdump(stdout, "KDF TREE", kdf_result, 64); - if (memcmp(kdf_result, kdf_etalon, 64) != 0) { - fprintf(stdout, "ERROR! test failed\n"); - err = 6; - } - } - - ret = gost_tlstree(NID_grasshopper_cbc, kroot, out, tlsseq); - if (ret <= 0) { - ERR_print_errors_fp(stderr); - err = 7; - } else { - hexdump(stdout, "Gost TLSTREE - grasshopper", out, 32); - if (memcmp(out, tlstree_gh_etalon, 32) != 0) { - fprintf(stdout, "ERROR! test failed\n"); - err = 8; - } - } - - ENGINE_finish(eng); - ENGINE_free(eng); - + kdf_seed, 8, 1); + err = expect_eq("KDF TREE", ret, kdf_result, kdf_etalon, 64); + if (err) + goto cleanup; + + ret = gost_tlstree(NID_grasshopper_cbc, kroot, out, tlsseq, TLSTREE_MODE_NONE); + err = expect_eq("Gost TLSTREE - grasshopper", ret, out, tlstree_gh_etalon, 32); + if (err) + goto cleanup; + + tlsseq[7] = 7; + ret = gost_tlstree(NID_kuznyechik_mgm, kroot_kuzn_s, out, tlsseq, TLSTREE_MODE_S); + err = expect_eq("Gost TLSTREE - kuznyechik", ret, out, tlstree_kuzn_s_etalon, 32); + if (err) + goto cleanup; + + tlsseq[7] = 7; + ret = gost_tlstree(NID_magma_mgm, kroot_magma_l, out, tlsseq, TLSTREE_MODE_L); + err = expect_eq("Gost TLSTREE - magma", ret, out, tlstree_magma_l_etalon, 32); + if (err) + goto cleanup; + +cleanup: + cleanup_openssl(eng); return err; -} +} \ No newline at end of file diff --git a/test_mgm.c b/test_mgm.c index 31f8570..74fcf2b 100644 --- a/test_mgm.c +++ b/test_mgm.c @@ -108,6 +108,36 @@ const unsigned char mg_e_tag[8] = { 0xa7, 0x92, 0x80, 0x69, 0xaa, 0x10, 0xfd, 0x10 }; +const unsigned char mg_tlstree_key[32] = { + 0xEB, 0xD2, 0x71, 0xDE, 0x19, 0xFE, 0xE1, 0x8B, + 0xB1, 0x99, 0x8F, 0x69, 0xAF, 0x5B, 0x6A, 0xE1, + 0x89, 0x58, 0xE8, 0xD3, 0x70, 0x2F, 0x12, 0xFB, + 0xB5, 0xB0, 0x3F, 0x6F, 0xD6, 0x91, 0xFE, 0xFA +}; + +const unsigned char mg_tlstree_seqnum[8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const unsigned char mg_tlstree_nonce[8] = { + 0x18, 0xFB, 0x03, 0x8D, 0xBF, 0x72, 0x41, 0xE6 +}; + +const unsigned char mg_tlstree_adata[5] = { + 0x17, 0x03, 0x03, 0x00, 0x0B +}; + +const unsigned char mg_tlstree_pdata[3] = { + 0x01, 0x00, 0x15 +}; + +const unsigned char mg_tlstree_e_cdata[3] = { + 0x46, 0x4A, 0xEE +}; + +const unsigned char mg_tlstree_e_tag[8] = { + 0xAD, 0x39, 0x1D, 0x97, 0x98, 0x71, 0x69, 0xF3 +}; static struct testcase { const char *sn; @@ -120,6 +150,8 @@ static struct testcase { size_t ptext_len; const unsigned char *expected; const unsigned char *expected_tag; + const char *tlstree_mode; + const unsigned char *seqnum; } testcases[] = { { .sn = SN_kuznyechik_mgm, @@ -131,7 +163,9 @@ static struct testcase { .plaintext = gh_pdata, .ptext_len = sizeof(gh_pdata), .expected = gh_e_cdata, - .expected_tag = gh_e_tag + .expected_tag = gh_e_tag, + .tlstree_mode = NULL, + .seqnum = NULL }, { .sn = SN_magma_mgm, @@ -143,7 +177,23 @@ static struct testcase { .plaintext = mg_pdata, .ptext_len = sizeof(mg_pdata), .expected = mg_e_cdata, - .expected_tag = mg_e_tag + .expected_tag = mg_e_tag, + .tlstree_mode = NULL, + .seqnum = NULL + }, + { + .sn = SN_magma_mgm, + .key = mg_tlstree_key, + .nonce = mg_tlstree_nonce, + .nonce_len = sizeof(mg_tlstree_nonce), + .aad = mg_tlstree_adata, + .aad_len = sizeof(mg_tlstree_adata), + .plaintext = mg_tlstree_pdata, + .ptext_len = sizeof(mg_tlstree_pdata), + .expected = mg_tlstree_e_cdata, + .expected_tag = mg_tlstree_e_tag, + .tlstree_mode = "light", + .seqnum = mg_tlstree_seqnum }, { 0 } }; @@ -151,7 +201,8 @@ static struct testcase { static int test_block(const EVP_CIPHER *ciph, const char *name, const unsigned char *nonce, size_t nlen, const unsigned char *aad, size_t alen, const unsigned char *ptext, size_t plen, const unsigned char *exp_ctext, const unsigned char *exp_tag, - const unsigned char * key, int small) + const unsigned char *key, const char *tlstree_mode, + const unsigned char *seqnum, int small) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); unsigned char *c = alloca(plen); @@ -168,6 +219,12 @@ static int test_block(const EVP_CIPHER *ciph, const char *name, const unsigned c EVP_EncryptInit_ex(ctx, ciph, NULL, NULL, NULL); // Set cipher type and mode EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, nlen, NULL); // Set IV length EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce); // Initialise key and IV + + if (seqnum && tlstree_mode) { + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_TLSTREE_PARAMS, 0, (void *)tlstree_mode); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_TLSTREE, 0, (void *)seqnum); + } + memset(c, 0, plen); if (!small) { // test big chunks @@ -203,6 +260,12 @@ static int test_block(const EVP_CIPHER *ciph, const char *name, const unsigned c EVP_DecryptInit_ex(ctx, ciph, NULL, NULL, NULL); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, nlen, NULL); EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce); + + if (seqnum && tlstree_mode) { + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_SET_TLSTREE_PARAMS, 0, (void *)tlstree_mode); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_TLSTREE, 0, (void *)seqnum); + } + memset(c, 0, plen); if (!small) { // test big chunks @@ -257,7 +320,8 @@ int main(void) for (small = 0; small <= 1; small++) ret |= test_block(ciph, name, t->nonce, t->nonce_len, t->aad, t->aad_len, t->plaintext, t->ptext_len, - t->expected, t->expected_tag, t->key, small); + t->expected, t->expected_tag, t->key, + t->tlstree_mode, t->seqnum, small); EVP_CIPHER_free(ciph_prov); } diff --git a/test_tls.c b/test_tls.c index 72a8e08..d51be9a 100644 --- a/test_tls.c +++ b/test_tls.c @@ -173,6 +173,8 @@ int test(const char *algname, const char *paramset) T(SSL_CTX_use_certificate(sctx, ck.cert)); T(SSL_CTX_use_PrivateKey(sctx, ck.pkey)); T(SSL_CTX_check_private_key(sctx)); + T(SSL_CTX_set_min_proto_version(sctx, TLS1_2_VERSION)); + T(SSL_CTX_set_max_proto_version(sctx, TLS1_2_VERSION)); T(cctx = SSL_CTX_new(TLS_client_method()));