]> www.wagner.pp.ru Git - openssl-gost/engine.git/commitdiff
KUZNYECHIK-CTR_ACPKM-OMAC implementation
authorDmitry Belyavskiy <beldmit@gmail.com>
Mon, 4 May 2020 11:14:19 +0000 (14:14 +0300)
committerDmitry Belyavskiy <beldmit@gmail.com>
Mon, 4 May 2020 12:50:12 +0000 (15:50 +0300)
gost_eng.c
gost_gost2015.h
gost_grasshopper_cipher.c
gost_grasshopper_cipher.h

index 249b5e413f9f51d83e4f4bf1d9cea2e1b5b85e2c..0ae4c9baa89a95eeb21b7192086633d5ad41be37 100644 (file)
@@ -158,6 +158,10 @@ static struct gost_cipher_minfo {
         NID_id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm,
         cipher_gost_grasshopper_ctracpkm,
     },
+    {
+        NID_kuznyechik_ctr_acpkm_omac,
+        cipher_gost_grasshopper_ctracpkm_omac,
+    },
     {
         NID_magma_kexp15,
         cipher_magma_wrap,
index 797fbca2e08ad4e598d5d0115b8fcd1feca67567..4feed585b3098f65de5843f286b463b577652c90 100644 (file)
@@ -5,6 +5,7 @@
 
 #define MAGMA_MAC_MAX_SIZE 8
 #define KUZNYECHIK_MAC_MAX_SIZE 16
+#define OID_GOST_CMS_MAC "1.2.643.7.1.0.6.1.1"
 
 int gost2015_final_call(EVP_CIPHER_CTX *ctx, EVP_MD_CTX *omac_ctx, size_t mac_size,
                        unsigned char *encrypted_mac,
index 75a0408e24b8e39eb073824369f08bd956f73aa3..9cef0c5f02fa1a596177385d32531c230e98e46a 100644 (file)
@@ -24,10 +24,11 @@ enum GRASSHOPPER_CIPHER_TYPE {
     GRASSHOPPER_CIPHER_CFB,
     GRASSHOPPER_CIPHER_CTR,
     GRASSHOPPER_CIPHER_CTRACPKM,
+    GRASSHOPPER_CIPHER_CTRACPKMOMAC,
 };
 
-static EVP_CIPHER *gost_grasshopper_ciphers[6] = {
-    NULL, NULL, NULL, NULL, NULL, NULL,
+static EVP_CIPHER *gost_grasshopper_ciphers[7] = {
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
 static GRASSHOPPER_INLINE void
@@ -42,69 +43,87 @@ struct GRASSHOPPER_CIPHER_PARAMS {
     int ctx_size;
     int iv_size;
     bool padding;
+    int extra_flags;
 };
 
-static struct GRASSHOPPER_CIPHER_PARAMS gost_cipher_params[6] = {
- {
-                                NID_grasshopper_ecb,
-                                gost_grasshopper_cipher_init_ecb,
-                                gost_grasshopper_cipher_do_ecb,
-                                NULL,
-                                16,
-                                sizeof(gost_grasshopper_cipher_ctx),
-                                0,
-                                true}
-    ,
-    {
-                                NID_grasshopper_cbc,
-                                gost_grasshopper_cipher_init_cbc,
-                                gost_grasshopper_cipher_do_cbc,
-                                NULL,
-                                16,
-                                sizeof(gost_grasshopper_cipher_ctx),
-                                16,
-                                true}
-    ,
-    {
-                                NID_grasshopper_ofb,
-                                gost_grasshopper_cipher_init_ofb,
-                                gost_grasshopper_cipher_do_ofb,
-                                NULL,
-                                1,
-                                sizeof(gost_grasshopper_cipher_ctx),
-                                16,
-                                false}
-    ,
-    {
-                                NID_grasshopper_cfb,
-                                gost_grasshopper_cipher_init_cfb,
-                                gost_grasshopper_cipher_do_cfb,
-                                NULL,
-                                1,
-                                sizeof(gost_grasshopper_cipher_ctx),
-                                16,
-                                false}
-    ,
-    {
-                                NID_grasshopper_ctr,
-                                gost_grasshopper_cipher_init_ctr,
-                                gost_grasshopper_cipher_do_ctr,
-                                gost_grasshopper_cipher_destroy_ctr,
-                                1,
-                                sizeof(gost_grasshopper_cipher_ctx_ctr),
-                                8,
-                                false}
-    ,
-    {
-                                NID_id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm,
-                                gost_grasshopper_cipher_init_ctracpkm,
-                                gost_grasshopper_cipher_do_ctracpkm,
-                                gost_grasshopper_cipher_destroy_ctr,
-                                1,
-                                sizeof(gost_grasshopper_cipher_ctx_ctr),
-                                8,
-                                false}
-    ,
+static struct GRASSHOPPER_CIPHER_PARAMS gost_cipher_params[7] = {
+       {
+               NID_grasshopper_ecb,
+               gost_grasshopper_cipher_init_ecb,
+               gost_grasshopper_cipher_do_ecb,
+               NULL,
+               16,
+               sizeof(gost_grasshopper_cipher_ctx),
+               0,
+               true,
+               0
+       },
+       {
+               NID_grasshopper_cbc,
+               gost_grasshopper_cipher_init_cbc,
+               gost_grasshopper_cipher_do_cbc,
+               NULL,
+               16,
+               sizeof(gost_grasshopper_cipher_ctx),
+               16,
+               true,
+               0
+       },
+       {
+               NID_grasshopper_ofb,
+               gost_grasshopper_cipher_init_ofb,
+               gost_grasshopper_cipher_do_ofb,
+               NULL,
+               1,
+               sizeof(gost_grasshopper_cipher_ctx),
+               16,
+               false,
+               0
+       },
+       {
+               NID_grasshopper_cfb,
+               gost_grasshopper_cipher_init_cfb,
+               gost_grasshopper_cipher_do_cfb,
+               NULL,
+               1,
+               sizeof(gost_grasshopper_cipher_ctx),
+               16,
+               false,
+               0
+       },
+       {
+               NID_grasshopper_ctr,
+               gost_grasshopper_cipher_init_ctr,
+               gost_grasshopper_cipher_do_ctr,
+               gost_grasshopper_cipher_destroy_ctr,
+               1,
+               sizeof(gost_grasshopper_cipher_ctx_ctr),
+               8,
+               false,
+               0
+       },
+       {
+               NID_kuznyechik_ctr_acpkm,
+               gost_grasshopper_cipher_init_ctracpkm,
+               gost_grasshopper_cipher_do_ctracpkm,
+               gost_grasshopper_cipher_destroy_ctr,
+               1,
+               sizeof(gost_grasshopper_cipher_ctx_ctr),
+               8,
+               false,
+               0
+       },
+       {
+               NID_kuznyechik_ctr_acpkm_omac,
+               gost_grasshopper_cipher_init_ctracpkm_omac,
+               gost_grasshopper_cipher_do_ctracpkm_omac,
+               gost_grasshopper_cipher_destroy_ctr,
+               1,
+               sizeof(gost_grasshopper_cipher_ctx_ctr),
+               8,
+               false,
+               EVP_CIPH_FLAG_CUSTOM_CIPHER|EVP_CIPH_FLAG_CIPHER_WITH_MAC|EVP_CIPH_CUSTOM_COPY
+       },
 };
 
 /* first 256 bit of D from draft-irtf-cfrg-re-keying-12 */
@@ -182,6 +201,9 @@ gost_grasshopper_cipher_destroy_ctr(gost_grasshopper_cipher_ctx * c)
     gost_grasshopper_cipher_ctx_ctr *ctx =
         (gost_grasshopper_cipher_ctx_ctr *) c;
 
+    if (ctx->omac_ctx)
+        EVP_MD_CTX_free(ctx->omac_ctx);
+
     grasshopper_zero128(&ctx->partial_buffer);
 }
 
@@ -278,6 +300,54 @@ GRASSHOPPER_INLINE int gost_grasshopper_cipher_init_ctracpkm(EVP_CIPHER_CTX
     return gost_grasshopper_cipher_init(ctx, key, iv, enc);
 }
 
+GRASSHOPPER_INLINE int gost_grasshopper_cipher_init_ctracpkm_omac(EVP_CIPHER_CTX
+                                                             *ctx, const unsigned
+                                                             char *key, const unsigned
+                                                             char *iv, int enc)
+{
+       gost_grasshopper_cipher_ctx_ctr *c = EVP_CIPHER_CTX_get_cipher_data(ctx);
+
+       /* NB: setting type makes EVP do_cipher callback useless */
+       c->c.type = GRASSHOPPER_CIPHER_CTRACPKMOMAC;
+       EVP_CIPHER_CTX_set_num(ctx, 0);
+       c->section_size = 4096;
+
+       if (key) {
+               unsigned char keys[64];
+               const EVP_MD *md = EVP_get_digestbynid(NID_kuznyechik_mac);
+               EVP_PKEY *mac_key;
+
+               if (md == NULL)
+                       return 0;
+
+               if (enc) {
+                       if (RAND_bytes(c->kdf_seed, 8) != 1)
+                               return 0;
+               }
+
+               if (gost_kdftree2012_256(keys, 64, key, 32, (const unsigned char *)"kdf tree", 8, c->kdf_seed, 8, 1) <= 0)
+                       return 0;
+
+               c->omac_ctx = EVP_MD_CTX_new();
+               mac_key = EVP_PKEY_new_mac_key(NID_kuznyechik_mac, NULL, keys+32, 32);
+
+               if (mac_key == NULL || c->omac_ctx == NULL) {
+                       EVP_PKEY_free(mac_key);
+                       return 0;
+               }
+
+               if (EVP_DigestInit_ex(c->omac_ctx, md, NULL) <= 0 ||
+                               EVP_DigestSignInit(c->omac_ctx, NULL, md, NULL, mac_key) <= 0) {
+                       EVP_PKEY_free(mac_key);
+                       return 0;
+               }
+               EVP_PKEY_free(mac_key);
+
+               return gost_grasshopper_cipher_init(ctx, keys, iv, enc);
+       }
+       return gost_grasshopper_cipher_init(ctx, key, iv, enc);
+}
+
 GRASSHOPPER_INLINE int gost_grasshopper_cipher_do(EVP_CIPHER_CTX *ctx,
                                                   unsigned char *out,
                                                   const unsigned char *in,
@@ -498,9 +568,31 @@ int gost_grasshopper_cipher_do_ctracpkm(EVP_CIPHER_CTX *ctx,
     }
     EVP_CIPHER_CTX_set_num(ctx, num);
 
-    return 1;
+    return inl;
 }
 
+int gost_grasshopper_cipher_do_ctracpkm_omac(EVP_CIPHER_CTX *ctx,
+                                        unsigned char *out,
+                                        const unsigned char *in, size_t inl)
+{
+       int result;
+  gost_grasshopper_cipher_ctx_ctr *c = EVP_CIPHER_CTX_get_cipher_data(ctx);
+       /* As in and out can be the same pointer, process unencrypted here */
+       if (EVP_CIPHER_CTX_encrypting(ctx))
+               EVP_DigestSignUpdate(c->omac_ctx, in, inl);
+
+       if (in == NULL && inl == 0) { /* Final call */
+               return gost2015_final_call(ctx, c->omac_ctx, KUZNYECHIK_MAC_MAX_SIZE, c->tag, gost_grasshopper_cipher_do_ctracpkm);
+       }
+
+       result = gost_grasshopper_cipher_do_ctracpkm(ctx, out, in, inl);
+
+       /* As in and out can be the same pointer, process decrypted here */
+       if (!EVP_CIPHER_CTX_encrypting(ctx))
+               EVP_DigestSignUpdate(c->omac_ctx, out, inl);
+
+       return result;
+}
 /*
  * Fixed 128-bit IV implementation make shift regiser redundant.
  */
@@ -691,6 +783,9 @@ int gost_grasshopper_set_asn1_parameters(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params)
        if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CTR_MODE) {
                gost_grasshopper_cipher_ctx_ctr *ctr = EVP_CIPHER_CTX_get_cipher_data(ctx);
 
+               /* CMS implies 256kb section_size */
+               ctr->section_size = 256*1024;
+
                return gost2015_set_asn1_params(params, EVP_CIPHER_CTX_original_iv(ctx), 8,
                                ctr->kdf_seed);
        }
@@ -714,7 +809,7 @@ GRASSHOPPER_INLINE int gost_grasshopper_get_asn1_parameters(EVP_CIPHER_CTX
                memcpy(EVP_CIPHER_CTX_iv_noconst(ctx), iv, iv_len);
                memcpy((unsigned char *)EVP_CIPHER_CTX_original_iv(ctx), iv, iv_len);
 
-               /* CMS implies 256kb mesh_section_size */
+               /* CMS implies 256kb section_size */
                ctr->section_size = 256*1024;
                return 1;
        }
@@ -736,7 +831,9 @@ int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg,
     case EVP_CTRL_KEY_MESH:{
             gost_grasshopper_cipher_ctx_ctr *c =
                 EVP_CIPHER_CTX_get_cipher_data(ctx);
-            if (c->c.type != GRASSHOPPER_CIPHER_CTRACPKM || !arg
+            if ((c->c.type != GRASSHOPPER_CIPHER_CTRACPKM &&
+                                                   c->c.type != GRASSHOPPER_CIPHER_CTRACPKMOMAC)
+                                                   || (arg == 0)
                 || (arg % GRASSHOPPER_BLOCK_SIZE))
                 return -1;
             c->section_size = arg;
@@ -796,6 +893,78 @@ int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX *ctx, int type, int arg,
         }
         return -1;
 #endif
+#if 0
+    case EVP_CTRL_AEAD_GET_TAG:
+    case EVP_CTRL_AEAD_SET_TAG:
+        {
+            int taglen = arg;
+            unsigned char *tag = ptr;
+
+            gost_grasshopper_cipher_ctx *c = EVP_CIPHER_CTX_get_cipher_data(ctx);
+            if (c->c.type != GRASSHOPPER_CIPHER_MGM)
+                return -1;
+
+            if (taglen > KUZNYECHIK_MAC_MAX_SIZE) {
+                CRYPTOCOMerr(CRYPTOCOM_F_GOST_GRASSHOPPER_CIPHER_CTL,
+                        CRYPTOCOM_R_INVALID_TAG_LENGTH);
+                return -1;
+            }
+
+            if (type == EVP_CTRL_AEAD_GET_TAG)
+                memcpy(tag, c->final_tag, taglen);
+            else
+                memcpy(c->final_tag, tag, taglen);
+
+            return 1;
+        }
+#endif
+               case EVP_CTRL_PROCESS_UNPROTECTED:
+                               {
+                                       gost_grasshopper_cipher_ctx_ctr *c = EVP_CIPHER_CTX_get_cipher_data(ctx);
+                                       ASN1_OBJECT *cmsmacobj = NULL;
+                                       if (c->c.type != GRASSHOPPER_CIPHER_CTRACPKMOMAC)
+                                               return -1;
+          cmsmacobj = OBJ_txt2obj(OID_GOST_CMS_MAC, 1);
+                                       if (cmsmacobj == NULL) {
+                                               GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_CTL, ERR_R_MALLOC_FAILURE);
+                                               return -1;
+                                       }
+                                       if (arg == 0) /*Decrypting*/ {
+                                               STACK_OF(X509_ATTRIBUTE) *x = ptr;
+                                               ASN1_OCTET_STRING *osExpectedMac = X509at_get0_data_by_OBJ(x,
+                                                               cmsmacobj, -3, V_ASN1_OCTET_STRING);
+                                               ASN1_OBJECT_free(cmsmacobj);
+
+                                               if (ptr == NULL || osExpectedMac ==NULL || osExpectedMac->length != KUZNYECHIK_MAC_MAX_SIZE)
+                                                       return -1;
+
+                                               memcpy(c->tag, osExpectedMac->data, osExpectedMac->length);
+                                               return 1;
+                                       } else {
+                                               STACK_OF(X509_ATTRIBUTE) *x = ptr;
+                                               return (X509at_add1_attr_by_OBJ(&x, cmsmacobj,
+                                                                       V_ASN1_OCTET_STRING, c->tag, KUZNYECHIK_MAC_MAX_SIZE) == NULL) ? -1 : 1;
+                                       }
+                               }
+                       return 1;
+    case EVP_CTRL_COPY: {
+                       EVP_CIPHER_CTX *out = ptr;
+
+      gost_grasshopper_cipher_ctx_ctr *out_cctx = EVP_CIPHER_CTX_get_cipher_data(out);
+      gost_grasshopper_cipher_ctx_ctr *in_cctx  = EVP_CIPHER_CTX_get_cipher_data(ctx);
+
+      if (in_cctx->c.type != GRASSHOPPER_CIPHER_CTRACPKMOMAC)
+          return -1;
+
+                       if (in_cctx->omac_ctx == out_cctx->omac_ctx) {
+                               out_cctx->omac_ctx = EVP_MD_CTX_new();
+                               if (out_cctx->omac_ctx == NULL) {
+                                       GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_CTL, ERR_R_MALLOC_FAILURE);
+                                       return -1;
+                               }
+                       }
+                       return EVP_MD_CTX_copy(out_cctx->omac_ctx, in_cctx->omac_ctx);
+               }
     default:
         GOSTerr(GOST_F_GOST_GRASSHOPPER_CIPHER_CTL,
                 GOST_R_UNSUPPORTED_CIPHER_CTL_COMMAND);
@@ -813,20 +982,16 @@ GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper_create(int
 }
 
 const int cipher_gost_grasshopper_setup(EVP_CIPHER *cipher, uint8_t mode,
-                                        int iv_size, bool padding)
+                                        int iv_size, bool padding, int extra_flags)
 {
+       unsigned long flags = (unsigned long)(mode
+                | ((!padding) ?  EVP_CIPH_NO_PADDING : 0)
+                                        | ((iv_size >  0) ?  EVP_CIPH_CUSTOM_IV : 0)
+                                        | EVP_CIPH_RAND_KEY |  EVP_CIPH_ALWAYS_CALL_INIT
+                                        | extra_flags);
+
     return EVP_CIPHER_meth_set_iv_length(cipher, iv_size)
-        && EVP_CIPHER_meth_set_flags(cipher,
-                                     (unsigned long)(mode |
-                                                     ((!padding) ?
-                                                      EVP_CIPH_NO_PADDING :
-                                                      0) | ((iv_size >
-                                                             0) ?
-                                                            EVP_CIPH_CUSTOM_IV
-                                                            : 0) |
-                                                     EVP_CIPH_RAND_KEY |
-                                                     EVP_CIPH_ALWAYS_CALL_INIT)
-        )
+        && EVP_CIPHER_meth_set_flags(cipher, flags)
         && EVP_CIPHER_meth_set_cleanup(cipher, gost_grasshopper_cipher_cleanup)
         && EVP_CIPHER_meth_set_set_asn1_params(cipher,
                                                gost_grasshopper_set_asn1_parameters)
@@ -846,7 +1011,7 @@ const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper(uint8_t mode,
 
     if (*cipher == NULL) {
         grasshopper_init_cipher_func init_cipher;
-        int nid, block_size, ctx_size, iv_size;
+        int nid, block_size, ctx_size, iv_size, extra_flags;
         bool padding;
 
         params = &gost_cipher_params[num];
@@ -857,13 +1022,14 @@ const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper(uint8_t mode,
         ctx_size = params->ctx_size;
         iv_size = params->iv_size;
         padding = params->padding;
+                               extra_flags = params->extra_flags;
 
         *cipher = cipher_gost_grasshopper_create(nid, block_size);
         if (*cipher == NULL) {
             return NULL;
         }
 
-        if (!cipher_gost_grasshopper_setup(*cipher, mode, iv_size, padding)
+        if (!cipher_gost_grasshopper_setup(*cipher, mode, iv_size, padding, extra_flags)
             || !EVP_CIPHER_meth_set_init(*cipher, init_cipher)
             || !EVP_CIPHER_meth_set_impl_ctx_size(*cipher, ctx_size)) {
             EVP_CIPHER_meth_free(*cipher);
@@ -905,6 +1071,12 @@ const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper_ctracpkm()
                                    GRASSHOPPER_CIPHER_CTRACPKM);
 }
 
+const GRASSHOPPER_INLINE EVP_CIPHER *cipher_gost_grasshopper_ctracpkm_omac()
+{
+    return cipher_gost_grasshopper(EVP_CIPH_CTR_MODE,
+                                   GRASSHOPPER_CIPHER_CTRACPKMOMAC);
+}
+
 void cipher_gost_grasshopper_destroy(void)
 {
     EVP_CIPHER_meth_free(gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_ECB]);
@@ -919,4 +1091,6 @@ void cipher_gost_grasshopper_destroy(void)
     gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTR] = NULL;
     EVP_CIPHER_meth_free(gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTRACPKM]);
     gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTRACPKM] = NULL;
+    EVP_CIPHER_meth_free(gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTRACPKMOMAC]);
+    gost_grasshopper_ciphers[GRASSHOPPER_CIPHER_CTRACPKMOMAC] = NULL;
 }
index a0acba4b496f6e948dc5eac8c08218c58e88b36c..f0ad7b3b7616f59fbc463fa9f5cc079ab25f525e 100644 (file)
@@ -31,6 +31,8 @@ typedef struct {
     unsigned int section_size;  /* After how much bytes mesh the key,
                                   if 0 never mesh and work like plain ctr. */
     unsigned char kdf_seed[8];
+               unsigned char tag[16];
+               EVP_MD_CTX *omac_ctx;
 } gost_grasshopper_cipher_ctx_ctr;
 
 typedef int (* grasshopper_init_cipher_func)(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv,
@@ -57,6 +59,8 @@ int gost_grasshopper_cipher_init_ctr(EVP_CIPHER_CTX* ctx, const unsigned char* k
 
 int gost_grasshopper_cipher_init_ctracpkm(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv, int enc);
 
+int gost_grasshopper_cipher_init_ctracpkm_omac(EVP_CIPHER_CTX* ctx, const unsigned char* key, const unsigned char* iv, int enc);
+
 int gost_grasshopper_cipher_init(EVP_CIPHER_CTX* ctx, const unsigned char* key,
                                  const unsigned char* iv, int enc);
 
@@ -79,6 +83,8 @@ int gost_grasshopper_cipher_do_ctr(EVP_CIPHER_CTX* ctx, unsigned char* out,
                                    const unsigned char* in, size_t inl);
 int gost_grasshopper_cipher_do_ctracpkm(EVP_CIPHER_CTX* ctx, unsigned char* out,
                                    const unsigned char* in, size_t inl);
+int gost_grasshopper_cipher_do_ctracpkm_omac(EVP_CIPHER_CTX* ctx, unsigned char* out,
+                                   const unsigned char* in, size_t inl);
 
 int gost_grasshopper_cipher_cleanup(EVP_CIPHER_CTX* ctx);
 
@@ -90,7 +96,7 @@ int gost_grasshopper_cipher_ctl(EVP_CIPHER_CTX* ctx, int type, int arg, void* pt
 
 EVP_CIPHER* cipher_gost_grasshopper_create(int cipher_type, int block_size);
 
-const int cipher_gost_grasshopper_setup(EVP_CIPHER* cipher, uint8_t mode, int iv_size, bool padding);
+const int cipher_gost_grasshopper_setup(EVP_CIPHER* cipher, uint8_t mode, int iv_size, bool padding, int extra_flags);
 
 const EVP_CIPHER* cipher_gost_grasshopper(uint8_t mode, uint8_t num);
 
@@ -100,6 +106,7 @@ extern const EVP_CIPHER* cipher_gost_grasshopper_ofb();
 extern const EVP_CIPHER* cipher_gost_grasshopper_cfb();
 extern const EVP_CIPHER* cipher_gost_grasshopper_ctr();
 extern const EVP_CIPHER* cipher_gost_grasshopper_ctracpkm();
+extern const EVP_CIPHER* cipher_gost_grasshopper_ctracpkm_omac();
 
 void cipher_gost_grasshopper_destroy(void);