/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 |