]> www.wagner.pp.ru Git - openssl-gost/engine.git/commitdiff
Decryption tests + formatting
authorDmitry Belyavskiy <beldmit@gmail.com>
Tue, 7 May 2019 13:02:18 +0000 (16:02 +0300)
committerDmitry Belyavskiy <beldmit@gmail.com>
Tue, 7 May 2019 13:02:18 +0000 (16:02 +0300)
test_mgm.c

index d6b5a740484e231dc42a7dd5abd3be592908d90c..d8b62d9927351084a04ad54ec94c136a80811618 100644 (file)
@@ -1,10 +1,59 @@
-# include <stdio.h>
-# include <string.h>
-# include <openssl/err.h>
-# include <openssl/evp.h>
+#include <stdio.h>
+#include <string.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
 #include <openssl/buffer.h>
 #include <byteswap.h>
 
+const unsigned char gh_key[] = {
+    0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33,
+        0x44, 0x55, 0x66, 0x77,
+    0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x01, 0x23, 0x45, 0x67,
+        0x89, 0xAB, 0xCD, 0xEF,
+};
+
+const unsigned char gh_nonce[] = {
+    0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC,
+        0xBB, 0xAA, 0x99, 0x88
+};
+
+const unsigned char gh_adata[] = {
+    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+        0x01, 0x01, 0x01, 0x01,
+    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
+        0x03, 0x03, 0x03, 0x03,
+    0xEA, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05
+};
+
+const unsigned char gh_pdata[] = {
+    0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC,
+        0xBB, 0xAA, 0x99, 0x88,
+    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
+        0xCC, 0xEE, 0xFF, 0x0A,
+    0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC,
+        0xEE, 0xFF, 0x0A, 0x00,
+    0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE,
+        0xFF, 0x0A, 0x00, 0x11,
+    0xAA, 0xBB, 0xCC
+};
+
+const unsigned char etalon_cdata[] = {
+    0xA9, 0x75, 0x7B, 0x81, 0x47, 0x95, 0x6E, 0x90, 0x55, 0xB8, 0xA3, 0x3D,
+        0xE8, 0x9F, 0x42, 0xFC,
+    0x80, 0x75, 0xD2, 0x21, 0x2B, 0xF9, 0xFD, 0x5B, 0xD3, 0xF7, 0x06, 0x9A,
+        0xAD, 0xC1, 0x6B, 0x39,
+    0x49, 0x7A, 0xB1, 0x59, 0x15, 0xA6, 0xBA, 0x85, 0x93, 0x6B, 0x5D, 0x0E,
+        0xA9, 0xF6, 0x85, 0x1C,
+    0xC6, 0x0C, 0x14, 0xD4, 0xD3, 0xF8, 0x83, 0xD0, 0xAB, 0x94, 0x42, 0x06,
+        0x95, 0xC7, 0x6D, 0xEB,
+    0x2C, 0x75, 0x52
+};
+
+const unsigned char etalon_tag[] = {
+    0xCF, 0x5D, 0x65, 0x6F, 0x40, 0xC3, 0x4F, 0x5C, 0x46, 0xE8, 0xBB, 0x0E,
+        0x29, 0xFC, 0xDB, 0x4C
+};
+
 static void hexdump(FILE *f, const char *title, const unsigned char *s, int l)
 {
     int n = 0;
@@ -18,251 +67,299 @@ static void hexdump(FILE *f, const char *title, const unsigned char *s, int l)
     fprintf(f, "\n");
 }
 
-void grasshopper_mgm_big()
+void grasshopper_enc_mgm_big(const EVP_CIPHER *ciph)
 {
-       const unsigned char gh_key[] = { 
-0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
-       };
-
-       const unsigned char gh_nonce[] = {
-       0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88
-       };
-
-       const unsigned char gh_adata[] = {
-       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-       0xEA, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05
-       };
-
-       const unsigned char gh_pdata[] = {
-       0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88,
-       0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A,
-       0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00,
-       0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, 0x11,
-       0xAA, 0xBB, 0xCC
-       };
-
-       const unsigned char etalon_tag[] = {
-               0xCF, 0x5D, 0x65, 0x6F, 0x40, 0xC3, 0x4F, 0x5C, 0x46, 0xE8, 0xBB, 0x0E, 0x29, 0xFC, 0xDB, 0x4C
-       };
-
-       unsigned char tag[16];
-       unsigned char gh_cdata[sizeof(gh_pdata)];
-       unsigned char *pcdata = gh_cdata;
-
-       int asize = sizeof(gh_adata);
-       int psize = sizeof(gh_pdata);
-       int fsize = 0;
-
-       const EVP_CIPHER *ciph;
-       EVP_CIPHER_CTX *enc = NULL;
-
-       int i;
-
-       ciph = EVP_get_cipherbynid(NID_kuznyechik_mgm);
-       if (ciph == NULL) {
-               fprintf(stderr, "Could not obtain cipher");
-               return;
-       }
-               
-       enc = EVP_CIPHER_CTX_new();
-       EVP_EncryptInit_ex(enc, ciph, NULL, gh_key, gh_nonce);
-
-       EVP_EncryptUpdate(enc, NULL, &asize, gh_adata, sizeof(gh_adata));
-       EVP_EncryptUpdate(enc, gh_cdata, &psize, gh_pdata, sizeof(gh_pdata));
-       EVP_EncryptFinal_ex(enc, NULL, &fsize);
-
-       EVP_CIPHER_CTX_ctrl(enc, EVP_CTRL_AEAD_GET_TAG, 16, tag);
-
-       if (memcmp (tag, etalon_tag, 16)) {
-               fprintf(stderr, "Shit happens!\n");
-               hexdump(stderr, "Etalon tag", etalon_tag, 16);
-               hexdump(stderr, "Got tag", tag, 16);
-               return;
-       }
-       fprintf(stderr, "OK - big chunks!\n");
-       EVP_CIPHER_CTX_free(enc);
-
-       fprintf(stderr, "================ \n");
+    unsigned char tag[16];
+    unsigned char gh_cdata[sizeof(gh_pdata)];
+    unsigned char *pcdata = gh_cdata;
+
+    int asize = sizeof(gh_adata);
+    int psize = sizeof(gh_pdata);
+    int fsize = 0;
+
+    EVP_CIPHER_CTX *enc = NULL;
+
+    int i;
+
+    enc = EVP_CIPHER_CTX_new();
+    EVP_EncryptInit_ex(enc, ciph, NULL, gh_key, gh_nonce);
+
+    EVP_EncryptUpdate(enc, NULL, &asize, gh_adata, sizeof(gh_adata));
+    EVP_EncryptUpdate(enc, gh_cdata, &psize, gh_pdata, sizeof(gh_pdata));
+    EVP_EncryptFinal_ex(enc, NULL, &fsize);
+
+    EVP_CIPHER_CTX_ctrl(enc, EVP_CTRL_AEAD_GET_TAG, 16, tag);
+
+    if (memcmp(etalon_cdata, gh_cdata, sizeof(gh_cdata))) {
+        fprintf(stderr, "Shit happens - encryption!\n");
+        hexdump(stderr, "Etalon cdata", etalon_cdata, sizeof(etalon_cdata));
+        hexdump(stderr, "Got cdata", gh_cdata, sizeof(gh_cdata));
+        return;
+    }
+
+    if (memcmp(tag, etalon_tag, 16)) {
+        fprintf(stderr, "Shit happens!\n");
+        hexdump(stderr, "Etalon tag", etalon_tag, 16);
+        hexdump(stderr, "Got tag", tag, 16);
+        return;
+    }
+    fprintf(stderr, "OK encryption - big chunks!\n");
+    EVP_CIPHER_CTX_free(enc);
 }
 
-void grasshopper_mgm_small()
+void grasshopper_enc_mgm_small(const EVP_CIPHER *ciph)
 {
-       const unsigned char gh_key[] = { 
-0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
-       };
-
-       const unsigned char gh_nonce[] = {
-       0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88
-       };
-
-       const unsigned char gh_adata[] = {
-       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
-       0xEA, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05
-       };
-
-       const unsigned char gh_pdata[] = {
-       0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88,
-       0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A,
-       0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00,
-       0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, 0x11,
-       0xAA, 0xBB, 0xCC
-       };
-
-       const unsigned char etalon_tag[] = {
-               0xCF, 0x5D, 0x65, 0x6F, 0x40, 0xC3, 0x4F, 0x5C, 0x46, 0xE8, 0xBB, 0x0E, 0x29, 0xFC, 0xDB, 0x4C
-       };
-
-       unsigned char tag[16];
-       unsigned char gh_cdata[sizeof(gh_pdata)];
-       unsigned char *pcdata = gh_cdata;
-
-       int asize = sizeof(gh_adata);
-       int psize = sizeof(gh_pdata);
-       int fsize = 0;
-
-       const EVP_CIPHER *ciph;
-       EVP_CIPHER_CTX *enc = NULL;
-
-       int i;
-
-       ciph = EVP_get_cipherbynid(NID_kuznyechik_mgm);
-       if (ciph == NULL) {
-               fprintf(stderr, "Could not obtain cipher");
-               return;
-       }
-               
-       enc = EVP_CIPHER_CTX_new();
-       EVP_EncryptInit_ex(enc, ciph, NULL, gh_key, gh_nonce);
-
-       for (i = 0; i < sizeof(gh_adata); i++)
-       {
-               asize = 1;
-               EVP_EncryptUpdate(enc, NULL, &asize, gh_adata+i, 1);
-       }
-
-       for (i = 0; i < sizeof(gh_pdata); i++)
-       {
-               psize = 1;
-               EVP_EncryptUpdate(enc, pcdata, &psize, gh_pdata+i, 1);
-               pcdata += psize;
-       }
-
-       EVP_EncryptFinal_ex(enc, NULL, &fsize);
-
-       EVP_CIPHER_CTX_ctrl(enc, EVP_CTRL_AEAD_GET_TAG, 16, tag);
-
-       if (memcmp (tag, etalon_tag, 16)) {
-               fprintf(stderr, "Shit happens!\n");
-               hexdump(stderr, "Etalon tag", etalon_tag, 16);
-               hexdump(stderr, "Got tag", tag, 16);
-               return;
-       }
-       fprintf(stderr, "OK - small chunks!\n");
-       EVP_CIPHER_CTX_free(enc);
+    unsigned char tag[16];
+    unsigned char gh_cdata[sizeof(gh_pdata)];
+    unsigned char *pcdata = gh_cdata;
+
+    int asize = sizeof(gh_adata);
+    int psize = sizeof(gh_pdata);
+    int fsize = 0;
+
+    EVP_CIPHER_CTX *enc = NULL;
+
+    int i;
+
+    enc = EVP_CIPHER_CTX_new();
+    EVP_EncryptInit_ex(enc, ciph, NULL, gh_key, gh_nonce);
+    for (i = 0; i < sizeof(gh_adata); i++) {
+        asize = 1;
+        EVP_EncryptUpdate(enc, NULL, &asize, gh_adata + i, 1);
+    }
+    for (i = 0; i < sizeof(gh_pdata); i++) {
+        psize = 1;
+        EVP_EncryptUpdate(enc, pcdata, &psize, gh_pdata + i, 1);
+        pcdata += psize;
+    }
+
+    EVP_EncryptFinal_ex(enc, NULL, &fsize);
+
+    EVP_CIPHER_CTX_ctrl(enc, EVP_CTRL_AEAD_GET_TAG, 16, tag);
+
+    if (memcmp(etalon_cdata, gh_cdata, sizeof(gh_cdata))) {
+        fprintf(stderr, "Shit happens - encryption!\n");
+        hexdump(stderr, "Etalon cdata", etalon_cdata, sizeof(etalon_cdata));
+        hexdump(stderr, "Got cdata", gh_cdata, sizeof(gh_cdata));
+        return;
+    }
+
+    if (memcmp(tag, etalon_tag, 16)) {
+        fprintf(stderr, "Shit happens!\n");
+        hexdump(stderr, "Etalon tag", etalon_tag, 16);
+        hexdump(stderr, "Got tag", tag, 16);
+        return;
+    }
+    fprintf(stderr, "OK encryption - small chunks!\n");
+    EVP_CIPHER_CTX_free(enc);
 
 }
 
 #define L_ENDIAN 1
 static void gf128_mul_uint64(uint64_t *z, uint64_t *x, uint64_t *y)
 {
- int i = 0, n = 0;
- uint64_t t, s0, s1;
   int i = 0, n = 0;
   uint64_t t, s0, s1;
 
-       BUF_reverse((unsigned char *)x, NULL, 16);
-       BUF_reverse((unsigned char *)y, NULL, 16);
+    BUF_reverse((unsigned char *)x, NULL, 16);
+    BUF_reverse((unsigned char *)y, NULL, 16);
 
 #ifdef L_ENDIAN
-  s0 = x[0];
-       s1 = x[1];
+    s0 = x[0];
+    s1 = x[1];
 #else
-  s0 = bswap_64(x[0]); 
-       s1 = bswap_64(x[1]);
+    s0 = bswap_64(x[0]);
+    s1 = bswap_64(x[1]);
 #endif
 
-       memset(z, 0, sizeof(uint64_t)*2);
+    memset(z, 0, sizeof(uint64_t) * 2);
 
- /* lower half */
   /* lower half */
 #ifdef L_ENDIAN
-  t = y[0];
+    t = y[0];
 #else
-  t = bswap_64(y[0]);
+    t = bswap_64(y[0]);
 #endif
 
- for( i = 0; i < 64; i++ ) {
-   if( t&0x1 ) { z[0] ^= s0; z[1] ^= s1; }
-   t >>= 1;
-   n = s1 >> 63;
-   s1 <<= 1; s1 ^= ( s0 >> 63 ); s0 <<= 1;
-   if(n) s0 ^= 0x87;
- }
+    for (i = 0; i < 64; i++) {
+        if (t & 0x1) {
+            z[0] ^= s0;
+            z[1] ^= s1;
+        }
+        t >>= 1;
+        n = s1 >> 63;
+        s1 <<= 1;
+        s1 ^= (s0 >> 63);
+        s0 <<= 1;
+        if (n)
+            s0 ^= 0x87;
+    }
 
- /* upper half */
   /* upper half */
 #ifdef L_ENDIAN
-  t = y[1];
+    t = y[1];
 #else
-  t = bswap_64(y[1]);
+    t = bswap_64(y[1]);
 #endif
 
- for( i = 0; i < 63; i++ ) {
-   if( t&0x1 ) { z[0] ^= s0; z[1] ^= s1; }
-   t >>= 1;
-   n = s1 >> 63;
-   s1 <<= 1; s1 ^= ( s0 >> 63 ); s0 <<= 1;
-   if( n ) s0 ^= 0x87;
- }
-
- if( t&0x1 ) {
-   z[0] ^= s0;
-   z[1] ^= s1;
- }
+    for (i = 0; i < 63; i++) {
+        if (t & 0x1) {
+            z[0] ^= s0;
+            z[1] ^= s1;
+        }
+        t >>= 1;
+        n = s1 >> 63;
+        s1 <<= 1;
+        s1 ^= (s0 >> 63);
+        s0 <<= 1;
+        if (n)
+            s0 ^= 0x87;
+    }
+
+    if (t & 0x1) {
+        z[0] ^= s0;
+        z[1] ^= s1;
+    }
 #ifndef L_ENDIAN
-   z[0] = bswap_64(z[0]);
-   z[1] = bswap_64(z[1]);
+    z[0] = bswap_64(z[0]);
+    z[1] = bswap_64(z[1]);
 #endif
-       BUF_reverse((unsigned char *)z, NULL, 16);
+    BUF_reverse((unsigned char *)z, NULL, 16);
 }
 
 void gf_mul(void)
 {
-       unsigned char H[16] = {
-               0x8D, 0xB1, 0x87, 0xD6, 0x53, 0x83, 0x0E, 0xA4, 0xBC, 0x44, 0x64, 0x76, 0x95, 0x2C, 0x30, 0x0B
-       };
+    unsigned char H[16] = {
+        0x8D, 0xB1, 0x87, 0xD6, 0x53, 0x83, 0x0E, 0xA4, 0xBC, 0x44, 0x64, 0x76,
+            0x95, 0x2C, 0x30, 0x0B
+    };
+
+    unsigned char A[16] = {
+        0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+            0x01, 0x01, 0x01, 0x01
+    };
+
+    unsigned char etalon[16] = {
+        0x4C, 0xF4, 0x27, 0xF4, 0xAD, 0xB7, 0x5C, 0xF4, 0xC0, 0xDA, 0x39, 0xD5,
+            0xAB, 0x48, 0xCF, 0x38
+    };
+
+    unsigned char result_bd[16];
+    gf128_mul_uint64((uint64_t *)result_bd, (uint64_t *)H, (uint64_t *)A);
+
+    hexdump(stderr, "Etalon", etalon, 16);
+
+    if (memcmp(etalon, result_bd, 16)) {
+        fprintf(stderr, "Shit happens - BD!\n");
+        hexdump(stderr, "Result - BD", result_bd, 16);
+    } else
+        fprintf(stderr, "OK - BD!\n");
+
+    return;
+}
+
+void grasshopper_dec_mgm_big(const EVP_CIPHER *ciph)
+{
+    unsigned char tag[16];
+    unsigned char gh_cdata[sizeof(gh_pdata)];
+    unsigned char *pcdata = gh_cdata;
 
-       unsigned char A[16] = {
-       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
-       };
+    int asize = sizeof(gh_adata);
+    int psize = sizeof(gh_pdata);
+    int fsize = 0;
 
-       unsigned char etalon[16] = {
-       0x4C, 0xF4, 0x27, 0xF4, 0xAD, 0xB7, 0x5C, 0xF4, 0xC0, 0xDA, 0x39, 0xD5, 0xAB, 0x48, 0xCF, 0x38
-       };
+    EVP_CIPHER_CTX *enc = NULL;
 
-       unsigned char result_bd[16];
-       gf128_mul_uint64((uint64_t *)result_bd, (uint64_t *)H, (uint64_t *)A);
+    int i;
 
+    memcpy(tag, etalon_tag, 16);
 
-       hexdump(stderr, "Etalon", etalon, 16);
+    enc = EVP_CIPHER_CTX_new();
+    EVP_DecryptInit_ex(enc, ciph, NULL, gh_key, gh_nonce);
+    EVP_CIPHER_CTX_ctrl(enc, EVP_CTRL_AEAD_SET_TAG, 16, tag);
 
-       if (memcmp (etalon, result_bd, 16)) {
-               fprintf(stderr, "Shit happens - BD!\n");
-               hexdump(stderr, "Result - BD", result_bd, 16);
-       } else
-               fprintf(stderr, "OK - BD!\n");
+    EVP_DecryptUpdate(enc, NULL, &asize, gh_adata, sizeof(gh_adata));
+    EVP_DecryptUpdate(enc, gh_cdata, &psize, etalon_cdata,
+                      sizeof(etalon_cdata));
 
-       return;
+    if (EVP_DecryptFinal_ex(enc, NULL, &fsize) <= 0) {
+        fprintf(stderr, "Shit happens - bad tag!\n");
+        return;
+    }
+
+    if (memcmp(gh_pdata, gh_cdata, sizeof(gh_cdata))) {
+        fprintf(stderr, "Shit happens - decryption!\n");
+        hexdump(stderr, "Etalon cdata", gh_pdata, sizeof(gh_pdata));
+        hexdump(stderr, "Got cdata", gh_cdata, sizeof(gh_cdata));
+        return;
+    }
+
+    fprintf(stderr, "OK decryption - big chunks!\n");
+    EVP_CIPHER_CTX_free(enc);
 }
 
-int main(void)
+void grasshopper_dec_mgm_small(const EVP_CIPHER *ciph)
 {
-       OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+    unsigned char tag[16];
+    unsigned char gh_cdata[sizeof(gh_pdata)];
+    unsigned char *pcdata = gh_cdata;
+
+    int asize = sizeof(gh_adata);
+    int psize = sizeof(gh_pdata);
+    int fsize = 0;
+
+    EVP_CIPHER_CTX *enc = NULL;
+
+    int i;
 
-       gf_mul();
-       grasshopper_mgm_big();
-       grasshopper_mgm_small();
-       ERR_print_errors_fp(stderr);
+    memcpy(tag, etalon_tag, 16);
 
-       return 0;
+    enc = EVP_CIPHER_CTX_new();
+    EVP_DecryptInit_ex(enc, ciph, NULL, gh_key, gh_nonce);
+    EVP_CIPHER_CTX_ctrl(enc, EVP_CTRL_AEAD_SET_TAG, 16, tag);
+
+    for (i = 0; i < sizeof(gh_adata); i++) {
+        asize = 1;
+        EVP_DecryptUpdate(enc, NULL, &asize, gh_adata + i, 1);
+    }
+    for (i = 0; i < sizeof(gh_pdata); i++) {
+        psize = 1;
+        EVP_DecryptUpdate(enc, pcdata, &psize, etalon_cdata + i, 1);
+        pcdata += psize;
+    }
+
+    if (EVP_DecryptFinal_ex(enc, NULL, &fsize) <= 0) {
+        fprintf(stderr, "Shit happens - bad tag!\n");
+        return;
+    }
+
+    if (memcmp(gh_pdata, gh_cdata, sizeof(gh_cdata))) {
+        fprintf(stderr, "Shit happens - decryption!\n");
+        hexdump(stderr, "Etalon cdata", gh_pdata, sizeof(gh_pdata));
+        hexdump(stderr, "Got cdata", gh_cdata, sizeof(gh_cdata));
+        return;
+    }
+
+    fprintf(stderr, "OK decryption - small chunks!\n");
+    EVP_CIPHER_CTX_free(enc);
+}
+
+int main(void)
+{
+    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+
+    const EVP_CIPHER *ciph;
+    ciph = EVP_get_cipherbynid(NID_kuznyechik_mgm);
+    if (ciph == NULL) {
+        fprintf(stderr, "Could not obtain cipher");
+        exit(1);
+    }
+//      gf_mul();
+    grasshopper_enc_mgm_big(ciph);
+    grasshopper_enc_mgm_small(ciph);
+    grasshopper_dec_mgm_big(ciph);
+    grasshopper_dec_mgm_small(ciph);
+    ERR_print_errors_fp(stderr);
+
+    return 0;
 }