fix one too small
[RRG-proxmark3.git] / client / src / cmdhfjooki.c
blob03ce6cf77e51bce2620c3af89f7b81455a09557c
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // High frequency Jooki commands
17 //-----------------------------------------------------------------------------
18 #include "cmdhfjooki.h"
19 #include <ctype.h>
20 #include <string.h> // memset
21 #include "commonutil.h" // ARRAYLEN
22 #include "ui.h" // PrintAndLog
23 #include "cmdparser.h"
24 #include "generator.h"
25 #include "base64.h"
26 #include "nfc/ndef.h" // print decode ndef
27 #include "mifare/mifarehost.h" // mfemlsetmem_xt
28 #include "cliparser.h"
29 #include "cmdhfmfu.h"
30 #include "cmdmain.h"
31 #include "fileutils.h" // convert_mfu..
33 static int CmdHelp(const char *Cmd);
35 typedef struct {
36 uint8_t typeid;
37 uint8_t figureid;
38 const char figdesc[40];
39 const char typedesc[12];
40 } PACKED jooki_figure_t;
42 typedef struct {
43 uint8_t uid[7];
44 const char b64[17];
45 uint8_t typeid;
46 uint8_t figureid;
47 } PACKED jooki_test_t;
49 // sample set for selftest.
50 static jooki_test_t jooks[] = {
51 { {0x04, 0xDA, 0xB7, 0x6A, 0xE7, 0x4C, 0x80}, "ruxow8lnn88uyeX+", 0x01, 0x00},
52 { {0x04, 0xf0, 0x22, 0xc2, 0x33, 0x5e, 0x80}, "\0", 0x01, 0x00},
53 { {0x04, 0x8C, 0xEC, 0xDA, 0xF0, 0x4A, 0x80}, "ONrsVf7jX6IaSNV6", 0x01, 0x01},
54 { {0x04, 0x92, 0xA7, 0x6A, 0xE7, 0x4C, 0x81}, "Hjjpcx/mZwuveTF+", 0x01, 0x02},
55 { {0x04, 0xD0, 0xB0, 0x3A, 0xD3, 0x63, 0x80}, "\0", 0x01, 0x02},
56 { {0x04, 0x96, 0x42, 0xDA, 0xF0, 0x4A, 0x80}, "vEWy0WO9wZNEzEok", 0x01, 0x03},
57 { {0x04, 0x33, 0xb5, 0x62, 0x39, 0x4d, 0x80}, "\0", 0x01, 0x03},
58 { {0x04, 0x17, 0xB7, 0x3A, 0xD3, 0x63, 0x81}, "f0axEma+g2WnLGAm", 0x01, 0x05},
59 { {0x04, 0x84, 0x27, 0x6A, 0xE7, 0x4C, 0x80}, "VZB/OLBwOiM5Mpnp", 0x01, 0x05},
60 { {0x04, 0x28, 0xF4, 0xDA, 0xF0, 0x4A, 0x81}, "7WzlgEzqLgwTnWNy", 0x01, 0x05},
63 static jooki_figure_t jooks_figures[] = {
64 {0x01, 0x00, "Dragon", "Figurine"},
65 {0x01, 0x01, "Fox", "Figurine"},
66 {0x01, 0x02, "Ghost", "Figurine"},
67 {0x01, 0x03, "Knight", "Figurine"},
68 {0x01, 0x04, "ThankYou", "Figurine"},
69 {0x01, 0x05, "Whale", "Figurine"},
70 {0x01, 0x06, "Black Dragon", "Figurine"},
71 {0x01, 0x07, "Black Fox", "Figurine"},
72 {0x01, 0x08, "Black Knight", "Figurine"},
73 {0x01, 0x09, "Black Whale", "Figurine"},
74 {0x01, 0x0A, "White Dragon", "Figurine"},
75 {0x01, 0x0B, "White Fox", "Figurine"},
76 {0x01, 0x0C, "White Knight", "Figurine"},
77 {0x01, 0x0D, "White Whale", "Figurine"},
79 {0x02, 0x00, "Generic Flat", "Stone"},
81 {0x03, 0x00, "record", "Sys"},
82 {0x03, 0x01, "factory_mode_on", "Sys"},
83 {0x03, 0x02, "factory_mode_off", "Sys"},
84 {0x03, 0x03, "airplane_mode_on", "Sys"},
85 {0x03, 0x04, "airplane_mode_off", "Sys"},
86 {0x03, 0x05, "toy_safe_on", "Sys"},
87 {0x03, 0x06, "toy_safe_off", "Sys"},
88 {0x03, 0x07, "wifi_on", "Sys"},
89 {0x03, 0x08, "wifi_off", "Sys"},
90 {0x03, 0x09, "bt_on", "Sys"},
91 {0x03, 0x0A, "bt_off", "Sys"},
92 {0x03, 0x0B, "production_finished", "Sys"},
94 {0x04, 0x00, "test.0", "Test"},
95 {0x04, 0x01, "test.1", "Test"},
96 {0x04, 0x02, "test.2", "Test"},
97 {0x04, 0x03, "test.3", "Test"},
98 {0x04, 0x04, "test.4", "Test"},
99 {0x04, 0x05, "test.5", "Test"},
100 {0x04, 0x06, "test.6", "Test"},
101 {0x04, 0x07, "test.7", "Test"},
102 {0x04, 0x08, "test.8", "Test"},
103 {0x04, 0x09, "test.9", "Test"},
104 {0x04, 0x10, "test.10", "Test"},
105 {0x04, 0x11, "test.11", "Test"},
106 {0x04, 0x12, "test.12", "Test"},
107 {0x04, 0x13, "test.13", "Test"},
108 {0x04, 0x14, "test.14", "Test"},
109 {0x04, 0x15, "test.15", "Test"},
110 {0x04, 0x16, "test.16", "Test"},
111 {0x04, 0x17, "test.17", "Test"},
112 {0x04, 0x18, "test.18", "Test"},
113 {0x04, 0x19, "test.19", "Test"},
114 {0x04, 0x20, "test.20", "Test"},
117 static int jooki_lookup(uint8_t tid, uint8_t fid) {
118 for (int i = 0; i < ARRAYLEN(jooks_figures); i++) {
119 jooki_figure_t tmp = jooks_figures[i];
120 if (tmp.typeid == tid && tmp.figureid == fid) {
121 return i;
124 return -1;
127 //static const uint8_t jooki_secret[] = {0x20, 0x20, 0x20, 0x6D, 0x24, 0x0B, 0xEB, 0x94, 0x2C, 0x80, 0x45, 0x16};
128 static const uint8_t nfc_secret[] = { 0x03, 0x9c, 0x25, 0x6f, 0xb9, 0x2e, 0xe8, 0x08, 0x09, 0x83, 0xd9, 0x33, 0x56};
130 #define JOOKI_UID_LEN 7
131 #define JOOKI_IV_LEN 3
132 #define JOOKI_B64_LEN (16 + 1)
133 #define JOOKI_PLAIN_LEN 12
135 static int jooki_encode(uint8_t *iv, uint8_t tid, uint8_t fid, uint8_t *uid, uint8_t *out) {
136 if (out == NULL) {
137 PrintAndLogEx(ERR, "(encode jooki) base64ndef param is NULL");
138 return PM3_EINVARG;
141 out[0] = 0x00;
142 if (iv == NULL || uid == NULL) {
143 PrintAndLogEx(ERR, "(encode jooki) iv or uid param is NULL");
144 return PM3_EINVARG;
147 const uint8_t d[JOOKI_PLAIN_LEN] = {iv[0], iv[1], iv[2], tid, fid, uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]};
148 uint8_t enc[JOOKI_PLAIN_LEN] = {0};
149 for (uint8_t i = 0; i < JOOKI_PLAIN_LEN; i++) {
151 if (i < 3)
152 enc[i] = d[i] ^ nfc_secret[i];
153 else
154 enc[i] = d[i] ^ nfc_secret[i] ^ d[i % 3];
157 PrintAndLogEx(DEBUG, "encoded result.... %s", sprint_hex(enc, sizeof(enc)));
159 size_t b64len = 0;
160 uint8_t b64[20];
161 memset(b64, 0, 20);
162 mbedtls_base64_encode(b64, sizeof(b64), &b64len, (const unsigned char *)enc, sizeof(enc));
163 memcpy(out, b64, b64len);
164 return PM3_SUCCESS;
167 static int jooki_decode(uint8_t *b64, uint8_t *result) {
168 uint8_t ndef[JOOKI_PLAIN_LEN] = {0};
169 size_t outputlen = 0;
170 mbedtls_base64_decode(ndef, sizeof(ndef), &outputlen, (const unsigned char *)b64, 16);
172 PrintAndLogEx(DEBUG, "(decode_jooki) raw encoded... " _GREEN_("%s"), sprint_hex(ndef, sizeof(ndef)));
174 for (uint8_t i = 0; i < JOOKI_PLAIN_LEN; i++) {
175 if (i < 3)
176 result[i] = ndef[i] ^ nfc_secret[i];
177 else
178 result[i] = ndef[i] ^ nfc_secret[i] ^ ndef[i % 3] ^ nfc_secret[i % 3];
180 PrintAndLogEx(DEBUG, "(decode_jooki) plain......... %s", sprint_hex(result, sizeof(ndef)));
181 return PM3_SUCCESS;
184 static int jooki_create_ndef(uint8_t *b64ndef, uint8_t *ndefrecord) {
185 // sample of url: https://s.jooki.rocks/s/?s=ONrsVf7jX6IaSNV6
186 if (ndefrecord == NULL) {
187 PrintAndLogEx(ERR, "(jooki_create_ndef) ndefrecord param is NULL");
188 return PM3_EINVARG;
190 memcpy(ndefrecord,
191 "\x01\x03\xa0\x0c\x34\x03\x29\xd1"
192 "\x01\x25\x55\x04\x73\x2e\x6a\x6f"
193 "\x6f\x6b\x69\x2e\x72\x6f\x63\x6b"
194 "\x73\x2f\x73\x2f\x3f\x73\x3d", 31);
195 memcpy(ndefrecord + 31, b64ndef, 16);
196 memcpy(ndefrecord + 47, "\x0a\xFE\x00\x00\x00", 5);
197 return PM3_SUCCESS;
200 static void jooki_printEx(uint8_t *b64, uint8_t *iv, uint8_t tid, uint8_t fid, uint8_t *uid, bool verbose) {
201 int idx = jooki_lookup(tid, fid);
203 PrintAndLogEx(INFO, "Encoded URL.. %s ( " _YELLOW_("%s") " )", sprint_hex(b64, 12), b64);
204 PrintAndLogEx(INFO, "Figurine..... %02x %02x - " _GREEN_("%s, %s")
205 , tid
206 , fid
207 , (idx != -1) ? jooks_figures[idx].typedesc : "n/a"
208 , (idx != -1) ? jooks_figures[idx].figdesc : "n/a"
210 PrintAndLogEx(INFO, "iv........... %s", sprint_hex(iv, JOOKI_IV_LEN));
211 PrintAndLogEx(INFO, "uid.......... %s", sprint_hex(uid, JOOKI_UID_LEN));
213 uint8_t ndefmsg[52] = {0};
214 jooki_create_ndef(b64, ndefmsg);
215 PrintAndLogEx(INFO, "NDEF raw..... %s", sprint_hex_inrow(ndefmsg, sizeof(ndefmsg)));
217 if (verbose) {
218 int res = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg), verbose);
219 if (res != PM3_SUCCESS) {
220 NDEFDecodeAndPrint(ndefmsg, sizeof(ndefmsg), verbose);
225 static void jooki_print(uint8_t *b64, uint8_t *result, bool verbose) {
226 if (b64 == NULL || result == NULL)
227 return;
229 uint8_t iv[JOOKI_IV_LEN] = {0};
230 uint8_t uid[JOOKI_UID_LEN] = {0};
231 memcpy(iv, result, JOOKI_IV_LEN);
232 uint8_t tid = result[3];
233 uint8_t fid = result[4];
234 memcpy(uid, result + 5, JOOKI_UID_LEN);
236 jooki_printEx(b64, iv, tid, fid, uid, verbose);
239 static int jooki_selftest(void) {
241 PrintAndLogEx(INFO, "======== " _CYAN_("self test") " ===========================================");
242 for (int i = 0; i < ARRAYLEN(jooks); i++) {
243 if (strlen(jooks[i].b64) == 0)
244 continue;
246 uint8_t iv[JOOKI_IV_LEN] = {0};
247 uint8_t uid[JOOKI_UID_LEN] = {0};
248 uint8_t result[JOOKI_PLAIN_LEN] = {0};
249 jooki_decode((uint8_t *)jooks[i].b64, result);
251 memcpy(iv, result, JOOKI_IV_LEN);
252 uint8_t tid = result[3];
253 uint8_t fid = result[4];
254 memcpy(uid, result + 5, sizeof(uid));
256 bool tid_ok = (tid == jooks[i].typeid);
257 bool fid_ok = (fid == jooks[i].figureid);
258 bool uid_ok = (memcmp(uid, jooks[i].uid, sizeof(uid)) == 0);
260 int idx = jooki_lookup(tid, fid);
262 PrintAndLogEx(INFO, "Encoded URL.. %s ( %s )", sprint_hex((const uint8_t *)jooks[i].b64, 12), jooks[i].b64);
263 PrintAndLogEx(INFO, "Type......... %02x - " _GREEN_("%s") " ( %s )", tid, (idx != -1) ? jooks_figures[idx].typedesc : "n/a", tid_ok ? _GREEN_("ok") : _RED_("fail"));
264 PrintAndLogEx(INFO, "Figurine..... %02x - " _GREEN_("%s") " ( %s )", fid, (idx != -1) ? jooks_figures[idx].figdesc : "n/a", fid_ok ? _GREEN_("ok") : _RED_("fail"));
265 PrintAndLogEx(INFO, "iv........... %s", sprint_hex(iv, sizeof(iv)));
266 PrintAndLogEx(INFO, "uid.......... %s ( %s )", sprint_hex(uid, sizeof(uid)), uid_ok ? _GREEN_("ok") : _RED_("fail"));
268 uint8_t b64[JOOKI_B64_LEN] = {0};
269 memset(b64, 0, sizeof(b64));
270 jooki_encode(iv, tid, fid, uid, b64);
272 uint8_t ndefmsg[52] = {0};
273 jooki_create_ndef(b64, ndefmsg);
274 PrintAndLogEx(INFO, "NDEF raw .... %s", sprint_hex(ndefmsg, sizeof(ndefmsg)));
276 int status = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg), true);
277 if (status != PM3_SUCCESS) {
278 status = NDEFDecodeAndPrint(ndefmsg, sizeof(ndefmsg), true);
280 PrintAndLogEx(INFO, "==================================================================");
282 return PM3_SUCCESS;
285 static int CmdHF14AJookiEncode(const char *Cmd) {
286 CLIParserContext *ctx;
287 CLIParserInit(&ctx, "hf jooki encode",
288 "Encode a Jooki token to base64 NDEF URI format",
289 "hf jooki encode --test --> self tests\n"
290 "hf jooki encode -r --dragon --> read uid from tag and use for encoding\n"
291 "hf jooki encode --uid 04010203040506 --dragon\n"
292 "hf jooki encode --uid 04010203040506 --tid 1 --fid 1"
295 void *argtable[] = {
296 arg_param_begin,
297 arg_str0("u", "uid", "<hex>", "uid bytes"),
298 arg_lit0("r", NULL, "read uid from tag instead"),
299 arg_lit0(NULL, "test", "self test"),
300 arg_lit0("v", "verbose", "verbose output"),
301 arg_lit0(NULL, "dragon", "figurine type"),
302 arg_lit0(NULL, "fox", "figurine type"),
303 arg_lit0(NULL, "ghost", "figurine type"),
304 arg_lit0(NULL, "knight", "figurine type"),
305 arg_lit0(NULL, "whale", "figurine type"),
306 arg_lit0(NULL, "blackdragon", "figurine type"),
307 arg_lit0(NULL, "blackfox", "figurine type"),
308 arg_lit0(NULL, "blackknight", "figurine type"),
309 arg_lit0(NULL, "blackwhale", "figurine type"),
310 arg_lit0(NULL, "whitedragon", "figurine type"),
311 arg_lit0(NULL, "whitefox", "figurine type"),
312 arg_lit0(NULL, "whiteknight", "figurine type"),
313 arg_lit0(NULL, "whitewhale", "figurine type"),
314 arg_u64_0(NULL, "tid", "<dec>", "figurine type id"),
315 arg_u64_0(NULL, "fid", "<dec>", "figurine id"),
316 arg_param_end
318 CLIExecWithReturn(ctx, Cmd, argtable, false);
319 int ulen = 0;
320 uint8_t uid[JOOKI_UID_LEN] = {0x00};
321 memset(uid, 0x0, sizeof(uid));
322 int res = CLIParamHexToBuf(arg_get_str(ctx, 1), uid, sizeof(uid), &ulen);
323 if (res) {
324 CLIParserFree(ctx);
325 return PM3_EINVARG;
328 bool use_tag = arg_get_lit(ctx, 2);
329 bool selftest = arg_get_lit(ctx, 3);
330 bool verbose = arg_get_lit(ctx, 4);
331 bool t0 = arg_get_lit(ctx, 5);
332 bool t1 = arg_get_lit(ctx, 6);
333 bool t2 = arg_get_lit(ctx, 7);
334 bool t3 = arg_get_lit(ctx, 8);
335 bool t5 = arg_get_lit(ctx, 9);
336 bool t6 = arg_get_lit(ctx, 10);
337 bool t7 = arg_get_lit(ctx, 11);
338 bool t8 = arg_get_lit(ctx, 12);
339 bool t9 = arg_get_lit(ctx, 13);
340 bool ta = arg_get_lit(ctx, 14);
341 bool tb = arg_get_lit(ctx, 15);
342 bool tc = arg_get_lit(ctx, 16);
343 bool td = arg_get_lit(ctx, 17);
345 uint8_t ftid = arg_get_u32_def(ctx, 18, 0);
346 uint8_t ffid = arg_get_u32_def(ctx, 19, 0);
348 bool figure_abbr = true;
350 CLIParserFree(ctx);
352 if (selftest) {
353 return jooki_selftest();
356 uint8_t tid, fid;
358 if (ftid || ffid) {
359 figure_abbr = false;
362 if (ftid > 0x04 || ffid > 0x20) {
363 PrintAndLogEx(ERR, "Use a valid Figure Type ID and Figure ID");
364 return PM3_EINVARG;
367 uint8_t figure_abbr_val = t0 + t1 + t2 + t3 + t5 + t6 + t7 + t8 + t9 + ta + tb + tc + td;
369 if (figure_abbr_val > 1) {
370 PrintAndLogEx(ERR, "Select one tag type or use figurine type id and figurine id");
371 return PM3_EINVARG;
374 if (figure_abbr_val == 1 && !figure_abbr) {
375 PrintAndLogEx(ERR, "Use either --tid and --fid or one of the figurine types");
376 return PM3_EINVARG;
379 if (figure_abbr) {
380 tid = 0x01;
381 } else {
382 tid = ftid;
384 fid = ffid;
386 if (t1)
387 fid = 0x01;
388 if (t2)
389 fid = 0x02;
390 if (t3)
391 fid = 0x03;
392 if (t5)
393 fid = 0x05;
394 if (t6)
395 fid = 0x06;
396 if (t7)
397 fid = 0x07;
398 if (t8)
399 fid = 0x08;
400 if (t9)
401 fid = 0x09;
402 if (ta)
403 fid = 0x0a;
404 if (tb)
405 fid = 0x0b;
406 if (tc)
407 fid = 0x0c;
408 if (td)
409 fid = 0x0d;
411 uint8_t iv[JOOKI_IV_LEN] = {0x80, 0x77, 0x51};
412 if (use_tag) {
413 res = ul_read_uid(uid);
414 if (res != PM3_SUCCESS) {
415 return res;
417 } else {
418 if (ulen != JOOKI_UID_LEN) {
419 PrintAndLogEx(ERR, "Wrong length of UID, expect %u, got %d", JOOKI_UID_LEN, ulen);
420 return PM3_EINVARG;
424 uint8_t b64[JOOKI_B64_LEN] = {0};
425 memset(b64, 0, sizeof(b64));
426 jooki_encode(iv, tid, fid, uid, b64);
427 jooki_printEx(b64, iv, tid, fid, uid, verbose);
428 return PM3_SUCCESS;
431 static int CmdHF14AJookiDecode(const char *Cmd) {
432 CLIParserContext *ctx;
433 CLIParserInit(&ctx, "hf jooki decode",
434 "Decode a base64-encode Jooki token in NDEF URI format",
435 "hf jooki decode -d 7WzlgEzqLgwTnWNy"
438 void *argtable[] = {
439 arg_param_begin,
440 arg_str1("d", "data", "<base64>", "base64 url parameter"),
441 arg_lit0("v", "verbose", "verbose output"),
442 arg_param_end
444 CLIExecWithReturn(ctx, Cmd, argtable, false);
445 uint8_t b64[JOOKI_B64_LEN] = {0x00};
446 int dlen = sizeof(b64) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
447 memset(b64, 0x0, sizeof(b64));
448 CLIGetStrWithReturn(ctx, 1, b64, &dlen);
449 bool verbose = arg_get_lit(ctx, 2);
450 CLIParserFree(ctx);
452 uint8_t result[JOOKI_PLAIN_LEN] = {0};
453 int res = jooki_decode(b64, result);
454 if (res == PM3_SUCCESS) {
455 jooki_print(b64, result, verbose);
457 return PM3_SUCCESS;
460 static int CmdHF14AJookiSim(const char *Cmd) {
461 CLIParserContext *ctx;
462 CLIParserInit(&ctx, "hf jooki sim",
463 "Simulate a Jooki token. Either `hf mfu eload` before or use `-d` param",
464 "hf jooki sim --> use token in emulator memory\n"
465 "hf jooki sim -b 7WzlgEzqLgwTnWNy --> using base64 url parameter"
468 void *argtable[] = {
469 arg_param_begin,
470 arg_str0("b", "b64", "<base64>", "base64 url parameter"),
471 arg_param_end
473 CLIExecWithReturn(ctx, Cmd, argtable, true);
474 uint8_t b64[JOOKI_B64_LEN] = {0x00};
475 int dlen = sizeof(b64) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
476 memset(b64, 0x0, sizeof(b64));
477 CLIGetStrWithReturn(ctx, 1, b64, &dlen);
478 CLIParserFree(ctx);
480 uint8_t result[JOOKI_PLAIN_LEN] = {0};
481 int res = jooki_decode(b64, result);
482 if (res != PM3_SUCCESS) {
483 return res;
486 jooki_print(b64, result, false);
488 // copy UID from base64 url parameter
489 uint8_t uid[7] = {0};
490 memcpy(uid, result + 5, 7);
492 // hf mfu sim...
493 uint8_t *data = calloc(144, sizeof(uint8_t));
495 memcpy(data, uid, 3);
496 memcpy(data + (1 * 4), uid + 3, 4);
498 // bbc0
499 data[3] = 0x88 ^ data[0] ^ data[1] ^ data[2];
501 // bbc1
502 data[8] = data[4] ^ data[5] ^ data[6] ^ data[7];
504 // copy NDEF magic firs, skip BBC1
505 memcpy(data + (2 * 4) + 1, "\x48\x00\x00\xE1\x10\x12\x00", 7);
507 // copy raw NDEF
508 jooki_create_ndef(b64, data + (4 * 4));
510 // convert plain or old mfu format to new format
511 size_t datalen = 144;
512 res = convert_mfu_dump_format(&data, &datalen, true);
513 if (res != PM3_SUCCESS) {
514 PrintAndLogEx(FAILED, "Failed convert on load to new Ultralight/NTAG format");
515 free(data);
516 return res;
519 mfu_dump_t *mfu_dump = (mfu_dump_t *)data;
520 memcpy(mfu_dump->version, "\x00\x04\x04\x02\x01\x00\x0F\x03", 8);
521 mfu_dump->counter_tearing[2][3] = 0xBD;
522 mfu_dump->pages = 0x2c;
524 mfu_print_dump(mfu_dump, mfu_dump->pages + 1, 0, false);
526 // upload to emulator memory
527 PrintAndLogEx(INFO, "Uploading to emulator memory");
528 PrintAndLogEx(INFO, "." NOLF);
530 // fast push mode
531 g_conn.block_after_ACK = true;
532 uint8_t blockwidth = 4, counter = 0, blockno = 0;
534 // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device
535 uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / blockwidth) * blockwidth;
537 while (datalen) {
538 if (datalen == blockwidth) {
539 // Disable fast mode on last packet
540 g_conn.block_after_ACK = false;
542 uint16_t chunk_size = MIN(max_avail_blocks, datalen);
543 uint16_t blocks_to_send = chunk_size / blockwidth;
545 if (mfEmlSetMem_xt(data + counter, blockno, blocks_to_send, blockwidth) != PM3_SUCCESS) {
546 PrintAndLogEx(FAILED, "Cant set emul block: %3d", blockno);
547 free(data);
548 return PM3_ESOFT;
550 blockno += blocks_to_send;
551 counter += chunk_size;
552 datalen -= chunk_size;
553 PrintAndLogEx(NORMAL, "." NOLF);
554 fflush(stdout);
556 PrintAndLogEx(NORMAL, "");
557 PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", counter);
559 struct {
560 uint8_t tagtype;
561 uint8_t flags;
562 uint8_t uid[10];
563 uint8_t exitAfter;
564 } PACKED payload;
566 // NTAG, 7 byte UID in eloaded data.
567 payload.tagtype = 7;
568 payload.flags = FLAG_UID_IN_EMUL;
569 payload.exitAfter = 0;
570 memcpy(payload.uid, uid, sizeof(uid));
572 clearCommandBuffer();
573 SendCommandNG(CMD_HF_ISO14443A_SIMULATE, (uint8_t *)&payload, sizeof(payload));
574 PacketResponseNG resp;
576 PrintAndLogEx(NORMAL, "");
577 PrintAndLogEx(SUCCESS, "Starting simulating");
578 PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("<Enter>") " to abort simulation");
579 for (;;) {
580 if (kbd_enter_pressed()) {
581 SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
582 PrintAndLogEx(DEBUG, "User aborted");
583 break;
586 if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false)
587 continue;
589 if (resp.status != PM3_SUCCESS)
590 break;
592 free(data);
593 PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14a list") "` to view trace log");
594 PrintAndLogEx(INFO, "Done!");
595 return PM3_SUCCESS;
598 static int CmdHF14AJookiClone(const char *Cmd) {
599 CLIParserContext *ctx;
600 CLIParserInit(&ctx, "hf jooki clone",
601 "Write a Jooki token to a Ultralight or NTAG tag",
602 "hf jooki clone -d <hex bytes> --> where hex is raw NDEF\n"
603 "hf jooki clone --b64 7WzlgEzqLgwTnWNy --> using base64 url parameter"
606 void *argtable[] = {
607 arg_param_begin,
608 arg_str0("b", "b64", "<base64>", "base64 url parameter"),
609 arg_str0("d", "data", "<hex>", "raw NDEF bytes"),
610 arg_str0("p", "pwd", "<hex>", "password for authentication (EV1/NTAG 4 bytes)"),
611 arg_param_end
613 CLIExecWithReturn(ctx, Cmd, argtable, false);
614 uint8_t b64[JOOKI_B64_LEN] = {0x00};
615 int blen = sizeof(b64) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
616 memset(b64, 0x0, sizeof(b64));
617 CLIGetStrWithReturn(ctx, 1, b64, &blen);
619 int dlen = 0;
620 uint8_t data[52] = {0x00};
621 memset(data, 0x0, sizeof(data));
622 int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &dlen);
623 if (res) {
624 CLIParserFree(ctx);
625 PrintAndLogEx(FAILED, "Error parsing bytes");
626 return PM3_EINVARG;
629 int plen = 0;
630 uint8_t pwd[4] = {0x00};
631 CLIGetHexWithReturn(ctx, 3, pwd, &plen);
632 CLIParserFree(ctx);
634 if (dlen != 52) {
635 PrintAndLogEx(ERR, "Wrong data length. Expected 52 got %d", dlen);
636 return PM3_EINVARG;
639 bool has_pwd = false;
640 if (plen == 4) {
641 has_pwd = true;
644 // 0 - no authentication
645 // 2 - pwd (4 bytes)
646 uint8_t keytype = 0, blockno = 4, i = 0;
648 while ((i * 4) < dlen) {
650 uint8_t cmddata[8] = {0};
651 memcpy(cmddata, data + (i * 4), 4);
652 if (has_pwd) {
653 memcpy(cmddata + 4, pwd, 4);
654 keytype = 2;
656 clearCommandBuffer();
657 SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, blockno, keytype, 0, cmddata, sizeof(cmddata));
659 PacketResponseNG resp;
660 if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
661 uint8_t isOK = resp.oldarg[0] & 0xff;
662 PrintAndLogEx(SUCCESS, "Write block %d ( %s )", blockno, isOK ? _GREEN_("ok") : _RED_("fail"));
663 } else {
664 PrintAndLogEx(WARNING, "Command execute timeout");
667 blockno++;
668 i++;
671 PrintAndLogEx(HINT, "Try `" _YELLOW_("hf mfu ndefread") "` to view");
672 PrintAndLogEx(INFO, "Done!");
673 return PM3_SUCCESS;
676 static command_t CommandTable[] = {
677 {"help", CmdHelp, AlwaysAvailable, "This help"},
678 {"clone", CmdHF14AJookiClone, IfPm3Iso14443a, "Write a Jooki token"},
679 {"decode", CmdHF14AJookiDecode, AlwaysAvailable, "Decode Jooki token"},
680 {"encode", CmdHF14AJookiEncode, AlwaysAvailable, "Encode Jooki token"},
681 {"sim", CmdHF14AJookiSim, IfPm3Iso14443a, "Simulate Jooki token"},
682 {NULL, NULL, NULL, NULL}
685 static int CmdHelp(const char *Cmd) {
686 (void)Cmd; // Cmd is not used so far
687 CmdsHelp(CommandTable);
688 return PM3_SUCCESS;
691 int CmdHF_Jooki(const char *Cmd) {
692 clearCommandBuffer();
693 return CmdsParse(CommandTable, Cmd);