/** * DesfireFileSettings.kt * * Copyright (C) 2011 Eric Butler * Copyright (C) 2019 * * 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 android.os.Parcel import android.os.Parcelable import com.codebutler.farebot.Utils import org.apache.commons.lang3.ArrayUtils import java.io.ByteArrayInputStream abstract class DesfireFileSettings : Parcelable { private val fileType: Byte private val commSetting: Byte private val accessRights: ByteArray @Suppress("unused") val fileTypeName: String get() { return when (fileType) { STANDARD_DATA_FILE -> "Standard" BACKUP_DATA_FILE -> "Backup" VALUE_FILE -> "Value" LINEAR_RECORD_FILE -> "Linear Record" CYCLIC_RECORD_FILE -> "Cyclic Record" else -> "Unknown" } } private constructor(stream: ByteArrayInputStream) { fileType = stream.read().toByte() commSetting = stream.read().toByte() accessRights = ByteArray(2) stream.read(accessRights, 0, accessRights.size) } private constructor(fileType: Byte, commSetting: Byte, accessRights: ByteArray) { this.fileType = fileType this.commSetting = commSetting this.accessRights = accessRights } override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeByte(fileType) parcel.writeByte(commSetting) parcel.writeInt(accessRights.size) parcel.writeByteArray(accessRights) } override fun describeContents(): Int { return 0 } class StandardDesfireFileSettings : DesfireFileSettings { private val fileSize: Int internal constructor(stream: ByteArrayInputStream) : super(stream) { val buf = ByteArray(3) stream.read(buf, 0, buf.size) ArrayUtils.reverse(buf) fileSize = Utils.byteArrayToInt(buf) } internal constructor( fileType: Byte, commSetting: Byte, accessRights: ByteArray, fileSize: Int ) : super(fileType, commSetting, accessRights) { this.fileSize = fileSize } override fun writeToParcel(parcel: Parcel, flags: Int) { super.writeToParcel(parcel, flags) parcel.writeInt(fileSize) } } class RecordDesfireFileSettings : DesfireFileSettings { private val maxRecords: Int val recordSize: Int val curRecords: Int constructor(stream: ByteArrayInputStream) : super(stream) { var buf = ByteArray(3) stream.read(buf, 0, buf.size) ArrayUtils.reverse(buf) recordSize = Utils.byteArrayToInt(buf) buf = ByteArray(3) stream.read(buf, 0, buf.size) ArrayUtils.reverse(buf) maxRecords = Utils.byteArrayToInt(buf) buf = ByteArray(3) stream.read(buf, 0, buf.size) ArrayUtils.reverse(buf) curRecords = Utils.byteArrayToInt(buf) } internal constructor( fileType: Byte, commSetting: Byte, accessRights: ByteArray, recordSize: Int, maxRecords: Int, curRecords: Int ) : super(fileType, commSetting, accessRights) { this.recordSize = recordSize this.maxRecords = maxRecords this.curRecords = curRecords } override fun writeToParcel(parcel: Parcel, flags: Int) { super.writeToParcel(parcel, flags) parcel.writeInt(recordSize) parcel.writeInt(maxRecords) parcel.writeInt(curRecords) } } class ValueDesfireFileSettings(stream: ByteArrayInputStream) : DesfireFileSettings(stream) { private val lowerLimit: Int private val upperLimit: Int val value: Int private val limitedCreditEnabled: Byte init { var buf = ByteArray(4) stream.read(buf, 0, buf.size) ArrayUtils.reverse(buf) lowerLimit = Utils.byteArrayToInt(buf) buf = ByteArray(4) stream.read(buf, 0, buf.size) ArrayUtils.reverse(buf) upperLimit = Utils.byteArrayToInt(buf) buf = ByteArray(4) stream.read(buf, 0, buf.size) ArrayUtils.reverse(buf) value = Utils.byteArrayToInt(buf) buf = ByteArray(1) stream.read(buf, 0, buf.size) limitedCreditEnabled = buf[0] //http://www.skyetek.com/docs/m2/desfire.pdf //http://neteril.org/files/M075031_desfire.pdf } override fun writeToParcel(parcel: Parcel, flags: Int) { super.writeToParcel(parcel, flags) parcel.writeInt(lowerLimit) parcel.writeInt(upperLimit) parcel.writeInt(value) parcel.writeByte(limitedCreditEnabled) } } class UnsupportedDesfireFileSettings(fileType: Byte) : DesfireFileSettings(fileType, java.lang.Byte.MIN_VALUE, ByteArray(0)) companion object { /* DesfireFile Types */ internal const val STANDARD_DATA_FILE = 0x00.toByte() internal const val BACKUP_DATA_FILE = 0x01.toByte() internal const val VALUE_FILE = 0x02.toByte() internal const val LINEAR_RECORD_FILE = 0x03.toByte() internal const val CYCLIC_RECORD_FILE = 0x04.toByte() @Throws(DesfireException::class) fun create(data: ByteArray): DesfireFileSettings { val fileType = data[0] val stream = ByteArrayInputStream(data) return if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE) StandardDesfireFileSettings(stream) else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE) RecordDesfireFileSettings(stream) else if (fileType == VALUE_FILE) ValueDesfireFileSettings(stream) else throw DesfireException("Unknown file type: " + Integer.toHexString(fileType.toInt())) } @Suppress("unused") @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { override fun createFromParcel(source: Parcel): DesfireFileSettings { val fileType = source.readByte() val commSetting = source.readByte() val accessRights = ByteArray(source.readInt()) source.readByteArray(accessRights) if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE) { val fileSize = source.readInt() return StandardDesfireFileSettings(fileType, commSetting, accessRights, fileSize) } else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE) { val recordSize = source.readInt() val maxRecords = source.readInt() val curRecords = source.readInt() return RecordDesfireFileSettings( fileType, commSetting, accessRights, recordSize, maxRecords, curRecords ) } else { return UnsupportedDesfireFileSettings(fileType) } } override fun newArray(size: Int): Array { return arrayOfNulls(size) } } } }