]> wagner.pp.ru Git - openssl-gost/engine.git/commitdiff
Making a gost provider - Add the ciphers
authorRichard Levitte <richard@levitte.org>
Sat, 13 Feb 2021 13:52:39 +0000 (14:52 +0100)
committerDmitry Belyavskiy <beldmit@users.noreply.github.com>
Mon, 11 Oct 2021 16:34:09 +0000 (19:34 +0300)
We add the ciphers for the provider as wrappers around the routines
designed for ENGINEs.  This is not the most elegant, but it does the
job.

When an algorithm has an OID, it's included in the OSSL_ALGORITHM name
as an aliase.  This is the way to avoid having to register the OIDs in
OpenSSL proper.

test/03-encrypt.t is modified to test the provider as well.

CMakeLists.txt
gost_lcl.h
gost_prov.c
gost_prov_cipher.c [new file with mode: 0644]
test/03-encrypt.t

index 499fc7e13d009f275adf443c85fc0bd7ac61e3bf..05645c32c22e1ef54fcdb0bab039cb03eccc5ee4 100644 (file)
@@ -199,6 +199,7 @@ set(GOST_ENGINE_SOURCE_FILES
 
 set(GOST_PROV_SOURCE_FILES
         gost_prov.c
+        gost_prov_cipher.c
         )
 
 set(TEST_ENVIRONMENT_COMMON
index 4801e17aeb14086e49a6ac10e0c7cf3978720f41..edfabcfd173d97479e086dfd2b4e065337d96607 100644 (file)
@@ -337,6 +337,7 @@ typedef struct gost_cipher_st GOST_cipher;
 EVP_CIPHER *GOST_init_cipher(GOST_cipher *c);
 void GOST_deinit_cipher(GOST_cipher *c);
 
+/* ENGINE implementation data */
 extern GOST_cipher Gost28147_89_cipher;
 extern GOST_cipher Gost28147_89_cbc_cipher;
 extern GOST_cipher Gost28147_89_cnt_cipher;
@@ -355,6 +356,10 @@ 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;
     int nid;
index fe65c4d14a686e99e89620ebbd7944793475be69..04f4f3de4f59b9c1adf7be0bdd1d12aaff64b400 100644 (file)
@@ -86,6 +86,10 @@ static const OSSL_ALGORITHM *gost_operation(void *vprovctx,
                                                 int operation_id,
                                                 const int *no_cache)
 {
+    switch (operation_id) {
+    case OSSL_OP_CIPHER:
+        return GOST_prov_ciphers;
+    }
     return NULL;
 }
 
@@ -105,6 +109,7 @@ static const OSSL_ITEM *gost_get_reason_strings(void *provctx)
 /* The function that tears down this provider */
 static void gost_teardown(void *vprovctx)
 {
+    GOST_prov_deinit_ciphers();
     provider_ctx_free(vprovctx);
 }
 
diff --git a/gost_prov_cipher.c b/gost_prov_cipher.c
new file mode 100644 (file)
index 0000000..d64b0ff
--- /dev/null
@@ -0,0 +1,352 @@
+/**********************************************************************
+ *             gost_prov_crypt.c - Initialize all ciphers             *
+ *                                                                    *
+ *      Copyright (c) 2021 Richard Levitte <richard@levitte.org>      *
+ *     This file is distributed under the same license as OpenSSL     *
+ *                                                                    *
+ *         OpenSSL provider interface to GOST cipher functions        *
+ *                Requires OpenSSL 3.0 for compilation                *
+ **********************************************************************/
+
+#include <openssl/core.h>
+#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 specific ones
+ * MAKE_FUNCTIONS() does it for us.
+ */
+static OSSL_FUNC_cipher_dupctx_fn cipher_dupctx;
+static OSSL_FUNC_cipher_freectx_fn cipher_freectx;
+static OSSL_FUNC_cipher_get_ctx_params_fn cipher_get_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn cipher_set_ctx_params;
+static OSSL_FUNC_cipher_encrypt_init_fn cipher_encrypt_init;
+static OSSL_FUNC_cipher_decrypt_init_fn cipher_decrypt_init;
+static OSSL_FUNC_cipher_update_fn cipher_update;
+static OSSL_FUNC_cipher_final_fn cipher_final;
+
+struct gost_prov_crypt_ctx_st {
+    /* Provider context */
+    PROV_CTX *provctx;
+    /* OSSL_PARAM descriptors */
+    const OSSL_PARAM *known_params;
+    /* GOST_cipher descriptor */
+    GOST_cipher *descriptor;
+
+    /*
+     * Since existing functionality is designed for ENGINEs, the functions
+     * in this file are accomodated and are simply wrappers that use a local
+     * EVP_CIPHER and EVP_CIPHER_CTX.
+     * Future development should take a more direct approach and have the
+     * appropriate cipher functions and cipher data directly in this context.
+     */
+
+    /* The EVP_CIPHER created from |descriptor| */
+    EVP_CIPHER *cipher;
+    /* The context for the EVP_CIPHER functions */
+    EVP_CIPHER_CTX *cctx;
+};
+typedef struct gost_prov_crypt_ctx_st GOST_CTX;
+
+static void cipher_freectx(void *vgctx)
+{
+    GOST_CTX *gctx = vgctx;
+
+    /*
+     * We don't free gctx->cipher here.
+     * That will be done by the provider teardown, via
+     * GOST_prov_deinit_ciphers() (defined at the bottom of this file).
+     */
+    EVP_CIPHER_CTX_free(gctx->cctx);
+    OPENSSL_free(gctx);
+}
+
+static GOST_CTX *cipher_newctx(void *provctx, GOST_cipher *descriptor,
+                                const OSSL_PARAM *known_params)
+{
+    GOST_CTX *gctx = NULL;
+
+    if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+        gctx->provctx = provctx;
+        gctx->known_params = known_params;
+        gctx->descriptor = descriptor;
+        gctx->cipher = GOST_init_cipher(descriptor);
+        gctx->cctx = EVP_CIPHER_CTX_new();
+
+        if (gctx->cipher == NULL || gctx->cctx == NULL) {
+            cipher_freectx(gctx);
+            gctx = NULL;
+        }
+    }
+    return gctx;
+}
+
+static void *cipher_dupctx(void *vsrc)
+{
+    GOST_CTX *src = vsrc;
+    GOST_CTX *dst =
+        cipher_newctx(src->provctx, src->descriptor, src->known_params);
+
+    if (dst != NULL)
+        EVP_CIPHER_CTX_copy(dst->cctx, src->cctx);
+    return dst;
+}
+
+static int cipher_get_params(EVP_CIPHER *c, OSSL_PARAM params[])
+{
+    OSSL_PARAM *p;
+
+    if (((p = OSSL_PARAM_locate(params, "blocksize")) != NULL
+         && !OSSL_PARAM_set_size_t(p, EVP_CIPHER_block_size(c)))
+        || ((p = OSSL_PARAM_locate(params, "ivlen")) != NULL
+            && !OSSL_PARAM_set_size_t(p, EVP_CIPHER_iv_length(c)))
+        || ((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))))
+        return 0;
+    return 1;
+}
+
+static int cipher_get_ctx_params(void *vgctx, OSSL_PARAM params[])
+{
+    GOST_CTX *gctx = vgctx;
+    OSSL_PARAM *p;
+
+    if (!cipher_get_params(gctx->cipher, params))
+        return 0;
+    if ((p = OSSL_PARAM_locate(params, "alg_id_param")) != NULL) {
+        ASN1_TYPE *algidparam = NULL;
+        unsigned char *der = NULL;
+        int derlen = 0;
+        int ret;
+
+        ret = (algidparam = ASN1_TYPE_new()) != NULL
+            && EVP_CIPHER_param_to_asn1(gctx->cctx, algidparam) > 0
+            && (derlen = i2d_ASN1_TYPE(algidparam, &der)) >= 0
+            && OSSL_PARAM_set_octet_string(p, &der, (size_t)derlen);
+
+        OPENSSL_free(der);
+        ASN1_TYPE_free(algidparam);
+        return ret;
+    }
+    if ((p = OSSL_PARAM_locate(params, "updated-iv")) != NULL) {
+        const void *iv = EVP_CIPHER_CTX_iv(gctx->cctx);
+        size_t ivlen = EVP_CIPHER_CTX_iv_length(gctx->cctx);
+
+        if (!OSSL_PARAM_set_octet_ptr(p, iv, ivlen)
+            && !OSSL_PARAM_set_octet_string(p, iv, ivlen))
+            return 0;
+    }
+    return 1;
+}
+
+static int cipher_set_ctx_params(void *vgctx, const OSSL_PARAM params[])
+{
+    GOST_CTX *gctx = vgctx;
+    const OSSL_PARAM *p;
+
+    if ((p = OSSL_PARAM_locate_const(params, "alg_id_param")) != NULL) {
+        ASN1_TYPE *algidparam = NULL;
+        const unsigned char *der = NULL;
+        size_t derlen = 0;
+        int ret;
+
+        ret = OSSL_PARAM_get_octet_string_ptr(p, (const void **)&der, &derlen)
+            && (algidparam = d2i_ASN1_TYPE(NULL, &der, (long)derlen)) != NULL
+            && EVP_CIPHER_asn1_to_param(gctx->cctx, algidparam) > 0;
+
+        ASN1_TYPE_free(algidparam);
+        return ret;
+    }
+    if ((p = OSSL_PARAM_locate_const(params, "padding")) != NULL) {
+        unsigned int pad = 0;
+
+        if (!OSSL_PARAM_get_uint(p, &pad)
+            || EVP_CIPHER_CTX_set_padding(gctx->cctx, pad) <= 0)
+            return 0;
+    }
+    if ((p = OSSL_PARAM_locate_const(params, "key-mesh")) != NULL) {
+        size_t key_mesh = 0;
+
+        if (!OSSL_PARAM_get_size_t(p, &key_mesh)
+            || EVP_CIPHER_CTX_ctrl(gctx->cctx, EVP_CTRL_KEY_MESH,
+                                   key_mesh, NULL) <= 0)
+            return 0;
+    }
+    return 1;
+}
+
+static int cipher_encrypt_init(void *vgctx,
+                               const unsigned char *key, size_t keylen,
+                               const unsigned char *iv, size_t ivlen,
+                               const OSSL_PARAM params[])
+{
+    GOST_CTX *gctx = vgctx;
+
+    if (!cipher_set_ctx_params(vgctx, params)
+        || keylen > EVP_CIPHER_key_length(gctx->cipher)
+        || ivlen > EVP_CIPHER_iv_length(gctx->cipher))
+        return 0;
+    return EVP_CipherInit_ex(gctx->cctx, gctx->cipher, gctx->provctx->e,
+                             key, iv, 1);
+}
+
+static int cipher_decrypt_init(void *vgctx,
+                               const unsigned char *key, size_t keylen,
+                               const unsigned char *iv, size_t ivlen,
+                               const OSSL_PARAM params[])
+{
+    GOST_CTX *gctx = vgctx;
+
+    if (!cipher_set_ctx_params(vgctx, params)
+        || keylen > EVP_CIPHER_key_length(gctx->cipher)
+        || ivlen > EVP_CIPHER_iv_length(gctx->cipher))
+        return 0;
+    return EVP_CipherInit_ex(gctx->cctx, gctx->cipher, gctx->provctx->e,
+                             key, iv, 0) > 0;
+}
+
+static int cipher_update(void *vgctx,
+                         unsigned char *out, size_t *outl, size_t outsize,
+                         const unsigned char *in, size_t inl)
+{
+    GOST_CTX *gctx = vgctx;
+    int int_outl = outl != NULL ? *outl : 0;
+    int res = EVP_CipherUpdate(gctx->cctx, out, &int_outl, in, (int)inl);
+
+    if (res > 0 && outl != NULL)
+        *outl = (size_t)int_outl;
+    return res > 0;
+}
+
+static int cipher_final(void *vgctx,
+                        unsigned char *out, size_t *outl, size_t outsize)
+{
+    GOST_CTX *gctx = vgctx;
+    int int_outl = outl != NULL ? *outl : 0;
+    int res = EVP_CipherFinal(gctx->cctx, out, &int_outl);
+
+    if (res > 0 && outl != NULL)
+        *outl = (size_t)int_outl;
+    return res > 0;
+}
+
+static const OSSL_PARAM known_Gost28147_89_cipher_params[] = {};
+static const OSSL_PARAM known_Gost28147_89_cbc_cipher_params[] = {};
+static const OSSL_PARAM known_Gost28147_89_cnt_cipher_params[] = {};
+static const OSSL_PARAM known_Gost28147_89_cnt_12_cipher_params[] = {};
+static const OSSL_PARAM known_grasshopper_ecb_cipher_params[] = {};
+static const OSSL_PARAM known_grasshopper_cbc_cipher_params[] = {};
+static const OSSL_PARAM known_grasshopper_cfb_cipher_params[] = {};
+static const OSSL_PARAM known_grasshopper_ofb_cipher_params[] = {};
+static const OSSL_PARAM known_grasshopper_ctr_cipher_params[] = {};
+static const OSSL_PARAM known_magma_ctr_cipher_params[] = {};
+static const OSSL_PARAM known_magma_ctr_acpkm_cipher_params[] = {};
+static const OSSL_PARAM known_magma_ctr_acpkm_omac_cipher_params[] = {};
+static const OSSL_PARAM known_magma_cbc_cipher_params[] = {};
+static const OSSL_PARAM known_grasshopper_ctr_acpkm_cipher_params[] = {};
+static const OSSL_PARAM known_grasshopper_ctr_acpkm_omac_cipher_params[] = {};
+/*
+ * These are named like the EVP_CIPHER templates in gost_crypt.c, with the
+ * added suffix "_functions".  Hopefully, that makes it easy to find the
+ * actual implementation.
+ */
+typedef void (*fptr_t)(void);
+#define MAKE_FUNCTIONS(name)                                            \
+    static OSSL_FUNC_cipher_get_params_fn name##_get_params;            \
+    static int name##_get_params(OSSL_PARAM *params)                    \
+    {                                                                   \
+        return cipher_get_params(GOST_init_cipher(&name), params);      \
+    }                                                                   \
+    static OSSL_FUNC_cipher_newctx_fn name##_newctx;                    \
+    static void *name##_newctx(void *provctx)                           \
+    {                                                                   \
+        return cipher_newctx(provctx, &name, known_##name##_params);    \
+    }                                                                   \
+    static const OSSL_DISPATCH name##_functions[] = {                   \
+        { OSSL_FUNC_CIPHER_GET_PARAMS, (fptr_t)name##_get_params },     \
+        { OSSL_FUNC_CIPHER_NEWCTX, (fptr_t)name##_newctx },             \
+        { OSSL_FUNC_CIPHER_DUPCTX, (fptr_t)cipher_dupctx },             \
+        { OSSL_FUNC_CIPHER_FREECTX, (fptr_t)cipher_freectx },           \
+        { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (fptr_t)cipher_get_ctx_params }, \
+        { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (fptr_t)cipher_set_ctx_params }, \
+        { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (fptr_t)cipher_encrypt_init }, \
+        { OSSL_FUNC_CIPHER_DECRYPT_INIT, (fptr_t)cipher_decrypt_init }, \
+        { OSSL_FUNC_CIPHER_UPDATE, (fptr_t)cipher_update },             \
+        { OSSL_FUNC_CIPHER_FINAL, (fptr_t)cipher_final },               \
+    }
+
+MAKE_FUNCTIONS(Gost28147_89_cipher);
+MAKE_FUNCTIONS(Gost28147_89_cnt_cipher);
+MAKE_FUNCTIONS(Gost28147_89_cnt_12_cipher);
+MAKE_FUNCTIONS(Gost28147_89_cbc_cipher);
+MAKE_FUNCTIONS(grasshopper_ecb_cipher);
+MAKE_FUNCTIONS(grasshopper_cbc_cipher);
+MAKE_FUNCTIONS(grasshopper_cfb_cipher);
+MAKE_FUNCTIONS(grasshopper_ofb_cipher);
+MAKE_FUNCTIONS(grasshopper_ctr_cipher);
+MAKE_FUNCTIONS(magma_cbc_cipher);
+MAKE_FUNCTIONS(magma_ctr_cipher);
+MAKE_FUNCTIONS(magma_ctr_acpkm_cipher);
+MAKE_FUNCTIONS(magma_ctr_acpkm_omac_cipher);
+MAKE_FUNCTIONS(grasshopper_ctr_acpkm_cipher);
+MAKE_FUNCTIONS(grasshopper_ctr_acpkm_omac_cipher);
+
+/* The OSSL_ALGORITHM for the provider's operation query function */
+const OSSL_ALGORITHM GOST_prov_ciphers[] = {
+    { SN_id_Gost28147_89 ":gost89:GOST 28147-89:1.2.643.2.2.21", NULL,
+      Gost28147_89_cipher_functions },
+    { SN_gost89_cnt, NULL, Gost28147_89_cnt_cipher_functions },
+    { SN_gost89_cnt_12, NULL, Gost28147_89_cnt_12_cipher_functions },
+    { SN_gost89_cbc, NULL, Gost28147_89_cbc_cipher_functions },
+    { SN_grasshopper_ecb, NULL, grasshopper_ecb_cipher_functions },
+    { SN_grasshopper_cbc, NULL, grasshopper_cbc_cipher_functions },
+    { SN_grasshopper_cfb, NULL, grasshopper_cfb_cipher_functions },
+    { SN_grasshopper_ofb, NULL, grasshopper_ofb_cipher_functions },
+    { SN_grasshopper_ctr, NULL, grasshopper_ctr_cipher_functions },
+    { SN_magma_cbc, NULL, magma_cbc_cipher_functions },
+    { SN_magma_ctr, NULL, magma_ctr_cipher_functions },
+    { SN_magma_ctr_acpkm ":1.2.643.7.1.1.5.1.1", NULL,
+      magma_ctr_acpkm_cipher_functions },
+    { SN_magma_ctr_acpkm_omac ":1.2.643.7.1.1.5.1.2", NULL,
+      magma_ctr_acpkm_omac_cipher_functions },
+    { SN_kuznyechik_ctr_acpkm ":1.2.643.7.1.1.5.2.1", NULL,
+      grasshopper_ctr_acpkm_cipher_functions },
+    { SN_kuznyechik_ctr_acpkm_omac ":1.2.643.7.1.1.5.2.2", NULL,
+      grasshopper_ctr_acpkm_omac_cipher_functions },
+#if 0                           /* Not yet implemented */
+    { SN_magma_kexp15 ":1.2.643.7.1.1.7.1.1", NULL,
+      magma_kexp15_cipher_functions },
+    { SN_kuznyechik_kexp15 ":1.2.643.7.1.1.7.2.1", NULL,
+      kuznyechik_kexp15_cipher_functions },
+#endif
+    { NULL , NULL, NULL }
+};
+
+void GOST_prov_deinit_ciphers(void) {
+    static GOST_cipher *list[] = {
+        &Gost28147_89_cipher,
+        &Gost28147_89_cnt_cipher,
+        &Gost28147_89_cnt_12_cipher,
+        &Gost28147_89_cbc_cipher,
+        &grasshopper_ecb_cipher,
+        &grasshopper_cbc_cipher,
+        &grasshopper_cfb_cipher,
+        &grasshopper_ofb_cipher,
+        &grasshopper_ctr_cipher,
+        &magma_cbc_cipher,
+        &magma_ctr_cipher,
+        &magma_ctr_acpkm_cipher,
+        &magma_ctr_acpkm_omac_cipher,
+        &grasshopper_ctr_acpkm_cipher,
+        &grasshopper_ctr_acpkm_omac_cipher,
+    };
+    size_t i;
+#define elems(l) (sizeof(l) / sizeof(l[0]))
+
+    for (i = 0; i < elems(list); i++)
+        GOST_deinit_cipher(list[i]);
+}
index 5b25f30977ed57753ad9ed4757828f8a90a7e6c0..91e887646d4872addb812af7586637c2be1ea8a0 100644 (file)
 #!/usr/bin/perl
 use Test2::V0;
-skip_all('TODO: add symmetric cipher support in provider')
-    unless $ARGV[0] eq 'engine';
-plan(48);
 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 $key='0123456789abcdef' x 2;
+my $engine_name = $ENV{ENGINE_NAME} || 'gost';
+my $provider_name = $ENV{PROVIDER_NAME} || 'gostprov';
 
+# Supported test types:
 #
-# You can redefine engine to use using ENGINE_NAME environment variable
-# 
-my $engine=$ENV{'ENGINE_NAME'}||"gost";
+# conf                          Only if there's a command line argument.
+#                               For this test type, we rely entirely on the
+#                               caller to define the environment variable
+#                               OPENSSL_CONF appropriately.
+# standalone-engine-conf        Tests the engine through a generated config
+#                               file.
+#                               This is done when there are no command line
+#                               arguments or when the environment variable
+#                               ENGINE_NAME is defined.
+# standalone-engine-args        Tests the engine through openssl command args.
+#                               This is done when there are no command line
+#                               arguments or when the environment variable
+#                               ENGINE_NAME is defined.
+# standalone-provider-conf      Tests the provider through a generated config
+#                               file.
+#                               This is done when there are no command line
+#                               arguments or when the environment variable
+#                               PROVIDER_NAME is defined.
+# standalone-provider-args      Tests the provider through openssl command args.
+#                               This is done when there are no command line
+#                               arguments or when the environment variable
+#                               PROVIDER_NAME is defined.
+my @test_types = ( $ARGV[0] ? 'conf' : (),
+                   ( !$ARGV[0] || $ENV{ENGINE_NAME}
+                     ? ( 'standalone-engine-conf', 'standalone-engine-args' )
+                     : () ),
+                   ( !$ARGV[0] || $ENV{PROVIDER_NAME}
+                     ? ( 'standalone-provider-conf', 'standalone-provider-args' )
+                     : () ) );
+
+plan(48 * scalar @test_types);
+
+# prepare data for
 
-# Reopen STDERR to eliminate extra output
-open STDERR, ">>","tests.err";
-
-our $count=0;
+my $key='0123456789abcdef' x 2;
 
-#
-# parameters -paramset = oid of the parameters
-# -cleartext - data to encrypt
-# -ciphertext - expected ciphertext (hex-encoded)
-# -key - key (hex-encoded)
-# -iv  - IV (hex-encoded)
-# 
-my $F;
-my $eng_param;
-
-open $F,">","test.cnf";
-if (defined($use_config) && $use_config) {
-       $eng_param = "";
-       open $F,">","test.cnf";
-       print $F <<EOCFG
+my %configurations = (
+    'standalone-engine-args' => {
+        'openssl-args'  => "-engine $engine_name",
+    },
+    'standalone-provider-args' => {
+        'openssl-args'  => "-provider $provider_name -provider default",
+    },
+    'standalone-engine-conf' => {
+        'openssl-conf'  => <<EOCFG,
 openssl_conf = openssl_def
 [openssl_def]
 engines = engines
 [engines]
-${engine}=gost_conf
-[gost_conf]
+${engine_name}=${engine_name}_conf
+[${engine_name}_conf]
 default_algorithms = ALL
-
 EOCFG
-} else {
-       $eng_param = "-engine $engine"
-}
-close $F;
-$ENV{'OPENSSL_CONF'}=abs_path('test.cnf');
-       
+    },
+    'standalone-provider-conf' => {
+        'openssl-conf'  => <<EOCFG,
+openssl_conf = openssl_def
+[openssl_def]
+providers = providers
+[providers]
+${provider_name}=${provider_name}_conf
+[${provider_name}_conf]
+EOCFG
+    },
+);
+
 sub crypt_test {
-       my %p = @_;
-       our $count++;
-       open my $f, ">", "test$count.clear";
-       print $f $p{-cleartext};
-       close $f;
-       
-       $ENV{'CRYPT_PARAMS'} = $p{-paramset} if exists $p{-paramset};
-       my $ctext = `openssl enc ${eng_param} -e -$p{-alg} -K $p{-key} -iv $p{-iv} -in test$count.clear`;
-       is($?,0,"$p{-name} - encrypt successful");
-       is(unpack("H*",$ctext),$p{-ciphertext},"$p{-name} - ciphertext expected");
-       open $f, ">", "test$count.enc";
-       print $f $ctext;
-       close $f;
-       my $otext = `openssl enc ${eng_param} -d -$p{-alg} -K $p{-key} -iv $p{-iv} -in test$count.enc`;
-       is($?,0,"$p{-name} - decrypt successful");
-       is($otext,$p{-cleartext},"$p{-name} - decrypted correctly");
-       unlink "test$count.enc";
-       unlink "test$count.clear";
-       delete $ENV{'CRYPT_PARAMS'};
+    my %p = @_;
+    my $test_type = $p{-testtype};
+    my $args = $p{-args};
+    my $count = ++${$p{-count}};
+    my $result_name = "$test_type$count";
+    open my $f, ">", "$result_name.clear";
+    print $f $p{-cleartext};
+    close $f;
+
+    $ENV{'CRYPT_PARAMS'} = $p{-paramset} if exists $p{-paramset};
+    my $ccmd = "openssl enc${args} -e -$p{-alg} -K $p{-key} -iv $p{-iv} -in $result_name.clear";
+    my $ctext = `$ccmd`;
+    unless (is($?,0,"$p{-name} - Trying to encrypt")) {
+        diag("Command was: $ccmd");
+    }
+    is(unpack("H*",$ctext),$p{-ciphertext},"$p{-name} - Checking that it encrypted correctly");
+    open $f, ">", "$result_name.enc";
+    print $f $ctext;
+    close $f;
+    my $ocmd = "openssl enc${args} -d -$p{-alg} -K $p{-key} -iv $p{-iv} -in $result_name.enc";
+    my $otext = `$ocmd`;
+    unless(is($?,0,"$p{-name} - Trying to decrypt")) {
+        diag("Command was: $ocmd");
+    }
+    is($otext,$p{-cleartext},"$p{-name} - Checking that it decrypted correctly");
+    unlink "$result_name.enc";
+    unlink "$result_name.clear";
+    delete $ENV{'CRYPT_PARAMS'};
 }
 
-$key = '0123456789ABCDEF' x 4;
-my $iv =  '0000000000000000';
-my $clear1 = "The quick brown fox jumps over the lazy dog\n";
-
-crypt_test(-paramset=> "1.2.643.2.2.31.1", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => '07f4102c6185c4a09e676e269bfa4bc9c5df6575916b879bd13a893a2285ee6690107cdeef7a315d2eb54bfa', 
-                  -alg => 'gost89',
-                  -name=> 'CFB short text, paramset A');
-
-crypt_test(-paramset=> "1.2.643.2.2.31.2", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => '11465c1c9708033e784fbb5536f2719c38353cb488b01f195c20d4c027022e8300d98bb66c138afbe878c88b', 
-                  -alg => 'gost89',
-                  -name=> 'CFB short text, paramset B');
-
-crypt_test(-paramset=> "1.2.643.2.2.31.3", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => '2f213b390c9b6ceb18de479686d23f4f03c76644a0aab8894b50b71a3bbb3c027ec4c2d569ba0e6a873bd46e', 
-                  -alg => 'gost89',
-                  -name=> 'CFB short text, paramset C');
-
-crypt_test(-paramset=> "1.2.643.2.2.31.4", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => 'e835f59a7fdfd84764efe1e987660327f5d0de187afea72f9cd040983a5e5bbeb4fe1aa5ff85d623ebc4d435', 
-                  -alg => 'gost89',
-                  -name=> 'CFB short text, paramset D');
-
-
-crypt_test(-paramset=> "1.2.643.2.2.31.1", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => 'bcb821452e459f10f92019171e7c3b27b87f24b174306667f67704812c07b70b5e7420f74a9d54feb4897df8', 
-                  -alg => 'gost89-cnt',
-                  -name=> 'CNT short text');
-
-crypt_test(-paramset=> "1.2.643.2.2.31.2", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => 'bcb821452e459f10f92019171e7c3b27b87f24b174306667f67704812c07b70b5e7420f74a9d54feb4897df8', 
-                  -alg => 'gost89-cnt',
-                  -name=> 'CNT short text, paramset param doesnt affect cnt');
-
-                  
-crypt_test(-paramset=> "1.2.643.2.2.31.1", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => 'cf3f5f713b3d10abd0c6f7bafb6aaffe13dfc12ef5c844f84873aeaaf6eb443a9747c9311b86f97ba3cdb5c4',
-                  -alg => 'gost89-cnt-12',
-                  -name=> 'CNT-12 short text');
-
-crypt_test(-paramset=> "1.2.643.2.2.31.2", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => 'cf3f5f713b3d10abd0c6f7bafb6aaffe13dfc12ef5c844f84873aeaaf6eb443a9747c9311b86f97ba3cdb5c4',
-                  -alg => 'gost89-cnt-12',
-                  -name=> 'CNT-12 short text, paramset param doesnt affect cnt');
-
-
-crypt_test(-paramset=> "1.2.643.2.2.31.1", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => '3a3293e75089376572da44966cd1759c29d2f1e5e1c3fa9674909a63026da3dc51a4266bff37fb74a3a07155c9ca8fcf', 
-                  -alg => 'gost89-cbc',
-                  -name=> 'CBC short text, paramset A');
-
-
-crypt_test(-paramset=> "1.2.643.2.2.31.2", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => 'af2a2167b75852378af176ac9950e3c4bffc94d3d4355191707adbb16d6c8e3f3a07868c4702babef18393edfac60a6d', 
-                  -alg => 'gost89-cbc',
-                  -name=> 'CBC short text, paramset B');
-
-crypt_test(-paramset=> "1.2.643.2.2.31.3", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => '987c0fb3d84530467a1973791e0a25e33c5d14591976f8c1573bdb9d056eb7b353f66fef3ffe2e3524583b3997123c8a', 
-                  -alg => 'gost89-cbc',
-                  -name=> 'CBC short text, paramset C');
-
-crypt_test(-paramset=> "1.2.643.2.2.31.4", -key => $key, -iv => $iv,
-                  -cleartext => $clear1,
-                  -ciphertext => 'e076b09822d4786a2863125d16594d765d8acd0f360e52df42e9d52c8e6c0e6595b5f6bbecb04a22c8ae5f4f87c1523b', 
-                  -alg => 'gost89-cbc',
-                  -name=> 'CBC short text, paramset D');
-
-unlink "test.cnf";
+foreach my $test_type (@test_types) {
+    my $configuration = $configurations{$test_type};
+    my $module_args = $configuration->{'openssl-args'} // '';
+    my $module_conf = $configuration->{'openssl-conf'};
+    # This is a trick to make a locally modifiable environment variable and
+    # retain it's current value as a default.
+    local $ENV{OPENSSL_CONF} = $ENV{OPENSSL_CONF};
+
+    note("Running tests for test type $test_type");
+
+    if ($module_args) {
+        $module_args = ' ' . $module_args;
+    }
+    if (defined $module_conf) {
+        my $confname = "$test_type.cnf";
+        open my $F, '>', $confname;
+        print $F $module_conf;
+        close $F;
+        $ENV{OPENSSL_CONF} = abs_path($confname);
+    }
+
+    # Reopen STDERR to eliminate extra output
+    #open STDERR, ">>","tests.err";
+
+    my $count=0;
+
+    #
+    # parameters -paramset = oid of the parameters
+    # -cleartext - data to encrypt
+    # -ciphertext - expected ciphertext (hex-encoded)
+    # -key - key (hex-encoded)
+    # -iv  - IV (hex-encoded)
+    #
+    $key = '0123456789ABCDEF' x 4;
+    my $iv =  '0000000000000000';
+    my $clear1 = "The quick brown fox jumps over the lazy dog\n";
+    my @common_args = ( -count          => \$count,
+                        -args           => $module_args,
+                        -key            => $key,
+                        -iv             => $iv,
+                        -cleartext      => $clear1 );
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.1",
+               -ciphertext      => '07f4102c6185c4a09e676e269bfa4bc9c5df6575916b879bd13a893a2285ee6690107cdeef7a315d2eb54bfa',
+               -alg             => 'gost89',
+               -name            => 'CFB short text, paramset A',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.2",
+               -ciphertext      => '11465c1c9708033e784fbb5536f2719c38353cb488b01f195c20d4c027022e8300d98bb66c138afbe878c88b',
+               -alg             => 'gost89',
+               -name            => 'CFB short text, paramset B',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.3",
+               -ciphertext      => '2f213b390c9b6ceb18de479686d23f4f03c76644a0aab8894b50b71a3bbb3c027ec4c2d569ba0e6a873bd46e',
+               -alg             => 'gost89',
+               -name            => 'CFB short text, paramset C',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.4",
+               -ciphertext      => 'e835f59a7fdfd84764efe1e987660327f5d0de187afea72f9cd040983a5e5bbeb4fe1aa5ff85d623ebc4d435',
+               -alg             => 'gost89',
+               -name            => 'CFB short text, paramset D',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.1",
+               -ciphertext      => 'bcb821452e459f10f92019171e7c3b27b87f24b174306667f67704812c07b70b5e7420f74a9d54feb4897df8',
+               -alg             => 'gost89-cnt',
+               -name            => 'CNT short text',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.2",
+               -ciphertext      => 'bcb821452e459f10f92019171e7c3b27b87f24b174306667f67704812c07b70b5e7420f74a9d54feb4897df8',
+               -alg             => 'gost89-cnt',
+               -name            => 'CNT short text, paramset param doesnt affect cnt',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.1",
+               -ciphertext      => 'cf3f5f713b3d10abd0c6f7bafb6aaffe13dfc12ef5c844f84873aeaaf6eb443a9747c9311b86f97ba3cdb5c4',
+               -alg             => 'gost89-cnt-12',
+               -name            => 'CNT-12 short text',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.2",
+               -ciphertext      => 'cf3f5f713b3d10abd0c6f7bafb6aaffe13dfc12ef5c844f84873aeaaf6eb443a9747c9311b86f97ba3cdb5c4',
+               -alg             => 'gost89-cnt-12',
+               -name            => 'CNT-12 short text, paramset param doesnt affect cnt',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.1",
+               -ciphertext      => '3a3293e75089376572da44966cd1759c29d2f1e5e1c3fa9674909a63026da3dc51a4266bff37fb74a3a07155c9ca8fcf',
+               -alg             => 'gost89-cbc',
+               -name            => 'CBC short text, paramset A',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.2",
+               -ciphertext      => 'af2a2167b75852378af176ac9950e3c4bffc94d3d4355191707adbb16d6c8e3f3a07868c4702babef18393edfac60a6d',
+               -alg             => 'gost89-cbc',
+               -name            => 'CBC short text, paramset B',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.3",
+               -ciphertext      => '987c0fb3d84530467a1973791e0a25e33c5d14591976f8c1573bdb9d056eb7b353f66fef3ffe2e3524583b3997123c8a',
+               -alg             => 'gost89-cbc',
+               -name            => 'CBC short text, paramset C',
+               @common_args);
+
+    crypt_test(-paramset        => "1.2.643.2.2.31.4",
+               -ciphertext      => 'e076b09822d4786a2863125d16594d765d8acd0f360e52df42e9d52c8e6c0e6595b5f6bbecb04a22c8ae5f4f87c1523b',
+               -alg             => 'gost89-cbc',
+               -name            => 'CBC short text, paramset D',
+               @common_args);
+
+    if (defined $module_conf) {
+        unlink "$test_type.cnf";
+    }
+}