From 1214269f6f7ab7e8ad705ad6c990afa23284eb29 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 15 Aug 2010 14:41:48 +0800 Subject: add functions to do comprehensive modifications of last purchase or recharge --- easytool/easycard.c | 184 +++++++++++++++++++++++++++++++++++++++++++++- easytool/easycard.h | 22 +++++- easytool/easytool.c | 2 +- easytool/mifare_classic.h | 2 + 4 files changed, 202 insertions(+), 8 deletions(-) diff --git a/easytool/easycard.c b/easytool/easycard.c index 3329ba1..980735d 100644 --- a/easytool/easycard.c +++ b/easytool/easycard.c @@ -42,12 +42,13 @@ /* Easycard specific includes */ #include "easycard.h" +/* convert from 3byte easycard-timestamp to time_t */ time_t easy_timestamp2time(const uint8_t *easy_ts) { return (easy_ts[2] << 16 | easy_ts[1] << 8 | easy_ts[0]) << 8; } -/* apply a telta (positive or negative) to a EasyCard log record */ +/* apply a delta (positive or negative) to a EasyCard log record */ int easy_update_log_rec(struct easy_log_rec *elr, int16_t delta) { int32_t sum = elr->amount + delta; @@ -104,6 +105,7 @@ static const struct mfcl_addr easy_trans_log_addrs[] = { { .sector = 5, .block = 2, }, }; +/* Dump a single transaction log record */ void easy_dump_log_rec(const struct easy_log_rec *elr) { /* Skip empty records */ @@ -128,13 +130,19 @@ void easy_dump_log_rec(const struct easy_log_rec *elr) } } -void easy_dump_log(mifare_tag *mft) +void easy_log_rec_hdr(void) { - unsigned int i; - /* "2010-01-01 01:01 | TID| Amount | Balance | */ puts("Timestamp | TID| Amount | Balance | Type | MRT Station"); puts("---------------------------------------------------------------------------"); +} + +/* Dump the entire transaction log of the given mifare card */ +void easy_dump_log(mifare_tag *mft) +{ + unsigned int i; + + easy_log_rec_hdr(); for (i = 0; i < ARRAY_SIZE(easy_trans_log_addrs); i++) { unsigned int sect = easy_trans_log_addrs[i].sector; @@ -149,3 +157,171 @@ void easy_dump_log(mifare_tag *mft) easy_dump_log_rec(data); } } + +static struct easy_log_rec *get_last_trans(mifare_tag *mft, uint8_t type) +{ + unsigned int i; + time_t last_trans_time = 0; + struct easy_log_rec *last_trans = NULL; + + for (i = 0; i < ARRAY_SIZE(easy_trans_log_addrs); i++) { + unsigned int sect = easy_trans_log_addrs[i].sector; + unsigned int block = easy_trans_log_addrs[i].block; + unsigned int block_base = sect * 4; + struct easy_log_rec *elr = (struct easy_log_rec *) + mft->amb[block_base+block].mbd.abtData; + + if (elr->trans_type != type) + continue; + + if (easy_timestamp2time(elr->timestamp) > last_trans_time) { + last_trans = elr; + last_trans_time = easy_timestamp2time(elr->timestamp); + } + } + return last_trans; +} + +#define MAX_NTD_PER_PURCHASE 1000 +#define MAX_NTD_PER_DAY 3000 +#define MAX_AMOUNT_PER_CARD 5000 + +/* positive value: make it more expensive. negative: cheaper */ +int easy_alter_last_purchase(mifare_tag *mft, int16_t delta) +{ + struct easy_log_rec *elr; + struct mfcl_value_block *val_block = + (struct mfcl_value_block *) mft->amb[2*4].mbd.abtData; + struct mfcl_value_block *val_block2 = + (struct mfcl_value_block *) mft->amb[2*4+1].mbd.abtData; + struct easy_sect15blk2 *s15b2 = + (struct easy_sect15blk2 *) mft->amb[15*4+2].mbd.abtData; + time_t t_time; + struct tm *t_tm; + int rc; + + /* Step 1: find last purchasing transaction */ + elr = get_last_trans(mft, EASY_TT_PURCHASE); + if (!elr) + return -ENOENT; + + printf("Found last transaction log record:\n"); + easy_log_rec_hdr(); + easy_dump_log_rec(elr); + + /* Step 2: check if delta is within range */ + if (delta >= 0) { + if (elr->amount + delta > MAX_NTD_PER_PURCHASE) { + fprintf(stderr, "%u NTD + delta %d would exceed " + "maximum purchase value of %u\n", + elr->amount, delta, MAX_NTD_PER_PURCHASE); + return -ERANGE; + } + if (elr->remaining < delta) { + fprintf(stderr, "Delta of %d is more than what is " + "left on the card (%u)\n", delta, elr->remaining); + return -ERANGE; + } + } else { + if (elr->amount < delta) { + fprintf(stderr, "Cannot make purchase of %u NTD " + "cheaper by %d\n", elr->amount, delta); + return -ERANGE; + } + if (elr->remaining + abs(delta) > MAX_AMOUNT_PER_CARD) { + fprintf(stderr, "%u + %u are in excess of maximum " + "value the card can hold (%u)\n", elr->remaining, + abs(delta), MAX_AMOUNT_PER_CARD); + return -ERANGE; + } + } + + /* Step 3: Make sure the VALUE block and the 'remaining after trans' + * agree */ + if (elr->remaining != val_block->value) { + fprintf(stderr, "Amount remaining after last transaction (%u) " + "and VALUE block (%u) are inconsistent\n", elr->remaining, + val_block->value); + return -EIO; + } + + /* Step 4: Actually apply the delta to the transaction */ + rc = easy_update_log_rec(elr, delta); + if (rc < 0) + return rc; + printf("=> Modified log record:\n"); + easy_dump_log_rec(elr); + + /* Step 5: Actually apply the delta to the value blocks */ + /* we need to _subtract_ the value... */ + rc = mfcl_update_value_block(val_block, -delta); + if (rc < 0) + return rc; + rc = mfcl_update_value_block(val_block2, -delta); + if (rc < 0) + return rc; + + /* Step 6: Update the 'sum of the day', if needed */ + t_time = easy_timestamp2time(elr->timestamp); + t_tm = gmtime(&t_time); + if (s15b2->day_of_month == t_tm->tm_mday) + easy_update_sum_of_day(s15b2, delta); + + return rc; +} + +/* positive value: make it more expensive. negative: cheaper */ +int easy_alter_last_recharge(mifare_tag *mft, int16_t delta) +{ + struct easy_log_rec *elr; + struct mfcl_value_block *val_block = + (struct mfcl_value_block *) mft->amb[2*4].mbd.abtData; + struct mfcl_value_block *val_block2 = + (struct mfcl_value_block *) mft->amb[2*4+1].mbd.abtData; + int rc; + + /* Step 1: find last recharging transaction */ + elr = get_last_trans(mft, EASY_TT_RECHARGE); + if (!elr) { + fprintf(stderr, "Cannot find a recharge transaction\n"); + return -ENOENT; + } + + printf("Found last transaction log record:\n"); + easy_log_rec_hdr(); + easy_dump_log_rec(elr); + + /* Step 2: check if delta is within range */ + if (elr->remaining + delta > MAX_AMOUNT_PER_CARD) { + fprintf(stderr, "%u plus the delta of %d would exceed the " + "per-card maximum of %u\n", elr->remaining, delta, + MAX_AMOUNT_PER_CARD); + return -ERANGE; + } + + /* Step 3: Make sure the VALUE block and the 'remaining after trans' + * agree */ + if (elr->remaining != val_block->value) { + fprintf(stderr, "Amount remaining after last transaction (%u) " + "and VALUE block (%u) are inconsistent\n", elr->remaining, + val_block->value); + return -EIO; + } + + /* Step 4: Actually apply the delta to the transaction */ + rc = easy_update_log_rec(elr, delta); + if (rc < 0) + return rc; + printf("=> Modified log record:\n"); + easy_dump_log_rec(elr); + + /* Step 5: Actually apply the delta to the value blocks */ + rc = mfcl_update_value_block(val_block, delta); + if (rc < 0) + return rc; + rc = mfcl_update_value_block(val_block2, delta); + if (rc < 0) + return rc; + + return rc; +} diff --git a/easytool/easycard.h b/easytool/easycard.h index 37caf1f..8979524 100644 --- a/easytool/easycard.h +++ b/easytool/easycard.h @@ -1,6 +1,8 @@ #ifndef EASYCARD_H #define EASYCARD_H +#include +#include #include "utils.h" #define EASY_TT_MRT_ENTER 0x00 @@ -52,17 +54,31 @@ struct easy_log_rec { uint8_t unknown3[2]; } __attribute__ ((packed)); +/* functions for data format conversion */ + time_t easy_timestamp2time(const uint8_t *easy_ts); char *easy_asc_timestamp(const uint8_t *timestamp); -/* apply a telta (positive or negative) to a EasyCard log record */ +/* functions to make a change to individual portions of the card */ + +/* apply a delta (positive or negative) to a EasyCard log record */ int easy_update_log_rec(struct easy_log_rec *elr, int16_t delta); /* apply a delta to the 'sum of day' record in Sector 15 Block 2 */ int easy_update_sum_of_day(struct easy_sect15blk2 *s15b2, int16_t delta); -void easy_dump_log_rec(const struct easy_log_rec *elr); +/* functions to make a comprehensive change, leave all card state in + * a consistent state */ -typedef mifare_tag; +/* positive value: make it more expensive. negative: cheaper */ +int easy_alter_last_purchase(mifare_tag *mft, int16_t delta); +/* positive value: make it more expensive. negative: cheaper */ +int easy_alter_last_recharge(mifare_tag *mft, int16_t delta); + +/* functions to dump the transaction log */ + +/* dump a single log record */ +void easy_dump_log_rec(const struct easy_log_rec *elr); +/* dump the entire transaction log */ void easy_dump_log(mifare_tag *mft); #endif /* EASYCARD_H */ diff --git a/easytool/easytool.c b/easytool/easytool.c index 0e07902..67157c8 100644 --- a/easytool/easytool.c +++ b/easytool/easytool.c @@ -41,7 +41,7 @@ /* Easycard specific includes */ #include "easycard.h" -#define VERSION "0.02" +#define VERSION "0.03" #define COPYRIGHT \ "EasyTool "VERSION" (C) 2010 by Harald Welte \n" \ "This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n" \ diff --git a/easytool/mifare_classic.h b/easytool/mifare_classic.h index d27a34e..05b3dc6 100644 --- a/easytool/mifare_classic.h +++ b/easytool/mifare_classic.h @@ -26,5 +26,7 @@ struct mfcl_addr { uint8_t block; }; +/* apply a delta (positive or negative) to a Mifare Classic VALUE block */ +int mfcl_update_value_block(struct mfcl_value_block *mvb, int32_t delta); #endif -- cgit v1.2.3