HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
sbox_database.cpp
Go to the documentation of this file.
2 
3 #include "rapidjson/document.h"
4 #include "rapidjson/filereadstream.h"
5 #include "rapidjson/stringbuffer.h"
6 #include "rapidjson/writer.h"
7 
8 #include <cmath>
9 #include <fstream>
10 #include <iostream>
11 #include <vector>
12 
13 // debug options to enforce using default implementation
14 // #undef __AVX2__
15 // #undef __ARM_NEON
16 
17 #ifdef __AVX2__
18 #include <immintrin.h>
19 
20 using smallset_t = __m256i;
21 #elif defined(__ARM_NEON)
22 #include <arm_neon.h>
23 
24 using smallset_t = uint64x2x2_t;
25 #else
26 const uint64_t _ONE_ = 1;
27 
29 {
30 public:
31  smallset_t(int preset = 0);
32 
33  void set(u8 bit);
34  bool is_set(u8 bit) const;
35 
36  void dump() const;
37 
38  smallset_t operator|(const smallset_t& other) const;
39  smallset_t operator&(const smallset_t& other) const;
40  smallset_t operator^(const smallset_t& other) const;
41  smallset_t shuffle(u8 shift) const;
42 
43  void to_array(u64* arr, bool swap = false) const;
44 
45  u8 least_bit() const;
46  static int least_bit(u64 dw);
47 
48  int size() const;
49  static int size(u64 dw, int level);
50 
51  bool empty() const;
52 
53 private:
54  u64 dw64[4];
55 };
56 
58 {
59  memset(dw64, 0, sizeof(dw64));
60  switch (preset)
61  {
62  case 8:
63  dw64[0] = 0xFF;
64  break;
65  case 16:
66  dw64[0] = 0xFFFF;
67  break;
68  case 32:
69  dw64[0] = 0xFFFFFFFF;
70  break;
71  case 64:
72  memset(dw64, 0xFF, sizeof(u64));
73  break;
74  case 128:
75  memset(dw64, 0xFF, 2 * sizeof(u64));
76  break;
77  case 256:
78  memset(dw64, 0xFF, sizeof(dw64));
79  break;
80  }
81 }
82 
83 bool smallset_t::empty() const
84 {
85  for (int i = 0; i < 4; i++)
86  {
87  if (dw64[i])
88  {
89  return false;
90  }
91  }
92  return true;
93 }
94 
96 {
97  for (int i = 0; i < 4; i++)
98  {
99  if (dw64[i])
100  {
101  return i * 64 + least_bit(dw64[i]);
102  }
103  }
104  std::cerr << "Called smallset_t::least_bit() on empty set\n" << std::endl;
105  return 0;
106 }
107 
109 {
110  smallset_t retval(*this);
111  if (shift & 0x80)
112  {
113  smallset_t temp = retval;
114  retval.dw64[0] = temp.dw64[2];
115  retval.dw64[1] = temp.dw64[3];
116  retval.dw64[2] = temp.dw64[0];
117  retval.dw64[3] = temp.dw64[1];
118  }
119  if (shift & 0x40)
120  {
121  smallset_t temp = retval;
122  retval.dw64[0] = temp.dw64[1];
123  retval.dw64[1] = temp.dw64[0];
124  retval.dw64[2] = temp.dw64[3];
125  retval.dw64[3] = temp.dw64[2];
126  }
127  if (shift & 0x20)
128  {
129  for (int i = 0; i < 4; i++)
130  {
131  retval.dw64[i] = ((retval.dw64[i] & 0xFFFFFFFF00000000ULL) >> 32) | ((retval.dw64[i] & 0x00000000FFFFFFFFULL) << 32);
132  }
133  }
134  if (shift & 0x10)
135  {
136  for (int i = 0; i < 4; i++)
137  {
138  retval.dw64[i] = ((retval.dw64[i] & 0xFFFF0000FFFF0000ULL) >> 16) | ((retval.dw64[i] & 0x0000FFFF0000FFFFULL) << 16);
139  }
140  }
141  if (shift & 0x08)
142  {
143  for (int i = 0; i < 4; i++)
144  {
145  retval.dw64[i] = ((retval.dw64[i] & 0xFF00FF00FF00FF00ULL) >> 8) | ((retval.dw64[i] & 0x00FF00FF00FF00FFULL) << 8);
146  }
147  }
148  if (shift & 0x04)
149  {
150  for (int i = 0; i < 4; i++)
151  {
152  retval.dw64[i] = ((retval.dw64[i] & 0xF0F0F0F0F0F0F0F0ULL) >> 4) | ((retval.dw64[i] & 0x0F0F0F0F0F0F0F0FULL) << 4);
153  }
154  }
155  if (shift & 0x02)
156  {
157  for (int i = 0; i < 4; i++)
158  {
159  retval.dw64[i] = ((retval.dw64[i] & 0xCCCCCCCCCCCCCCCCULL) >> 2) | ((retval.dw64[i] & 0x3333333333333333ULL) << 2);
160  }
161  }
162  if (shift & 0x01)
163  {
164  for (int i = 0; i < 4; i++)
165  {
166  retval.dw64[i] = ((retval.dw64[i] & 0xAAAAAAAAAAAAAAAAULL) >> 1) | ((retval.dw64[i] & 0x5555555555555555ULL) << 1);
167  }
168  }
169  return retval;
170 }
171 
172 void smallset_t::to_array(u64* arr, bool swap) const
173 {
174  for (int i = 0; i < 4; i++)
175  {
176  arr[i] = dw64[swap ? 3 - i : i];
177  }
178 }
179 
181 {
182  dw64[bit / 64] |= (_ONE_ << (bit % 64));
183 }
184 
185 bool smallset_t::is_set(u8 bit) const
186 {
187  return (dw64[bit / 64] & (_ONE_ << (bit % 64))) != 0;
188 }
189 
190 int smallset_t::size() const
191 {
192  int retval = 0;
193  for (int i = 0; i < 4; i++)
194  retval += size(dw64[i], 0);
195  return retval;
196 }
197 
198 int smallset_t::size(u64 dw, int level)
199 {
200  static const u64 segmask[] = {0xFFFFFFFF, 0xFFFF, 0xFF, 0xF};
201  static const int segshft[] = {32, 16, 8, 4};
202  static const int szlookup[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
203 
204  if (level >= 4)
205  return szlookup[dw & 0xF];
206 
207  int retval = 0;
208  retval += size(dw & segmask[level], level + 1);
209  dw >>= segshft[level];
210  retval += size(dw & segmask[level], level + 1);
211 
212  return retval;
213 }
214 
216 {
217  static const u64 segmask[] = {0xFFFFFFFF, 0xFFFF, 0xFF, 0xF};
218  static const int lblookup[16] = {-61, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
219 
220  int retval = 0;
221  int segval = 32;
222 
223  for (int iseg = 0; iseg < 4; iseg++)
224  {
225  if (!(dw & segmask[iseg]))
226  {
227  retval += segval;
228  dw >>= segval;
229  }
230  segval /= 2;
231  }
232 
233  return retval + lblookup[dw & 0xF];
234 }
235 
236 void smallset_t::dump() const
237 {
238  for (int i = 3; i >= 0; i--)
239  printf("%016lx", dw64[i]);
240  printf("\n");
241 
242  for (unsigned int i = 0; i < 256; i++)
243  {
244  if (dw64[i / 64] & (_ONE_ << (i % 64)))
245  printf("%8d\n", i);
246  }
247 }
248 
250 {
251  smallset_t retval = other;
252  for (int i = 0; i < 4; i++)
253  retval.dw64[i] |= dw64[i];
254  return retval;
255 }
256 
258 {
259  smallset_t retval = other;
260  for (int i = 0; i < 4; i++)
261  retval.dw64[i] &= dw64[i];
262  return retval;
263 }
264 
266 {
267  smallset_t retval = other;
268  for (int i = 0; i < 4; i++)
269  retval.dw64[i] ^= dw64[i];
270  return retval;
271 }
272 
273 #endif
274 
275 namespace hal
276 {
277  namespace hawkeye
278  {
279  SBoxDatabase::SBoxDatabase(const std::map<std::string, std::vector<u8>>& sboxes)
280  {
281  add(sboxes).is_ok();
282  }
283 
284  Result<SBoxDatabase> SBoxDatabase::from_file(const std::filesystem::path& file_path)
285  {
286  auto db = SBoxDatabase();
287  if (const auto res = db.load(file_path); res.is_ok())
288  {
289  return OK(db);
290  }
291  else
292  {
293  return ERR(res.get_error());
294  }
295  }
296 
297  Result<std::monostate> SBoxDatabase::add(const std::string& name, const std::vector<u8>& sbox)
298 
299  {
300  u32 bit_size = std::log2(sbox.size());
301 
302  if (bit_size > 8)
303  {
304  return ERR("S-box '" + name + "' has bit-size greater 8, but only S-boxes of up to 8 bits are supported");
305  }
306 
307  for (size_t alpha = 0; alpha < sbox.size(); alpha++)
308  {
309  std::vector<u8> sbox_alpha;
310  for (u32 i = 0; i < sbox.size(); i++)
311  {
312  sbox_alpha.push_back(sbox.at(i) ^ alpha);
313  }
314  auto lin_rep = compute_linear_representative(sbox_alpha);
315  m_data[bit_size][lin_rep].push_back(std::make_pair(name, alpha));
316  }
317  return OK({});
318  }
319 
320  Result<std::monostate> SBoxDatabase::add(const std::map<std::string, std::vector<u8>>& sboxes)
321  {
322  for (const auto& [name, sbox] : sboxes)
323  {
324  if (const auto res = add(name, sbox); res.is_error())
325  {
326  return ERR(res.get_error());
327  }
328  }
329  return OK({});
330  }
331 
332  Result<std::monostate> SBoxDatabase::load(const std::filesystem::path& file_path, bool overwrite)
333  {
334  FILE* fp = fopen(file_path.string().c_str(), "r");
335  if (fp == NULL)
336  {
337  return ERR("could not parse S-box database file '" + file_path.string() + "' : unable to open file");
338  }
339 
340  char buffer[65536];
341  rapidjson::FileReadStream is(fp, buffer, sizeof(buffer));
342  rapidjson::Document document;
343  document.ParseStream<0, rapidjson::UTF8<>, rapidjson::FileReadStream>(is);
344  fclose(fp);
345 
346  if (document.HasParseError())
347  {
348  return ERR("could not parse S-box database file '" + file_path.string() + "': failed parsing JSON format");
349  }
350 
351  if (overwrite)
352  {
353  m_data.clear();
354  }
355 
356  for (auto size_it = document.MemberBegin(); size_it != document.MemberEnd(); ++size_it)
357  {
358  u32 bit_size = std::stoul(std::string(size_it->name.GetString()));
359  const rapidjson::Value& cipher_val = size_it->value;
360 
361  for (auto cipher_it = cipher_val.MemberBegin(); cipher_it != cipher_val.MemberEnd(); ++cipher_it)
362  {
363  std::string cipher_name = cipher_it->name.GetString();
364  const rapidjson::Value& const_val = cipher_it->value;
365 
366  for (auto const_it = const_val.MemberBegin(); const_it != const_val.MemberEnd(); ++const_it)
367  {
368  u8 const_alpha = (u8)std::stoul(std::string(const_it->name.GetString()));
369  const rapidjson::Value& lin_rep_val = const_it->value;
370 
371  std::vector<u8> lin_rep;
372  for (u32 i = 0; i < lin_rep_val.Size(); i++)
373  {
374  lin_rep.push_back((u8)(lin_rep_val[i].GetUint()));
375  }
376 
377  m_data[bit_size][lin_rep].push_back(std::make_pair(cipher_name, const_alpha));
378  }
379  }
380  }
381 
382  return OK({});
383  }
384 
385  Result<std::monostate> SBoxDatabase::store(const std::filesystem::path& file_path) const
386  {
387  FILE* fp = fopen(file_path.string().c_str(), "w");
388  if (fp == NULL)
389  {
390  return ERR("could not write S-box database file '" + file_path.string() + "' : unable to open file");
391  }
392 
393  rapidjson::Document document;
394  document.SetObject();
395 
396  rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
397 
398  for (const auto& [bit_size, lin_rep_map] : m_data)
399  {
400  std::map<std::string, std::map<u8, std::vector<u8>>> pretty_data;
401  for (const auto& [lin_rep, cipher_vec] : lin_rep_map)
402  {
403  for (const auto& [name, alpha] : cipher_vec)
404  {
405  pretty_data[name][alpha] = lin_rep;
406  }
407  }
408 
409  rapidjson::Value cipher_json(rapidjson::kObjectType);
410  for (const auto& [cipher_name, lin_rep_map] : pretty_data)
411  {
412  rapidjson::Value alpha_json(rapidjson::kObjectType);
413  for (const auto& [const_alph, lin_rep] : lin_rep_map)
414  {
415  rapidjson::Value lin_rep_json(rapidjson::kArrayType);
416  for (const auto val : lin_rep)
417  {
418  lin_rep_json.PushBack(val, allocator);
419  }
420  alpha_json.AddMember(rapidjson::Value(std::to_string(const_alph).c_str(), allocator).Move(), lin_rep_json, allocator);
421  }
422  cipher_json.AddMember(rapidjson::Value(cipher_name.c_str(), allocator).Move(), alpha_json, allocator);
423  }
424  document.AddMember(rapidjson::Value(std::to_string(bit_size).c_str(), allocator).Move(), cipher_json, allocator);
425  }
426 
427  rapidjson::StringBuffer buffer;
428  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
429 
430  document.Accept(writer);
431 
432  std::ofstream file(file_path);
433  file << buffer.GetString();
434  file.close();
435 
436  return ERR("not implemented");
437  }
438 
439  Result<std::string> SBoxDatabase::lookup(const std::vector<u8>& sbox) const
440  {
441  u32 bit_size = std::log2(sbox.size());
442 
443  if (bit_size > 8)
444  {
445  return ERR("S-box has bit-size greater 8, but only S-boxes of up to 8 bits are supported");
446  }
447 
448  const auto size_it = m_data.find(bit_size);
449  if (size_it == m_data.end())
450  {
451  return ERR("no S-box of matching bit-size of " + std::to_string(bit_size) + " bits contained in database");
452  }
453 
454  for (u8 beta = 0; beta < sbox.size(); beta++)
455  {
456  std::vector<u8> sbox_beta;
457  for (u32 i = 0; i < sbox.size(); i++)
458  {
459  sbox_beta.push_back(sbox.at(i) ^ beta);
460  }
461  auto lin_rep = compute_linear_representative(sbox_beta);
462 
463  const auto& matching_size_data = std::get<1>(*size_it);
464  const auto rep_it = matching_size_data.find(lin_rep);
465  if (rep_it != matching_size_data.end())
466  {
467  return OK(rep_it->second.front().first);
468  }
469  }
470 
471  return ERR("no match found within database");
472  }
473 
474  void SBoxDatabase::print() const
475  {
476  for (const auto& [bit_size, lin_rep_map] : m_data)
477  {
478  std::cout << std::endl;
479  std::cout << "### WIDTH: " << bit_size << std::endl;
480  std::cout << "#######################" << std::endl;
481 
482  std::map<std::string, std::map<u8, std::vector<u8>>> pretty_data;
483  for (const auto& [lin_rep, cipher_vec] : lin_rep_map)
484  {
485  for (const auto& [name, alpha] : cipher_vec)
486  {
487  pretty_data[name][alpha] = lin_rep;
488  }
489  }
490 
491  for (const auto& [cipher_name, lin_rep_map] : pretty_data)
492  {
493  std::cout << "* " << cipher_name << std::endl;
494 
495  for (const auto& [const_alph, lin_rep] : lin_rep_map)
496  {
497  std::cout << " - " << (u32)const_alph << ": [" << (u32)(lin_rep.at(0));
498  for (u32 i = 1; i < lin_rep.size(); i++)
499  {
500  std::cout << ", " << (u32)(lin_rep.at(i));
501  }
502  std::cout << "]" << std::endl;
503  }
504  }
505  }
506 
507  std::cout << std::endl;
508  }
509 
510  namespace
511  {
512  void smallset_print(const std::string& name, const smallset_t& a)
513  {
514  u64 elements[4];
515 #ifdef __AVX2__
516 
517  elements[0] = _mm256_extract_epi64(a, 3);
518  elements[1] = _mm256_extract_epi64(a, 2);
519  elements[2] = _mm256_extract_epi64(a, 1);
520  elements[3] = _mm256_extract_epi64(a, 0);
521 #elif defined(__ARM_NEON)
522  elements[0] = a.val[1][1];
523  elements[1] = a.val[1][0];
524  elements[2] = a.val[0][1];
525  elements[3] = a.val[0][0];
526 #else
527  a.to_array(elements, true);
528 #endif
529  std::cout << name << ": 0b";
530  for (u32 i = 0; i < 4; i++)
531  {
532  for (int j = 63; j >= 0; j--)
533  {
534  u32 bit = (elements[i] >> j) & 1;
535  std::cout << bit;
536  }
537  std::cout << " ";
538  }
539  std::cout << std::endl;
540  }
541 
542  u8 smallset_least_element(const smallset_t& a)
543  {
544  u64 chunks[4];
545 #ifdef __AVX2__
546  chunks[0] = _mm256_extract_epi64(a, 0);
547  chunks[1] = _mm256_extract_epi64(a, 1);
548  chunks[2] = _mm256_extract_epi64(a, 2);
549  chunks[3] = _mm256_extract_epi64(a, 3);
550 #elif defined(__ARM_NEON)
551  chunks[0] = a.val[0][0];
552  chunks[1] = a.val[0][1];
553  chunks[2] = a.val[1][0];
554  chunks[3] = a.val[1][1];
555 #else
556  return a.least_bit();
557 #endif
558  for (u32 i = 0; i < 4; i++)
559  {
560  u64 current_chunk = chunks[i];
561  if (current_chunk != 0)
562  {
563  u8 idx = __builtin_ctzll(current_chunk) + i * 64;
564  return idx;
565  }
566  }
567 
568  // set is empty -- caller's fault
569  std::cout << "CALLED LEAST ELEMENT ON EMPTY SET!" << std::endl;
570  return 0;
571  }
572 
573  inline smallset_t smallset_intersect(const smallset_t& a, const smallset_t& b)
574  {
575 #ifdef __AVX2__
576  return _mm256_and_si256(a, b);
577 #elif defined(__ARM_NEON)
578  return {vandq_u64(a.val[0], b.val[0]), vandq_u64(a.val[1], b.val[1])};
579 #else
580  return (a & b);
581 #endif
582  }
583 
584  inline smallset_t smallset_union(const smallset_t& a, const smallset_t& b)
585  {
586 #ifdef __AVX2__
587  return _mm256_or_si256(a, b);
588 #elif defined(__ARM_NEON)
589  return {vorrq_u64(a.val[0], b.val[0]), vorrq_u64(a.val[1], b.val[1])};
590 #else
591  return (a | b);
592 #endif
593  }
594 
595  inline u16 smallset_size(const smallset_t& a)
596  {
597  u16 count = 0;
598 #ifdef __AVX2__
599  u64 chunk = _mm256_extract_epi64(a, 0);
600  count += __builtin_popcountll(chunk);
601  chunk = _mm256_extract_epi64(a, 1);
602  count += __builtin_popcountll(chunk);
603  chunk = _mm256_extract_epi64(a, 2);
604  count += __builtin_popcountll(chunk);
605  chunk = _mm256_extract_epi64(a, 3);
606  count += __builtin_popcountll(chunk);
607 #elif defined(__ARM_NEON)
608  count += __builtin_popcountll(a.val[0][0]);
609  count += __builtin_popcountll(a.val[0][1]);
610  count += __builtin_popcountll(a.val[1][0]);
611  count += __builtin_popcountll(a.val[1][1]);
612 #else
613  return a.size();
614 #endif
615  return count;
616  }
617 
618  inline bool smallset_is_empty(const smallset_t& a)
619  {
620 #ifdef __AVX2__
621  return _mm256_testz_si256(a, a);
622 #elif defined(__ARM_NEON)
623  auto tmp = vandq_u64(vceqzq_u64(a.val[0]), vceqzq_u64(a.val[1]));
624  return (tmp[0] & tmp[1]) & 1;
625 #else
626  return a.empty();
627 #endif
628  }
629 
630  smallset_t smallset_add_element(const smallset_t& a, const u8 elm)
631  {
632  // compute union of a and {elm}
633  u32 index = elm / 64;
634 #ifdef __AVX2__
635  u64 mask[4] = {0};
636  mask[index] = (u64)1 << (elm % 64);
637  __m256i _mask = _mm256_set_epi64x(mask[3], mask[2], mask[1], mask[0]);
638  return _mm256_or_si256(a, _mask);
639 #elif defined(__ARM_NEON)
640  u64 mask[2] = {0};
641  mask[index & 1] = (u64)1 << (elm % 64);
642  auto _mask = vld1q_u64(mask);
643  if (index < 2)
644  {
645  return {vorrq_u64(a.val[0], _mask), a.val[1]};
646  }
647  else
648  {
649  return {a.val[0], vorrq_u64(a.val[1], _mask)};
650  }
651 #else
652  smallset_t retval(a);
653  retval.set(elm);
654  return retval;
655 #endif
656  }
657 
658  smallset_t smallset_shift(const smallset_t& b, const u8 shift)
659  {
660 #if !defined(__AVX2__) && !defined(__ARM_NEON)
661  return b.shuffle(shift);
662 #endif
663 
664  auto a = b;
665  // compute a \oplus shift
666  if ((shift >> 7) & 0x1)
667  {
668 #ifdef __AVX2__
669  a = _mm256_permute2x128_si256(a, a, 1);
670 #elif defined(__ARM_NEON)
671  a.val[0] = b.val[1];
672  a.val[1] = b.val[0];
673 #endif
674  }
675  if ((shift >> 6) & 0x1)
676  {
677 #ifdef __AVX2__
678  a = _mm256_permute4x64_epi64(a, _MM_SHUFFLE(2, 3, 0, 1));
679 #elif defined(__ARM_NEON)
680  a.val[0] = vextq_u64(a.val[0], a.val[0], 1);
681  a.val[1] = vextq_u64(a.val[1], a.val[1], 1);
682 #endif
683  }
684  if ((shift >> 5) & 0x1)
685  {
686 #ifdef __AVX2__
687  a = _mm256_shuffle_epi32(a, _MM_SHUFFLE(2, 3, 0, 1));
688 #elif defined(__ARM_NEON)
689  a.val[0] = (uint64x2_t) vrev64q_u32((uint32x4_t) a.val[0]);
690  a.val[1] = (uint64x2_t) vrev64q_u32((uint32x4_t) a.val[1]);
691 
692 #endif
693  }
694  if ((shift >> 4) & 0x1)
695  {
696 #ifdef __AVX2__
697  a = _mm256_shufflelo_epi16(a, _MM_SHUFFLE(2, 3, 0, 1));
698  a = _mm256_shufflehi_epi16(a, _MM_SHUFFLE(2, 3, 0, 1));
699 #elif defined(__ARM_NEON)
700  a.val[0] = (uint64x2_t) vrev64q_u16((uint16x8_t) a.val[0]);
701  a.val[0] = (uint64x2_t) vrev64q_u32((uint32x4_t) a.val[0]);
702  a.val[1] = (uint64x2_t) vrev64q_u16((uint16x8_t) a.val[1]);
703  a.val[1] = (uint64x2_t) vrev64q_u32((uint32x4_t) a.val[1]);
704 #endif
705  }
706  if ((shift >> 3) & 0x1)
707  {
708 #ifdef __AVX2__
709  const __m256i mask = _mm256_set_epi8(14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1);
710  a = _mm256_shuffle_epi8(a, mask);
711 #elif defined(__ARM_NEON)
712  a.val[0] = (uint64x2_t) vrev64q_u8 ((uint8x16_t) a.val[0]);
713  a.val[0] = (uint64x2_t) vrev64q_u16((uint16x8_t) a.val[0]);
714  a.val[1] = (uint64x2_t) vrev64q_u8 ((uint8x16_t) a.val[1]);
715  a.val[1] = (uint64x2_t) vrev64q_u16((uint16x8_t) a.val[1]);
716 #endif
717  }
718  if ((shift >> 2) & 0x1)
719  {
720 #ifdef __AVX2__
721  const __m256i mask_high = _mm256_set1_epi8((char)0xF0);
722  const __m256i mask_low = _mm256_set1_epi8(0x0F);
723  const __m256i high = _mm256_and_si256(a, mask_high);
724  const __m256i low = _mm256_and_si256(a, mask_low);
725  a = _mm256_or_si256(_mm256_srli_epi16(high, 4), _mm256_slli_epi16(low, 4));
726 #elif defined(__ARM_NEON)
727  const auto mask_high = vdupq_n_u64(0xF0F0F0F0F0F0F0F0);
728  const auto mask_low = vdupq_n_u64(0x0F0F0F0F0F0F0F0F);
729 
730  for (u32 i = 0; i < 2; i++)
731  {
732  const auto high = vandq_u64(a.val[i], mask_high);
733  const auto low = vandq_u64(a.val[i], mask_low);
734 
735  a.val[i] = vorrq_u64(vshrq_n_u64(high, 4), vshlq_n_u64(low, 4));
736  }
737 #endif
738  }
739  if ((shift >> 1) & 0x1)
740  {
741 #ifdef __AVX2__
742  const __m256i mask_high = _mm256_set1_epi8((char)0xCC);
743  const __m256i mask_low = _mm256_set1_epi8(0x33);
744  const __m256i high = _mm256_and_si256(a, mask_high);
745  const __m256i low = _mm256_and_si256(a, mask_low);
746  a = _mm256_or_si256(_mm256_srli_epi16(high, 2), _mm256_slli_epi16(low, 2));
747 #elif defined(__ARM_NEON)
748  const auto mask_high = vdupq_n_u64(0xCCCCCCCCCCCCCCCC);
749  const auto mask_low = vdupq_n_u64(0x3333333333333333);
750 
751  for (u32 i = 0; i < 2; i++)
752  {
753  const auto high = vandq_u64(a.val[i], mask_high);
754  const auto low = vandq_u64(a.val[i], mask_low);
755 
756  a.val[i] = vorrq_u64(vshrq_n_u64(high, 2), vshlq_n_u64(low, 2));
757  }
758 #endif
759  }
760  if (shift & 0x1)
761  {
762 #ifdef __AVX2__
763  const __m256i mask_high = _mm256_set1_epi8((char)0xAA);
764  const __m256i mask_low = _mm256_set1_epi8(0x55);
765  const __m256i high = _mm256_and_si256(a, mask_high);
766  const __m256i low = _mm256_and_si256(a, mask_low);
767  a = _mm256_or_si256(_mm256_srli_epi16(high, 1), _mm256_slli_epi16(low, 1));
768 #elif defined(__ARM_NEON)
769  const auto mask_high = vdupq_n_u64(0xAAAAAAAAAAAAAAAA);
770  const auto mask_low = vdupq_n_u64(0x5555555555555555);
771 
772  for (u32 i = 0; i < 2; i++)
773  {
774  const auto high = vandq_u64(a.val[i], mask_high);
775  const auto low = vandq_u64(a.val[i], mask_low);
776 
777  a.val[i] = vorrq_u64(vshrq_n_u64(high, 1), vshlq_n_u64(low, 1));
778  }
779 #endif
780  }
781  return a;
782  }
783 
784  smallset_t smallset_shift_union(const smallset_t& a, const u8 shift)
785  {
786  smallset_t b = smallset_shift(a, shift);
787  return smallset_union(a, b);
788  }
789 
790  std::vector<u8> smallset_get_elements(const smallset_t& a)
791  {
792  std::vector<u8> e;
793  u64 chunks[4];
794 #ifdef __AVX2__
795  chunks[0] = _mm256_extract_epi64(a, 0);
796  chunks[1] = _mm256_extract_epi64(a, 1);
797  chunks[2] = _mm256_extract_epi64(a, 2);
798  chunks[3] = _mm256_extract_epi64(a, 3);
799 #elif defined(__ARM_NEON)
800  chunks[0] = a.val[0][0];
801  chunks[1] = a.val[0][1];
802  chunks[2] = a.val[1][0];
803  chunks[3] = a.val[1][1];
804 #else
805  a.to_array(chunks);
806 #endif
807  for (u32 i = 0; i < 4; i++)
808  {
809  u64 current_chunk = chunks[i];
810  while (current_chunk != 0)
811  {
812  u8 idx = __builtin_ctzll(current_chunk) + i * 64;
813  e.push_back(idx);
814  current_chunk &= (current_chunk - 1);
815  }
816  }
817  return e;
818  }
819 
820  inline smallset_t smallset_init_empty()
821  {
822 #ifdef __AVX2__
823  return _mm256_setzero_si256();
824 #elif defined(__ARM_NEON)
825  return {vdupq_n_u64(0), vdupq_n_u64(0)};
826 #else
827  return smallset_t();
828 #endif
829  }
830 
831  inline smallset_t smallset_init_full(const u32 len)
832  {
833 #if !defined(__AVX2__) && !defined(__ARM_NEON)
834  return smallset_t(len);
835 #endif
836  // N must be in {256, 128, 64, 32, 16, 8}
837  if (len == 256)
838  {
839 #ifdef __AVX2__
840  return _mm256_set_epi64x(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF);
841 #elif defined(__ARM_NEON)
842  return {vdupq_n_u64(0xFFFFFFFFFFFFFFFF), vdupq_n_u64(0xFFFFFFFFFFFFFFFF)};
843 #endif
844  }
845  else if (len == 128)
846  {
847 #ifdef __AVX2__
848  return _mm256_set_epi64x(0, 0, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF);
849 #elif defined(__ARM_NEON)
850  return {vdupq_n_u64(0xFFFFFFFFFFFFFFFF), vdupq_n_u64(0)};
851 #endif
852  }
853  else if (len == 64)
854  {
855 #ifdef __AVX2__
856  return _mm256_set_epi64x(0, 0, 0, 0xFFFFFFFFFFFFFFFF);
857 #elif defined(__ARM_NEON)
858  auto tmp = vdupq_n_u64(0);
859  return {vsetq_lane_u64(0xFFFFFFFFFFFFFFFF, tmp, 0), vdupq_n_u64(0)};
860 #endif
861  }
862  else if (len == 32)
863  {
864 #ifdef __AVX2__
865  return _mm256_set_epi64x(0, 0, 0, 0xFFFFFFFF);
866 #elif defined(__ARM_NEON)
867  auto tmp = vdupq_n_u64(0);
868  return {(uint64x2_t)vsetq_lane_u32(0xFFFFFFFF, (uint32x4_t)tmp, 0), vdupq_n_u64(0)};
869 #endif
870  }
871  else if (len == 16)
872  {
873 #ifdef __AVX2__
874  return _mm256_set_epi64x(0, 0, 0, 0xFFFF);
875 #elif defined(__ARM_NEON)
876  auto tmp = vdupq_n_u64(0);
877  return {(uint64x2_t)vsetq_lane_u16(0xFFFF, (uint16x8_t)tmp, 0), vdupq_n_u64(0)};
878 #endif
879  }
880  else if (len == 8)
881  {
882 #ifdef __AVX2__
883  return _mm256_set_epi64x(0, 0, 0, 0xFF);
884 #elif defined(__ARM_NEON)
885  auto tmp = vdupq_n_u64(0);
886  return {(uint64x2_t)vsetq_lane_u8(0xFF, (uint8x16_t)tmp, 0), vdupq_n_u64(0)};
887 #endif
888  }
889  else
890  {
891 #ifdef __AVX2__
892  return _mm256_set_epi64x(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF);
893 #elif defined(__ARM_NEON)
894  return {vdupq_n_u64(0xFFFFFFFFFFFFFFFF), vdupq_n_u64(0xFFFFFFFFFFFFFFFF)};
895 #endif
896  }
897  }
898 
899  inline smallset_t smallset_invert(const smallset_t& a, const u32 len)
900  {
901  const smallset_t b = smallset_init_full(len);
902 #ifdef __AVX2__
903  return _mm256_xor_si256(a, b);
904 #elif defined(__ARM_NEON)
905  return {veorq_u64(a.val[0], b.val[0]), veorq_u64(a.val[1], b.val[1])};
906 #else
907  return (a ^ b);
908 #endif
909  }
910 
911  inline smallset_t smallset_setminus(const smallset_t& a, const smallset_t& b, const u32 len)
912  {
913  const smallset_t b_not = smallset_invert(b, len);
914  return smallset_intersect(a, b_not);
915  }
916 
917  bool smallset_elm_is_in_set(const u8 e, const smallset_t& a)
918  {
919 #if !defined(__AVX2__) && !defined(__ARM_NEON)
920  return a.is_set(e);
921 #else
922  smallset_t b = smallset_init_empty();
923  b = smallset_add_element(b, e);
924  b = smallset_intersect(a, b);
925  return !smallset_is_empty(b);
926 #endif
927  }
928  // END OF SMALL SET //
929 
930  // state of the linear_representative algorithm
931  typedef struct
932  {
933  std::vector<u8> A;
934  std::vector<u8> B;
935  std::vector<u8> R_S;
944  } state_t;
945 
946  // lexicographically compare R_S and R_S_best
947  bool is_greater(const std::vector<u8>& R_S, const std::vector<u8>& R_S_best, const u32 len)
948  {
949  if ((R_S_best[0] == 0) && (R_S_best[1] == 0))
950  return false;
951 
952  for (u32 x = 0; x < len; x++)
953  {
954  // special case: R_S[x] not defined (=> 0) and R_S_best[x] = 0
955  // works out with this
956  if (R_S[x] > R_S_best[x])
957  return true;
958  if (R_S[x] < R_S_best[x])
959  return false;
960  }
961  // can happen if there are self equivalences (?)
962  return false;
963  }
964 
965  bool update_linear(std::vector<u8>& A, u8 new_x, const u32 len)
966  {
967  u8 new_y = A[new_x];
968  for (u32 i = 1; i < len; i++)
969  {
970  u8 e = A[i];
971  if (e == 0)
972  continue;
973  else if (A[new_x ^ i] == 0)
974  A[new_x ^ i] = e ^ new_y;
975  else if (A[new_x ^ i] != (e ^ new_y))
976  {
977  return false;
978  }
979  }
980  return true;
981  }
982 
983  bool subroutine(const std::vector<u8>& S, const std::vector<u8>& S_inv, const state_t& state, std::vector<u8>& R_S_best, const u32 len)
984  {
985  std::vector<u8> A(state.A);
986  std::vector<u8> B(state.B);
987  std::vector<u8> R_S(state.R_S);
988 
989  smallset_t D_A = (state.D_A);
990  smallset_t D_B = (state.D_B);
991  smallset_t C_A = (state.C_A);
992  smallset_t C_B = (state.C_B);
993  smallset_t N_A = (state.N_A);
994  smallset_t N_B = (state.N_B);
995  smallset_t U_A = (state.U_A);
996  smallset_t U_B = (state.U_B);
997 
998  while (!smallset_is_empty(N_A))
999  {
1000  u8 x = smallset_least_element(N_A);
1001  u8 y = smallset_least_element(U_B);
1002 
1003  B[y] = S[A[x]];
1004  if (!update_linear(B, y, len))
1005  return false;
1006  smallset_t D_B_new = smallset_shift(D_B, y);
1007  D_B = smallset_union(D_B, D_B_new);
1008  U_B = smallset_setminus(U_B, D_B_new, len);
1009 
1010  smallset_t SoA_N_A = smallset_init_empty();
1011  for (u8 x : smallset_get_elements(N_A))
1012  {
1013  SoA_N_A = smallset_add_element(SoA_N_A, S[A[x]]);
1014  }
1015  smallset_t B_D_B_new = smallset_init_empty();
1016  for (u8 d : smallset_get_elements(D_B_new))
1017  {
1018  B_D_B_new = smallset_add_element(B_D_B_new, B[d]);
1019  if (smallset_elm_is_in_set(B[d], SoA_N_A))
1020  {
1021  C_B = smallset_add_element(C_B, d);
1022  }
1023  else
1024  {
1025  N_B = smallset_add_element(N_B, d);
1026  }
1027  }
1028  smallset_t C_A_new = smallset_init_empty();
1029  for (u8 x : smallset_get_elements(N_A))
1030  {
1031  if (smallset_elm_is_in_set(S[A[x]], B_D_B_new))
1032  {
1033  C_A_new = smallset_add_element(C_A_new, x);
1034  }
1035  }
1036  C_A = smallset_union(C_A, C_A_new);
1037  N_A = smallset_setminus(N_A, C_A_new, len);
1038  for (u8 x : smallset_get_elements(C_A_new))
1039  {
1040  u8 y = 0;
1041  for (u32 i = 0; i < len; i++)
1042  {
1043  if (B[i] == S[A[x]])
1044  {
1045  y = i;
1046  break;
1047  }
1048  }
1049  R_S[x] = y;
1050  }
1051  if (is_greater(R_S, R_S_best, len))
1052  {
1053  return false;
1054  }
1055 
1056  while (smallset_is_empty(N_A) && !smallset_is_empty(N_B))
1057  {
1058  u8 x = smallset_least_element(U_A);
1059  u8 y = smallset_least_element(N_B);
1060  A[x] = S_inv[B[y]];
1061  if (!update_linear(A, x, len))
1062  {
1063  return false;
1064  }
1065  smallset_t D_A_new = smallset_shift(D_A, x);
1066  D_A = smallset_union(D_A, D_A_new);
1067  U_A = smallset_setminus(U_A, D_A_new, len);
1068  smallset_t SinvoB_N_B = smallset_init_empty();
1069  for (u8 y : smallset_get_elements(N_B))
1070  {
1071  SinvoB_N_B = smallset_add_element(SinvoB_N_B, S_inv[B[y]]);
1072  }
1073  smallset_t A_D_A_new = smallset_init_empty();
1074  for (u8 d : smallset_get_elements(D_A_new))
1075  {
1076  A_D_A_new = smallset_add_element(A_D_A_new, A[d]);
1077  if (smallset_elm_is_in_set(A[d], SinvoB_N_B))
1078  {
1079  C_A = smallset_add_element(C_A, d);
1080  }
1081  else
1082  {
1083  N_A = smallset_add_element(N_A, d);
1084  }
1085  }
1086  smallset_t C_B_new = smallset_init_empty();
1087  for (u8 y : smallset_get_elements(N_B))
1088  {
1089  if (smallset_elm_is_in_set(S_inv[B[y]], A_D_A_new))
1090  {
1091  C_B_new = smallset_add_element(C_B_new, y);
1092  }
1093  }
1094  C_B = smallset_union(C_B, C_B_new);
1095  N_B = smallset_setminus(N_B, C_B_new, len);
1096  for (u8 y : smallset_get_elements(C_B_new))
1097  {
1098  u8 x = 0;
1099  for (u32 i = 0; i < len; i++)
1100  {
1101  if (A[i] == S_inv[B[y]])
1102  {
1103  x = i;
1104  break;
1105  }
1106  }
1107  R_S[x] = y;
1108  }
1109  if (is_greater(R_S, R_S_best, len))
1110  {
1111  return false;
1112  }
1113  }
1114  }
1115  if (smallset_is_empty(U_A) && smallset_is_empty(U_B))
1116  {
1117  for (u32 i = 0; i < len; i++)
1118  {
1119  // new best
1120  R_S_best[i] = R_S[i];
1121  }
1122  return true;
1123  }
1124  else
1125  {
1126  u8 x = smallset_least_element(U_A);
1127  smallset_t D_A_new = smallset_shift(D_A, x);
1128  U_A = smallset_setminus(U_A, D_A_new, len);
1129  D_A = smallset_union(D_A, D_A_new);
1130  N_A = smallset_union(N_A, D_A_new);
1131  bool flag = false;
1132  smallset_t Y = smallset_init_full(len);
1133  smallset_t A_set = smallset_init_empty();
1134  for (u32 i = 0; i < len; i++)
1135  {
1136  A_set = smallset_add_element(A_set, A[i]);
1137  }
1138  Y = smallset_setminus(Y, A_set, len);
1139  for (u8 y : smallset_get_elements(Y))
1140  {
1141  std::vector<u8> A_next_guess(len);
1142 
1143  for (u32 i = 0; i < len; i++)
1144  {
1145  A_next_guess[i] = A[i];
1146  }
1147  A_next_guess[x] = y;
1148  if (!update_linear(A_next_guess, x, len))
1149  continue;
1150  state_t state_next;
1151  state_next.A = A_next_guess;
1152  state_next.B = B;
1153  state_next.R_S = R_S;
1154  state_next.D_A = D_A;
1155  state_next.D_B = D_B;
1156  state_next.C_A = C_A;
1157  state_next.C_B = C_B;
1158  state_next.N_A = N_A;
1159  state_next.N_B = N_B;
1160  state_next.U_A = U_A;
1161  state_next.U_B = U_B;
1162 
1163  if (subroutine(S, S_inv, state_next, R_S_best, len))
1164  {
1165  flag = true;
1166  }
1167  }
1168 
1169  return flag;
1170  }
1171  }
1172  } // namespace
1173 
1174  std::vector<u8> SBoxDatabase::compute_linear_representative(const std::vector<u8>& sbox)
1175  {
1176  u32 len = sbox.size();
1177 
1178  // variable for current best candidate
1179  std::vector<u8> R_S_best(len, 0);
1180 
1181  // invert sbox
1182  std::vector<u8> S_inv(len, 0);
1183  for (u32 x = 0; x < len; x++)
1184  {
1185  u8 y = sbox[x];
1186  S_inv[y] = x;
1187  }
1188 
1189  // init the state of the algorithm
1190  state_t state;
1191  state.A = std::vector<u8>(len, 0);
1192  state.B = std::vector<u8>(len, 0);
1193  state.R_S = std::vector<u8>(len, 0);
1194 
1195  state.D_A = smallset_add_element(smallset_init_empty(), 0);
1196  state.D_B = smallset_add_element(smallset_init_empty(), 0);
1197 
1198  state.C_A = smallset_init_empty();
1199  state.C_B = smallset_init_empty();
1200 
1201  state.N_A = smallset_add_element(smallset_init_empty(), 0);
1202  state.N_B = smallset_add_element(smallset_init_empty(), 0);
1203 
1204  state.U_A = smallset_setminus(smallset_init_full(len), state.D_A, len);
1205  state.U_B = smallset_setminus(smallset_init_full(len), state.D_A, len);
1206 
1207  // init in special case S[0] == 0
1208  if (sbox[0] == 0)
1209  {
1210  state.C_A = smallset_add_element(smallset_init_empty(), 0);
1211  state.C_B = smallset_add_element(smallset_init_empty(), 0);
1212 
1213  state.N_A = smallset_init_empty();
1214  state.N_B = smallset_init_empty();
1215  }
1216 
1217  // compute linear representative recursively
1218  subroutine(sbox, S_inv, state, R_S_best, len);
1219 
1220  return R_S_best;
1221  }
1222  } // namespace hawkeye
1223 } // namespace hal
Result< std::monostate > add(const std::string &name, const std::vector< u8 > &sbox)
Add an S-box to the database.
SBoxDatabase()=default
Construct an empty S-box database.
Result< std::string > lookup(const std::vector< u8 > &sbox) const
Attempt to look up an S-box in the database.
Result< std::monostate > load(const std::filesystem::path &file_path, bool overwrite=false)
Load S-boxes from a file and add them to the existing database.
static Result< SBoxDatabase > from_file(const std::filesystem::path &file_path)
Construct an S-box database from file.
void print() const
Print the database.
Result< std::monostate > store(const std::filesystem::path &file_path) const
Store the S-box database to a database file.
static std::vector< u8 > compute_linear_representative(const std::vector< u8 > &sbox)
Compute the linear representative of the given S-box.
smallset_t operator^(const smallset_t &other) const
smallset_t(int preset=0)
void set(u8 bit)
void to_array(u64 *arr, bool swap=false) const
void dump() const
bool is_set(u8 bit) const
bool empty() const
smallset_t operator|(const smallset_t &other) const
smallset_t shuffle(u8 shift) const
smallset_t operator&(const smallset_t &other) const
int size() const
u8 least_bit() const
uint64_t u64
Definition: defines.h:42
uint16_t u16
Definition: defines.h:40
uint32_t u32
Definition: defines.h:41
uint8_t u8
Definition: defines.h:39
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
Definition: defines.h:45
std::string name
std::vector< u8 > R_S
smallset_t U_B
smallset_t U_A
std::vector< u8 > B
smallset_t N_A
smallset_t C_A
std::vector< u8 > A
smallset_t D_A
smallset_t C_B
smallset_t D_B
const uint64_t _ONE_
smallset_t N_B
This file contains the S-box database class that holds and manages known cryptographic S-boxes up to ...