From 168223cd873dddbebdaf1b8e2379b96005090fcb Mon Sep 17 00:00:00 2001
From: Nikita Romanov <nsromanov_1@edu.hse.ru>
Date: Sat, 14 Dec 2024 19:50:07 +0300
Subject: [PATCH] ADD: belt block cipher and tests from STB

---
 CMakeLists.txt                 |   1 +
 examples/tests/test-belt-dec.c |  78 +++++++++++++
 examples/tests/test-belt-enc.c |  80 +++++++++++++
 source/ak_belt.c               | 207 +++++++++++++++++++++++++++++++++
 source/ak_oid.c                |  11 ++
 source/libakrypt.h             |   2 +
 6 files changed, 379 insertions(+)
 create mode 100644 examples/tests/test-belt-dec.c
 create mode 100644 examples/tests/test-belt-enc.c
 create mode 100644 source/ak_belt.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3aee7e98..58c74902 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -95,6 +95,7 @@ set( AKRYPT_SOURCES
    source/ak_blom.c
    source/ak_kdf.c
    source/ak_encrypt.c
+   source/ak_belt.c
 )
 
 # -------------------------------------------------------------------------------------------------- #
diff --git a/examples/tests/test-belt-dec.c b/examples/tests/test-belt-dec.c
new file mode 100644
index 00000000..05ea04bd
--- /dev/null
+++ b/examples/tests/test-belt-dec.c
@@ -0,0 +1,78 @@
+/* --------------------------------------------------------------------------------- */
+/* Контрольный пример расшифрования одного блока из стандарта (STB 34.101.31-2020)   */
+/* --------------------------------------------------------------------------------- */
+#include <stdio.h>
+#include <libakrypt.h>
+
+ int main( void )
+{
+ /* код ошибки, возвращаемый функциями библиотеки */
+  int error = ak_error_ok;
+ /* статус выполнения программы */
+  int exitstatus = EXIT_FAILURE;
+
+  /* входной блок (уже зашифрован)*/
+  ak_uint8 indec[16] =
+  {
+    0xE1,0x2B,0xDC,0x1A,
+    0xE2,0x82,0x57,0xEC,
+    0x70,0x3F,0xCC,0xF0,
+    0x95,0xEE,0x8D,0xF1
+  };
+
+  /* расшифрованный блок */
+  ak_uint8 out[16] = {0};
+
+ /* константное значение ключа */  
+  ak_uint8 keydec[32] =
+  {
+    0x92,0xBD,0x9B,0x1C,
+    0xE5,0xD1,0x41,0x01,
+    0x54,0x45,0xFB,0xC9,
+    0x5E,0x4D,0x0E,0xF2,
+    0x68,0x20,0x80,0xAA,
+    0x22,0x7D,0x64,0x2F,
+    0x26,0x87,0xF9,0x34,
+    0x90,0x40,0x55,0x11
+  };
+
+ /* инициализируем библиотеку */
+  if( ak_libakrypt_create( NULL ) != ak_true ) {
+    ak_libakrypt_destroy();
+    return EXIT_FAILURE;
+  }
+
+  /* контекст алгоритма блочного шифрования */
+   struct bckey ctx;
+
+  /* выполняем последовательный вызов двух функций:
+     создаем ключ алгоритма Belt и присваиваем ему константное значение */
+  ak_bckey_create_belt( &ctx );
+  ak_bckey_set_key( &ctx, keydec, 32 );
+
+  size_t i;
+
+  // расшифровываем в режиме простой замены один блок
+  if(( ak_bckey_decrypt_ecb( &ctx, indec, out, 16 ))
+     != ak_error_ok ) goto exlab;
+
+  printf("Decryption:\n");
+  printf("m: ");
+  for (i = 0; i < 16; ++i) { printf("%02X ", indec[i]); }
+  printf("\nk: ");
+  for (i = 0; i < 32; ++i) { printf("%02X ", keydec[i]); }
+  printf("\nc: ");
+  for (i = 0; i < 16; ++i) { printf("%02X ", out[i]); }
+  printf("\n");
+
+  printf("Expected:\n");
+  printf("c: 0D C5 30 06 00 CA B8 40 B3 84 48 E5 E9 93 F4 21\n");
+
+ /* после использования необходимо удалить контекст секретного ключа */
+  exlab: ak_bckey_destroy( &ctx );
+
+ /* завершаем работу */
+  if( error == ak_error_ok ) exitstatus = EXIT_SUCCESS;
+  ak_libakrypt_destroy();
+ return exitstatus;
+}
diff --git a/examples/tests/test-belt-enc.c b/examples/tests/test-belt-enc.c
new file mode 100644
index 00000000..6e5eddf0
--- /dev/null
+++ b/examples/tests/test-belt-enc.c
@@ -0,0 +1,80 @@
+/* --------------------------------------------------------------------------------- */
+/* Контрольный пример зашифрования одного блока из стандарта (STB 34.101.31-2020)    */
+/* --------------------------------------------------------------------------------- */
+#include <stdio.h>
+#include <libakrypt.h>
+
+ int main( void )
+{
+ /* код ошибки, возвращаемый функциями библиотеки */
+  int error = ak_error_ok;
+ /* статус выполнения программы */
+  int exitstatus = EXIT_FAILURE;
+
+  /* входной блок */
+  ak_uint8 inenc[16] =
+  {
+    0xB1,0x94,0xBA,0xC8,
+    0x0A,0x08,0xF5,0x3B,
+    0x36,0x6D,0x00,0x8E,
+    0x58,0x4A,0x5D,0xE4,
+  };
+
+  /* выходной блок после зашифрования */
+  ak_uint8 outenc[16] = {0};
+
+  /* расшифрованный блок */
+  ak_uint8 outdec[16] = {0};
+
+ /* константное значение ключа */  
+  ak_uint8 key[32] = {
+    0xE9,0xDE,0xE7,0x2C,
+    0x8F,0x0C,0x0F,0xA6,
+    0x2D,0xDB,0x49,0xF4,
+    0x6F,0x73,0x96,0x47,
+    0x06,0x07,0x53,0x16,
+    0xED,0x24,0x7A,0x37,
+    0x39,0xCB,0xA3,0x83,
+    0x03,0xA9,0x8B,0xF6,
+  };
+
+ /* инициализируем библиотеку */
+  if( ak_libakrypt_create( NULL ) != ak_true ) {
+    ak_libakrypt_destroy();
+    return EXIT_FAILURE;
+  }
+
+  /* контекст алгоритма блочного шифрования */
+   struct bckey ctx;
+
+  /* выполняем последовательный вызов двух функций:
+     создаем ключ алгоритма Belt и присваиваем ему константное значение */
+  ak_bckey_create_belt( &ctx );
+  ak_bckey_set_key( &ctx, key, 32 );
+
+  size_t i;
+
+  // зашифровываем в режиме простой замены один блок
+  if(( ak_bckey_encrypt_ecb( &ctx, inenc, outenc, 16 ))
+     != ak_error_ok ) goto exlab;
+
+  printf("Encryption:\n");
+  printf("m: ");
+  for (i = 0; i < 16; ++i) { printf("%02X ", inenc[i]); }
+  printf("\nk: ");
+  for (i = 0; i < 32; ++i) { printf("%02X ", key[i]); }
+  printf("\nc: ");
+  for (i = 0; i < 16; ++i) { printf("%02X ", outenc[i]); }
+  printf("\n");
+
+  printf("Expected:\n");
+  printf("c: 69 CC A1 C9 35 57 C9 E3 D6 6B C3 E0 FA 88 FA 6E\n");
+
+ /* после использования необходимо удалить контекст секретного ключа */
+  exlab: ak_bckey_destroy( &ctx );
+
+ /* завершаем работу */
+  if( error == ak_error_ok ) exitstatus = EXIT_SUCCESS;
+  ak_libakrypt_destroy();
+ return exitstatus;
+}
diff --git a/source/ak_belt.c b/source/ak_belt.c
new file mode 100644
index 00000000..f58c942a
--- /dev/null
+++ b/source/ak_belt.c
@@ -0,0 +1,207 @@
+/* ----------------------------------------------------------------------------------------------- */
+/*                                                                                                 */
+/*  Реализация зашифрования и расшифрования одного блока шифром                                    */
+/*  из Белорусского стандарта STB 34.101.31-2020                                                   */
+/*                                                                                                 */
+/* ----------------------------------------------------------------------------------------------- */
+#include <libakrypt-internal.h>
+
+#define ROTL32(x, r) (((x) << (r)) | ((x) >> (32 - (r))))
+#define HU1(x,H) ((ak_uint32) ((H)[ (((x) >> 24) & 0xff) ]) << 24 )
+#define HU2(x,H) ((ak_uint32) ((H)[ (((x) >> 16) & 0xff) ]) << 16 )
+#define HU3(x,H) ((ak_uint32) ((H)[ (((x) >>  8) & 0xff) ]) <<  8 )
+#define HU4(x,H) ((ak_uint32) ((H)[ (((x) >>  0) & 0xff) ]) <<  0 )
+#define G(x,H,r) ROTL32(HU4((x),(H)) | HU3((x),(H)) | HU2((x),(H)) | HU1((x),(H)),(r))
+#define SWAP(x,y,tmp) (tmp) = (x); (x) = (y); (y) = (tmp);
+
+ static ak_uint32 load32(const void *in)
+{
+  const ak_uint8 *p = (const ak_uint8 *)in;
+  return ((ak_uint32)p[0] <<  0) |
+         ((ak_uint32)p[1] <<  8) |
+         ((ak_uint32)p[2] << 16) |
+         ((ak_uint32)p[3] << 24);
+}
+
+ static void store32(void *out, const ak_uint32 v)
+{
+  ak_uint8 *p = (ak_uint8 *)out;
+  p[0] = (ak_uint8)(v >>  0);
+  p[1] = (ak_uint8)(v >>  8);
+  p[2] = (ak_uint8)(v >> 16);
+  p[3] = (ak_uint8)(v >> 24);
+}
+
+ static const ak_uint8 H[256] =
+{
+    0xB1, 0x94, 0xBA, 0xC8, 0x0A, 0x08, 0xF5, 0x3B, 0x36, 0x6D, 0x00, 0x8E, 0x58, 0x4A, 0x5D, 0xE4,
+    0x85, 0x04, 0xFA, 0x9D, 0x1B, 0xB6, 0xC7, 0xAC, 0x25, 0x2E, 0x72, 0xC2, 0x02, 0xFD, 0xCE, 0x0D,
+    0x5B, 0xE3, 0xD6, 0x12, 0x17, 0xB9, 0x61, 0x81, 0xFE, 0x67, 0x86, 0xAD, 0x71, 0x6B, 0x89, 0x0B,
+    0x5C, 0xB0, 0xC0, 0xFF, 0x33, 0xC3, 0x56, 0xB8, 0x35, 0xC4, 0x05, 0xAE, 0xD8, 0xE0, 0x7F, 0x99,
+    0xE1, 0x2B, 0xDC, 0x1A, 0xE2, 0x82, 0x57, 0xEC, 0x70, 0x3F, 0xCC, 0xF0, 0x95, 0xEE, 0x8D, 0xF1,
+    0xC1, 0xAB, 0x76, 0x38, 0x9F, 0xE6, 0x78, 0xCA, 0xF7, 0xC6, 0xF8, 0x60, 0xD5, 0xBB, 0x9C, 0x4F,
+    0xF3, 0x3C, 0x65, 0x7B, 0x63, 0x7C, 0x30, 0x6A, 0xDD, 0x4E, 0xA7, 0x79, 0x9E, 0xB2, 0x3D, 0x31,
+    0x3E, 0x98, 0xB5, 0x6E, 0x27, 0xD3, 0xBC, 0xCF, 0x59, 0x1E, 0x18, 0x1F, 0x4C, 0x5A, 0xB7, 0x93,
+    0xE9, 0xDE, 0xE7, 0x2C, 0x8F, 0x0C, 0x0F, 0xA6, 0x2D, 0xDB, 0x49, 0xF4, 0x6F, 0x73, 0x96, 0x47,
+    0x06, 0x07, 0x53, 0x16, 0xED, 0x24, 0x7A, 0x37, 0x39, 0xCB, 0xA3, 0x83, 0x03, 0xA9, 0x8B, 0xF6,
+    0x92, 0xBD, 0x9B, 0x1C, 0xE5, 0xD1, 0x41, 0x01, 0x54, 0x45, 0xFB, 0xC9, 0x5E, 0x4D, 0x0E, 0xF2,
+    0x68, 0x20, 0x80, 0xAA, 0x22, 0x7D, 0x64, 0x2F, 0x26, 0x87, 0xF9, 0x34, 0x90, 0x40, 0x55, 0x11,
+    0xBE, 0x32, 0x97, 0x13, 0x43, 0xFC, 0x9A, 0x48, 0xA0, 0x2A, 0x88, 0x5F, 0x19, 0x4B, 0x09, 0xA1,
+    0x7E, 0xCD, 0xA4, 0xD0, 0x15, 0x44, 0xAF, 0x8C, 0xA5, 0x84, 0x50, 0xBF, 0x66, 0xD2, 0xE8, 0x8A,
+    0xA2, 0xD7, 0x46, 0x52, 0x42, 0xA8, 0xDF, 0xB3, 0x69, 0x74, 0xC5, 0x51, 0xEB, 0x23, 0x29, 0x21,
+    0xD4, 0xEF, 0xD9, 0xB4, 0x3A, 0x62, 0x28, 0x75, 0x91, 0x14, 0x10, 0xEA, 0x77, 0x6C, 0xDA, 0x1D
+};
+
+ static const size_t KeyIndex[8][7] =
+{
+    { 0, 1, 2, 3, 4, 5, 6 },
+    { 7, 0, 1, 2, 3, 4, 5 },
+    { 6, 7, 0, 1, 2, 3, 4 },
+    { 5, 6, 7, 0, 1, 2, 3 },
+    { 4, 5, 6, 7, 0, 1, 2 },
+    { 3, 4, 5, 6, 7, 0, 1 },
+    { 2, 3, 4, 5, 6, 7, 0 },
+    { 1, 2, 3, 4, 5, 6, 7 }
+};
+
+/* ----------------------------------------------------------------------------------------------- */
+/*! \brief Функция зашифрования одного блока информации алгоритмом блочного шифрования
+ *   Belt (STB 34.101.31-2020).
+
+    @param skey Контекст секретного ключа.
+    @param input Блок входной информации (открытый текст).
+    @param output Блок выходной информации (шифртекст).                                               */
+/* ----------------------------------------------------------------------------------------------- */
+ static void ak_belt_encrypt( ak_skey skey, ak_pointer input, ak_pointer output )
+{
+  // Преобразование входных и выходных указателей в массивы данных
+  const ak_uint8 *in = (const ak_uint8 *)input;
+  ak_uint8 *out = (ak_uint8 *)output;
+
+  ak_uint32 a = load32(in +  0);
+  ak_uint32 b = load32(in +  4);
+  ak_uint32 c = load32(in +  8);
+  ak_uint32 d = load32(in + 12);
+  ak_uint32 e;
+  size_t i;
+  ak_uint32 tmp;
+  ak_uint32 key[8] = {0};
+  ak_uint32 msk[8] = {0};
+
+  for (i = 0; i < 8; ++i)
+  {
+    key[i] = load32(skey->key + (4*i));
+    msk[i] = load32(skey->key + skey->key_size + (4*i));
+  }
+
+  for (i = 0; i < 8; ++i)
+  {
+    b ^= G((a + (key[KeyIndex[i][0]] ^ msk[KeyIndex[i][0]])),H,5);
+    c ^= G((d + (key[KeyIndex[i][1]] ^ msk[KeyIndex[i][1]])),H,21);
+    a -= G((b + (key[KeyIndex[i][2]] ^ msk[KeyIndex[i][2]])),H,13);
+    e = (G((b + c + (key[KeyIndex[i][3]] ^ msk[KeyIndex[i][3]])),H,21) ^ (ak_uint32)(i + 1));
+    b += e;
+    c -= e;
+    d += G((c + (key[KeyIndex[i][4]] ^ msk[KeyIndex[i][4]])),H,13);
+    b ^= G((a + (key[KeyIndex[i][5]] ^ msk[KeyIndex[i][5]])),H,21);
+    c ^= G((d + (key[KeyIndex[i][6]] ^ msk[KeyIndex[i][6]])),H,5);
+    SWAP(a, b, tmp);
+    SWAP(c, d, tmp);
+    SWAP(b, c, tmp);
+  }
+  store32(out +  0, b);
+  store32(out +  4, d);
+  store32(out +  8, a);
+  store32(out + 12, c);
+}
+
+/* ----------------------------------------------------------------------------------------------- */
+ /*! \brief Функция расшифрования одного блока информации алгоритмом Belt (STB 34.101.31-2020).
+
+   @param skey Контекст секретного ключа.
+   @param input Блок входной информации (шифртекст).
+   @param output Блок выходной информации (открытый текст).                                           */
+/* ----------------------------------------------------------------------------------------------- */
+ static void ak_belt_decrypt( ak_skey skey, ak_pointer input, ak_pointer output )
+{
+  // Преобразование входных и выходных указателей в массивы данных
+  const ak_uint8 *in = (const ak_uint8 *)input;
+  ak_uint8 *out = (ak_uint8 *)output;
+
+  ak_uint32 a = load32(in +  0);
+  ak_uint32 b = load32(in +  4);
+  ak_uint32 c = load32(in +  8);
+  ak_uint32 d = load32(in + 12);
+  ak_uint32 e;
+  size_t i;
+  ak_uint32 tmp;
+  ak_uint32 key[8] = {0};
+  ak_uint32 msk[8] = {0};
+
+  for (i = 0; i < 8; ++i)
+  {
+    key[i] = load32(skey->key + (4*i));
+    msk[i] = load32(skey->key + skey->key_size + (4*i));
+  }
+
+  for (i = 0; i < 8; ++i)
+  {
+    b ^= G((a + (key[KeyIndex[7 - i][6]] ^ msk[KeyIndex[7 - i][6]])), H, 5);
+    c ^= G((d + (key[KeyIndex[7 - i][5]] ^ msk[KeyIndex[7 - i][5]])), H, 21);
+    a -= G((b + (key[KeyIndex[7 - i][4]] ^ msk[KeyIndex[7 - i][4]])), H, 13);
+    e = (G((b + c + (key[KeyIndex[7 - i][3]] ^ msk[KeyIndex[7 - i][3]])), H, 21) ^ (ak_uint32)(7 - i + 1));
+    b += e;
+    c -= e;
+    d += G((c + (key[KeyIndex[7 - i][2]] ^ msk[KeyIndex[7 - i][2]])), H, 13);
+    b ^= G((a + (key[KeyIndex[7 - i][1]] ^ msk[KeyIndex[7 - i][1]])), H, 21);
+    c ^= G((d + (key[KeyIndex[7 - i][0]] ^ msk[KeyIndex[7 - i][0]])), H, 5);
+    SWAP(a, b, tmp);
+    SWAP(c, d, tmp);
+    SWAP(a, d, tmp);
+  }
+  store32(out +  0, c);
+  store32(out +  4, a);
+  store32(out +  8, d);
+  store32(out + 12, b);
+}
+
+/* ----------------------------------------------------------------------------------------------- */
+/*! Функция инициализирует контекст ключа алгоритма блочного шифрования Belt (STB 34.101.31-2020).
+    После инициализации устанавливаются обработчики (функции класса). Однако само значение
+    ключу не присваивается - поле `bkey->key` остается неопределенным.
+
+    @param bkey Контекст секретного ключа алгоритма блочного шифрования.
+
+    @return Функция возвращает код ошибки. В случаее успеха возвращается \ref ak_error_ok.         */
+/* ----------------------------------------------------------------------------------------------- */
+ int ak_bckey_create_belt( ak_bckey bkey )
+{
+  int error = ak_error_ok;
+
+  if( bkey == NULL ) return ak_error_message( ak_error_null_pointer, __func__,
+                                               "using null pointer to block cipher key context" );
+
+ /* создаем ключ алгоритма шифрования и определяем его методы */
+  if(( error = ak_bckey_create( bkey, 32, 16 )) != ak_error_ok )
+    return ak_error_message( error, __func__, "wrong initalization of block cipher key context" );
+
+ /* устанавливаем OID алгоритма шифрования */
+  if(( bkey->key.oid = ak_oid_find_by_name( "belt" )) == NULL ) {
+    error = ak_error_get_value();
+    ak_error_message( error, __func__, "wrong search of predefined belt block cipher OID" );
+    ak_bckey_destroy( bkey );
+    return error;
+  }
+
+  /* устанавливаем методы наложения маски и подсчёта контрольной суммы */
+  bkey->key.set_mask = ak_skey_set_mask_xor;
+  bkey->key.unmask = ak_skey_unmask_xor;
+  bkey->key.set_icode = ak_skey_set_icode_xor;
+  bkey->key.check_icode = ak_skey_check_icode_xor;
+
+  /* устанавливам методы зашифрования и расшифрования */
+  bkey->encrypt = ak_belt_encrypt;
+  bkey->decrypt = ak_belt_decrypt;
+
+  return error;
+}
diff --git a/source/ak_oid.c b/source/ak_oid.c
index 9411cd81..b3c1c148 100644
--- a/source/ak_oid.c
+++ b/source/ak_oid.c
@@ -82,6 +82,8 @@
  static const char *asn1_magma_i[] =       { "1.2.643.7.1.1.5.1", NULL };
  static const char *asn1_kuznechik_n[] =   { "kuznechik", "kuznyechik", "grasshopper", NULL };
  static const char *asn1_kuznechik_i[] =   { "1.2.643.7.1.1.5.2", NULL };
+ static const char *asn1_belt_n[] =        { "belt", NULL };
+ static const char *asn1_belt_i[] =        { "1.2.112.0.2.0.34.101.31.81", NULL };
 
  static const char *asn1_ctr_magma_n[] =   { "ctr-magma", NULL };
  static const char *asn1_ctr_magma_i[] =   { "1.2.643.2.52.1.5.1.1", NULL };
@@ -509,6 +511,12 @@
                           ( ak_function_create_object *) ak_verifykey_create_streebog512, \
                           ( ak_function_destroy_object *) ak_verifykey_destroy, \
                                                                                 NULL, NULL, NULL }
+ #define ak_object_bckey_belt { sizeof( struct bckey ), \
+                           ( ak_function_create_object *) ak_bckey_create_belt, \
+                           ( ak_function_destroy_object *) ak_bckey_destroy, \
+                           ( ak_function_set_key_object *)ak_bckey_set_key, \
+                           ( ak_function_set_key_random_object *)ak_bckey_set_key_random, \
+                      ( ak_function_set_key_from_password_object *)ak_bckey_set_key_from_password }
 
 /* ----------------------------------------------------------------------------------------------- */
 /*! Константные значения OID библиотеки */
@@ -580,6 +588,9 @@ static struct oid libakrypt_oids[] =
  { block_cipher, algorithm, asn1_kuznechik_i, asn1_kuznechik_n, NULL,
                                    { ak_object_bckey_kuznechik, ak_object_undefined, NULL, NULL }},
 
+ { block_cipher, algorithm, asn1_belt_i, asn1_belt_n, NULL,
+                                   { ak_object_bckey_belt, ak_object_undefined, NULL, NULL }},
+
 /* базовые режимы блочного шифрования */
  { block_cipher, encrypt_mode, asn1_ctr_magma_i, asn1_ctr_magma_n, NULL,
   { ak_object_bckey_magma, ak_object_undefined, ( ak_function_run_object *) ak_bckey_ctr,
diff --git a/source/libakrypt.h b/source/libakrypt.h
index 9bcbf756..ec6d3a61 100644
--- a/source/libakrypt.h
+++ b/source/libakrypt.h
@@ -734,6 +734,8 @@ extern "C" {
 /*! \brief Присвоение ключу алгоритма блочного шифрования значения, выработанного из пароля. */
  dll_export int ak_bckey_set_key_from_password( ak_bckey ,
                                const ak_pointer , const size_t , const ak_pointer , const size_t );
+ /*! \brief Инициализация секретного ключа алгоритма блочного шифрования Belt. */
+  dll_export int ak_bckey_create_belt( ak_bckey );
 
 /* ----------------------------------------------------------------------------------------------- */
 /*! \brief Функция вырабатывает пару ключей алгоритма блочного шифрования из заданного
-- 
GitLab