rolling_flaws_keeloq.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #include "rolling_flaws_keeloq.h"
  2. #include "rolling_flaws_utils.h"
  3. #include "rolling_flaws_settings.h"
  4. typedef struct {
  5. uint32_t fix;
  6. uint32_t hop;
  7. uint32_t sn;
  8. uint32_t btn;
  9. uint32_t cnt;
  10. uint32_t enc;
  11. FuriString* mf;
  12. } KeeLoqData;
  13. KeeLoqData* keeloq_data_alloc() {
  14. KeeLoqData* data = malloc(sizeof(KeeLoqData));
  15. data->mf = furi_string_alloc();
  16. return data;
  17. }
  18. void keeloq_data_free(KeeLoqData* data) {
  19. furi_string_free(data->mf);
  20. free(data);
  21. }
  22. static uint32_t get_forward_distance(uint32_t current_count, uint32_t new_count) {
  23. uint32_t distance = 0;
  24. if(new_count >= current_count) {
  25. distance = new_count - current_count;
  26. } else {
  27. distance = (0xFFFF - current_count) + new_count;
  28. }
  29. return distance;
  30. }
  31. static bool is_open(RollingFlawsModel* model, KeeLoqData* data) {
  32. bool any_mf = rolling_flaws_setting_protocol_mf_name_get(model)[0] == '*';
  33. if(!any_mf &&
  34. furi_string_cmp(data->mf, rolling_flaws_setting_protocol_mf_name_get(model)) != 0) {
  35. FURI_LOG_I(
  36. TAG,
  37. "Wrong MF. Expected >%s< but got >%s<",
  38. rolling_flaws_setting_protocol_mf_name_get(model),
  39. furi_string_get_cstr(data->mf));
  40. furi_string_set(model->status, "BAD MF");
  41. return false;
  42. }
  43. if(data->fix != rolling_flaws_setting_fix_get(model)) {
  44. FURI_LOG_I(
  45. TAG,
  46. "Wrong fix. Expected >%08lX< but got >%08lX<",
  47. rolling_flaws_setting_fix_get(model),
  48. data->fix);
  49. furi_string_set(model->status, "BAD FIX");
  50. return false;
  51. }
  52. if((rolling_flaws_setting_fix_get(model) & 0xFFFFFFF) == 0) {
  53. FURI_LOG_I(TAG, "Fix is test. Not checking data.");
  54. furi_string_set(model->status, "TEST");
  55. model->future_count = 0xFFFFFFFF;
  56. model->count = data->cnt;
  57. return true;
  58. }
  59. if(data->enc != FAILED_TO_PARSE) {
  60. FURI_LOG_I(TAG, "Encrypted payload is %08lX", data->enc);
  61. if(!rolling_flaws_setting_sn_zero_get(model)) {
  62. FURI_LOG_I(TAG, "SN wildcard by 00 disabled.");
  63. if((data->fix & 0xFF) != 0) {
  64. FURI_LOG_I(TAG, "SN does not end in 00, validating enc %08lX.", data->enc);
  65. if((data->enc & 0xFF) == 0) {
  66. FURI_LOG_I(TAG, "Encrypted payload SN is zero.");
  67. furi_string_set(model->status, "SN 00");
  68. return false;
  69. }
  70. }
  71. }
  72. uint8_t match_bits = rolling_flaws_setting_sn_bits_get(model);
  73. if(match_bits != 0) {
  74. uint32_t mask = 0xFFFFFFFF;
  75. mask = mask >> (32 - match_bits);
  76. uint32_t fix_sn = data->fix & mask;
  77. uint32_t enc_sn = data->enc & mask;
  78. if(fix_sn != enc_sn) {
  79. FURI_LOG_I(TAG, "SN does not match. Fix: %08lX Enc: %08lX", fix_sn, enc_sn);
  80. furi_string_set(model->status, "BAD SN");
  81. return false;
  82. } else {
  83. FURI_LOG_I(TAG, "SN matches. Fix: %08lX Enc: %08lX", fix_sn, enc_sn);
  84. }
  85. }
  86. }
  87. uint32_t distance = get_forward_distance(model->count, data->cnt);
  88. FURI_LOG_I(TAG, "Distance: %08lX", distance);
  89. if(distance == 0 && rolling_flaws_setting_replay_get(model)) {
  90. FURI_LOG_I(TAG, "Replay attack detected");
  91. furi_string_set(model->status, "REPLAY");
  92. model->future_count = 0xFFFFFFFF;
  93. model->count = data->cnt;
  94. return true;
  95. }
  96. if(rolling_flaws_setting_count_zero_get(model) && data->cnt == 0) {
  97. FURI_LOG_I(TAG, "Count zero allowed.");
  98. furi_string_set(model->status, "COUNT0");
  99. model->future_count = 0xFFFFFFFF;
  100. // We don't reset count in this case.
  101. return true;
  102. }
  103. if(distance == 0) {
  104. distance = 0x10000;
  105. }
  106. if(distance <= rolling_flaws_setting_window_next_get(model)) {
  107. FURI_LOG_I(TAG, "Within next window");
  108. furi_string_set(model->status, "NEXT");
  109. model->future_count = 0xFFFFFFFF;
  110. model->count = data->cnt;
  111. return true;
  112. }
  113. if(distance <= rolling_flaws_setting_window_future_get(model)) {
  114. FURI_LOG_I(TAG, "Within future window");
  115. if(model->future_count > 0xFFFF) {
  116. FURI_LOG_I(TAG, "Set future value to %08lX.", data->cnt);
  117. furi_string_set(model->status, "FUTURE");
  118. model->future_count = data->cnt;
  119. return false;
  120. }
  121. uint32_t future_gap = get_forward_distance(model->future_count, data->cnt);
  122. if(future_gap > 0 && future_gap <= rolling_flaws_setting_window_future_gap_get(model)) {
  123. FURI_LOG_I(TAG, "Future gap accepted. Gap is %08lX", future_gap);
  124. furi_string_set(model->status, "GAP");
  125. model->future_count = 0xFFFFFFFF;
  126. model->count = data->cnt;
  127. return true;
  128. }
  129. if(future_gap == 0) {
  130. FURI_LOG_I(TAG, "Future gap is zero. Set future value to %08lX.", data->cnt);
  131. furi_string_set(model->status, "FUTURE");
  132. model->future_count = data->cnt;
  133. return false;
  134. }
  135. FURI_LOG_I(
  136. TAG,
  137. "Future gap too large. %08lX > %08lX",
  138. future_gap,
  139. rolling_flaws_setting_window_future_gap_get(model));
  140. furi_string_set(model->status, "BAD GAP");
  141. model->future_count = data->cnt;
  142. return false;
  143. }
  144. FURI_LOG_I(TAG, "Signal must be from the past (non-future).");
  145. furi_string_set(model->status, "PAST");
  146. return false;
  147. }
  148. uint32_t last_decode = 0;
  149. void decode_keeloq(RollingFlawsModel* model, FuriString* buffer, bool sync) {
  150. FURI_LOG_T(TAG, "Decoding KeeLoq 64bit");
  151. uint32_t now = furi_get_tick();
  152. if(now - last_decode < furi_ms_to_ticks(500)) {
  153. FURI_LOG_D(TAG, "Ignoring decode. Too soon.");
  154. last_decode = now;
  155. return;
  156. }
  157. last_decode = now;
  158. KeeLoqData* data = keeloq_data_alloc();
  159. __furi_string_extract_string_until(buffer, 0, "MF:", '\r', data->mf);
  160. __furi_string_extract_string(buffer, 0, "Key:", '\r', model->key);
  161. data->fix = __furi_string_extract_int(buffer, "Fix:0x", ' ', FAILED_TO_PARSE);
  162. data->hop = __furi_string_extract_int(buffer, "Hop:0x", ' ', FAILED_TO_PARSE);
  163. data->sn = __furi_string_extract_int(buffer, "Sn:0x", ' ', FAILED_TO_PARSE);
  164. if(data->sn == FAILED_TO_PARSE) {
  165. FURI_LOG_I(TAG, "Sn:0x not found. Using Fix data.");
  166. data->sn = data->fix & 0x0FFFFFFF;
  167. }
  168. data->btn = __furi_string_extract_int(buffer, "Btn:", '\r', FAILED_TO_PARSE);
  169. data->cnt = __furi_string_extract_int(buffer, "Cnt:", '\r', FAILED_TO_PARSE);
  170. // NOTE: "Enc:" needs to be added to "keeloq.c" subghz_protocol_decoder_keeloq_get_string() method.
  171. data->enc = __furi_string_extract_int(buffer, "Enc:", '\r', FAILED_TO_PARSE);
  172. FURI_LOG_I(
  173. TAG,
  174. "fix: %08lX hop: %08lX sn: %08lX btn: %08lX cnt: %08lX enc:%08lX key:%s mf:%s",
  175. data->fix,
  176. data->hop,
  177. data->sn,
  178. data->btn,
  179. data->cnt,
  180. data->enc,
  181. furi_string_get_cstr(model->key),
  182. furi_string_get_cstr(data->mf));
  183. if(!sync) {
  184. model->opened = is_open(model, data);
  185. if(model->opened) {
  186. model->count = data->cnt;
  187. }
  188. __gui_redraw();
  189. } else {
  190. model->custom_fix = data->fix;
  191. model->count = data->cnt;
  192. model->future_count = 0xFFFFFFFF;
  193. model->opened = false;
  194. rolling_flaws_setting_protocol_custom_mf_set(model, data->mf);
  195. furi_string_set(model->status, "SYNCED");
  196. }
  197. keeloq_data_free(data);
  198. }