summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--easytool/easycard.c184
-rw-r--r--easytool/easycard.h22
-rw-r--r--easytool/easytool.c2
-rw-r--r--easytool/mifare_classic.h2
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 <nfc/nfc-types.h>
+#include <nfc/mifaretag.h>
#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 <laforge@gnumonks.org>\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
personal git repositories of Harald Welte. Your mileage may vary