first working implementation of the mensa card reader

* based on farebot's desfire protocol implementation
* see #21
This commit is contained in:
2019-08-17 18:59:28 +02:00
parent 3a0a31f781
commit 733b675ffa
11 changed files with 1185 additions and 9 deletions

View File

@ -0,0 +1,77 @@
/*
* DesfireApplication.java
*
* Copyright (C) 2011 Eric Butler
*
* 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;
public class DesfireApplication implements Parcelable {
private int mId;
private DesfireFile[] mFiles;
public DesfireApplication (int id, DesfireFile[] files) {
mId = id;
mFiles = files;
}
public int getId () {
return mId;
}
public DesfireFile[] getFiles () {
return mFiles;
}
public DesfireFile getFile (int fileId) {
for (DesfireFile file : mFiles) {
if (file.getId() == fileId)
return file;
}
return null;
}
public static final Parcelable.Creator<DesfireApplication> CREATOR = new Parcelable.Creator<DesfireApplication>() {
public DesfireApplication createFromParcel(Parcel source) {
int id = source.readInt();
DesfireFile[] files = new DesfireFile[source.readInt()];
source.readTypedArray(files, DesfireFile.CREATOR);
return new DesfireApplication(id, files);
}
public DesfireApplication[] newArray (int size) {
return new DesfireApplication[size];
}
};
public void writeToParcel (Parcel parcel, int flags) {
parcel.writeInt(mId);
parcel.writeInt(mFiles.length);
parcel.writeTypedArray(mFiles, flags);
}
public int describeContents () {
return 0;
}
}

View File

@ -0,0 +1,13 @@
package com.codebutler.farebot.card.desfire;
/**
* Created by Jakob Wenzel on 16.11.13.
*/
public class DesfireException extends Exception {
public DesfireException(String message) {
super(message);
}
public DesfireException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,135 @@
/*
* DesfireFile.java
*
* Copyright (C) 2011 Eric Butler
*
* 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 org.apache.commons.lang3.ArrayUtils;
import android.os.Parcel;
import android.os.Parcelable;
import com.codebutler.farebot.card.desfire.DesfireFileSettings.RecordDesfireFileSettings;
public class DesfireFile implements Parcelable {
private int mId;
private DesfireFileSettings mSettings;
private byte[] mData;
public static DesfireFile create (int fileId, DesfireFileSettings fileSettings, byte[] fileData) {
if (fileSettings instanceof RecordDesfireFileSettings)
return new RecordDesfireFile(fileId, fileSettings, fileData);
else
return new DesfireFile(fileId, fileSettings, fileData);
}
private DesfireFile (int fileId, DesfireFileSettings fileSettings, byte[] fileData) {
mId = fileId;
mSettings = fileSettings;
mData = fileData;
}
public DesfireFileSettings getFileSettings () {
return mSettings;
}
public int getId () {
return mId;
}
public byte[] getData () {
return mData;
}
public static final Parcelable.Creator<DesfireFile> CREATOR = new Parcelable.Creator<DesfireFile>() {
public DesfireFile createFromParcel(Parcel source) {
int fileId = source.readInt();
boolean isError = (source.readInt() == 1);
if (!isError) {
DesfireFileSettings fileSettings = (DesfireFileSettings) source.readParcelable(DesfireFileSettings.class.getClassLoader());
int dataLength = source.readInt();
byte[] fileData = new byte[dataLength];
source.readByteArray(fileData);
return DesfireFile.create(fileId, fileSettings, fileData);
} else {
return new InvalidDesfireFile(fileId, source.readString());
}
}
public DesfireFile[] newArray (int size) {
return new DesfireFile[size];
}
};
public void writeToParcel (Parcel parcel, int flags) {
parcel.writeInt(mId);
if (this instanceof InvalidDesfireFile) {
parcel.writeInt(1);
parcel.writeString(((InvalidDesfireFile)this).getErrorMessage());
} else {
parcel.writeInt(0);
parcel.writeParcelable(mSettings, 0);
parcel.writeInt(mData.length);
parcel.writeByteArray(mData);
}
}
public int describeContents () {
return 0;
}
public static class RecordDesfireFile extends DesfireFile {
private DesfireRecord[] mRecords;
private RecordDesfireFile (int fileId, DesfireFileSettings fileSettings, byte[] fileData) {
super(fileId, fileSettings, fileData);
RecordDesfireFileSettings settings = (RecordDesfireFileSettings) fileSettings;
DesfireRecord[] records = new DesfireRecord[settings.curRecords];
for (int i = 0; i < settings.curRecords; i++) {
int offset = settings.recordSize * i;
records[i] = new DesfireRecord(ArrayUtils.subarray(getData(), offset, offset + settings.recordSize));
}
mRecords = records;
}
public DesfireRecord[] getRecords () {
return mRecords;
}
}
public static class InvalidDesfireFile extends DesfireFile {
private String mErrorMessage;
public InvalidDesfireFile (int fileId, String errorMessage) {
super(fileId, null, new byte[0]);
mErrorMessage = errorMessage;
}
public String getErrorMessage () {
return mErrorMessage;
}
}
}

View File

@ -0,0 +1,241 @@
/*
* DesfireFileSettings.java
*
* Copyright (C) 2011 Eric Butler
*
* 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 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<DesfireFileSettings> CREATOR = new Parcelable.Creator<DesfireFileSettings>() {
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]);
}
}
}

View File

@ -0,0 +1,173 @@
/*
* DesfireManufacturingData.java
*
* Copyright (C) 2011 Eric Butler
*
* 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.w3c.dom.Element;
import java.io.ByteArrayInputStream;
public class DesfireManufacturingData implements Parcelable {
public final int hwVendorID;
public final int hwType;
public final int hwSubType;
public final int hwMajorVersion;
public final int hwMinorVersion;
public final int hwStorageSize;
public final int hwProtocol;
public final int swVendorID;
public final int swType;
public final int swSubType;
public final int swMajorVersion;
public final int swMinorVersion;
public final int swStorageSize;
public final int swProtocol;
public final int uid;
public final int batchNo;
public final int weekProd;
public final int yearProd;
public DesfireManufacturingData (byte[] data) {
ByteArrayInputStream stream = new ByteArrayInputStream(data);
hwVendorID = stream.read();
hwType = stream.read();
hwSubType = stream.read();
hwMajorVersion = stream.read();
hwMinorVersion = stream.read();
hwStorageSize = stream.read();
hwProtocol = stream.read();
swVendorID = stream.read();
swType = stream.read();
swSubType = stream.read();
swMajorVersion = stream.read();
swMinorVersion = stream.read();
swStorageSize = stream.read();
swProtocol = stream.read();
// FIXME: This has fewer digits than what's contained in EXTRA_ID, why?
byte[] buf = new byte[7];
stream.read(buf, 0, buf.length);
uid = Utils.byteArrayToInt(buf);
// FIXME: This is returning a negative number. Probably is unsigned.
buf = new byte[5];
stream.read(buf, 0, buf.length);
batchNo = Utils.byteArrayToInt(buf);
// FIXME: These numbers aren't making sense.
weekProd = stream.read();
yearProd = stream.read();
}
public static DesfireManufacturingData fromXml (Element element) {
return new DesfireManufacturingData(element);
}
private DesfireManufacturingData (Element element) {
hwVendorID = Integer.parseInt(element.getElementsByTagName("hw-vendor-id").item(0).getTextContent());
hwType = Integer.parseInt(element.getElementsByTagName("hw-type").item(0).getTextContent());
hwSubType = Integer.parseInt(element.getElementsByTagName("hw-sub-type").item(0).getTextContent());
hwMajorVersion = Integer.parseInt(element.getElementsByTagName("hw-major-version").item(0).getTextContent());
hwMinorVersion = Integer.parseInt(element.getElementsByTagName("hw-minor-version").item(0).getTextContent());
hwStorageSize = Integer.parseInt(element.getElementsByTagName("hw-storage-size").item(0).getTextContent());
hwProtocol = Integer.parseInt(element.getElementsByTagName("hw-protocol").item(0).getTextContent());
swVendorID = Integer.parseInt(element.getElementsByTagName("sw-vendor-id").item(0).getTextContent());
swType = Integer.parseInt(element.getElementsByTagName("sw-type").item(0).getTextContent());
swSubType = Integer.parseInt(element.getElementsByTagName("sw-sub-type").item(0).getTextContent());
swMajorVersion = Integer.parseInt(element.getElementsByTagName("sw-major-version").item(0).getTextContent());
swMinorVersion = Integer.parseInt(element.getElementsByTagName("sw-minor-version").item(0).getTextContent());
swStorageSize = Integer.parseInt(element.getElementsByTagName("sw-storage-size").item(0).getTextContent());
swProtocol = Integer.parseInt(element.getElementsByTagName("sw-protocol").item(0).getTextContent());
uid = Integer.parseInt(element.getElementsByTagName("uid").item(0).getTextContent());
batchNo = Integer.parseInt(element.getElementsByTagName("batch-no").item(0).getTextContent());
weekProd = Integer.parseInt(element.getElementsByTagName("week-prod").item(0).getTextContent());
yearProd = Integer.parseInt(element.getElementsByTagName("year-prod").item(0).getTextContent());
}
private DesfireManufacturingData (Parcel parcel) {
hwVendorID = parcel.readInt();
hwType = parcel.readInt();
hwSubType = parcel.readInt();
hwMajorVersion = parcel.readInt();
hwMinorVersion = parcel.readInt();
hwStorageSize = parcel.readInt();
hwProtocol = parcel.readInt();
swVendorID = parcel.readInt();
swType = parcel.readInt();
swSubType = parcel.readInt();
swMajorVersion = parcel.readInt();
swMinorVersion = parcel.readInt();
swStorageSize = parcel.readInt();
swProtocol = parcel.readInt();
uid = parcel.readInt();
batchNo = parcel.readInt();
weekProd = parcel.readInt();
yearProd = parcel.readInt();
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(hwVendorID);
parcel.writeInt(hwType);
parcel.writeInt(hwSubType);
parcel.writeInt(hwMajorVersion);
parcel.writeInt(hwMinorVersion);
parcel.writeInt(hwStorageSize);
parcel.writeInt(hwProtocol);
parcel.writeInt(swVendorID);
parcel.writeInt(swType);
parcel.writeInt(swSubType);
parcel.writeInt(swMajorVersion);
parcel.writeInt(swMinorVersion);
parcel.writeInt(swStorageSize);
parcel.writeInt(swProtocol);
parcel.writeInt(uid);
parcel.writeInt(batchNo);
parcel.writeInt(weekProd);
parcel.writeInt(yearProd);
}
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<DesfireManufacturingData> CREATOR = new Parcelable.Creator<DesfireManufacturingData>() {
public DesfireManufacturingData createFromParcel(Parcel source) {
return new DesfireManufacturingData(source);
}
public DesfireManufacturingData[] newArray(int size) {
return new DesfireManufacturingData[size];
}
};
}

View File

@ -0,0 +1,188 @@
/*
* DesfireProtocol.java
*
* Copyright (C) 2011 Eric Butler
*
* 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.nfc.tech.IsoDep;
import com.codebutler.farebot.Utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.commons.lang3.ArrayUtils;
public class DesfireProtocol {
/* Commands */
static final byte GET_MANUFACTURING_DATA = (byte) 0x60;
static final byte GET_APPLICATION_DIRECTORY = (byte) 0x6A;
static final byte GET_ADDITIONAL_FRAME = (byte) 0xAF;
static final byte SELECT_APPLICATION = (byte) 0x5A;
static final byte READ_DATA = (byte) 0xBD;
static final byte READ_RECORD = (byte) 0xBB;
static final byte READ_VALUE = (byte) 0x6C;
static final byte GET_FILES = (byte) 0x6F;
static final byte GET_FILE_SETTINGS = (byte) 0xF5;
/* Status codes */
static final byte OPERATION_OK = (byte) 0x00;
static final byte PERMISSION_DENIED = (byte) 0x9D;
static final byte ADDITIONAL_FRAME = (byte) 0xAF;
private IsoDep mTagTech;
public DesfireProtocol(IsoDep tagTech) {
mTagTech = tagTech;
}
public DesfireManufacturingData getManufacturingData() throws DesfireException {
byte[] respBuffer = sendRequest(GET_MANUFACTURING_DATA);
if (respBuffer.length != 28)
throw new DesfireException("Invalid response");
return new DesfireManufacturingData(respBuffer);
}
public int[] getAppList() throws DesfireException {
byte[] appDirBuf = sendRequest(GET_APPLICATION_DIRECTORY);
int[] appIds = new int[appDirBuf.length / 3];
for (int app = 0; app < appDirBuf.length; app += 3) {
byte[] appId = new byte[3];
System.arraycopy(appDirBuf, app, appId, 0, 3);
appIds[app / 3] = Utils.byteArrayToInt(appId);
}
return appIds;
}
public void selectApp (int appId) throws DesfireException {
byte[] appIdBuff = new byte[3];
appIdBuff[0] = (byte) ((appId & 0xFF0000) >> 16);
appIdBuff[1] = (byte) ((appId & 0xFF00) >> 8);
appIdBuff[2] = (byte) (appId & 0xFF);
sendRequest(SELECT_APPLICATION, appIdBuff);
}
public int[] getFileList() throws DesfireException {
byte[] buf = sendRequest(GET_FILES);
int[] fileIds = new int[buf.length];
for (int x = 0; x < buf.length; x++) {
fileIds[x] = (int)buf[x];
}
return fileIds;
}
public DesfireFileSettings getFileSettings (int fileNo) throws DesfireException {
byte[] data = new byte[0];
data = sendRequest(GET_FILE_SETTINGS, new byte[] { (byte) fileNo });
return DesfireFileSettings.Create(data);
}
public byte[] readFile (int fileNo) throws DesfireException {
return sendRequest(READ_DATA, new byte[] {
(byte) fileNo,
(byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0
});
}
public byte[] readRecord (int fileNum) throws DesfireException {
return sendRequest(READ_RECORD, new byte[]{
(byte) fileNum,
(byte) 0x0, (byte) 0x0, (byte) 0x0,
(byte) 0x0, (byte) 0x0, (byte) 0x0
});
}
public int readValue(int fileNum) throws DesfireException {
byte[] buf = sendRequest(READ_VALUE, new byte[]{
(byte) fileNum
});
ArrayUtils.reverse(buf);
return Utils.byteArrayToInt(buf);
}
private byte[] sendRequest (byte command) throws DesfireException {
return sendRequest(command, null);
}
private byte[] sendRequest (byte command, byte[] parameters) throws DesfireException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] recvBuffer = new byte[0];
try {
recvBuffer = mTagTech.transceive(wrapMessage(command, parameters));
} catch (IOException e) {
throw new DesfireException(e);
}
while (true) {
if (recvBuffer[recvBuffer.length - 2] != (byte) 0x91)
throw new DesfireException("Invalid response");
output.write(recvBuffer, 0, recvBuffer.length - 2);
byte status = recvBuffer[recvBuffer.length - 1];
if (status == OPERATION_OK) {
break;
} else if (status == ADDITIONAL_FRAME) {
try {
recvBuffer = mTagTech.transceive(wrapMessage(GET_ADDITIONAL_FRAME, null));
} catch (IOException e) {
throw new DesfireException(e);
}
} else if (status == PERMISSION_DENIED) {
throw new DesfireException("Permission denied");
} else {
throw new DesfireException("Unknown status code: " + Integer.toHexString(status & 0xFF));
}
}
return output.toByteArray();
}
private byte[] wrapMessage (byte command, byte[] parameters) throws DesfireException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
stream.write((byte) 0x90);
stream.write(command);
stream.write((byte) 0x00);
stream.write((byte) 0x00);
if (parameters != null) {
stream.write((byte) parameters.length);
try {
stream.write(parameters);
} catch (IOException e) {
throw new DesfireException(e);
}
}
stream.write((byte) 0x00);
return stream.toByteArray();
}
}

View File

@ -0,0 +1,35 @@
/*
* DesfireRecord.java
*
* Copyright (C) 2011 Eric Butler
*
* 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;
public class DesfireRecord {
private byte[] mData;
public DesfireRecord (byte[] data) {
mData = data;
}
public byte[] getData () {
return mData;
}
}