weebo_scene_write.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. #include "../weebo_i.h"
  2. #include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h>
  3. #define TAG "SceneWrite"
  4. static uint8_t SLB[] = {0x00, 0x00, 0x0F, 0xE0};
  5. static uint8_t CC[] = {0xf1, 0x10, 0xff, 0xee};
  6. static uint8_t DLB[] = {0x01, 0x00, 0x0f, 0xbd};
  7. static uint8_t CFG0[] = {0x00, 0x00, 0x00, 0x04};
  8. static uint8_t CFG1[] = {0x5f, 0x00, 0x00, 0x00};
  9. static uint8_t PACKRFUI[] = {0x80, 0x80, 0x00, 0x00};
  10. enum NTAG215Pages {
  11. staticLockBits = 2,
  12. capabilityContainer = 3,
  13. userMemoryFirst = 4,
  14. userMemoryLast = 129,
  15. dynamicLockBits = 130,
  16. cfg0 = 131,
  17. cfg1 = 132,
  18. pwd = 133,
  19. pack = 134,
  20. total = 135
  21. };
  22. NfcCommand weebo_scene_write_poller_callback(NfcGenericEvent event, void* context) {
  23. furi_assert(event.protocol == NfcProtocolMfUltralight);
  24. Weebo* weebo = context;
  25. NfcCommand ret = NfcCommandContinue;
  26. const MfUltralightPollerEvent* mf_ultralight_event = event.event_data;
  27. MfUltralightPoller* poller = event.instance;
  28. if(mf_ultralight_event->type == MfUltralightPollerEventTypeRequestMode) {
  29. // no-op
  30. } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) {
  31. mf_ultralight_event->data->auth_context.skip_auth = true;
  32. } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) {
  33. nfc_device_set_data(
  34. weebo->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(weebo->poller));
  35. const MfUltralightData* data =
  36. nfc_device_get_data(weebo->nfc_device, NfcProtocolMfUltralight);
  37. if(!mf_ultralight_is_all_data_read(data)) {
  38. view_dispatcher_send_custom_event(weebo->view_dispatcher, WeeboCustomEventWrongCard);
  39. ret = NfcCommandStop;
  40. return ret;
  41. }
  42. if(data->type != MfUltralightTypeNTAG215) {
  43. view_dispatcher_send_custom_event(weebo->view_dispatcher, WeeboCustomEventWrongCard);
  44. ret = NfcCommandStop;
  45. return ret;
  46. }
  47. view_dispatcher_send_custom_event(weebo->view_dispatcher, WeeboCustomEventCardDetected);
  48. uint8_t PWD[4];
  49. weebo_calculate_pwd(data->iso14443_3a_data->uid, PWD);
  50. for(size_t p = 0; p < 2; p++) {
  51. for(size_t i = 0; i < MF_ULTRALIGHT_PAGE_SIZE; i++) {
  52. weebo->figure[NFC3D_UID_OFFSET + p * MF_ULTRALIGHT_PAGE_SIZE + i] =
  53. data->page[p].data[i];
  54. }
  55. }
  56. uint8_t modified[NTAG215_SIZE];
  57. nfc3d_amiibo_pack(&weebo->amiiboKeys, weebo->figure, modified);
  58. MfUltralightError error;
  59. MfUltralightPage page;
  60. // You might think it odd that I'm doing this writing "by hand" and not using the flipper SDK, but this is for two reasons:
  61. // 1. The flipper SDK doesn't write beyond user memory
  62. // 2. I order the writes from least destructive to most destructive, so that if something goes wrong, recovery _might_ be possible
  63. do {
  64. // user data
  65. FURI_LOG_D(TAG, "Writing user data");
  66. view_dispatcher_send_custom_event(
  67. weebo->view_dispatcher, WeeboCustomEventWritingUserData);
  68. for(size_t i = userMemoryFirst; i <= userMemoryLast; i++) {
  69. memcpy(
  70. page.data, modified + (i * MF_ULTRALIGHT_PAGE_SIZE), MF_ULTRALIGHT_PAGE_SIZE);
  71. error = mf_ultralight_poller_write_page(poller, i, &page);
  72. if(error != MfUltralightErrorNone) {
  73. FURI_LOG_E(TAG, "Error writing page %zu: %d", i, error);
  74. view_dispatcher_send_custom_event(
  75. weebo->view_dispatcher, WeeboCustomEventWriteFailure);
  76. ret = NfcCommandStop;
  77. break;
  78. }
  79. }
  80. if(error != MfUltralightErrorNone) {
  81. ret = NfcCommandStop;
  82. break;
  83. }
  84. view_dispatcher_send_custom_event(
  85. weebo->view_dispatcher, WeeboCustomEventWritingConfigData);
  86. FURI_LOG_D(TAG, "Writing config");
  87. // pwd
  88. memcpy(page.data, PWD, sizeof(PWD));
  89. error = mf_ultralight_poller_write_page(poller, pwd, &page);
  90. if(error != MfUltralightErrorNone) {
  91. view_dispatcher_send_custom_event(
  92. weebo->view_dispatcher, WeeboCustomEventWriteFailure);
  93. FURI_LOG_E(TAG, "Error writing PWD: %d", error);
  94. ret = NfcCommandStop;
  95. break;
  96. }
  97. // pack
  98. memcpy(page.data, PACKRFUI, sizeof(PACKRFUI));
  99. error = mf_ultralight_poller_write_page(poller, pack, &page);
  100. if(error != MfUltralightErrorNone) {
  101. view_dispatcher_send_custom_event(
  102. weebo->view_dispatcher, WeeboCustomEventWriteFailure);
  103. FURI_LOG_E(TAG, "Error writing PACKRFUI: %d", error);
  104. ret = NfcCommandStop;
  105. break;
  106. }
  107. // capability container
  108. memcpy(page.data, CC, sizeof(CC));
  109. error = mf_ultralight_poller_write_page(poller, capabilityContainer, &page);
  110. if(error != MfUltralightErrorNone) {
  111. view_dispatcher_send_custom_event(
  112. weebo->view_dispatcher, WeeboCustomEventWriteFailure);
  113. FURI_LOG_E(TAG, "Error writing CC: %d", error);
  114. ret = NfcCommandStop;
  115. break;
  116. }
  117. // cfg0
  118. memcpy(page.data, CFG0, sizeof(CFG0));
  119. error = mf_ultralight_poller_write_page(poller, cfg0, &page);
  120. if(error != MfUltralightErrorNone) {
  121. view_dispatcher_send_custom_event(
  122. weebo->view_dispatcher, WeeboCustomEventWriteFailure);
  123. FURI_LOG_E(TAG, "Error writing CFG0: %d", error);
  124. ret = NfcCommandStop;
  125. break;
  126. }
  127. // cfg1
  128. memcpy(page.data, CFG1, sizeof(CFG1));
  129. error = mf_ultralight_poller_write_page(poller, cfg1, &page);
  130. if(error != MfUltralightErrorNone) {
  131. view_dispatcher_send_custom_event(
  132. weebo->view_dispatcher, WeeboCustomEventWriteFailure);
  133. FURI_LOG_E(TAG, "Error writing CFG1: %d", error);
  134. ret = NfcCommandStop;
  135. break;
  136. }
  137. // dynamic lock bits
  138. memcpy(page.data, DLB, sizeof(DLB));
  139. error = mf_ultralight_poller_write_page(poller, dynamicLockBits, &page);
  140. if(error != MfUltralightErrorNone) {
  141. view_dispatcher_send_custom_event(
  142. weebo->view_dispatcher, WeeboCustomEventWriteFailure);
  143. FURI_LOG_E(TAG, "Error writing DLB: %d", error);
  144. ret = NfcCommandStop;
  145. break;
  146. }
  147. // static lock bits
  148. memcpy(page.data, SLB, sizeof(SLB));
  149. error = mf_ultralight_poller_write_page(poller, staticLockBits, &page);
  150. if(error != MfUltralightErrorNone) {
  151. view_dispatcher_send_custom_event(
  152. weebo->view_dispatcher, WeeboCustomEventWriteFailure);
  153. FURI_LOG_E(TAG, "Error writing SLB: %d", error);
  154. ret = NfcCommandStop;
  155. break;
  156. }
  157. } while(false);
  158. ret = NfcCommandStop;
  159. view_dispatcher_send_custom_event(weebo->view_dispatcher, WeeboCustomEventWriteSuccess);
  160. } else {
  161. FURI_LOG_D(TAG, "Unhandled event type: %d", mf_ultralight_event->type);
  162. }
  163. return ret;
  164. }
  165. void weebo_scene_write_on_enter(void* context) {
  166. Weebo* weebo = context;
  167. Popup* popup = weebo->popup;
  168. popup_set_header(popup, "Present NTAG215", 58, 28, AlignCenter, AlignCenter);
  169. weebo->poller = nfc_poller_alloc(weebo->nfc, NfcProtocolMfUltralight);
  170. nfc_poller_start(weebo->poller, weebo_scene_write_poller_callback, weebo);
  171. weebo_blink_start(weebo);
  172. view_dispatcher_switch_to_view(weebo->view_dispatcher, WeeboViewPopup);
  173. }
  174. bool weebo_scene_write_on_event(void* context, SceneManagerEvent event) {
  175. Weebo* weebo = context;
  176. bool consumed = false;
  177. if(event.type == SceneManagerEventTypeCustom) {
  178. scene_manager_set_scene_state(weebo->scene_manager, WeeboSceneWrite, event.event);
  179. if(event.event == WeeboCustomEventCardDetected) {
  180. popup_set_text(weebo->popup, "Card detected", 64, 36, AlignCenter, AlignTop);
  181. consumed = true;
  182. } else if(event.event == WeeboCustomEventWritingUserData) {
  183. popup_set_text(weebo->popup, "Writing user data", 64, 36, AlignCenter, AlignTop);
  184. consumed = true;
  185. } else if(event.event == WeeboCustomEventWritingConfigData) {
  186. popup_set_text(weebo->popup, "Writing config data", 64, 36, AlignCenter, AlignTop);
  187. consumed = true;
  188. } else if(event.event == WeeboCustomEventWriteSuccess) {
  189. popup_set_text(weebo->popup, "Write success", 64, 36, AlignCenter, AlignTop);
  190. consumed = true;
  191. scene_manager_next_scene(weebo->scene_manager, WeeboSceneWriteCardSuccess);
  192. } else if(event.event == WeeboCustomEventWrongCard) {
  193. popup_set_text(weebo->popup, "Wrong card", 64, 36, AlignCenter, AlignTop);
  194. consumed = true;
  195. }
  196. }
  197. return consumed;
  198. }
  199. void weebo_scene_write_on_exit(void* context) {
  200. Weebo* weebo = context;
  201. if(weebo->poller) {
  202. nfc_poller_stop(weebo->poller);
  203. nfc_poller_free(weebo->poller);
  204. weebo->poller = NULL;
  205. }
  206. popup_reset(weebo->popup);
  207. weebo_blink_stop(weebo);
  208. }