OTPChecker.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /**
  2. * @file OTPChecker.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the author nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. #include <string.h>
  30. #include <misc/balloc.h>
  31. #include <security/OTPChecker.h>
  32. static void OTPChecker_Table_Empty (OTPChecker *mc, struct OTPChecker_table *t);
  33. static void OTPChecker_Table_AddOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp);
  34. static void OTPChecker_Table_Generate (OTPChecker *mc, struct OTPChecker_table *t, OTPCalculator *calc, uint8_t *key, uint8_t *iv);
  35. static int OTPChecker_Table_CheckOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp);
  36. void OTPChecker_Table_Empty (OTPChecker *mc, struct OTPChecker_table *t)
  37. {
  38. for (int i = 0; i < mc->num_entries; i++) {
  39. t->entries[i].avail = -1;
  40. }
  41. }
  42. void OTPChecker_Table_AddOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp)
  43. {
  44. // calculate starting index
  45. int start_index = otp % mc->num_entries;
  46. // try indexes starting with the base position
  47. for (int i = 0; i < mc->num_entries; i++) {
  48. int index = bmodadd_int(start_index, i, mc->num_entries);
  49. struct OTPChecker_entry *entry = &t->entries[index];
  50. // if we find a free index, use it
  51. if (entry->avail < 0) {
  52. entry->otp = otp;
  53. entry->avail = 1;
  54. return;
  55. }
  56. // if we find a used index with the same mac,
  57. // use it by incrementing its count
  58. if (entry->otp == otp) {
  59. entry->avail++;
  60. return;
  61. }
  62. }
  63. // will never add more macs than we can hold
  64. ASSERT(0)
  65. }
  66. void OTPChecker_Table_Generate (OTPChecker *mc, struct OTPChecker_table *t, OTPCalculator *calc, uint8_t *key, uint8_t *iv)
  67. {
  68. // calculate values
  69. otp_t *otps = OTPCalculator_Generate(calc, key, iv, 0);
  70. // empty table
  71. OTPChecker_Table_Empty(mc ,t);
  72. // add calculated values to table
  73. for (int i = 0; i < mc->num_otps; i++) {
  74. OTPChecker_Table_AddOTP(mc, t, otps[i]);
  75. }
  76. }
  77. int OTPChecker_Table_CheckOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp)
  78. {
  79. // calculate starting index
  80. int start_index = otp % mc->num_entries;
  81. // try indexes starting with the base position
  82. for (int i = 0; i < mc->num_entries; i++) {
  83. int index = bmodadd_int(start_index, i, mc->num_entries);
  84. struct OTPChecker_entry *entry = &t->entries[index];
  85. // if we find an empty entry, there is no such mac
  86. if (entry->avail < 0) {
  87. return 0;
  88. }
  89. // if we find a matching entry, check its count
  90. if (entry->otp == otp) {
  91. if (entry->avail > 0) {
  92. entry->avail--;
  93. return 1;
  94. }
  95. return 0;
  96. }
  97. }
  98. // there are always empty slots
  99. ASSERT(0)
  100. return 0;
  101. }
  102. static void work_func (OTPChecker *mc)
  103. {
  104. struct OTPChecker_table *table = &mc->tables[mc->next_table];
  105. OTPChecker_Table_Generate(mc, table, &mc->calc, mc->tw_key, mc->tw_iv);
  106. }
  107. static void work_done_handler (OTPChecker *mc)
  108. {
  109. ASSERT(mc->tw_have)
  110. DebugObject_Access(&mc->d_obj);
  111. // free work
  112. BThreadWork_Free(&mc->tw);
  113. mc->tw_have = 0;
  114. // update next table number
  115. mc->next_table = bmodadd_int(mc->next_table, 1, mc->num_tables);
  116. // update number of used tables if not all are used yet
  117. if (mc->tables_used < mc->num_tables) {
  118. mc->tables_used++;
  119. }
  120. // call handler
  121. if (mc->handler) {
  122. mc->handler(mc->user);
  123. return;
  124. }
  125. }
  126. int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables, BThreadWorkDispatcher *twd)
  127. {
  128. ASSERT(num_otps > 0)
  129. ASSERT(BEncryption_cipher_valid(cipher))
  130. ASSERT(num_tables > 0)
  131. // init arguments
  132. mc->num_otps = num_otps;
  133. mc->cipher = cipher;
  134. mc->num_tables = num_tables;
  135. mc->twd = twd;
  136. // set no handlers
  137. mc->handler = NULL;
  138. // set number of entries
  139. if (mc->num_otps > INT_MAX / 2) {
  140. goto fail0;
  141. }
  142. mc->num_entries = 2 * mc->num_otps;
  143. // set no tables used
  144. mc->tables_used = 0;
  145. mc->next_table = 0;
  146. // initialize calculator
  147. if (!OTPCalculator_Init(&mc->calc, mc->num_otps, cipher)) {
  148. goto fail0;
  149. }
  150. // allocate tables
  151. if (!(mc->tables = BAllocArray(mc->num_tables, sizeof(mc->tables[0])))) {
  152. goto fail1;
  153. }
  154. // allocate entries
  155. if (!(mc->entries = BAllocArray2(mc->num_tables, mc->num_entries, sizeof(mc->entries[0])))) {
  156. goto fail2;
  157. }
  158. // initialize tables
  159. for (int i = 0; i < mc->num_tables; i++) {
  160. struct OTPChecker_table *table = &mc->tables[i];
  161. table->entries = mc->entries + (size_t)i * mc->num_entries;
  162. OTPChecker_Table_Empty(mc, table);
  163. }
  164. // have no work
  165. mc->tw_have = 0;
  166. DebugObject_Init(&mc->d_obj);
  167. return 1;
  168. fail2:
  169. BFree(mc->tables);
  170. fail1:
  171. OTPCalculator_Free(&mc->calc);
  172. fail0:
  173. return 0;
  174. }
  175. void OTPChecker_Free (OTPChecker *mc)
  176. {
  177. DebugObject_Free(&mc->d_obj);
  178. // free work
  179. if (mc->tw_have) {
  180. BThreadWork_Free(&mc->tw);
  181. }
  182. // free entries
  183. BFree(mc->entries);
  184. // free tables
  185. BFree(mc->tables);
  186. // free calculator
  187. OTPCalculator_Free(&mc->calc);
  188. }
  189. void OTPChecker_AddSeed (OTPChecker *mc, uint16_t seed_id, uint8_t *key, uint8_t *iv)
  190. {
  191. ASSERT(mc->next_table >= 0)
  192. ASSERT(mc->next_table < mc->num_tables)
  193. DebugObject_Access(&mc->d_obj);
  194. // free existing work
  195. if (mc->tw_have) {
  196. BThreadWork_Free(&mc->tw);
  197. }
  198. // set table's seed ID
  199. mc->tables[mc->next_table].id = seed_id;
  200. // copy key and IV
  201. memcpy(mc->tw_key, key, BEncryption_cipher_key_size(mc->cipher));
  202. memcpy(mc->tw_iv, iv, BEncryption_cipher_block_size(mc->cipher));
  203. // start work
  204. BThreadWork_Init(&mc->tw, mc->twd, (BThreadWork_handler_done)work_done_handler, mc, (BThreadWork_work_func)work_func, mc);
  205. // set have work
  206. mc->tw_have = 1;
  207. }
  208. void OTPChecker_RemoveSeeds (OTPChecker *mc)
  209. {
  210. DebugObject_Access(&mc->d_obj);
  211. // free existing work
  212. if (mc->tw_have) {
  213. BThreadWork_Free(&mc->tw);
  214. mc->tw_have = 0;
  215. }
  216. mc->tables_used = 0;
  217. mc->next_table = 0;
  218. }
  219. int OTPChecker_CheckOTP (OTPChecker *mc, uint16_t seed_id, otp_t otp)
  220. {
  221. DebugObject_Access(&mc->d_obj);
  222. // try tables in reverse order
  223. for (int i = 1; i <= mc->tables_used; i++) {
  224. int table_index = bmodadd_int(mc->next_table, mc->num_tables - i, mc->num_tables);
  225. if (table_index == mc->next_table && mc->tw_have) {
  226. // ignore table that is being generated
  227. continue;
  228. }
  229. struct OTPChecker_table *table = &mc->tables[table_index];
  230. if (table->id == seed_id) {
  231. return OTPChecker_Table_CheckOTP(mc, table, otp);
  232. }
  233. }
  234. return 0;
  235. }
  236. void OTPChecker_SetHandlers (OTPChecker *mc, OTPChecker_handler handler, void *user)
  237. {
  238. DebugObject_Access(&mc->d_obj);
  239. mc->handler = handler;
  240. mc->user = user;
  241. }