Coverage Report

Created: 2026-05-24 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/block/kuznyechik/kuznyechik.cpp
Line
Count
Source
1
/*
2
* GOST R 34.12-2015: Block Cipher "Kuznyechik" (RFC 7801)
3
* (C) 2023 Richard Huveneers
4
*     2024 Jack Lloyd
5
*
6
* This code is written by kerukuro for cppcrypto library (http://cppcrypto.sourceforge.net/)
7
* and released into public domain.
8
*
9
* Botan is released under the Simplified BSD License (see license.txt)
10
*/
11
12
#include <botan/internal/kuznyechik.h>
13
14
#include <botan/mem_ops.h>
15
#include <botan/internal/loadstor.h>
16
#include <algorithm>
17
18
namespace Botan {
19
20
namespace {
21
22
namespace Kuznyechik_F {
23
24
alignas(256) const constexpr uint8_t S[256] = {
25
   252, 238, 221, 17,  207, 110, 49,  22,  251, 196, 250, 218, 35,  197, 4,   77,  233, 119, 240, 219, 147, 46,
26
   153, 186, 23,  54,  241, 187, 20,  205, 95,  193, 249, 24,  101, 90,  226, 92,  239, 33,  129, 28,  60,  66,
27
   139, 1,   142, 79,  5,   132, 2,   174, 227, 106, 143, 160, 6,   11,  237, 152, 127, 212, 211, 31,  235, 52,
28
   44,  81,  234, 200, 72,  171, 242, 42,  104, 162, 253, 58,  206, 204, 181, 112, 14,  86,  8,   12,  118, 18,
29
   191, 114, 19,  71,  156, 183, 93,  135, 21,  161, 150, 41,  16,  123, 154, 199, 243, 145, 120, 111, 157, 158,
30
   178, 177, 50,  117, 25,  61,  255, 53,  138, 126, 109, 84,  198, 128, 195, 189, 13,  87,  223, 245, 36,  169,
31
   62,  168, 67,  201, 215, 121, 214, 246, 124, 34,  185, 3,   224, 15,  236, 222, 122, 148, 176, 188, 220, 232,
32
   40,  80,  78,  51,  10,  74,  167, 151, 96,  115, 30,  0,   98,  68,  26,  184, 56,  130, 100, 159, 38,  65,
33
   173, 69,  70,  146, 39,  94,  85,  47,  140, 163, 165, 125, 105, 213, 149, 59,  7,   88,  179, 64,  134, 172,
34
   29,  247, 48,  55,  107, 228, 136, 217, 231, 137, 225, 27,  131, 73,  76,  63,  248, 254, 141, 83,  170, 144,
35
   202, 216, 133, 97,  32,  113, 103, 164, 45,  43,  9,   91,  203, 155, 37,  208, 190, 229, 108, 82,  89,  166,
36
   116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57,  75,  99,  182};
37
38
alignas(256) const constexpr uint8_t IS[256] = {
39
   165, 45,  50,  143, 14,  48,  56,  192, 84,  230, 158, 57,  85,  126, 82,  145, 100, 3,   87,  90,  28,  96,
40
   7,   24,  33,  114, 168, 209, 41,  198, 164, 63,  224, 39,  141, 12,  130, 234, 174, 180, 154, 99,  73,  229,
41
   66,  228, 21,  183, 200, 6,   112, 157, 65,  117, 25,  201, 170, 252, 77,  191, 42,  115, 132, 213, 195, 175,
42
   43,  134, 167, 177, 178, 91,  70,  211, 159, 253, 212, 15,  156, 47,  155, 67,  239, 217, 121, 182, 83,  127,
43
   193, 240, 35,  231, 37,  94,  181, 30,  162, 223, 166, 254, 172, 34,  249, 226, 74,  188, 53,  202, 238, 120,
44
   5,   107, 81,  225, 89,  163, 242, 113, 86,  17,  106, 137, 148, 101, 140, 187, 119, 60,  123, 40,  171, 210,
45
   49,  222, 196, 95,  204, 207, 118, 44,  184, 216, 46,  54,  219, 105, 179, 20,  149, 190, 98,  161, 59,  22,
46
   102, 233, 92,  108, 109, 173, 55,  97,  75,  185, 227, 186, 241, 160, 133, 131, 218, 71,  197, 176, 51,  250,
47
   150, 111, 110, 194, 246, 80,  255, 93,  169, 142, 23,  27,  151, 125, 236, 88,  247, 31,  251, 124, 9,   13,
48
   122, 103, 69,  135, 220, 232, 79,  29,  78,  4,   235, 248, 243, 62,  61,  189, 138, 136, 221, 205, 11,  19,
49
   152, 2,   147, 128, 144, 208, 36,  52,  203, 237, 244, 206, 153, 16,  68,  64,  146, 58,  1,   38,  18,  26,
50
   72,  104, 245, 129, 139, 199, 214, 32,  10,  8,   0,   76,  215, 116};
51
52
namespace Kuznyechik_T {
53
54
const constexpr uint8_t LINEAR[16] = {
55
   0x94, 0x20, 0x85, 0x10, 0xC2, 0xC0, 0x01, 0xFB, 0x01, 0xC0, 0xC2, 0x10, 0x85, 0x20, 0x94, 0x01};
56
57
0
constexpr uint8_t poly_mul(uint8_t x, uint8_t y) {
58
0
   const uint8_t poly = 0xC3;
59
0
60
0
   uint8_t r = 0;
61
0
   while(x > 0 && y > 0) {
62
0
      if(y & 1) {
63
0
         r ^= x;
64
0
      }
65
0
      x = (x << 1) ^ ((x >> 7) * poly);
66
0
      y >>= 1;
67
0
   }
68
0
   return r;
69
0
}
70
71
0
constexpr uint64_t poly_mul(uint64_t x, uint8_t y) {
72
0
   const uint64_t lo_bit = 0x0101010101010101;
73
0
   const uint64_t mask = 0x7F7F7F7F7F7F7F7F;
74
0
   const uint64_t poly = 0xC3;
75
0
76
0
   uint64_t r = 0;
77
0
   while(x > 0 && y > 0) {
78
0
      if(y & 1) {
79
0
         r ^= x;
80
0
      }
81
0
      x = ((x & mask) << 1) ^ (((x >> 7) & lo_bit) * poly);
82
0
      y >>= 1;
83
0
   }
84
0
   return r;
85
0
}
86
87
consteval std::array<uint8_t, 256> L_table(bool forward) {
88
   std::array<uint8_t, 256> L = {};
89
90
   for(size_t i = 0; i != 16; ++i) {
91
      L[i] = LINEAR[i];
92
      if(i > 0) {
93
         L[17 * i - 1] = 1;
94
      }
95
   }
96
97
   if(!forward) {
98
      std::reverse(L.begin(), L.end());
99
   }
100
101
   auto sqr_matrix = [](std::span<const uint8_t, 256> mat) {
102
      std::array<uint8_t, 256> res = {};
103
      for(size_t i = 0; i != 16; ++i) {
104
         for(size_t j = 0; j != 16; ++j) {
105
            for(size_t k = 0; k != 16; ++k) {
106
               res[16 * i + j] ^= poly_mul(mat[16 * i + k], mat[16 * k + j]);
107
            }
108
         }
109
      }
110
      return res;
111
   };
112
113
   for(size_t i = 0; i != 4; ++i) {
114
      L = sqr_matrix(L);
115
   }
116
117
   return L;
118
}
119
120
consteval std::array<uint64_t, 16 * 256 * 2> T_table(std::span<const uint8_t> L, bool forward) {
121
   const auto SB = forward ? S : IS;
122
123
   std::array<uint64_t, 16 * 256 * 2> T = {};
124
125
   for(size_t i = 0; i != 16; ++i) {
126
      uint64_t L_stride_0 = 0;
127
      uint64_t L_stride_1 = 0;
128
      for(size_t j = 0; j != 8; ++j) {
129
         L_stride_0 |= static_cast<uint64_t>(L[i + 16 * j]) << (8 * (j % 8));
130
         L_stride_1 |= static_cast<uint64_t>(L[i + 16 * (j + 8)]) << (8 * (j % 8));
131
      }
132
133
      for(size_t j = 0; j != 256; ++j) {
134
         const uint8_t Sj = SB[j];
135
         T[512 * i + 2 * j] = poly_mul(L_stride_0, Sj);
136
         T[512 * i + 2 * j + 1] = poly_mul(L_stride_1, Sj);
137
      }
138
   }
139
140
   return T;
141
}
142
143
}  // namespace Kuznyechik_T
144
145
// TODO(Botan4) this indirection with L/IL is required to work around a problem
146
// with Clang 19, where suddenly T_table became too much for it to handle as constexpr.
147
// Check if it's possible to remove this.
148
constexpr auto L = Kuznyechik_T::L_table(true);
149
constexpr auto IL = Kuznyechik_T::L_table(false);
150
const constinit auto T = Kuznyechik_T::T_table(L, true);
151
const constinit auto IT = Kuznyechik_T::T_table(IL, false);
152
153
const uint64_t C[32][2] = {{0xb87a486c7276a26e, 0x019484dd10bd275d}, {0xb3f490d8e4ec87dc, 0x02ebcb7920b94eba},
154
                           {0x0b8ed8b4969a25b2, 0x037f4fa4300469e7}, {0xa52be3730b1bcd7b, 0x041555f240b19cb7},
155
                           {0x1d51ab1f796d6f15, 0x0581d12f500cbbea}, {0x16df73abeff74aa7, 0x06fe9e8b6008d20d},
156
                           {0xaea53bc79d81e8c9, 0x076a1a5670b5f550}, {0x895605e6163659f6, 0x082aaa2780a1fbad},
157
                           {0x312c4d8a6440fb98, 0x09be2efa901cdcf0}, {0x3aa2953ef2dade2a, 0x0ac1615ea018b517},
158
                           {0x82d8dd5280ac7c44, 0x0b55e583b0a5924a}, {0x2c7de6951d2d948d, 0x0c3fffd5c010671a},
159
                           {0x9407aef96f5b36e3, 0x0dab7b08d0ad4047}, {0x9f89764df9c11351, 0x0ed434ace0a929a0},
160
                           {0x27f33e218bb7b13f, 0x0f40b071f0140efd}, {0xd1ac0a0f2c6cb22f, 0x1054974ec3813599},
161
                           {0x69d642635e1a1041, 0x11c01393d33c12c4}, {0x62589ad7c88035f3, 0x12bf5c37e3387b23},
162
                           {0xda22d2bbbaf6979d, 0x132bd8eaf3855c7e}, {0x7487e97c27777f54, 0x1441c2bc8330a92e},
163
                           {0xccfda1105501dd3a, 0x15d54661938d8e73}, {0xc77379a4c39bf888, 0x16aa09c5a389e794},
164
                           {0x7f0931c8b1ed5ae6, 0x173e8d18b334c0c9}, {0x58fa0fe93a5aebd9, 0x187e3d694320ce34},
165
                           {0xe0804785482c49b7, 0x19eab9b4539de969}, {0xeb0e9f31deb66c05, 0x1a95f6106399808e},
166
                           {0x5374d75dacc0ce6b, 0x1b0172cd7324a7d3}, {0xfdd1ec9a314126a2, 0x1c6b689b03915283},
167
                           {0x45aba4f6433784cc, 0x1dffec46132c75de}, {0x4e257c42d5ada17e, 0x1e80a3e223281c39},
168
                           {0xf65f342ea7db0310, 0x1f14273f33953b64}, {0x619b141e58d8a75e, 0x20a8ed9c45c16af1}};
169
170
0
inline void LS(uint64_t& x1, uint64_t& x2) {
171
0
   uint64_t t1 = 0;
172
0
   uint64_t t2 = 0;
173
0
   for(size_t i = 0; i != 16; ++i) {
174
0
      const uint8_t x = get_byte_var(7 - (i % 8), (i < 8) ? x1 : x2);
175
0
      t1 ^= T[512 * i + 2 * x + 0];
176
0
      t2 ^= T[512 * i + 2 * x + 1];
177
0
   }
178
179
0
   x1 = t1;
180
0
   x2 = t2;
181
0
}
182
183
0
inline void ILS(uint64_t& x1, uint64_t& x2) {
184
0
   uint64_t t1 = 0;
185
0
   uint64_t t2 = 0;
186
0
   for(size_t i = 0; i != 16; ++i) {
187
0
      const uint8_t x = get_byte_var(7 - (i % 8), (i < 8) ? x1 : x2);
188
0
      t1 ^= IT[512 * i + 2 * x + 0];
189
0
      t2 ^= IT[512 * i + 2 * x + 1];
190
0
   }
191
0
   x1 = t1;
192
0
   x2 = t2;
193
0
}
194
195
0
inline void ILSS(uint64_t& x1, uint64_t& x2) {
196
0
   uint64_t t1 = 0;
197
0
   uint64_t t2 = 0;
198
0
   for(size_t i = 0; i != 16; ++i) {
199
0
      const uint8_t x = S[get_byte_var(7 - (i % 8), (i < 8) ? x1 : x2)];
200
0
      t1 ^= IT[512 * i + 2 * x + 0];
201
0
      t2 ^= IT[512 * i + 2 * x + 1];
202
0
   }
203
0
   x1 = t1;
204
0
   x2 = t2;
205
0
}
206
207
0
inline uint64_t ISI(uint64_t val) {
208
0
   uint64_t out = 0;
209
0
   for(size_t i = 0; i != 8; ++i) {
210
0
      out <<= 8;
211
0
      out |= IS[get_byte_var(i, val)];
212
0
   }
213
0
   return out;
214
0
}
215
216
}  // namespace Kuznyechik_F
217
218
}  // namespace
219
220
0
Kuznyechik::~Kuznyechik() {
221
0
   clear();
222
0
}
223
224
0
void Kuznyechik::clear() {
225
0
   secure_scrub_memory(m_rke, sizeof(m_rke));
226
0
   secure_scrub_memory(m_rkd, sizeof(m_rkd));
227
0
   m_has_keying_material = false;
228
0
}
229
230
0
bool Kuznyechik::has_keying_material() const {
231
0
   return m_has_keying_material;
232
0
}
233
234
0
void Kuznyechik::key_schedule(std::span<const uint8_t> key) {
235
0
   using namespace Kuznyechik_F;
236
237
0
   BOTAN_ASSERT_NOMSG(key.size() == 32);
238
239
0
   uint64_t k0 = load_le<uint64_t>(key.data(), 0);
240
0
   uint64_t k1 = load_le<uint64_t>(key.data(), 1);
241
0
   uint64_t k2 = load_le<uint64_t>(key.data(), 2);
242
0
   uint64_t k3 = load_le<uint64_t>(key.data(), 3);
243
244
0
   m_rke[0][0] = k0;
245
0
   m_rke[0][1] = k1;
246
0
   m_rke[1][0] = k2;
247
0
   m_rke[1][1] = k3;
248
249
0
   for(size_t i = 0; i != 4; ++i) {
250
0
      for(size_t r = 0; r != 8; r += 2) {
251
0
         uint64_t t0, t1, t2, t3;
252
253
0
         t0 = k0 ^ C[8 * i + r][0];
254
0
         t1 = k1 ^ C[8 * i + r][1];
255
0
         t2 = k0;
256
0
         t3 = k1;
257
0
         LS(t0, t1);
258
0
         t0 ^= k2;
259
0
         t1 ^= k3;
260
261
0
         k0 = t0 ^ C[8 * i + r + 1][0];
262
0
         k1 = t1 ^ C[8 * i + r + 1][1];
263
0
         k2 = t0;
264
0
         k3 = t1;
265
0
         LS(k0, k1);
266
0
         k0 ^= t2;
267
0
         k1 ^= t3;
268
0
      }
269
270
0
      m_rke[2 * i + 2][0] = k0;
271
0
      m_rke[2 * i + 2][1] = k1;
272
0
      m_rke[2 * i + 3][0] = k2;
273
0
      m_rke[2 * i + 3][1] = k3;
274
0
   }
275
276
0
   for(size_t i = 0; i != 10; i++) {
277
0
      uint64_t t0 = m_rke[i][0];
278
0
      uint64_t t1 = m_rke[i][1];
279
280
0
      if(i > 0) {
281
0
         Kuznyechik_F::ILSS(t0, t1);
282
0
      }
283
284
0
      const size_t dest = 9 - i;
285
286
0
      m_rkd[dest][0] = t0;
287
0
      m_rkd[dest][1] = t1;
288
0
   }
289
290
0
   m_has_keying_material = true;
291
0
}
292
293
0
void Kuznyechik::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const {
294
0
   assert_key_material_set();
295
0
   while(blocks) {
296
0
      uint64_t x1 = load_le<uint64_t>(in, 0);
297
0
      uint64_t x2 = load_le<uint64_t>(in, 1);
298
299
0
      x1 ^= m_rke[0][0];
300
0
      x2 ^= m_rke[0][1];
301
0
      Kuznyechik_F::LS(x1, x2);
302
303
0
      x1 ^= m_rke[1][0];
304
0
      x2 ^= m_rke[1][1];
305
0
      Kuznyechik_F::LS(x1, x2);
306
307
0
      x1 ^= m_rke[2][0];
308
0
      x2 ^= m_rke[2][1];
309
0
      Kuznyechik_F::LS(x1, x2);
310
311
0
      x1 ^= m_rke[3][0];
312
0
      x2 ^= m_rke[3][1];
313
0
      Kuznyechik_F::LS(x1, x2);
314
315
0
      x1 ^= m_rke[4][0];
316
0
      x2 ^= m_rke[4][1];
317
0
      Kuznyechik_F::LS(x1, x2);
318
319
0
      x1 ^= m_rke[5][0];
320
0
      x2 ^= m_rke[5][1];
321
0
      Kuznyechik_F::LS(x1, x2);
322
323
0
      x1 ^= m_rke[6][0];
324
0
      x2 ^= m_rke[6][1];
325
0
      Kuznyechik_F::LS(x1, x2);
326
327
0
      x1 ^= m_rke[7][0];
328
0
      x2 ^= m_rke[7][1];
329
0
      Kuznyechik_F::LS(x1, x2);
330
331
0
      x1 ^= m_rke[8][0];
332
0
      x2 ^= m_rke[8][1];
333
0
      Kuznyechik_F::LS(x1, x2);
334
335
0
      x1 ^= m_rke[9][0];
336
0
      x2 ^= m_rke[9][1];
337
338
0
      store_le(out, x1, x2);
339
340
0
      in += 16;
341
0
      out += 16;
342
0
      blocks--;
343
0
   }
344
0
}
345
346
0
void Kuznyechik::decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const {
347
0
   assert_key_material_set();
348
0
   while(blocks) {
349
0
      uint64_t x1 = load_le<uint64_t>(in, 0);
350
0
      uint64_t x2 = load_le<uint64_t>(in, 1);
351
352
0
      Kuznyechik_F::ILSS(x1, x2);
353
354
0
      x1 ^= m_rkd[0][0];
355
0
      x2 ^= m_rkd[0][1];
356
0
      Kuznyechik_F::ILS(x1, x2);
357
358
0
      x1 ^= m_rkd[1][0];
359
0
      x2 ^= m_rkd[1][1];
360
0
      Kuznyechik_F::ILS(x1, x2);
361
362
0
      x1 ^= m_rkd[2][0];
363
0
      x2 ^= m_rkd[2][1];
364
0
      Kuznyechik_F::ILS(x1, x2);
365
366
0
      x1 ^= m_rkd[3][0];
367
0
      x2 ^= m_rkd[3][1];
368
0
      Kuznyechik_F::ILS(x1, x2);
369
370
0
      x1 ^= m_rkd[4][0];
371
0
      x2 ^= m_rkd[4][1];
372
0
      Kuznyechik_F::ILS(x1, x2);
373
374
0
      x1 ^= m_rkd[5][0];
375
0
      x2 ^= m_rkd[5][1];
376
0
      Kuznyechik_F::ILS(x1, x2);
377
378
0
      x1 ^= m_rkd[6][0];
379
0
      x2 ^= m_rkd[6][1];
380
0
      Kuznyechik_F::ILS(x1, x2);
381
382
0
      x1 ^= m_rkd[7][0];
383
0
      x2 ^= m_rkd[7][1];
384
0
      Kuznyechik_F::ILS(x1, x2);
385
386
0
      x1 ^= m_rkd[8][0];
387
0
      x2 ^= m_rkd[8][1];
388
0
      x1 = Kuznyechik_F::ISI(x1);
389
0
      x2 = Kuznyechik_F::ISI(x2);
390
391
0
      x1 ^= m_rkd[9][0];
392
0
      x2 ^= m_rkd[9][1];
393
394
0
      store_le(out, x1, x2);
395
396
0
      in += 16;
397
0
      out += 16;
398
0
      blocks--;
399
0
   }
400
0
}
401
402
}  // namespace Botan