wiegand.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #include "interface.h"
  2. #include <lib/bit_lib/bit_lib.h>
  3. #include <flipper_application/flipper_application.h>
  4. /*
  5. * Huge thanks to the proxmark codebase:
  6. * https://github.com/RfidResearchGroup/proxmark3/blob/master/client/src/wiegand_formats.c
  7. */
  8. // Structure for packed wiegand messages
  9. // Always align lowest value (last transmitted) bit to ordinal position 0 (lowest valued bit bottom)
  10. typedef struct {
  11. uint8_t Length; // Number of encoded bits in wiegand message (excluding headers and preamble)
  12. uint32_t Top; // Bits in x<<64 positions
  13. uint32_t Mid; // Bits in x<<32 positions
  14. uint32_t Bot; // Lowest ordinal positions
  15. } wiegand_message_t;
  16. static inline uint8_t oddparity32(uint32_t x) {
  17. return bit_lib_test_parity_32(x, BitLibParityOdd);
  18. }
  19. static inline uint8_t evenparity32(uint32_t x) {
  20. return bit_lib_test_parity_32(x, BitLibParityEven);
  21. }
  22. uint8_t get_bit_by_position(wiegand_message_t* data, uint8_t pos) {
  23. if(pos >= data->Length) return false;
  24. pos = (data->Length - pos) -
  25. 1; // invert ordering; Indexing goes from 0 to 1. Subtract 1 for weight of bit.
  26. uint8_t result = 0;
  27. if(pos > 95)
  28. result = 0;
  29. else if(pos > 63)
  30. result = (data->Top >> (pos - 64)) & 1;
  31. else if(pos > 31)
  32. result = (data->Mid >> (pos - 32)) & 1;
  33. else
  34. result = (data->Bot >> pos) & 1;
  35. return result;
  36. }
  37. uint64_t get_linear_field(wiegand_message_t* data, uint8_t firstBit, uint8_t length) {
  38. uint64_t result = 0;
  39. for(uint8_t i = 0; i < length; i++) {
  40. result = (result << 1) | get_bit_by_position(data, firstBit + i);
  41. }
  42. return result;
  43. }
  44. static int wiegand_C1k35s_parse(uint8_t bit_length, uint64_t bits, FuriString* description) {
  45. wiegand_message_t value;
  46. value.Length = bit_length;
  47. value.Mid = bits >> 32;
  48. value.Bot = bits;
  49. wiegand_message_t* packed = &value;
  50. if(packed->Length != 35) return false; // Wrong length? Stop here.
  51. uint32_t cn = (packed->Bot >> 1) & 0x000FFFFF;
  52. uint32_t fc = ((packed->Mid & 1) << 11) | ((packed->Bot >> 21));
  53. bool valid = (evenparity32((packed->Mid & 0x1) ^ (packed->Bot & 0xB6DB6DB6)) ==
  54. ((packed->Mid >> 1) & 1)) &&
  55. (oddparity32((packed->Mid & 0x3) ^ (packed->Bot & 0x6DB6DB6C)) ==
  56. ((packed->Bot >> 0) & 1)) &&
  57. (oddparity32((packed->Mid & 0x3) ^ (packed->Bot & 0xFFFFFFFF)) ==
  58. ((packed->Mid >> 2) & 1));
  59. if(valid) {
  60. furi_string_cat_printf(description, "C1k35s\nFC: %ld CN: %ld\n", fc, cn);
  61. return 1;
  62. } else {
  63. FURI_LOG_D(PLUGIN_APP_ID, "C1k35s invalid");
  64. }
  65. return 0;
  66. }
  67. static int wiegand_h10301_parse(uint8_t bit_length, uint64_t bits, FuriString* description) {
  68. if(bit_length != 26) {
  69. return 0;
  70. }
  71. //E XXXX XXXX XXXX
  72. //XXXX XXXX XXXX O
  73. uint32_t eBitMask = 0x02000000;
  74. uint32_t oBitMask = 0x00000001;
  75. uint32_t eParityMask = 0x01FFE000;
  76. uint32_t oParityMask = 0x00001FFE;
  77. uint8_t eBit = (eBitMask & bits) >> 25;
  78. uint8_t oBit = (oBitMask & bits) >> 0;
  79. bool eParity = bit_lib_test_parity_32((bits & eParityMask) >> 13, BitLibParityEven) ==
  80. (eBit == 1);
  81. bool oParity = bit_lib_test_parity_32((bits & oParityMask) >> 1, BitLibParityOdd) ==
  82. (oBit == 1);
  83. FURI_LOG_D(
  84. PLUGIN_APP_ID,
  85. "eBit: %d, oBit: %d, eParity: %d, oParity: %d",
  86. eBit,
  87. oBit,
  88. eParity,
  89. oParity);
  90. if(eParity && oParity) {
  91. uint32_t cnMask = 0x1FFFE;
  92. uint16_t cn = ((bits & cnMask) >> 1);
  93. uint32_t fcMask = 0x1FE0000;
  94. uint16_t fc = ((bits & fcMask) >> 17);
  95. furi_string_cat_printf(description, "H10301\nFC: %d CN: %d\n", fc, cn);
  96. return 1;
  97. } else {
  98. FURI_LOG_D(PLUGIN_APP_ID, "H10301 invalid");
  99. }
  100. return 0;
  101. }
  102. static int wiegand_H10304_parse(uint8_t bit_length, uint64_t bits, FuriString* description) {
  103. wiegand_message_t value;
  104. value.Length = bit_length;
  105. value.Mid = bits >> 32;
  106. value.Bot = bits;
  107. wiegand_message_t* packed = &value;
  108. if(packed->Length != 37) return false; // Wrong length? Stop here.
  109. uint32_t fc = get_linear_field(packed, 1, 16);
  110. uint32_t cn = get_linear_field(packed, 17, 19);
  111. bool valid =
  112. (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 18))) &&
  113. (get_bit_by_position(packed, 36) == oddparity32(get_linear_field(packed, 18, 18)));
  114. if(valid) {
  115. furi_string_cat_printf(description, "H10304\nFC: %ld CN: %ld\n", fc, cn);
  116. return 1;
  117. } else {
  118. FURI_LOG_D(PLUGIN_APP_ID, "H10304 invalid");
  119. }
  120. return 0;
  121. }
  122. static int wiegand_H10302_parse(uint8_t bit_length, uint64_t bits, FuriString* description) {
  123. wiegand_message_t value;
  124. value.Length = bit_length;
  125. value.Mid = bits >> 32;
  126. value.Bot = bits;
  127. wiegand_message_t* packed = &value;
  128. if(packed->Length != 37) return false; // Wrong length? Stop here.
  129. uint64_t cn = get_linear_field(packed, 1, 35);
  130. bool valid =
  131. (get_bit_by_position(packed, 0) == evenparity32(get_linear_field(packed, 1, 18))) &&
  132. (get_bit_by_position(packed, 36) == oddparity32(get_linear_field(packed, 18, 18)));
  133. if(valid) {
  134. furi_string_cat_printf(description, "H10302\nCN: %lld\n", cn);
  135. return 1;
  136. } else {
  137. FURI_LOG_D(PLUGIN_APP_ID, "H10302 invalid");
  138. }
  139. return 0;
  140. }
  141. static int wiegand_format_count(uint8_t bit_length, uint64_t bits) {
  142. UNUSED(bit_length);
  143. UNUSED(bits);
  144. int count = 0;
  145. FuriString* ignore = furi_string_alloc();
  146. // NOTE: Always update the `total` and add to the wiegand_format_description function
  147. // TODO: Make this into a function pointer array
  148. count += wiegand_h10301_parse(bit_length, bits, ignore);
  149. count += wiegand_C1k35s_parse(bit_length, bits, ignore);
  150. count += wiegand_H10302_parse(bit_length, bits, ignore);
  151. count += wiegand_H10304_parse(bit_length, bits, ignore);
  152. int total = 4;
  153. furi_string_free(ignore);
  154. FURI_LOG_I(PLUGIN_APP_ID, "count: %i/%i", count, total);
  155. return count;
  156. }
  157. static void wiegand_format_description(
  158. uint8_t bit_length,
  159. uint64_t bits,
  160. size_t index,
  161. FuriString* description) {
  162. FURI_LOG_I(PLUGIN_APP_ID, "description %d", index);
  163. // Turns out I did this wrong and trying to use the index means the results get repeated. Instead, just return the results for index == 0
  164. if(index != 0) {
  165. return;
  166. }
  167. wiegand_h10301_parse(bit_length, bits, description);
  168. wiegand_C1k35s_parse(bit_length, bits, description);
  169. wiegand_H10302_parse(bit_length, bits, description);
  170. wiegand_H10304_parse(bit_length, bits, description);
  171. }
  172. /* Actual implementation of app<>plugin interface */
  173. static const PluginWiegand plugin_wiegand = {
  174. .name = "Plugin Wiegand",
  175. .count = &wiegand_format_count,
  176. .description = &wiegand_format_description,
  177. };
  178. /* Plugin descriptor to comply with basic plugin specification */
  179. static const FlipperAppPluginDescriptor plugin_wiegand_descriptor = {
  180. .appid = PLUGIN_APP_ID,
  181. .ep_api_version = PLUGIN_API_VERSION,
  182. .entry_point = &plugin_wiegand,
  183. };
  184. /* Plugin entry point - must return a pointer to const descriptor */
  185. const FlipperAppPluginDescriptor* plugin_wiegand_ep(void) {
  186. return &plugin_wiegand_descriptor;
  187. }