/* * DesfireFileSettings.java * * Copyright (C) 2011 Eric Butler * * Authors: * Eric Butler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.codebutler.farebot.card.desfire; import java.io.ByteArrayInputStream; import org.apache.commons.lang3.ArrayUtils; import android.os.Parcel; import android.os.Parcelable; import com.codebutler.farebot.Utils; public abstract class DesfireFileSettings implements Parcelable { public final byte fileType; public final byte commSetting; public final byte[] accessRights; /* DesfireFile Types */ static final byte STANDARD_DATA_FILE = (byte) 0x00; static final byte BACKUP_DATA_FILE = (byte) 0x01; static final byte VALUE_FILE = (byte) 0x02; static final byte LINEAR_RECORD_FILE = (byte) 0x03; static final byte CYCLIC_RECORD_FILE = (byte) 0x04; public static DesfireFileSettings Create (byte[] data) throws DesfireException { byte fileType = (byte) data[0]; ByteArrayInputStream stream = new ByteArrayInputStream(data); if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE) return new StandardDesfireFileSettings(stream); else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE) return new RecordDesfireFileSettings(stream); else if (fileType == VALUE_FILE) return new ValueDesfireFileSettings(stream); else throw new DesfireException("Unknown file type: " + Integer.toHexString(fileType)); } private DesfireFileSettings (ByteArrayInputStream stream) { fileType = (byte) stream.read(); commSetting = (byte) stream.read(); accessRights = new byte[2]; stream.read(accessRights, 0, accessRights.length); } private DesfireFileSettings (byte fileType, byte commSetting, byte[] accessRights) { this.fileType = fileType; this.commSetting = commSetting; this.accessRights = accessRights; } public String getFileTypeName () { switch (fileType) { case STANDARD_DATA_FILE: return "Standard"; case BACKUP_DATA_FILE: return "Backup"; case VALUE_FILE: return "Value"; case LINEAR_RECORD_FILE: return "Linear Record"; case CYCLIC_RECORD_FILE: return "Cyclic Record"; default: return "Unknown"; } } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public DesfireFileSettings createFromParcel(Parcel source) { byte fileType = source.readByte(); byte commSetting = source.readByte(); byte[] accessRights = new byte[source.readInt()]; source.readByteArray(accessRights); if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE) { int fileSize = source.readInt(); return new StandardDesfireFileSettings(fileType, commSetting, accessRights, fileSize); } else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE) { int recordSize = source.readInt(); int maxRecords = source.readInt(); int curRecords = source.readInt(); return new RecordDesfireFileSettings(fileType, commSetting, accessRights, recordSize, maxRecords, curRecords); } else { return new UnsupportedDesfireFileSettings(fileType); } } public DesfireFileSettings[] newArray(int size) { return new DesfireFileSettings[size]; } }; public void writeToParcel (Parcel parcel, int flags) { parcel.writeByte(fileType); parcel.writeByte(commSetting); parcel.writeInt(accessRights.length); parcel.writeByteArray(accessRights); } public int describeContents () { return 0; } public static class StandardDesfireFileSettings extends DesfireFileSettings { public final int fileSize; private StandardDesfireFileSettings (ByteArrayInputStream stream) { super(stream); byte[] buf = new byte[3]; stream.read(buf, 0, buf.length); ArrayUtils.reverse(buf); fileSize = Utils.byteArrayToInt(buf); } StandardDesfireFileSettings (byte fileType, byte commSetting, byte[] accessRights, int fileSize) { super(fileType, commSetting, accessRights); this.fileSize = fileSize; } @Override public void writeToParcel (Parcel parcel, int flags) { super.writeToParcel(parcel, flags); parcel.writeInt(fileSize); } } public static class RecordDesfireFileSettings extends DesfireFileSettings { public final int recordSize; public final int maxRecords; public final int curRecords; public RecordDesfireFileSettings(ByteArrayInputStream stream) { super(stream); byte[] buf = new byte[3]; stream.read(buf, 0, buf.length); ArrayUtils.reverse(buf); recordSize = Utils.byteArrayToInt(buf); buf = new byte[3]; stream.read(buf, 0, buf.length); ArrayUtils.reverse(buf); maxRecords = Utils.byteArrayToInt(buf); buf = new byte[3]; stream.read(buf, 0, buf.length); ArrayUtils.reverse(buf); curRecords = Utils.byteArrayToInt(buf); } RecordDesfireFileSettings (byte fileType, byte commSetting, byte[] accessRights, int recordSize, int maxRecords, int curRecords) { super(fileType, commSetting, accessRights); this.recordSize = recordSize; this.maxRecords = maxRecords; this.curRecords = curRecords; } @Override public void writeToParcel (Parcel parcel, int flags) { super.writeToParcel(parcel, flags); parcel.writeInt(recordSize); parcel.writeInt(maxRecords); parcel.writeInt(curRecords); } } public static class ValueDesfireFileSettings extends DesfireFileSettings { public final int lowerLimit; public final int upperLimit; public final int value; public final byte limitedCreditEnabled; public ValueDesfireFileSettings(ByteArrayInputStream stream) { super(stream); byte[] buf = new byte[4]; stream.read(buf, 0, buf.length); ArrayUtils.reverse(buf); lowerLimit = Utils.byteArrayToInt(buf); buf = new byte[4]; stream.read(buf, 0, buf.length); ArrayUtils.reverse(buf); upperLimit = Utils.byteArrayToInt(buf); buf = new byte[4]; stream.read(buf, 0, buf.length); ArrayUtils.reverse(buf); value = Utils.byteArrayToInt(buf); buf = new byte[1]; stream.read(buf, 0, buf.length); limitedCreditEnabled = buf[0]; //http://www.skyetek.com/docs/m2/desfire.pdf //http://neteril.org/files/M075031_desfire.pdf } @Override public void writeToParcel (Parcel parcel, int flags) { super.writeToParcel(parcel, flags); parcel.writeInt(lowerLimit); parcel.writeInt(upperLimit); parcel.writeInt(value); parcel.writeByte(limitedCreditEnabled); } } public static class UnsupportedDesfireFileSettings extends DesfireFileSettings { public UnsupportedDesfireFileSettings(byte fileType) { super(fileType, Byte.MIN_VALUE, new byte[0]); } } }