Merge pull request #2654 from Antiklesys/master
[RRG-proxmark3.git] / client / src / cmdhfksx6924.c
blob7616ca766d054b56e1dfa86ef51a56a0232924f5
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 // Commands for KS X 6924 transit cards (T-Money, Snapper+)
17 //-----------------------------------------------------------------------------
18 // This is used in T-Money (South Korea) and Snapper plus (Wellington, New
19 // Zealand).
21 // References:
22 // - https://github.com/micolous/metrodroid/wiki/T-Money (in English)
23 // - https://github.com/micolous/metrodroid/wiki/Snapper (in English)
24 // - https://kssn.net/StdKS/ks_detail.asp?k1=X&k2=6924-1&k3=4
25 // (KS X 6924, only available in Korean)
26 // - http://www.tta.or.kr/include/Download.jsp?filename=stnfile/TTAK.KO-12.0240_%5B2%5D.pdf
27 // (TTAK.KO 12.0240, only available in Korean)
28 //-----------------------------------------------------------------------------
31 #include "cmdhfksx6924.h"
33 #include <inttypes.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <ctype.h>
38 #include <unistd.h>
39 #include "comms.h"
40 #include "cmdmain.h"
41 #include "util.h"
42 #include "ui.h"
43 #include "proxmark3.h"
44 #include "cliparser.h"
45 #include "ksx6924/ksx6924core.h"
46 #include "emv/tlv.h"
47 #include "iso7816/apduinfo.h"
48 #include "cmdhf14a.h"
49 #include "protocols.h" // ISO7816 APDU return codes
51 static int CmdHelp(const char *Cmd);
53 static int get_and_print_balance(void) {
54 uint32_t balance = 0;
55 if (KSX6924GetBalance(&balance) == false) {
56 PrintAndLogEx(ERR, "Error getting balance");
57 return PM3_ESOFT;
60 PrintAndLogEx(SUCCESS, "Current balance: " _YELLOW_("%u") " won/cents", balance);
61 return PM3_SUCCESS;
64 static int CmdHFKSX6924Balance(const char *Cmd) {
65 CLIParserContext *ctx;
66 CLIParserInit(&ctx, "hf ksx6924 balance",
67 "Gets the current purse balance",
68 "hf ksx6924 balance\n");
70 void *argtable[] = {
71 arg_param_begin,
72 arg_lit0("k", "keep", "keep field ON for next command"),
73 arg_lit0("a", "apdu", "Show APDU requests and responses"),
74 arg_param_end
76 CLIExecWithReturn(ctx, Cmd, argtable, true);
78 bool keep = arg_get_lit(ctx, 1);
79 bool APDULogging = arg_get_lit(ctx, 2);
81 CLIParserFree(ctx);
82 SetAPDULogging(APDULogging);
84 if (KSX6924TrySelect()) {
85 get_and_print_balance();
88 if (keep == false) {
89 DropField();
92 return PM3_SUCCESS;
95 static int CmdHFKSX6924Info(const char *Cmd) {
96 CLIParserContext *ctx;
97 CLIParserInit(&ctx, "hf ksx6924 info",
98 "Get info about a KS X 6924 transit card.\n"
99 "This application is used by T-Money (South Korea) and\n"
100 "Snapper+ (Wellington, New Zealand).",
101 "hf ksx6924 info\n");
103 void *argtable[] = {
104 arg_param_begin,
105 arg_lit0("k", "keep", "keep field ON for next command"),
106 arg_lit0("a", "apdu", "Show APDU requests and responses"),
107 arg_param_end
109 CLIExecWithReturn(ctx, Cmd, argtable, true);
111 bool keep = arg_get_lit(ctx, 1);
112 bool APDULogging = arg_get_lit(ctx, 2);
114 CLIParserFree(ctx);
115 SetAPDULogging(APDULogging);
117 // KSX6924 info
118 uint8_t buf[APDU_RES_LEN] = {0};
119 size_t len = 0;
120 uint16_t sw = 0;
121 int res = KSX6924Select(true, true, buf, sizeof(buf), &len, &sw);
123 if (res || (len == 0)) {
124 if (keep == false) {
125 DropField();
127 return res;
130 if (sw != ISO7816_OK) {
131 if (sw) {
132 PrintAndLogEx(INFO, "Not a KS X 6924 card! APDU response: %04x - %s",
133 sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
134 } else {
135 PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000.");
137 goto end;
141 // PrintAndLogEx(DEBUG, "APDU response: %s", sprint_hex(buf, len));
143 // FCI Response is a BER-TLV, we are interested in tag 6F,B0 only.
144 const uint8_t *p = buf;
145 struct tlv fci_tag;
146 memset(&fci_tag, 0, sizeof(fci_tag));
148 while (len > 0) {
149 memset(&fci_tag, 0, sizeof(fci_tag));
150 bool ret = tlv_parse_tl(&p, &len, &fci_tag);
152 if (!ret) {
153 PrintAndLogEx(FAILED, "Error parsing FCI!");
154 goto end;
157 // PrintAndLog("tag %02x, len %d, value %s",
158 // fci_tag.tag, fci_tag.len,
159 // sprint_hex(p, fci_tag.len));
161 if (fci_tag.tag == 0x6f) { /* FCI template */
162 break;
163 } else {
164 p += fci_tag.len;
165 continue;
169 if (fci_tag.tag != 0x6f) {
170 PrintAndLogEx(ERR, "Couldn't find tag 6F (FCI) in SELECT response");
171 goto end;
174 // We now are at Tag 6F (FCI template), get Tag B0 inside of it
175 while (len > 0) {
176 memset(&fci_tag, 0, sizeof(fci_tag));
177 bool ret = tlv_parse_tl(&p, &len, &fci_tag);
179 if (!ret) {
180 PrintAndLogEx(ERR, "Error parsing FCI!");
181 goto end;
184 // PrintAndLog("tag %02x, len %d, value %s",
185 // fci_tag.tag, fci_tag.len,
186 // sprint_hex(p, fci_tag.len));
188 if (fci_tag.tag == 0xb0) { /* KS X 6924 purse info */
189 break;
190 } else {
191 p += fci_tag.len;
192 continue;
196 if (fci_tag.tag != 0xb0) {
197 PrintAndLogEx(FAILED, "Couldn't find tag B0 (KS X 6924 purse info) in FCI");
198 goto end;
201 struct ksx6924_purse_info purseInfo;
202 bool ret = KSX6924ParsePurseInfo(p, fci_tag.len, &purseInfo);
204 if (!ret) {
205 PrintAndLogEx(FAILED, "Error parsing KS X 6924 purse info");
206 goto end;
209 KSX6924PrintPurseInfo(&purseInfo);
211 get_and_print_balance();
213 end:
214 if (keep == false) {
215 DropField();
217 return PM3_SUCCESS;
220 static int CmdHFKSX6924Select(const char *Cmd) {
221 CLIParserContext *ctx;
222 CLIParserInit(&ctx, "hf ksx6924 select",
223 "Selects KS X 6924 application, and leaves field up",
224 "hf ksx6924 select\n");
226 void *argtable[] = {
227 arg_param_begin,
228 arg_lit0("a", "apdu", "Show APDU requests and responses"),
229 arg_param_end
231 CLIExecWithReturn(ctx, Cmd, argtable, true);
233 bool APDULogging = arg_get_lit(ctx, 1);
234 CLIParserFree(ctx);
235 SetAPDULogging(APDULogging);
237 if (KSX6924TrySelect()) {
238 PrintAndLogEx(SUCCESS, "Card is selected and field is up");
239 } else {
240 // Wrong app, drop field.
241 DropField();
244 return PM3_SUCCESS;
247 static int CmdHFKSX6924Initialize(const char *Cmd) {
248 CLIParserContext *ctx;
249 CLIParserInit(&ctx, "hf ksx6924 init",
250 "Perform transaction initialization with Mpda (Money of Purchase Transaction)",
251 "hf ksx6924 init 000003e8 -> Mpda\n");
253 void *argtable[] = {
254 arg_param_begin,
255 arg_lit0("k", "keep", "keep field ON for next command"),
256 arg_lit0("a", "apdu", "Show APDU requests and responses"),
257 arg_str1(NULL, NULL, "<Mpda 4 bytes hex>", NULL),
258 arg_param_end
260 CLIExecWithReturn(ctx, Cmd, argtable, true);
262 bool keep = arg_get_lit(ctx, 1);
263 bool APDULogging = arg_get_lit(ctx, 2);
265 uint8_t data[APDU_RES_LEN] = {0};
266 int datalen = 0;
267 CLIGetHexWithReturn(ctx, 3, data, &datalen);
269 CLIParserFree(ctx);
270 SetAPDULogging(APDULogging);
272 if (datalen != 4) {
273 PrintAndLogEx(WARNING, "Mpda parameter must be 4 bytes long (eg: 000003e8)");
274 return PM3_EINVARG;
277 // try selecting card
278 if (KSX6924TrySelect() == false) {
279 goto end;
282 uint8_t resp[APDU_RES_LEN] = {0};
283 size_t resp_len = 0;
284 if (KSX6924InitializeCard(data[0], data[1], data[2], data[3], resp, &resp_len) == false) {
285 goto end;
288 uint8_t *r = resp;
289 struct ksx6924_initialize_card_response initCardResponse;
290 bool ret = KSX6924ParseInitializeCardResponse(r, resp_len, &initCardResponse);
292 if (!ret) {
293 PrintAndLogEx(FAILED, "Error parsing KS X 6924 initialize card response");
294 goto end;
297 KSX6924PrintInitializeCardResponse(&initCardResponse);
299 end:
300 if (keep == false) {
301 DropField();
304 return PM3_SUCCESS;
307 static int CmdHFKSX6924PRec(const char *Cmd) {
308 CLIParserContext *ctx;
309 CLIParserInit(&ctx, "hf ksx6924 prec",
310 "Executes proprietary read record command.\n"
311 "Data format is unknown. Other records are available with 'emv getrec'.\n",
312 "hf ksx6924 prec 0b -> read proprietary record 0x0b");
314 void *argtable[] = {
315 arg_param_begin,
316 arg_lit0("k", "keep", "keep field ON for next command"),
317 arg_lit0("a", "apdu", "Show APDU requests and responses"),
318 arg_str1(NULL, NULL, "<record 1byte HEX>", NULL),
319 arg_param_end
321 CLIExecWithReturn(ctx, Cmd, argtable, true);
323 bool keep = arg_get_lit(ctx, 1);
324 bool APDULogging = arg_get_lit(ctx, 2);
326 uint8_t data[APDU_RES_LEN] = {0};
327 int datalen = 0;
328 CLIGetHexWithReturn(ctx, 3, data, &datalen);
330 CLIParserFree(ctx);
331 SetAPDULogging(APDULogging);
333 if (datalen != 1) {
334 PrintAndLogEx(WARNING, "Record parameter must be 1 byte long (eg: 0f)");
335 return PM3_EINVARG;
338 if (KSX6924TrySelect() == false) {
339 goto end;
342 PrintAndLogEx(SUCCESS, "Getting record %02x ...", data[0]);
344 uint8_t recordData[0x10] = {0};
345 if (KSX6924ProprietaryGetRecord(data[0], recordData, sizeof(recordData))) {
346 PrintAndLogEx(SUCCESS, " %s", sprint_hex(recordData, sizeof(recordData)));
347 } else {
348 PrintAndLogEx(FAILED, "Error getting record");
351 end:
352 if (keep == false) {
353 DropField();
355 return PM3_SUCCESS;
358 static command_t CommandTable[] = {
359 {"help", CmdHelp, AlwaysAvailable, "This help"},
360 {"select", CmdHFKSX6924Select, IfPm3Iso14443a, "Select application, and leave field up"},
361 {"info", CmdHFKSX6924Info, IfPm3Iso14443a, "Get info about a KS X 6924 (T-Money, Snapper+) transit card"},
362 {"balance", CmdHFKSX6924Balance, IfPm3Iso14443a, "Get current purse balance"},
363 {"init", CmdHFKSX6924Initialize, IfPm3Iso14443a, "Perform transaction initialization with Mpda"},
364 {"prec", CmdHFKSX6924PRec, IfPm3Iso14443a, "Send proprietary get record command (CLA=90, INS=4C)"},
365 {NULL, NULL, NULL, NULL}
368 static int CmdHelp(const char *Cmd) {
369 (void)Cmd; // Cmd is not used so far
370 CmdsHelp(CommandTable);
371 return PM3_SUCCESS;
374 int CmdHFKSX6924(const char *Cmd) {
375 clearCommandBuffer();
376 return CmdsParse(CommandTable, Cmd);