|
|
@ -0,0 +1,248 @@ |
|
|
|
/** |
|
|
|
* DesfireFileSettings.kt |
|
|
|
* |
|
|
|
* Copyright (C) 2011 Eric Butler |
|
|
|
* Copyright (C) 2019 <seil0@mosad.xyz> |
|
|
|
* |
|
|
|
* Authors: |
|
|
|
* Eric Butler <eric@codebutler.com> |
|
|
|
* |
|
|
|
* 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 <http://www.gnu.org/licenses/>. |
|
|
|
*/ |
|
|
|
|
|
|
|
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<DesfireFileSettings> = object : Parcelable.Creator<DesfireFileSettings> { |
|
|
|
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<DesfireFileSettings?> { |
|
|
|
return arrayOfNulls(size) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |