cmd_rng.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*
  2. *******************************************************************************
  3. \file cmd_rng.c
  4. \brief Command-line interface to Bee2: random number generation
  5. \project bee2/cmd
  6. \created 2022.06.08
  7. \version 2024.06.14
  8. \copyright The Bee2 authors
  9. \license Licensed under the Apache License, Version 2.0 (see LICENSE.txt).
  10. *******************************************************************************
  11. */
  12. #include "../cmd.h"
  13. #include <bee2/core/blob.h>
  14. #include <bee2/core/err.h>
  15. #include <bee2/core/mem.h>
  16. #include <bee2/core/prng.h>
  17. #include <bee2/core/rng.h>
  18. #include <bee2/core/str.h>
  19. #include <bee2/core/util.h>
  20. #include <bee2/crypto/belt.h>
  21. #include <stdio.h>
  22. /*
  23. *******************************************************************************
  24. Терминал
  25. \thanks
  26. https://www.flipcode.com/archives/_kbhit_for_Linux.shtml
  27. (Morgan McGuire [morgan@cs.brown.edu])
  28. https://stackoverflow.com/questions/29335758/using-kbhit-and-getch-on-linux
  29. https://askcodes.net/questions/how-to-implement-getch---function-of-c-in-linux-
  30. *******************************************************************************
  31. */
  32. #ifdef OS_UNIX
  33. #include <termios.h>
  34. #include <unistd.h>
  35. #include <stdio.h>
  36. static bool_t cmdTermEcho(bool_t on)
  37. {
  38. bool_t prev;
  39. struct termios attr;
  40. fflush(stdout);
  41. tcgetattr(STDIN_FILENO, &attr);
  42. prev = (attr.c_lflag & ECHO) != 0;
  43. if (on)
  44. attr.c_lflag |= ECHO;
  45. else
  46. attr.c_lflag &= ~ECHO;
  47. tcsetattr(STDIN_FILENO, TCSANOW, &attr);
  48. return prev;
  49. }
  50. #else
  51. static bool_t cmdTermEcho(bool_t on)
  52. {
  53. static bool_t state;
  54. bool_t ret = state;
  55. state = on;
  56. return ret;
  57. }
  58. #endif
  59. /*
  60. *******************************************************************************
  61. ГСЧ
  62. \todo В cmdRngKbRead() проверяется, что таймер обновлялся не реже 1 раза
  63. в 50 нс, т.е. с частотой не ниже 20 МГц. Для сравнения, в СТБ 34.101.27
  64. объявлен порог в 600 МГц. При соблюдении этого порога оценка энтропии
  65. клавиатурного источника не падала ниже 27.1 битов на наблюдение.
  66. Следует уточнить оценки энтропии при снижения порога частоты.
  67. *******************************************************************************
  68. */
  69. err_t cmdRngKbRead(tm_ticks_t data[128])
  70. {
  71. const tm_ticks_t freq = tmFreq(); /* число обновлений таймера в секунду */
  72. const tm_ticks_t max_delay = freq * 5; /* 5 с */
  73. const tm_ticks_t min_delay = freq / 20; /* 50 мс */
  74. register tm_ticks_t ticks;
  75. register tm_ticks_t t;
  76. err_t code;
  77. size_t pos;
  78. bool_t echo;
  79. int ch;
  80. // pre
  81. ASSERT(memIsValid(data, sizeof(tm_ticks_t) * 128));
  82. // таймер достаточно точен?
  83. code = freq >= 20000000u ? ERR_OK : ERR_FILE_NOT_FOUND;
  84. ERR_CALL_CHECK(code);
  85. // приглашение к сбору энтропии
  86. printf("Collecting entropy from keyboard...\n");
  87. printf("Please, press different keys avoiding repetitions and long pauses:\n");
  88. for (pos = 128; pos; pos -= 2)
  89. printf("%c", '*');
  90. printf("\r");
  91. // сбор энтропии
  92. echo = cmdTermEcho(FALSE);
  93. ASSERT(pos == 0);
  94. for (ticks = tmTicks(), ch = 0; pos < 128; )
  95. {
  96. int c;
  97. // превышен интервал ожидания?
  98. t = tmTicks();
  99. if (t > ticks + max_delay)
  100. {
  101. code = ERR_TIMEOUT;
  102. break;
  103. }
  104. // клавиша не нажата? нажата слишком быcтро?
  105. // нажали ту же клавишу? функциональную клавишу?
  106. if (!cmdTermKbhit() || t < ticks + min_delay ||
  107. (c = cmdTermGetch()) == ch || c == 0 || c == 0xE0)
  108. continue;
  109. // обрабатать нажатие
  110. data[pos++] = t - ticks, ticks = t, ch = c;
  111. if (pos % 2)
  112. printf(".");
  113. }
  114. ticks = t = 0;
  115. cmdTermEcho(echo);
  116. printf("\n");
  117. return code;
  118. }
  119. static err_t prngEchoRead(size_t* read, void* buf, size_t count, void* state)
  120. {
  121. ASSERT(memIsValid(read, O_PER_S));
  122. prngEchoStepR(buf, count, state);
  123. *read = count;
  124. return ERR_OK;
  125. }
  126. err_t cmdRngStart(bool_t verbose)
  127. {
  128. err_t code;
  129. // ГСЧ уже запущен?
  130. if (rngIsValid())
  131. return ERR_OK;
  132. // печать информации об источниках
  133. if (verbose)
  134. {
  135. const char* sources[] = { "trng", "trng2", "sys", "sys2", "timer" };
  136. size_t pos;
  137. size_t count;
  138. size_t read;
  139. printf("Starting RNG[");
  140. for (pos = count = 0; pos < COUNT_OF(sources); ++pos)
  141. if (rngESRead(&read, 0, 0, sources[pos]) == ERR_OK)
  142. printf(count++ ? ", %s" : "%s", sources[pos]);
  143. printf("]... ");
  144. }
  145. // энтропии достаточно?
  146. code = rngESHealth();
  147. if (code == ERR_OK)
  148. code = rngCreate(0, 0);
  149. // нет, подключить клавиатурный источник
  150. else if (code == ERR_NOT_ENOUGH_ENTROPY)
  151. {
  152. void* stack;
  153. tm_ticks_t* data;
  154. octet* hash;
  155. void* state;
  156. // выделить и разметить память
  157. code = cmdBlobCreate(stack, 128 * sizeof(tm_ticks_t) +
  158. MAX2(beltHash_keep(), prngEcho_keep()));
  159. ERR_CALL_CHECK(code);
  160. data = (tm_ticks_t*)stack;
  161. hash = (octet*)data;
  162. state = data + 128;
  163. // собрать данные от клавиатурного источника
  164. code = cmdRngKbRead(data);
  165. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  166. // хэшировать
  167. beltHashStart(state);
  168. beltHashStepH(data, 128 * sizeof(tm_ticks_t), state);
  169. beltHashStepG(hash, state);
  170. // запустить echo-генератор
  171. prngEchoStart(state, hash, 32);
  172. // запустить генератор
  173. code = rngCreate(prngEchoRead, state);
  174. // освободить память
  175. cmdBlobClose(stack);
  176. }
  177. if (verbose)
  178. printf("%s\n", errMsg(code));
  179. return code;
  180. }