1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
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.
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"
20 #include <string.h> // memset
21 #include "commonutil.h" // ARRAYLEN
22 #include "ui.h" // PrintAndLog
23 #include "cmdparser.h"
24 #include "generator.h"
26 #include "nfc/ndef.h" // print decode ndef
27 #include "mifare/mifarehost.h" // mfemlsetmem_xt
28 #include "cliparser.h"
31 #include "fileutils.h" // convert_mfu..
33 static int CmdHelp(const char *Cmd
);
38 const char figdesc
[40];
39 const char typedesc
[12];
40 } PACKED jooki_figure_t
;
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
) {
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
) {
137 PrintAndLogEx(ERR
, "(encode jooki) base64ndef param is NULL");
142 if (iv
== NULL
|| uid
== NULL
) {
143 PrintAndLogEx(ERR
, "(encode jooki) iv or uid param is NULL");
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
++) {
152 enc
[i
] = d
[i
] ^ nfc_secret
[i
];
154 enc
[i
] = d
[i
] ^ nfc_secret
[i
] ^ d
[i
% 3];
157 PrintAndLogEx(DEBUG
, "encoded result.... %s", sprint_hex(enc
, sizeof(enc
)));
162 mbedtls_base64_encode(b64
, sizeof(b64
), &b64len
, (const unsigned char *)enc
, sizeof(enc
));
163 memcpy(out
, b64
, b64len
);
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
++) {
176 result
[i
] = ndef
[i
] ^ nfc_secret
[i
];
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
)));
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");
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);
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")
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
)));
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
)
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)
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
, "==================================================================");
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"
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"),
318 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
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
);
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;
353 return jooki_selftest();
362 if (ftid
> 0x04 || ffid
> 0x20) {
363 PrintAndLogEx(ERR
, "Use a valid Figure Type ID and Figure ID");
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");
374 if (figure_abbr_val
== 1 && !figure_abbr
) {
375 PrintAndLogEx(ERR
, "Use either --tid and --fid or one of the figurine types");
411 uint8_t iv
[JOOKI_IV_LEN
] = {0x80, 0x77, 0x51};
413 res
= ul_read_uid(uid
);
414 if (res
!= PM3_SUCCESS
) {
418 if (ulen
!= JOOKI_UID_LEN
) {
419 PrintAndLogEx(ERR
, "Wrong length of UID, expect %u, got %d", JOOKI_UID_LEN
, ulen
);
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
);
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"
440 arg_str1("d", "data", "<base64>", "base64 url parameter"),
441 arg_lit0("v", "verbose", "verbose output"),
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);
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
);
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"
470 arg_str0("b", "b64", "<base64>", "base64 url parameter"),
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
);
480 uint8_t result
[JOOKI_PLAIN_LEN
] = {0};
481 int res
= jooki_decode(b64
, result
);
482 if (res
!= PM3_SUCCESS
) {
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);
493 uint8_t *data
= calloc(144, sizeof(uint8_t));
495 memcpy(data
, uid
, 3);
496 memcpy(data
+ (1 * 4), uid
+ 3, 4);
499 data
[3] = 0x88 ^ data
[0] ^ data
[1] ^ data
[2];
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);
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");
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
);
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
;
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
);
550 blockno
+= blocks_to_send
;
551 counter
+= chunk_size
;
552 datalen
-= chunk_size
;
553 PrintAndLogEx(NORMAL
, "." NOLF
);
556 PrintAndLogEx(NORMAL
, "");
557 PrintAndLogEx(SUCCESS
, "uploaded " _YELLOW_("%d") " bytes to emulator memory", counter
);
566 // NTAG, 7 byte UID in eloaded data.
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");
580 if (kbd_enter_pressed()) {
581 SendCommandNG(CMD_BREAK_LOOP
, NULL
, 0);
582 PrintAndLogEx(DEBUG
, "User aborted");
586 if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE
, &resp
, 1500) == false)
589 if (resp
.status
!= PM3_SUCCESS
)
593 PrintAndLogEx(HINT
, "Try `" _YELLOW_("hf 14a list") "` to view trace log");
594 PrintAndLogEx(INFO
, "Done!");
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"
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)"),
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
);
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
);
625 PrintAndLogEx(FAILED
, "Error parsing bytes");
630 uint8_t pwd
[4] = {0x00};
631 CLIGetHexWithReturn(ctx
, 3, pwd
, &plen
);
635 PrintAndLogEx(ERR
, "Wrong data length. Expected 52 got %d", dlen
);
639 bool has_pwd
= false;
644 // 0 - no authentication
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);
653 memcpy(cmddata
+ 4, pwd
, 4);
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"));
664 PrintAndLogEx(WARNING
, "Command execute timeout");
671 PrintAndLogEx(HINT
, "Try `" _YELLOW_("hf mfu ndefread") "` to view");
672 PrintAndLogEx(INFO
, "Done!");
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
);
691 int CmdHF_Jooki(const char *Cmd
) {
692 clearCommandBuffer();
693 return CmdsParse(CommandTable
, Cmd
);