]> www.wagner.pp.ru Git - openssl-gost/engine.git/commitdiff
Making a gost provider - Add the macs
authorRichard Levitte <richard@levitte.org>
Mon, 5 Apr 2021 06:08:10 +0000 (08:08 +0200)
committerDmitry Belyavskiy <beldmit@users.noreply.github.com>
Mon, 11 Oct 2021 16:34:09 +0000 (19:34 +0300)
We add the macs for the provider as wrappers around the EVP_MD
implementations 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 alias.  This is the way to avoid having to register the OIDs in
OpenSSL proper.

CMakeLists.txt
gost_lcl.h
gost_prov.c
gost_prov_mac.c [new file with mode: 0644]
test/02-mac.t

index c95e5ea49cf1593f7388ed8d1fd1337c22557ed8..740cac3388183322957f2d4d40f9e2f5c71007b1 100644 (file)
@@ -201,6 +201,7 @@ set(GOST_PROV_SOURCE_FILES
         gost_prov.c
         gost_prov_cipher.c
         gost_prov_digest.c
+        gost_prov_mac.c
         )
 
 set(TEST_ENVIRONMENT_COMMON
index 0e544eee66e6b91941399fbb7493eddcf4b1997d..e78540465b73920444a414eb5150a50f5be66fdf 100644 (file)
@@ -53,6 +53,10 @@ 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);
index ba7120b094a2328304d7fd6aa39e280c69337318..e7fdd967d2b0856fc197929694c7b4f8b98b7d94 100644 (file)
@@ -91,6 +91,8 @@ static const OSSL_ALGORITHM *gost_operation(void *vprovctx,
         return GOST_prov_ciphers;
     case OSSL_OP_DIGEST:
         return GOST_prov_digests;
+    case OSSL_OP_MAC:
+        return GOST_prov_macs;
     }
     return NULL;
 }
@@ -113,6 +115,7 @@ static void gost_teardown(void *vprovctx)
 {
     GOST_prov_deinit_ciphers();
     GOST_prov_deinit_digests();
+    GOST_prov_deinit_mac_digests();
     provider_ctx_free(vprovctx);
 }
 
diff --git a/gost_prov_mac.c b/gost_prov_mac.c
new file mode 100644 (file)
index 0000000..ab04a9c
--- /dev/null
@@ -0,0 +1,361 @@
+/**********************************************************************
+ *               gost_prov_mac.c - Initialize all macs                *
+ *                                                                    *
+ *      Copyright (c) 2021 Richard Levitte <richard@levitte.org>      *
+ *     This file is distributed under the same license as OpenSSL     *
+ *                                                                    *
+ *          OpenSSL provider interface to GOST mac 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_mac_dupctx_fn mac_dupctx;
+static OSSL_FUNC_mac_freectx_fn mac_freectx;
+static OSSL_FUNC_mac_init_fn mac_init;
+static OSSL_FUNC_mac_update_fn mac_update;
+static OSSL_FUNC_mac_final_fn mac_final;
+static OSSL_FUNC_mac_get_ctx_params_fn mac_get_ctx_params;
+static OSSL_FUNC_mac_set_ctx_params_fn mac_set_ctx_params;
+
+struct gost_prov_mac_desc_st {
+    /*
+     * In the GOST engine, the MAC implementation bases itself heavily on
+     * digests with the same name.  We can re-use that part.
+     */
+    GOST_digest *digest_desc;
+    size_t initial_mac_size;
+};
+typedef struct gost_prov_mac_desc_st GOST_DESC;
+
+struct gost_prov_mac_ctx_st {
+    /* Provider context */
+    PROV_CTX *provctx;
+    const GOST_DESC *descriptor;
+
+    /* Output MAC size */
+    size_t mac_size;
+    /* XOF mode, where applicable */
+    int xof_mode;
+
+    /*
+     * Since existing functionality is mainly designed as EVP_MDs for
+     * ENGINEs, the functions in this file are accomodated and are simply
+     * wrappers that use a local EVP_MD and EVP_MD_CTX.
+     * Future development should take a more direct approach and have the
+     * appropriate digest functions and digest data directly in this context.
+     */
+
+    /* The EVP_MD created from |descriptor| */
+    EVP_MD *digest;
+    /* The context for the EVP_MD functions */
+    EVP_MD_CTX *dctx;
+};
+typedef struct gost_prov_mac_ctx_st GOST_CTX;
+
+static void mac_freectx(void *vgctx)
+{
+    GOST_CTX *gctx = vgctx;
+
+    /*
+     * We don't free gctx->digest here.
+     * That will be done by the provider teardown, via
+     * GOST_prov_deinit_digests() (defined at the bottom of this file).
+     */
+    EVP_MD_CTX_free(gctx->dctx);
+    OPENSSL_free(gctx);
+}
+
+static GOST_CTX *mac_newctx(void *provctx, const GOST_DESC *descriptor)
+{
+    GOST_CTX *gctx = NULL;
+
+    if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+        gctx->provctx = provctx;
+        gctx->descriptor = descriptor;
+        gctx->mac_size = descriptor->initial_mac_size;
+        gctx->digest = GOST_init_digest(descriptor->digest_desc);
+        gctx->dctx = EVP_MD_CTX_new();
+
+        if (gctx->digest == NULL
+            || gctx->dctx == NULL
+            || EVP_DigestInit_ex(gctx->dctx, gctx->digest,
+                                 gctx->provctx->e) <= 0) {
+            mac_freectx(gctx);
+            gctx = NULL;
+        }
+    }
+    return gctx;
+}
+
+static void *mac_dupctx(void *vsrc)
+{
+    GOST_CTX *src = vsrc;
+    GOST_CTX *dst =
+        mac_newctx(src->provctx, src->descriptor);
+
+    if (dst != NULL)
+        EVP_MD_CTX_copy(dst->dctx, src->dctx);
+    return dst;
+}
+
+static int mac_init(void *mctx, const unsigned char *key,
+                    size_t keylen, const OSSL_PARAM params[])
+{
+    GOST_CTX *gctx = mctx;
+
+    return mac_set_ctx_params(gctx, params)
+        && (key == NULL
+            || EVP_MD_CTX_ctrl(gctx->dctx, EVP_MD_CTRL_SET_KEY,
+                               (int)keylen, (void *)key) > 0);
+}
+
+static int mac_update(void *mctx, const unsigned char *in, size_t inl)
+{
+    GOST_CTX *gctx = mctx;
+
+    return EVP_DigestUpdate(gctx->dctx, in, inl) > 0;
+}
+
+static int mac_final(void *mctx, unsigned char *out, size_t *outl,
+                     size_t outsize)
+{
+    GOST_CTX *gctx = mctx;
+    unsigned int tmpoutl;
+    int ret = 0;
+
+    /* This is strange code...  but it duplicates pkey_gost_mac_signctx() */
+
+    if (outl == NULL)
+        return 0;
+
+    /* for platforms where sizeof(int) != * sizeof(size_t) */
+    tmpoutl = *outl;
+
+    if (out != NULL) {
+        /* We ignore the error for GOST MDs that don't support setting
+           the size */
+        EVP_MD_CTX_ctrl(gctx->dctx, EVP_MD_CTRL_XOF_LEN, gctx->mac_size, NULL);
+        ret = EVP_DigestFinal_ex(gctx->dctx, out, &tmpoutl);
+    }
+    if (outl != NULL)
+        *outl = (size_t)gctx->mac_size;
+    return ret;
+}
+
+static const OSSL_PARAM *mac_gettable_params(void *provctx,
+                                             const GOST_DESC * descriptor)
+{
+    static const OSSL_PARAM params[] = {
+        OSSL_PARAM_size_t("size", NULL),
+        OSSL_PARAM_size_t("keylen", NULL),
+        OSSL_PARAM_END
+    };
+
+    return params;
+}
+
+static const OSSL_PARAM *mac_gettable_ctx_params(void *mctx, void *provctx)
+{
+    static const OSSL_PARAM params[] = {
+        OSSL_PARAM_size_t("size", NULL),
+        OSSL_PARAM_size_t("keylen", NULL),
+        OSSL_PARAM_END
+    };
+
+    return params;
+}
+
+static const OSSL_PARAM *mac_settable_ctx_params(void *mctx, void *provctx)
+{
+    static const OSSL_PARAM params[] = {
+        OSSL_PARAM_size_t("size", NULL),
+        OSSL_PARAM_octet_string("key", NULL, 0),
+        OSSL_PARAM_END
+    };
+
+    return params;
+}
+
+static int mac_get_params(const GOST_DESC * descriptor, OSSL_PARAM params[])
+{
+    OSSL_PARAM *p = NULL;
+
+    if (((p = OSSL_PARAM_locate(params, "size")) != NULL
+         && !OSSL_PARAM_set_size_t(p, descriptor->initial_mac_size))
+        || ((p = OSSL_PARAM_locate(params, "keylen")) != NULL
+            && !OSSL_PARAM_set_size_t(p, 32)))
+        return 0;
+    return 1;
+}
+
+static int mac_get_ctx_params(void *mctx, OSSL_PARAM params[])
+{
+    GOST_CTX *gctx = mctx;
+    OSSL_PARAM *p = NULL;
+
+    if ((p = OSSL_PARAM_locate(params, "size")) != NULL
+        && !OSSL_PARAM_set_size_t(p, gctx->mac_size))
+        return 0;
+
+    if ((p = OSSL_PARAM_locate(params, "keylen")) != NULL) {
+        unsigned int len = 0;
+
+        if (EVP_MD_CTX_ctrl(gctx->dctx, EVP_MD_CTRL_KEY_LEN, 0, &len) <= 0
+            || !OSSL_PARAM_set_size_t(p, len))
+            return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate(params, "xof")) != NULL
+        && (!(EVP_MD_flags(EVP_MD_CTX_md(gctx->dctx)) & EVP_MD_FLAG_XOF)
+            || !OSSL_PARAM_set_int(p, gctx->xof_mode)))
+        return 0;
+
+    return 1;
+}
+
+static int mac_set_ctx_params(void *mctx, const OSSL_PARAM params[])
+{
+    GOST_CTX *gctx = mctx;
+    const OSSL_PARAM *p = NULL;
+
+    if ((p = OSSL_PARAM_locate_const(params, "size")) != NULL
+        && !OSSL_PARAM_get_size_t(p, &gctx->mac_size))
+        return 0;
+    if ((p = OSSL_PARAM_locate_const(params, "key")) != NULL) {
+        const unsigned char *key = NULL;
+        size_t keylen = 0;
+        int ret;
+
+        if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&key, &keylen))
+            return 0;
+
+        ret = EVP_MD_CTX_ctrl(gctx->dctx, EVP_MD_CTRL_SET_KEY,
+                              (int)keylen, (void *)key);
+        if (ret <= 0 && ret != -2)
+            return 0;
+    }
+    if ((p = OSSL_PARAM_locate_const(params, "xof")) != NULL
+        && (!(EVP_MD_flags(EVP_MD_CTX_md(gctx->dctx)) & EVP_MD_FLAG_XOF)
+            || !OSSL_PARAM_get_int(p, &gctx->xof_mode)))
+        return 0;
+    if ((p = OSSL_PARAM_locate_const(params, "key-mesh")) != NULL) {
+        size_t key_mesh = 0;
+        int i_cipher_key_mesh = 0, *p_cipher_key_mesh = NULL;
+
+        if (!OSSL_PARAM_get_size_t(p, &key_mesh))
+            return 0;
+
+        if ((p = OSSL_PARAM_locate_const(params, "cipher-key-mesh")) != NULL) {
+            size_t cipher_key_mesh = 0;
+
+            if (!OSSL_PARAM_get_size_t(p, &cipher_key_mesh)) {
+                return 0;
+            } else {
+                i_cipher_key_mesh = (int)cipher_key_mesh;
+                p_cipher_key_mesh = &i_cipher_key_mesh;
+            }
+        }
+
+        if (EVP_MD_CTX_ctrl(gctx->dctx, EVP_CTRL_KEY_MESH,
+                            key_mesh, p_cipher_key_mesh) <= 0)
+            return 0;
+    }
+    return 1;
+}
+
+/*
+ * Macros to map the MAC algorithms to their respective GOST_digest
+ * implementation where necessary.  Not needed for magma and grasshopper, as
+ * they already have fitting names.
+ */
+#define id_Gost28147_89_MAC_digest      Gost28147_89_MAC_digest
+#define gost_mac_12_digest              Gost28147_89_mac_12_digest
+#define id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm_omac_digest \
+    kuznyechik_ctracpkm_omac_digest
+
+typedef void (*fptr_t)(void);
+#define MAKE_FUNCTIONS(name, macsize)                                   \
+    const GOST_DESC name##_desc = {                                     \
+        &name##_digest,                                                 \
+        macsize,                                                        \
+    };                                                                  \
+    static OSSL_FUNC_mac_newctx_fn name##_newctx;                       \
+    static void *name##_newctx(void *provctx)                           \
+    {                                                                   \
+        return mac_newctx(provctx, &name##_desc);                       \
+    }                                                                   \
+    static OSSL_FUNC_mac_gettable_params_fn name##_gettable_params;     \
+    static const OSSL_PARAM *name##_gettable_params(void *provctx)      \
+    {                                                                   \
+        return mac_gettable_params(provctx, &name##_desc);              \
+    }                                                                   \
+    static OSSL_FUNC_mac_get_params_fn name##_get_params;               \
+    static int name##_get_params(OSSL_PARAM *params)                    \
+    {                                                                   \
+        return mac_get_params(&name##_desc, params);                    \
+    }                                                                   \
+    static const OSSL_DISPATCH name##_functions[] = {                   \
+        { OSSL_FUNC_MAC_GETTABLE_PARAMS,                                \
+          (fptr_t)name##_gettable_params },                             \
+        { OSSL_FUNC_MAC_GET_PARAMS, (fptr_t)name##_get_params },        \
+        { OSSL_FUNC_MAC_NEWCTX, (fptr_t)name##_newctx },                \
+        { OSSL_FUNC_MAC_DUPCTX, (fptr_t)mac_dupctx },                   \
+        { OSSL_FUNC_MAC_FREECTX, (fptr_t)mac_freectx },                 \
+        { OSSL_FUNC_MAC_INIT, (fptr_t)mac_init },                       \
+        { OSSL_FUNC_MAC_UPDATE, (fptr_t)mac_update },                   \
+        { OSSL_FUNC_MAC_FINAL, (fptr_t)mac_final },                     \
+        { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS,                            \
+          (fptr_t)mac_gettable_ctx_params },                            \
+        { OSSL_FUNC_MAC_GET_CTX_PARAMS, (fptr_t)mac_get_ctx_params },   \
+        { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS,                            \
+          (fptr_t)mac_settable_ctx_params },                            \
+        { OSSL_FUNC_MAC_SET_CTX_PARAMS, (fptr_t)mac_set_ctx_params },   \
+    }
+
+/*
+ * The name used here is the same as the NID name.  Some of the names are
+ * horribly long, but that can't be helped...
+ */
+MAKE_FUNCTIONS(id_Gost28147_89_MAC, 4);
+MAKE_FUNCTIONS(gost_mac_12, 4);
+MAKE_FUNCTIONS(magma_mac, 8);
+MAKE_FUNCTIONS(grasshopper_mac, 16);
+MAKE_FUNCTIONS(id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm_omac, 16);
+
+/* The OSSL_ALGORITHM for the provider's operation query function */
+const OSSL_ALGORITHM GOST_prov_macs[] = {
+    { SN_id_Gost28147_89_MAC ":1.2.643.2.2.22", NULL,
+      id_Gost28147_89_MAC_functions, "GOST 28147-89 MAC" },
+    { SN_gost_mac_12, NULL, gost_mac_12_functions },
+    { SN_magma_mac, NULL, magma_mac_functions },
+    { SN_grasshopper_mac, NULL, grasshopper_mac_functions },
+    { SN_id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm_omac
+      ":1.2.643.7.1.1.5.2.2", NULL,
+      id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm_omac_functions },
+    { NULL , NULL, NULL }
+};
+
+void GOST_prov_deinit_mac_digests(void) {
+    static GOST_digest *list[] = {
+        &Gost28147_89_MAC_digest,
+        &Gost28147_89_mac_12_digest,
+        &magma_mac_digest,
+        &grasshopper_mac_digest,
+        &kuznyechik_ctracpkm_omac_digest
+    };
+    size_t i;
+#define elems(l) (sizeof(l) / sizeof(l[0]))
+
+    for (i = 0; i < elems(list); i++)
+        GOST_deinit_digest(list[i]);
+}
index 04a637720ac0e702993809ce6204d6223b276447..7ecb38cabc55e916c39f119ddc047eb3dc39f7e8 100644 (file)
@@ -1,8 +1,42 @@
 #!/usr/bin/perl 
 use Test2::V0;
-skip_all('TODO: add mac support in provider')
-    unless $ARGV[0] eq 'engine';
-plan(19);
+
+my $engine_name = $ENV{ENGINE_NAME} || 'gost';
+my $provider_name = $ENV{PROVIDER_NAME} || 'gostprov';
+
+# Supported test types:
+#
+# 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(19 * scalar @test_types);
 
 # prepare data for 
 my $F;
@@ -15,38 +49,164 @@ print $F ("12345670" x 8 . "\n") x  4096;
 close $F;
 
 my $key='0123456789abcdef' x 2;
+note("\@ARGV = (", join(', ', @ARGV), ")");
+my %configurations = (
+    'conf' => {
+        'module-type'   => $ARGV[0],
+    },
+    'standalone-engine-args' => {
+        'module-type'   => 'engine',
+        'openssl-args'  => "-engine $engine_name",
+    },
+    'standalone-provider-args' => {
+        'module-type'   => 'provider',
+        'openssl-args'  => "-provider $provider_name -provider default",
+    },
+    'standalone-engine-conf' => {
+        'module-type'   => 'engine',
+        'openssl-conf'  => <<EOCFG,
+openssl_conf = openssl_def
+[openssl_def]
+engines = engines
+[engines]
+${engine_name}=${engine_name}_conf
+[${engine_name}_conf]
+default_algorithms = ALL
+EOCFG
+    },
+    'standalone-provider-conf' => {
+        'module-type'   => 'provider',
+        'openssl-conf'  => <<EOCFG,
+openssl_conf = openssl_def
+[openssl_def]
+providers = providers
+[providers]
+${provider_name}=${provider_name}_conf
+[${provider_name}_conf]
+EOCFG
+    },
+);
 
-my $engine=$ENV{'ENGINE_NAME'}||"gost";
+my %executors = (
+    engine => {
+        mac_cmd => sub {
+            my %opts = @_;
+            my $cmd = "openssl dgst $opts{-args}"
+                . " -mac $opts{-mac} -macopt key:$opts{-key}"
+                . (defined $opts{-size} ? " -sigopt size:$opts{-size}" : "")
+                . " $opts{-infile}";
 
-# Reopen STDERR to eliminate extra output
-open STDERR, ">>","tests.err";
+            return $cmd;
+        },
+        check_expected => sub {
+            my %opts = @_;
 
-is(`openssl dgst -engine ${engine} -mac gost-mac -macopt key:${key} testdata.dat`,
-"GOST-MAC-gost-mac(testdata.dat)= 2ee8d13d\n",
-"GOST MAC - default size");
+            return "$opts{-mac}($opts{-infile})= $opts{-result}\n";
+        },
+    },
+    provider => {
+        mac_cmd => sub {
+            my %opts = @_;
+            my $cmd = "openssl mac $opts{-args} -macopt key:$opts{-key}"
+                . (defined $opts{-size} ? " -macopt size:$opts{-size}" : "")
+                . " -in $opts{-infile} $opts{-mac}";
 
-my $i;
-for ($i=1;$i<=8; $i++) {
-       is(`openssl dgst -engine ${engine} -mac gost-mac -macopt key:${key} -sigopt size:$i testdata.dat`,
-"GOST-MAC-gost-mac(testdata.dat)= ".substr("2ee8d13dff7f037d",0,$i*2)."\n",
-"GOST MAC - size $i bytes");
-}
+            return $cmd;
+        },
+        check_expected => sub {
+            my %opts = @_;
+
+            return uc($opts{-result})."\n";
+        },
+    },
+);
+
+foreach my $test_type (@test_types) {
+    my $configuration = $configurations{$test_type};
+    my $module_type = $configuration->{'module-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};
 
+  SKIP: {
+      skip "No module type detected for test type '$test_type'", 19
+          unless $module_type;
 
+      note("Running tests for test type $test_type");
 
-is(`openssl dgst -engine ${engine} -mac gost-mac -macopt key:${key} testbig.dat`,
-"GOST-MAC-gost-mac(testbig.dat)= 5efab81f\n",
-"GOST MAC - big data");
+      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);
+      }
 
-is(`openssl dgst -engine ${engine} -mac gost-mac-12 -macopt key:${key} testdata.dat`,
-"GOST-MAC-12-gost-mac-12(testdata.dat)= be4453ec\n",
-"GOST MAC - parameters 2012");
+      # Reopen STDERR to eliminate extra output
+      #open STDERR, ">>","tests.err";
 
+      my $mac_cmd = $executors{$module_type}->{mac_cmd};
+      my $mac_expected = $executors{$module_type}->{check_expected};
+      my $cmd;
+      my $expected;
 
-for ($i=1;$i<=8; $i++) {
-       is(`openssl dgst -engine ${engine} -mac gost-mac-12 -macopt key:${key} -sigopt size:$i testdata.dat`,
-"GOST-MAC-12-gost-mac-12(testdata.dat)= ".substr("be4453ec1ec327be",0,$i*2)."\n",
-"GOST MAC parameters 2012 - size $i bytes");
+      $cmd = $mac_cmd->(-mac => 'gost-mac', -key => $key,
+                        -args => $module_args, -infile => 'testdata.dat');
+      $expected = $mac_expected->(-mac => 'GOST-MAC-gost-mac',
+                                  -infile => 'testdata.dat',
+                                  -result => '2ee8d13d');
+      unless (is(`$cmd`, $expected, "GOST MAC - default size")) {
+          diag("Command was: $cmd");
+      }
+
+      my $i;
+      for ($i=1;$i<=8; $i++) {
+          $cmd = $mac_cmd->(-mac => 'gost-mac', -key => $key, -size => $i,
+                            -args => $module_args, -infile => 'testdata.dat');
+          $expected = $mac_expected->(-mac => 'GOST-MAC-gost-mac',
+                                      -infile => 'testdata.dat',
+                                      -result => substr("2ee8d13dff7f037d",0,$i*2));
+          unless (is(`$cmd`, $expected, "GOST MAC - size $i bytes")) {
+              diag("Command was: $cmd");
+          }
+      }
+
+
+
+      $cmd = $mac_cmd->(-mac => 'gost-mac', -key => $key,
+                        -args => $module_args, -infile => 'testbig.dat');
+      $expected = $mac_expected->(-mac => 'GOST-MAC-gost-mac',
+                                  -infile => 'testbig.dat',
+                                  -result => '5efab81f');
+      unless (is(`$cmd`, $expected, "GOST MAC - big data")) {
+          diag("Command was: $cmd");
+      }
+
+      $cmd = $mac_cmd->(-mac => 'gost-mac-12', -key => $key,
+                        -args => $module_args, -infile => 'testdata.dat');
+      $expected = $mac_expected->(-mac => 'GOST-MAC-12-gost-mac-12',
+                                  -infile => 'testdata.dat',
+                                  -result => 'be4453ec');
+      unless (is(`$cmd`, $expected, "GOST MAC parameters 2012 - default size")) {
+          diag("Command was: $cmd");
+      }
+      for ($i=1;$i<=8; $i++) {
+          $cmd = $mac_cmd->(-mac => 'gost-mac-12', -key => $key, -size => $i,
+                            -args => $module_args, -infile => 'testdata.dat');
+          $expected = $mac_expected->(-mac => 'GOST-MAC-12-gost-mac-12',
+                                      -infile => 'testdata.dat',
+                                      -result => substr("be4453ec1ec327be",0,$i*2));
+          unless (is(`$cmd`, $expected, "GOST MAC parameters 2012 - size $i bytes")) {
+              diag("Command was: $cmd");
+          }
+      }
+    }
 }
+
 unlink('testdata.dat');
 unlink('testbig.dat');