Files
ChameleonBLEAPI/appmain/utils/mifare/ChameleonBatchAdapterImpl.java
2020-08-27 12:33:42 +08:00

180 lines
7.1 KiB
Java

package com.proxgrind.chameleon.utils.mifare;
import com.proxgrind.chameleon.packets.DataPackets;
import com.proxgrind.chameleon.utils.system.LogUtils;
import com.proxgrind.chameleon.utils.tools.HexUtil;
import com.proxgrind.devices.BleSerialControl;
import java.io.IOException;
import java.util.Arrays;
public class ChameleonBatchAdapterImpl implements BatchAdapter {
// 持有一个原生通讯的句柄!
private BleSerialControl control = BleSerialControl.get();
// 一个空的数组!
public static final byte[] EMPTY_DATA_ONE = new byte[]{0x00};
/**
* 读取函数,变色龙的读取实现,依赖起始块
*
* @param startBlock 起始块,如果当前的模式是读取单块模式,则此参数是需要读取的块
* @param isKeyA 当前是否是用keyA来验证并且读取的
* @param key 当前被用来读取的秘钥
* @param isReadSector 当前是否是读取全部扇区模式,注意,如果是读取扇区模式,
* 遇到4K的大扇区的情况下,需要进行每次4个块的读取,也就是
* 从startBlock开始表示读取第一个大块
* 从startBlock + 4 开始表示读取第二个大块
* 从startBlock + 8 开始表示读取第三个大块
* 从startBlock + 12 开始表示读取第四个大块
*/
@Override
public byte[][] read(int startBlock, boolean isKeyA, byte[] key, boolean isReadSector) throws IOException {
// 组包!
byte[] dataOfParam = HexUtil.bytesMerge(
getType(isReadSector ? 2 : 1), // 置入类型,如果当前是读取扇区,则设置操作标志为2,否则设置为1
EMPTY_DATA_ONE, // 置入状态码!
new byte[]{(byte) startBlock}, // 置入起始块!
isKeyA(isKeyA), // 置入当前验证的秘钥类型!
key, // 置入秘钥!
isReadSector ? new byte[16 * 4] : new byte[16] // 置入空字节
);
byte[] dataOfFinal = new DataPackets(0x72, dataOfParam).getData();
LogUtils.d("发送的数据长度: " + dataOfParam.length);
// 发送数据!
byte[] respDatas = sendAndReadResponse(dataOfFinal, dataOfFinal.length, dataOfParam.length);
if (respDatas != null && respDatas.length == dataOfParam.length) {
// 得到最终的秘钥索引,并且进行下标 -1 的内容返回!
byte status = respDatas[1];
checkTagStatus(status);
if (status == 0) {
LogUtils.d("读取失败");
return null;
}
if (status == 1) {
return HexUtil.splitBytes(
Arrays.copyOfRange(respDatas, 10, respDatas.length),
16
);
}
}
return null;
}
@Override
public boolean write(int sector, boolean isKeyA, byte[] key, byte[] dataGroup) throws IOException {
// 组包!
byte[] dataOfParam = HexUtil.bytesMerge(
getType(3), // 置入类型!
EMPTY_DATA_ONE, // 置入状态码!
new byte[]{(byte) sector}, // 置入扇区!
isKeyA(isKeyA), // 置入当前验证的秘钥类型!
key, // 置入秘钥!
dataGroup // 置入数据!
);
byte[] dataOfFinal = new DataPackets(0x72, dataOfParam).getData();
// 发送数据!
byte[] respDatas = sendAndReadResponse(dataOfFinal, dataOfFinal.length, dataOfParam.length);
if (respDatas != null && respDatas.length == dataOfParam.length) {
// 得到最终的秘钥索引,并且进行下标 -1 的内容返回!
byte status = respDatas[1];
checkTagStatus(status);
if (status == 0) {
LogUtils.d("写入失败");
return false;
}
return status == 1;
}
return false;
}
/**
* 经过测试,以35个秘钥为一组进行验证比较好!
*/
@Override
public byte[] verity(int sector, byte[][] keysGroup, boolean isKeyA) throws IOException {
// 组包!
byte[] dataOfParam = HexUtil.bytesMerge(
getType(0x04), // 置入类型! 04
EMPTY_DATA_ONE, // 置入状态码! 00
getBlock(sector), // 置入扇区! ~3F
isKeyA(isKeyA), // 置入当前验证的秘钥类型! 00
getKeyCount(keysGroup), // 获得当前的秘钥总数!
HexUtil.bytesMerge(keysGroup) // 合并秘钥!
);
byte[] dataOfFinal = new DataPackets(0x72, dataOfParam).getData();
byte[] respDatas = sendAndReadResponse(dataOfFinal, dataOfFinal.length, 11);
if (respDatas != null && respDatas.length == 11) {
// 得到最终的秘钥索引,并且进行下标 -1 的内容返回!
byte index = respDatas[1];
checkTagStatus(index);
// 只有当索引大于1的时候,才是真正的有应答,当应答FF的时候,则是卡片失联了
if (index > 0 && index <= keysGroup.length) {
return keysGroup[index - 1];
}
}
return null;
}
public void checkTagStatus(int statusCode) throws IOException {
if (statusCode == 0xFF || statusCode == -1) {
throw new IOException("Tag lost.");
}
}
public byte[] sendAndReadResponse(byte[] data, int dataLength, int acceptRespLength) throws IOException {
// 发送数据!
int timeout = getTimeout();
// 刷新缓冲区且发送
control.flush();
int maxLen = BleSerialControl.MTU;
if (control.write(data, 0, dataLength, timeout) == dataLength) {
// 读取应答数据
byte[] responseBuffer = new byte[acceptRespLength <= 0 ? maxLen : acceptRespLength];
// 直接读取
control.read(
responseBuffer,
0,
Math.max(acceptRespLength, 0),
timeout
);
return responseBuffer;
}
return null;
}
/**
* 获取byte数组型的类型,分别可能是
* 0 读取
* 1 写入
* 2 验证
*/
public byte[] getType(int type) {
return new byte[]{(byte) type};
}
/**
* 将单个字节的sector转为数组!
*/
public byte[] getBlock(int sector) {
return new byte[]{(byte) MfDataUtils.get_trailer_block(MfDataUtils.sectorToBlock(sector))};
}
public byte[] isKeyA(boolean isKeyA) {
return new byte[]{(byte) (isKeyA ? 0 : 1)};
}
public byte[] getKeyCount(byte[][] keys) {
return new byte[]{(byte) keys.length};
}
@Override
public int getTimeout() {
return 2333;
}
@Override
public void setTimeout(int timeout) {
}
}