cmd_rng.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 2023.12.16
  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. if (verbose)
  130. {
  131. const char* sources[] = { "trng", "trng2", "sys", "sys2", "timer" };
  132. size_t pos;
  133. size_t count;
  134. size_t read;
  135. printf("Starting RNG[");
  136. for (pos = count = 0; pos < COUNT_OF(sources); ++pos)
  137. if (rngESRead(&read, 0, 0, sources[pos]) == ERR_OK)
  138. printf(count++ ? ", %s" : "%s", sources[pos]);
  139. printf("]... ");
  140. }
  141. code = rngESHealth();
  142. if (code == ERR_OK)
  143. code = rngCreate(0, 0);
  144. else if (code == ERR_NOT_ENOUGH_ENTROPY)
  145. {
  146. void* stack;
  147. tm_ticks_t* data;
  148. octet* hash;
  149. void* state;
  150. // выделить и разметить память
  151. code = cmdBlobCreate(stack, 128 * sizeof(tm_ticks_t) +
  152. MAX2(beltHash_keep(), prngEcho_keep()));
  153. ERR_CALL_CHECK(code);
  154. data = (tm_ticks_t*)stack;
  155. hash = (octet*)data;
  156. state = data + 128;
  157. // собрать данные от клавиатурного источника
  158. code = cmdRngKbRead(data);
  159. ERR_CALL_HANDLE(code, cmdBlobClose(stack));
  160. // хэшировать
  161. beltHashStart(state);
  162. beltHashStepH(data, 128 * sizeof(tm_ticks_t), state);
  163. beltHashStepG(hash, state);
  164. // запустить echo-генератор
  165. prngEchoStart(state, hash, 32);
  166. // запустить генератор
  167. code = rngCreate(prngEchoRead, state);
  168. // освободить память
  169. cmdBlobClose(stack);
  170. }
  171. if (verbose)
  172. printf("%s\n", errMsg(code));
  173. return code;
  174. }