]> www.wagner.pp.ru Git - openssl-gost/engine.git/commitdiff
Implement provider interface to support GOST TLS 1.3 482/head
authorEugene Mironenko <mironenko@rutoken.ru>
Wed, 20 Aug 2025 16:15:29 +0000 (19:15 +0300)
committerEugene Mironenko <mironenko@rutoken.ru>
Wed, 20 Aug 2025 16:15:30 +0000 (19:15 +0300)
Source id: 9d91e6da264b7ae075a83c5281964fb74d8832fe

74 files changed:
.github/before_script.sh
.github/script.sh
.github/workflows/ci.yml
.github/workflows/codeql-analysis.yml
.github/workflows/coverity.yml
.github/workflows/windows.yml
CMakeLists.txt
README.prov.md
gost_ameth.c
gost_crypt.c
gost_ec_keyx.c
gost_eng.c
gost_grasshopper_cipher.c
gost_keyexpimp.c
gost_lcl.h
gost_omac.c
gost_pmeth.c
gost_prov.c
gost_prov.h
gost_prov_cipher.c
gost_prov_decoder.c [new file with mode: 0644]
gost_prov_digest.c
gost_prov_encoder.c [new file with mode: 0644]
gost_prov_keyexch.c [new file with mode: 0644]
gost_prov_keymgmt.c [new file with mode: 0644]
gost_prov_signature.c [new file with mode: 0644]
gost_prov_tls.c [new file with mode: 0644]
gost_prov_tls.h [new file with mode: 0644]
patches/openssl-tls1.3.patch [new file with mode: 0644]
tcl_tests/ca.try
tcl_tests/cms.try
tcl_tests/cms_cs.try
tcl_tests/cms_io.try
tcl_tests/cmsenc.try
tcl_tests/cmsenc_cs.try
tcl_tests/cmsenc_io.try
tcl_tests/cmsenc_sc.try
tcl_tests/dgst.try
tcl_tests/engine.try
tcl_tests/get_test_target_name.tcl [new file with mode: 0644]
tcl_tests/getengine.tcl [deleted file]
tcl_tests/mac.try
tcl_tests/ocsp.try
tcl_tests/openssl-gost-engine.cnf [new file with mode: 0644]
tcl_tests/openssl-gost-provider.cnf [new file with mode: 0644]
tcl_tests/openssl-gost.cnf [deleted file]
tcl_tests/ossltest.tcl
tcl_tests/pkcs12.try
tcl_tests/pkcs8.try
tcl_tests/provider.try [new file with mode: 0644]
tcl_tests/req-genpkey.try
tcl_tests/req-newkey.try
tcl_tests/runtest.bat
tcl_tests/runtest.sh
tcl_tests/runtest1.bat
tcl_tests/runtest2.bat
tcl_tests/server.try
tcl_tests/smime.try
tcl_tests/smime_cs.try
tcl_tests/smime_io.try
tcl_tests/smimeenc.try
tcl_tests/smimeenc_io.try
tcl_tests/ssl.try
tcl_tests/tls13.try [new file with mode: 0644]
tcl_tests/ts.try
test/04-pkey.t
test/05-tls13.t [new file with mode: 0755]
test/ProcessInteractor.pm [new file with mode: 0644]
test/Utils.pm [new file with mode: 0644]
test/tls13-configs/infotecs.json [new file with mode: 0644]
test_ecdhe.c [new file with mode: 0644]
test_keyexpimp.c
test_mgm.c
test_tls.c

index 2164d7f7f897079b671713d5bd8cd1ba37944c09..d77ba17f6c483c7a4343e0509946c1d51d04ae48 100755 (executable)
@@ -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
 
index f8b053cb1d073d7febab65ce40852df8362dac8e..51154eeb14748c2e918a6a17432ebf636142ad52 100755 (executable)
@@ -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
index 3b046b0eb33d9c97571298cba51aa5a741b1998e..bcb5e95a36a39b971185ad0e85d107e38e054af3 100644 (file)
@@ -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:
index 194a08da4a4151bb6a8d5db35bd735260eeb4757..73e59bf9d59ad7ba8789f09b54185d14e563a63d 100644 (file)
@@ -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}
index 191a834ea4f8f5465175fcd40066f299d481cca3..984c6e193a618fb39c7c979c8929a29fb290edbe 100644 (file)
@@ -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
index 5581a8a407fb957a6cc6e9eaab93f1337617dd2a..728a3083381ddbfa722938e649e6981196087381 100644 (file)
@@ -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
index cae27c3b1b8e39a40bbcb454c1d035bd0d34e074..91e9377ae25e3c0558ee5515be7287fc948f840f 100644 (file)
@@ -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)
 
index 0749104e26fbe8c7941570efcd950a6a0202833b..d2a3e5eea5dafdecc0748aef67ed0f1f3f67c05f 100644 (file)
@@ -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.
 
index d7e7d6394aa363a0fa04772f01a0a260e31ab619..dd116f7de637ad34a15113cc08e5e6a818be3565 100644 (file)
@@ -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, "<undefined>");
     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;
 }
 
 /* ----------------------------------------------------------------------*/
index be44595e12c6f5537176ca9f3f419b2add0ccb78..fdd38bbfbb03a759a1403bf80913ab9e0ae0c027 100644 (file)
@@ -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--)
index 8839c4ac8e8082348ab86bc06c077cb53f368cb0..bc658996f8b22b36f3808e2ec3be73ec6d4c6f4a 100644 (file)
 #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;
 }
index 4df83e8a05a0833901f2f9e982965962b8ed9737..ddb59d483046aead0bed5916a63b63b979a60dbb 100644 (file)
@@ -17,6 +17,7 @@
 #include "e_gost_err.h"
 #include "gost_lcl.h"
 #include "gost-engine.h"
+#include <assert.h>
 
 #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:
index a213ac494c59502ec03a0d9f9a2593f5fdebca38..8f4cce6071281dc4594214a904556094d9abc42c 100644 (file)
@@ -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--)
index e9da9b5f835a5292b310fc43c756786979e522de..8f66d60bac18e76328fd8b3999b326359eb831ef 100644 (file)
@@ -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;
index 21a519782fa30fa81f7ba3270ef233acc3f88831..44621fd33d166f4d47b93a8aee3cbe4d61302c5c 100644 (file)
 # include <openssl/asn1.h>
 # 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 {
index f97adec94f68d5944fa42a2439684da4f6fdf0e7..370c979a749baec84eee065c59200869fd3e5da1 100644 (file)
@@ -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)))
index bf7528c0ed42d82514268647aea4d7ab721759d7..8f8c057d6c9a98a70c8d45441a7ee2e6813b8d4a 100644 (file)
@@ -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, &param_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, &param_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)
 {
index eab5918ac229adffdf5a74873197f5390d0c2364..4ae77a187c9e916cbbd33a7ddf90c69a95ea37bf 100644 (file)
@@ -10,6 +10,7 @@
 #include <openssl/core_dispatch.h>
 #include <openssl/core_names.h>
 #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
index c7f6e661bdcc4c19423aa3c7ed1ba4ec0f6dd506..b86c509a6bf9a9399154d5c931f1b8770e700ab4 100644 (file)
@@ -1,3 +1,5 @@
+#pragma once
+
 /**********************************************************************
  *                 gost_prov.h - The provider itself                  *
  *                                                                    *
 #include <openssl/core.h>
 #include <openssl/engine.h>
 
+/* 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[];
index ce9665ea4fa18afc6d15e629f4b6a2c63f3e0674..ce688dff09244156ac3b640fe4625727de0a4563 100644 (file)
 #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 (file)
index 0000000..963761f
--- /dev/null
@@ -0,0 +1,317 @@
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include "gost_prov.h"
+#include "gost_lcl.h"
+#include <openssl/asn1t.h>
+#include <ctype.h>
+
+/*
+ * 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,
+                           &params->data,
+                           &params->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 }
+};
index 79eb5a30a128faf537aaccf3a41291f45fe8d4d7..a79c09fc39acd21ec213fb74d36381b62e2ce1b3 100644 (file)
@@ -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 (file)
index 0000000..a323803
--- /dev/null
@@ -0,0 +1,435 @@
+#include <openssl/core_dispatch.h>
+#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 (file)
index 0000000..7ae94c9
--- /dev/null
@@ -0,0 +1,139 @@
+#include <openssl/core_names.h>
+#include <assert.h>
+#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 (file)
index 0000000..f8ef3d0
--- /dev/null
@@ -0,0 +1,805 @@
+#include <openssl/ec.h>
+#include <openssl/core_names.h>
+#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, &paramset))
+        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 (file)
index 0000000..4d32b95
--- /dev/null
@@ -0,0 +1,428 @@
+#include <assert.h>
+#include <openssl/core_names.h>
+#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 (file)
index 0000000..b2f2551
--- /dev/null
@@ -0,0 +1,145 @@
+#include "gost_prov_tls.h"
+
+#include <openssl/core_names.h>
+#include <openssl/objects.h>
+#include <openssl/params.h>
+#include <openssl/prov_ssl.h>
+
+#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 (file)
index 0000000..b5f4d7f
--- /dev/null
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <openssl/core.h>
+
+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 (file)
index 0000000..95cfc4c
--- /dev/null
@@ -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
index a1ac8ede3954a2c81e404069ad4d2e1560c9fecd..ae6dd3e36feaf6182db92cabb7f7dce4bba72126 100644 (file)
@@ -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
+                       }
+               }
        }
 }
 
index 1cc6d0218fa52925b01a9e944866dd4942178567..33930801c665701dd365eafd2eb386c69f9eb0d5 100644 (file)
@@ -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}}
        }
index 2a619b2d843158fa7523ee9388edba80edf1aa26..abe1992bf5f6d3cade334f5cbc2e5d9bc8db8694 100644 (file)
@@ -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}}
        }
index 0283419c96add39785a99fdc69ce41db39308dce..d5d656313ea76c38dfd0ae2c11821a17eabec40a 100644 (file)
@@ -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}}
        }
index f9e66666cca3f79ade78809443f99a37e9f8abc1..f9d463c2224ca81bf6bbe24f5fe0935bd40405d6 100644 (file)
@@ -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:}}
        }
index 10cb08a9f76588bc0f253c0cc7e30c3c5eb98b82..96fd1e6cc8098525d27ead1e72ffc08ebe2fcd93 100644 (file)
@@ -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 }}
        }
index 01eac8a59df642218d9e624e0a7c9882202e7ece..ea49f982e45d5c70b64b2c53d6ae4d6840e52c75 100644 (file)
@@ -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:}}
        }
index ab489d0737d9231276e0ca6bc77437e623f0ee98..c7de4c2faa0c7beffb9508ab234b560041d1ad59 100644 (file)
@@ -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 }}
        }
index 77d6365a28b7f98f563c170f1bf9939202a7f23b..41bce98ac56b7e32c3b1b77c9d0aca2ccf47deb3 100644 (file)
@@ -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
     }
 
   }
index f42dbf087260f190af8187dd719b40424d71cb23..1aa80f9a5b540c679029a8d2a5d7bec2f293c896 100644 (file)
@@ -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 (file)
index 0000000..c966ebf
--- /dev/null
@@ -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 (file)
index 49176c0..0000000
+++ /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]
-
-
-
-
index 8305f5dbff99bfdf64f8895c72e83a6a260f343c..6e68acb855df2c383ce34336f3f5b2e4efd6ec19 100644 (file)
@@ -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 "Ð\92Ñ\8bÑ\87иÑ\81лиение MAC gost89 Ñ\81 Ñ\88еÑ\81Ñ\82нацатиричным ключом" {
-       grep gost-mac [openssl "dgst -mac gost-mac -macopt hexkey:3132333435363738393031323334353637383930313233343536373839303132 dgst.dat"]
-} 0 "GOST-MAC-gost-mac(dgst.dat)= 37f646d2\n"
+test "Ð\92Ñ\8bÑ\87иÑ\81ление MAC gost89 Ñ\81 Ñ\88еÑ\81Ñ\82надцатиричным ключом" {
+       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
index 92687b12e121b3a0f23e6f3a59388eb5d2644036..30b9b8035dd59a819cfd4aba42b2c31b761937a9 100644 (file)
@@ -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 (file)
index 0000000..6fd5305
--- /dev/null
@@ -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 (file)
index 0000000..14b1e3a
--- /dev/null
@@ -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 (file)
index 6fd5305..0000000
+++ /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
index ed657d6271936827fa83fcaf02164d9d40f6d924..0990a3b40cafa4bdd9ec95dc635e7242a4ee9cb6 100644 (file)
@@ -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"
index 40dce89c9b8a2a5b3509f056f61ea041bb81d15c..a9f3c238ab824ca47872e849ec8eb0f716810061 100644 (file)
@@ -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 {
index d40c6123de8958c02a16eb7411a5102a7e0e0ea2..5f802a13f03217d0deafd852685aae7a33a4e36e 100644 (file)
@@ -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 (file)
index 0000000..f496e44
--- /dev/null
@@ -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
index e8b9c419718ff2aee7f4f09edf331743a87038a2..c23aeb6fb9026018fdff8fbd40b6abed290b6c6b 100644 (file)
@@ -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}}
        }
 }
 
index 634e0dfabb3ed9674ae4618a56244970d7ced8d7..86056c1ba6153360ca55a0c16e40ae9cf5f82c25 100644 (file)
@@ -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
index 68da053c22d710f0ace87d609847ea3b19276114..36f4db7aab5e94a55a42ea141f8647dbbe1f59b9 100755 (executable)
@@ -34,24 +34,24 @@ rem выставлять "любой компьютер", либо явно за
 IF "%OPENSSL_APP%"=="" set OPENSSL_APP=c:\cryptopack3\bin\openssl.exe\r
 IF "%TCLSH%"=="" set TCLSH=c:\Tcl\bin\tclsh.exe\r
 \r
-%TCLSH% getengine.tcl > engine_name.txt\r
-set /p ENGINE_NAME= < engine_name.txt\r
-del engine_name.txt\r
+%TCLSH% get_test_target_name.tcl > test_target_name.txt\r
+set /p TEST_TARGET_NAME= < test_target_name.txt\r
+del test_target_name.txt\r
 \r
 hostname > host_name.txt\r
 set /p HOST_NAME= < host_name.txt\r
 del host_name.txt\r
-set TESTDIR=%HOST_NAME%-bat-%ENGINE_NAME%\r
+set TESTDIR=%HOST_NAME%-bat-%TEST_TARGET_NAME%\r
 rmdir /s /q %TESTDIR%\r
 mkdir %TESTDIR%\r
 copy oidfile %TESTDIR%\r
 set OTHER_VERSION=../OtherVersion\r
 \r
-IF %ENGINE_NAME%==cryptocom (\r
+IF %TEST_TARGET_NAME%==cryptocom (\r
                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\r
                set OTHER_DIR=../%HOST_NAME%-bat-gost\r
 ) ELSE (\r
-       IF %ENGINE_NAME%==gost (\r
+       IF %TEST_TARGET_NAME%==gost (\r
                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\r
                set OTHER_DIR=../%HOST_NAME%-bat-cryptocom\r
        ) ELSE (\r
@@ -75,7 +75,7 @@ FOR %%t IN (%WINCLIENT_TESTS%) DO %TCLSH% wcli.try %%t
 IF EXIST %TESTDIR%\%OTHER_DIR% %TCLSH% interop.try\r
 IF EXIST %TESTDIR%\%OTHER_VERSION% (\r
        set OTHER_DIR=%OTHER_VERSION%\r
-       IF %ENGINE_NAME%==cryptocom (\r
+       IF %TEST_TARGET_NAME%==cryptocom (\r
                set ALG_LIST="gost2001:A gost2001:B gost2001:C" \r
                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:"\r
        ) ELSE (\r
index 9969102d711c1935929b1e75441876cdd1f5aa5d..6de4201e80ffc85fe062b040143d1befd58795f6 100644 (file)
@@ -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
index 8986104848f022112c527bd8d5d30964391ee59d..1623508c0e6b8cfecbc1052e4fbb5d27130b38a2 100755 (executable)
@@ -27,24 +27,24 @@ rem выставлять "любой компьютер", либо явно за
 IF "%OPENSSL_APP%"=="" set OPENSSL_APP=c:\cryptopack3\bin\openssl.exe\r
 IF "%TCLSH%"=="" set TCLSH=c:\Tcl\bin\tclsh.exe\r
 \r
-%TCLSH% getengine.tcl > engine_name.txt\r
-set /p ENGINE_NAME= < engine_name.txt\r
-del engine_name.txt\r
+%TCLSH% get_test_target_name.tcl > test_target_name.txt\r
+set /p TEST_TARGET_NAME= < test_target_name.txt\r
+del test_target_name.txt\r
 \r
 hostname > host_name.txt\r
 set /p HOST_NAME= < host_name.txt\r
 del host_name.txt\r
-set TESTDIR=%HOST_NAME%-bat-%ENGINE_NAME%\r
+set TESTDIR=%HOST_NAME%-bat-%TEST_TARGET_NAME%\r
 rmdir /s /q %TESTDIR%\r
 mkdir %TESTDIR%\r
 copy oidfile %TESTDIR%\r
 set OTHER_VERSION=../OtherVersion\r
 \r
-IF %ENGINE_NAME%==cryptocom (\r
+IF %TEST_TARGET_NAME%==cryptocom (\r
                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\r
                set OTHER_DIR=../%HOST_NAME%-bat-gost\r
 ) ELSE (\r
-       IF %ENGINE_NAME%==gost (\r
+       IF %TEST_TARGET_NAME%==gost (\r
                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\r
                set OTHER_DIR=../%HOST_NAME%-bat-cryptocom\r
        ) ELSE (\r
@@ -68,7 +68,7 @@ rem FOR %%t IN (%WINCLIENT_TESTS%) DO %TCLSH% wcli.try %%t
 IF EXIST %TESTDIR%\%OTHER_DIR% %TCLSH% interop.try\r
 IF EXIST %TESTDIR%\%OTHER_VERSION% (\r
        set OTHER_DIR=%OTHER_VERSION%\r
-       IF %ENGINE_NAME%==cryptocom (\r
+       IF %TEST_TARGET_NAME%==cryptocom (\r
                set ALG_LIST="gost2001:A gost2001:B gost2001:C" \r
                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:"\r
        ) ELSE (\r
index ab7ccaf05ce1a15ab498de1f58a4d8471c7ef24d..9d529167ecd912fc9e0c4d2582f577c507a5d3be 100755 (executable)
@@ -27,24 +27,24 @@ rem выставлять "любой компьютер", либо явно за
 IF "%OPENSSL_APP%"=="" set OPENSSL_APP=c:\cryptopack3\bin\openssl.exe\r
 IF "%TCLSH%"=="" set TCLSH=c:\Tcl\bin\tclsh.exe\r
 \r
-%TCLSH% getengine.tcl > engine_name.txt\r
-set /p ENGINE_NAME= < engine_name.txt\r
-del engine_name.txt\r
+%TCLSH% get_test_target_name.tcl > test_target_name.txt\r
+set /p TEST_TARGET_NAME= < test_target_name.txt\r
+del test_target_name.txt\r
 \r
 hostname > host_name.txt\r
 set /p HOST_NAME= < host_name.txt\r
 del host_name.txt\r
-set TESTDIR=%HOST_NAME%-bat-%ENGINE_NAME%\r
+set TESTDIR=%HOST_NAME%-bat-%TEST_TARGET_NAME%\r
 rem emdir /s /q %TESTDIR%\r
 rem mkdir %TESTDIR%\r
 rem copy oidfile %TESTDIR%\r
 set OTHER_VERSION=../OtherVersion\r
 \r
-IF %ENGINE_NAME%==cryptocom (\r
+IF %TEST_TARGET_NAME%==cryptocom (\r
                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\r
                set OTHER_DIR=../%HOST_NAME%-bat-gost\r
 ) ELSE (\r
-       IF %ENGINE_NAME%==gost (\r
+       IF %TEST_TARGET_NAME%==gost (\r
                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\r
                set OTHER_DIR=../%HOST_NAME%-bat-cryptocom\r
        ) ELSE (\r
@@ -68,7 +68,7 @@ FOR %%t IN (%WINCLIENT_TESTS%) DO %TCLSH% wcli.try %%t
 IF EXIST %TESTDIR%\%OTHER_DIR% %TCLSH% interop.try\r
 IF EXIST %TESTDIR%\%OTHER_VERSION% (\r
        set OTHER_DIR=%OTHER_VERSION%\r
-       IF %ENGINE_NAME%==cryptocom (\r
+       IF %TEST_TARGET_NAME%==cryptocom (\r
                set ALG_LIST="gost2001:A gost2001:B gost2001:C" \r
                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:"\r
        ) ELSE (\r
index 5c4048ae5ef08c93ddcee43f99961fb34133665c..819599e294e220b3f60d1de15a705a24edd11116 100644 (file)
@@ -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*
 }
 
index 3fd722a2307980976a2d9973c75a22182076836b..613f5133dac1863d0881d04fd25aa3e4022fdc58 100644 (file)
@@ -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}}
        }
index 3792bc4c72787c35290a30f66b79aa9731628752..34f62d85158987a817c73b61513c1c6b61f49d56 100644 (file)
@@ -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}}
        }
index e0c7235c745f33a94fbc904ada4d93f5f9a62b2d..e945f210051bb5f4f8dbd3d886237593d66c8ecc 100644 (file)
@@ -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}}
        }
index da0c340b6832f80cd5272ea1ef4def487f38885e..4467f4031a261256e6bbcfe65d107f03a77e7497 100644 (file)
@@ -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}}
        }
index 5e54d945258bd2e7792c10a337bc78af9a3a115c..aeec5bf37bb4634b4e1c22c329d7a251fd4a5880 100644 (file)
@@ -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}}
        }
index 1c5f9e97c02136b4718157683080c2d3e7ba98e6..07179aa1c3f53bf89b2867ef20ac676cd1958595 100644 (file)
@@ -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 (file)
index 0000000..7bcb105
--- /dev/null
@@ -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: <NULL>, (\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: <NULL>, (\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
index e3a4bade6ba1e21de5ad040ddfa9e5b047b842bf..c4a6591d1338b6e2ce53e0309d07f1ea2a4902b6 100644 (file)
@@ -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}}
        }
index 4469f86b2fc78d639d97f3551206628e03d89272..679e2e9b27359bbeee48bb60e4750f93a3ff64fd 100644 (file)
@@ -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 <<EOCFG;
 openssl_conf = openssl_def
 [openssl_def]
 engines = engines
 [engines]
-${engine}=gost_conf
+${engine} = gost_conf
 [gost_conf]
 default_algorithms = ALL
-
 EOCFG
-} else {
-    $eng_param = "-engine $engine"
+}
+elsif ($target eq 'provider') {
+    print $F <<EOCFG;
+openssl_conf = openssl_def
+[openssl_def]
+providers = providers
+[providers]
+${provider} = provider_conf
+default = provider_conf
+[provider_conf]
+activate = 1
+EOCFG
 }
 close $F;
 $ENV{'OPENSSL_CONF'}=abs_path('test.cnf');
@@ -126,16 +131,16 @@ ENjS+gA=
         print $F $seckey;
         close $F;
         #1.  Прочитать секретный ключ и напечатать публичный и секретный ключи
-        is(`openssl pkey -noout -text -in tmp.pem`,$sectext . $pubtext,
+        is(`$openssl_bin  pkey -noout -text -in tmp.pem`,$sectext . $pubtext,
             "Print key pair $alg:$paramset");
         #2. Прочитать секретный ключ и вывести публичный (все алгоритмы)
-        is(`openssl pkey -pubout -in tmp.pem`,$pubkey,
+        is(`$openssl_bin  pkey -pubout -in tmp.pem`,$pubkey,
             "Compute public key $alg:$paramset");
         open $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 (executable)
index 0000000..f320d11
--- /dev/null
@@ -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 (file)
index 0000000..0bdcd8e
--- /dev/null
@@ -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 (file)
index 0000000..c2828d7
--- /dev/null
@@ -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 (file)
index 0000000..2724fb9
--- /dev/null
@@ -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 (file)
index 0000000..5b75bfa
--- /dev/null
@@ -0,0 +1,196 @@
+#include <openssl/ec.h>
+#include <openssl/bn.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#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
index 0e61b6f43c3d0453331416278ecdfbc9cdbdf61a..305b7f9f6e6bc47a419c2ba4662dd158205cc613 100644 (file)
@@ -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
index 31f8570e82d6b6221aa06ff44aee2154b5cb68f4..74fcf2b1857067b788caaa0d796bf37691975314 100644 (file)
@@ -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);
     }
 
index 72a8e081db221d4792bae9f67c3803e85954c69d..d51be9a9e757263591fb2cade4e5501f62abdbb2 100644 (file)
@@ -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()));