You've already forked ChameleonBLEAPI
mirror of
https://github.com/RfidResearchGroup/ChameleonBLEAPI.git
synced 2026-05-12 11:20:47 -07:00
180 lines
7.1 KiB
Java
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) {
|
|
|
|
}
|
|
}
|