summaryrefslogtreecommitdiff
path: root/easytool/easycard.c
diff options
context:
space:
mode:
Diffstat (limited to 'easytool/easycard.c')
-rw-r--r--easytool/easycard.c184
1 files changed, 180 insertions, 4 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;
+}
personal git repositories of Harald Welte. Your mileage may vary