Added support for mifare classic value block operations

This commit is contained in:
taichunmin
2023-11-13 23:38:56 +08:00
parent 4d670f051a
commit e79568298f
9 changed files with 494 additions and 43 deletions
+1
View File
@@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Added support for mifare classic value block operations (@taichunmin)
- Added regression tests (@doegox)
- Changed git version to version tag instead of dev tag (@taichunmin)
- Fixed 14A emulate bug and MF1 emulate bug (@spp2000 and @xianglin1998)
+4
View File
@@ -290,6 +290,10 @@ Notes:
* `reserved`:2
* Response: data sent by the card
* CLI: cf `hf 14a raw`
### 2011: MF1_MANIPULATE_VALUE_BLOCK
* Command: 21 bytes: `src_type|src_block|src_key[6]|operator|operand[4]|dst_type|dst_block|dst_key[6]`. Key as 6 bytes. Type=`0x60` for key A, `0x61` for key B. Operator=`0xC0` for decrement, `0xC1` for increment, `0xC2` for restore. Operand as I32 in Network byte order.
* Response: no data
* CLI: cf `hf mf value`
### 3000: EM410X_SCAN
* Command: no data
* Response: 5 bytes. `id[5]`. ID as 5 bytes.
+53
View File
@@ -447,6 +447,58 @@ static data_frame_tx_t *cmd_processor_hf14a_raw(uint16_t cmd, uint16_t status, u
return data_frame_make(cmd, status, resp_length, resp);
}
static data_frame_tx_t *cmd_processor_mf1_manipulate_value_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
typedef struct {
uint8_t src_type;
uint8_t src_block;
uint8_t src_key[6];
uint8_t operator;
uint32_t operand;
uint8_t dst_type;
uint8_t dst_block;
uint8_t dst_key[6];
} PACKED payload_t;
if (length != sizeof(payload_t)) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
payload_t *payload = (payload_t *)data;
// scan tag
picc_14a_tag_t taginfo;
if (pcd_14a_reader_scan_auto(&taginfo) != STATUS_HF_TAG_OK) {
return data_frame_make(cmd, STATUS_HF_TAG_NO, 0, NULL);
}
// auth src
status = pcd_14a_reader_mf1_auth(&taginfo, payload->src_type, payload->src_block, payload->src_key);
if (status != STATUS_HF_TAG_OK) {
return data_frame_make(cmd, status, 0, NULL);
}
// value block operation
status = pcd_14a_reader_mf1_manipulate_value_block(payload->operator, payload->src_block, (int32_t) U32NTOHL(payload->operand));
if (status != STATUS_HF_TAG_OK) {
return data_frame_make(cmd, status, 0, NULL);
}
// auth dst if needed
if (payload->src_block != payload->dst_block || payload->src_type != payload->dst_type) {
status = pcd_14a_reader_mf1_auth(&taginfo, payload->dst_type, payload->dst_block, payload->dst_key);
if (status != STATUS_HF_TAG_OK) {
return data_frame_make(cmd, status, 0, NULL);
}
}
// transfer value block
status = pcd_14a_reader_mf1_transfer_value_block(payload->dst_block);
if (status != STATUS_HF_TAG_OK) {
return data_frame_make(cmd, status, 0, NULL);
}
return data_frame_make(cmd, status, 0, NULL);
}
static data_frame_tx_t *cmd_processor_em410x_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
uint8_t id_buffer[5] = { 0x00 };
status = PcdScanEM410X(id_buffer);
@@ -1015,6 +1067,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_MF1_READ_ONE_BLOCK, before_hf_reader_run, cmd_processor_mf1_read_one_block, after_hf_reader_run },
{ DATA_CMD_MF1_WRITE_ONE_BLOCK, before_hf_reader_run, cmd_processor_mf1_write_one_block, after_hf_reader_run },
{ DATA_CMD_HF14A_RAW, before_reader_run, cmd_processor_hf14a_raw, NULL },
{ DATA_CMD_MF1_MANIPULATE_VALUE_BLOCK, before_hf_reader_run, cmd_processor_mf1_manipulate_value_block, after_hf_reader_run },
{ DATA_CMD_EM410X_SCAN, before_reader_run, cmd_processor_em410x_scan, NULL },
{ DATA_CMD_EM410X_WRITE_TO_T55XX, before_reader_run, cmd_processor_em410x_write_to_t55XX, NULL },
+1
View File
@@ -66,6 +66,7 @@
#define DATA_CMD_MF1_READ_ONE_BLOCK (2008)
#define DATA_CMD_MF1_WRITE_ONE_BLOCK (2009)
#define DATA_CMD_HF14A_RAW (2010)
#define DATA_CMD_MF1_MANIPULATE_VALUE_BLOCK (2011)
//
// ******************************************************************
+245 -36
View File
@@ -256,15 +256,13 @@ uint16_t pcd_14a_reader_timeout_get() {
* POUTLENBIT: Bit the length of the data
* @retval : Status value mi_ok, successful
*/
uint8_t pcd_14a_reader_bytes_transfer(uint8_t Command, uint8_t *pIn, uint8_t InLenByte, uint8_t *pOut, uint16_t *pOutLenBit, uint16_t maxOutLenBit) {
uint8_t pcd_14a_reader_bytes_transfer(uint8_t Command, uint8_t *pIn, uint8_t InLenByte, uint8_t *pOut, uint16_t *pOutLenBit, uint16_t maxOutLenBit) {
uint8_t status = STATUS_HF_ERR_STAT;
uint8_t waitFor = 0x00;
uint8_t lastBits = 0;
uint8_t n = 0;
uint8_t pcd_err_val = 0;
uint8_t not_timeout = 0;
// Reset the length of the received data
*pOutLenBit = 0;
switch (Command) {
case PCD_AUTHENT: // MiFare certification
@@ -292,6 +290,8 @@ uint8_t pcd_14a_reader_bytes_transfer(uint8_t Command, uint8_t *pIn, uint8_t In
while ((read_register_single(Status2Reg) & 0x07) == 0x03);
return STATUS_HF_TAG_OK;
}
// Reset the length of the received data
*pOutLenBit = 0;
bsp_set_timer(g_timeout_auto_timer, 0); // Before starting the operation, return to zero over time counting
@@ -482,69 +482,189 @@ uint8_t pcd_14a_reader_bits_transfer(uint8_t *pTx, uint16_t szTxBits, uint8_t *
return STATUS_HF_TAG_OK;
}
/**
* @brief : Through RC522 and ISO14443 cartoon communication
* @param : Command: RC522 command word
* PIN: Data sent to the card through RC522
* Inlenbyte: The byte length of sending the data
* POUT: The receiving card returns the data
* POUTLENBIT: Bit the length of the data
* FLAGS:
* - PCD_TRANSMIT_FLAG_NO_RESET_MF_CRYPTO1_ON: Do not reset MFCrypto1On
* @retval : Status value mi_ok, successful
*/
uint8_t pcd_14a_reader_bytes_transfer_flags(uint8_t Command, uint8_t *pIn, uint8_t InLenByte, uint8_t *pOut, uint16_t *pOutLenBit, uint16_t maxOutLenBit, uint32_t flags) {
uint8_t status = STATUS_HF_ERR_STAT;
uint8_t waitFor = 0x00;
uint8_t lastBits = 0;
uint8_t n = 0;
uint8_t pcd_err_val = 0;
uint8_t not_timeout = 0;
switch (Command) {
case PCD_AUTHENT: // MiFare certification
waitFor = 0x10; // Query the free interrupt logo when the certification card is waiting
break;
case PCD_TRANSCEIVE:
waitFor = 0x30; // Inquiry the receiving interrupt logo position and Leisure interrupt logo
break;
}
write_register_single(CommandReg, PCD_IDLE); // Flushbuffer clearing the internal FIFO read and writing pointer and ErRreg's Bufferovfl logo position is cleared
clear_register_mask(ComIrqReg, 0x80); // When Set1 is cleared, the shielding position of commonricqreg is clear zero
set_register_mask(FIFOLevelReg, 0x80); // Write an empty order
write_register_buffer(FIFODataReg, pIn, InLenByte); // Write data into FIFODATA
write_register_single(CommandReg, Command); // Write command
if (Command == PCD_TRANSCEIVE) {
set_register_mask(BitFramingReg, 0x80); // StartSend places to start the data to send this bit and send and receive commands when it is valid
}
if (pOut == NULL) {
// If the developer does not need to receive data, then return directly after the sending!
while ((read_register_single(Status2Reg) & 0x07) == 0x03);
return STATUS_HF_TAG_OK;
}
// Reset the length of the received data
*pOutLenBit = 0;
bsp_set_timer(g_timeout_auto_timer, 0); // Before starting the operation, return to zero over time counting
do {
n = read_register_single(ComIrqReg); // Read the communication interrupt register to determine whether the current IO task is completed!
not_timeout = NO_TIMEOUT_1MS(g_timeout_auto_timer, g_com_timeout_ms);
} while (not_timeout && (!(n & waitFor))); // Exit conditions: timeout interruption, interrupt with empty command commands
// NRF_LOG_INFO("N = %02x\n", n);
if (Command == PCD_TRANSCEIVE) {
clear_register_mask(BitFramingReg, 0x80); // Clean up allows the startsend bit and the bit length position
}
// Whether to receive timeout
if (not_timeout) {
// First determine whether there is a place where there is an error register
if (n & 0x02) {
// Error occur
// Read an error logo register BufferOfI CollErr ParityErr ProtocolErr
pcd_err_val = read_register_single(ErrorReg);
// Detect whether there are abnormalities
if (pcd_err_val & 0x01) { // ProtocolErr Error only appears in the following two cases:
if (Command == PCD_AUTHENT) { // During the execution of the MFAUTHENT command, if the number of bytes received by a data stream, the position of the place
// Therefore, we need to deal with it well, assuming that there are problems during the verification process, then we need to think that this is normal
status = STATUS_MF_ERR_AUTH;
} else { // If the SOF is wrong, the position is set up and the receiver is automatically cleared during the start -up stage, which is effective at the rate of 106kbd
NRF_LOG_INFO("Protocol error\n");
status = STATUS_HF_ERR_STAT;
}
} else if (pcd_err_val & 0x02) {
// Detecting whether there are even strange errors
NRF_LOG_INFO("Parity error\n");
status = STATUS_HF_ERR_PARITY;
} else if (pcd_err_val & 0x04) { // Detect whether there are CRC errors
NRF_LOG_INFO("CRC error\n");
status = STATUS_HF_ERR_CRC;
} else if (pcd_err_val & 0x08) { // There is a conflict to detect the label
NRF_LOG_INFO("Collision tag\n");
status = STATUS_HF_COLLISION;
} else { // There are other unrepaired abnormalities
NRF_LOG_INFO("HF error: 0x%0x2\n", pcd_err_val);
status = STATUS_HF_ERR_STAT;
}
} else {
// Occasionally occur
// NRF_LOG_INFO("COM OK\n");
if (Command == PCD_TRANSCEIVE) {
n = read_register_single(FIFOLevelReg); // Read the number of bytes saved in FIFO
if (n == 0) { n = 1; }
lastBits = read_register_single(Control522Reg) & 0x07; // Finally receive the validity of the byte
if (lastBits) { *pOutLenBit = (n - 1) * 8 + lastBits; } // N -byte number minus 1 (last byte)+ the number of bits of the last bit The total number of data readings read
else { *pOutLenBit = n * 8; } // Finally received the entire bytes received by the byte valid
if (*pOutLenBit <= maxOutLenBit) {
// Read all the data in FIFO
read_register_buffer(FIFODataReg, pOut, n);
// Transmission instructions can be considered success when reading normal data!
status = STATUS_HF_TAG_OK;
} else {
NRF_LOG_INFO("pcd_14a_reader_bytes_transfer receive response overflow: %d, max = %d\n", *pOutLenBit, maxOutLenBit);
// We can't pass the problem with problems, which is meaningless for the time being
*pOutLenBit = 0;
// Since there is a problem with the data, let's notify the upper layer and inform me
status = STATUS_HF_ERR_STAT;
}
} else {
// Non -transmitted instructions, the execution is completed without errors and considered success!
status = STATUS_HF_TAG_OK;
}
}
} else {
status = STATUS_HF_TAG_NO;
// NRF_LOG_INFO("Tag lost(timeout).\n");
}
if ((flags & PCD_TRANSMIT_FLAG_NO_RESET_MF_CRYPTO1_ON) == 0 && status != STATUS_HF_TAG_OK) {
// If there are certain operations,
// We may need to remove MFCrypto1On This register logo,
// Because it may be because of the error encryption communication caused by verification
clear_register_mask(Status2Reg, 0x08);
}
// NRF_LOG_INFO("Com status: %d\n", status);
return status;
}
/**
* @brief : ISO14443-A Fast Select
* @param :tag: tag info buffer
* @retval : if return STATUS_HF_TAG_OK, the tag is selected.
*/
uint8_t pcd_14a_reader_fast_select(picc_14a_tag_t *tag) {
uint8_t resp[5] = {0}; // theoretically. A usual RATS will be much smaller
uint8_t uid_resp[4] = {0};
uint8_t sak = 0x04; // cascade uid
uint8_t pcd_14a_reader_fast_select(picc_14a_tag_t *tag) {
uint8_t dat_buff[9] = { 0x00 };
uint8_t status = STATUS_HF_TAG_OK;
uint8_t cascade_level = 0;
uint16_t len;
uint16_t dat_len;
// Wakeup
if (pcd_14a_reader_atqa_request(resp, NULL, U8ARR_BIT_LEN(resp)) != STATUS_HF_TAG_OK) {
if (pcd_14a_reader_atqa_request(dat_buff, NULL, U8ARR_BIT_LEN(dat_buff)) != STATUS_HF_TAG_OK) {
return STATUS_HF_TAG_NO;
}
// OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in
// which case we need to make a cascade 2 request and select - this is a long UID
// While the UID is not complete, the 3nd bit (from the right) is set in the SAK.
for (; sak & 0x04; cascade_level++) {
// uint8_t sel_all[] = { PICC_ANTICOLL1, 0x20 };
uint8_t sel_uid[] = { PICC_ANTICOLL1, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
for (; cascade_level < tag->cascade; cascade_level++) {
// Construct SELECT UID command (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC)
// SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97)
sel_uid[0] = /*sel_all[0] = */ 0x93 + cascade_level * 2;
dat_buff[0] = PICC_ANTICOLL1 + cascade_level * 2;
dat_buff[1] = 0x70; // transmitting a full UID
// Copy the UID information of the tag to the dat_buff buffer
if (cascade_level < tag->cascade - 1) {
uid_resp[0] = 0x88;
memcpy(uid_resp + 1, tag->uid + cascade_level * 3, 3);
dat_buff[2] = 0x88;
memcpy(dat_buff + 3, tag->uid + cascade_level * 3, 3);
} else {
memcpy(uid_resp, tag->uid + cascade_level * 3, 4);
memcpy(dat_buff + 2, tag->uid + cascade_level * 3, 4);
}
// Construct SELECT UID command
//sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC)
memcpy(sel_uid + 2, uid_resp, 4); // the UID received during anticollision, or the provided UID
sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC
crc_14a_append(sel_uid, 7); // calculate and add CRC
status = pcd_14a_reader_bytes_transfer(PCD_TRANSCEIVE, sel_uid, sizeof(sel_uid), resp, &len, U8ARR_BIT_LEN(resp));
dat_buff[6] = dat_buff[2] ^ dat_buff[3] ^ dat_buff[4] ^ dat_buff[5]; // calculate BCC
crc_14a_append(dat_buff, 7); // calculate and add CRC
status = pcd_14a_reader_bytes_transfer(PCD_TRANSCEIVE, dat_buff, sizeof(dat_buff), dat_buff, &dat_len, U8ARR_BIT_LEN(dat_buff));
// Receive the SAK
if (status != STATUS_HF_TAG_OK || !len) {
// printf("SAK Err: %d, %d\r\n", status, recv_len);
if (status != STATUS_HF_TAG_OK || !dat_len) {
// printf("SAK Err: %d, %d\r\n", status, dat_len);
return STATUS_HF_TAG_NO;
}
sak = resp[0];
// Test if more parts of the uid are coming
if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) {
// Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of:
// http://www.nxp.com/documents/application_note/AN10927.pdf
uid_resp[0] = uid_resp[1];
uid_resp[1] = uid_resp[2];
uid_resp[2] = uid_resp[3];
}
}
return STATUS_HF_TAG_OK;
}
/**
* @brief : ISO14443-A Find a card, only execute once!
* @param :tag: Buffer that stores card information
* @param : tag: Buffer that stores card information
* @retval : Status value hf_tag_ok, success
*/
uint8_t pcd_14a_reader_scan_once(picc_14a_tag_t *tag) {
@@ -978,6 +1098,95 @@ uint8_t pcd_14a_reader_mf1_write(uint8_t addr, uint8_t *p) {
return pcd_14a_reader_mf1_write_by_cmd(PICC_WRITE, addr, p);
}
/**
* @brief : Increment: increments the contents of a block and stores the result in the internal Transfer Buffer
* Decrement: decrements the contents of a block and stores the result in the internal Transfer Buffer
* Restore: reads the contents of a block into the internal Transfer Buffer
* @param : Operator: Increment, Decrement, Restore
* Addr: block address
* Operand: The written data, I32
* @retval : Status value hf_tag_ok, success
*/
uint8_t pcd_14a_reader_mf1_manipulate_value_block(uint8_t operator, uint8_t addr, int32_t operand) {
// operator can only be PICC_DECREMENT, PICC_INCREMENT, PICC_RESTORE
if (operator != PICC_DECREMENT && operator != PICC_INCREMENT && operator != PICC_RESTORE) {
return STATUS_PAR_ERR;
}
uint8_t status;
uint16_t dat_len;
// Prepare the cmd data to manipulate the value block
uint8_t dat_buff[6] = { operator, addr };
crc_14a_append(dat_buff, 2);
// NRF_LOG_INFO("0 pcd_14a_reader_mf1_manipulate_value_block addr = %d\r\n", addr);
// Request to manipulate block, at this time, the card should reply to ACK
status = pcd_14a_reader_bytes_transfer(PCD_TRANSCEIVE, dat_buff, 4, dat_buff, &dat_len, U8ARR_BIT_LEN(dat_buff));
// The communication fails, the reason is returned directly
if (status != STATUS_HF_TAG_OK) {
return status;
}
// The communication was successful, but the operation was rejected by the card!
if ((dat_len != 4) || ((dat_buff[0] & 0x0F) != 0x0A)) {
// NRF_LOG_INFO("1 status = %d, datalen = %d, data = %02x\n", status, dat_len, dat_buff[0]);
return STATUS_HF_ERR_STAT;
}
// The communication was successful, the card accepted the card value block operand
// 1. Copy data and calculate CRC
memcpy(dat_buff, &operand, 4);
crc_14a_append(dat_buff, 4);
// NRF_LOG_INFO_hex("Will send: ", (uint8_t *)dat_buff, 6);
// NRF_LOG_INFO("\n");
// 2. Transfer the operand to complete the value block manipulation
status = pcd_14a_reader_bytes_transfer_flags(
PCD_TRANSCEIVE,
dat_buff,
6,
dat_buff,
&dat_len,
U8ARR_BIT_LEN(dat_buff),
PCD_TRANSMIT_FLAG_NO_RESET_MF_CRYPTO1_ON);
// Operand Part of Increment/Decrement/Restore does not acknowledge, so Timeout means success
if (status != STATUS_HF_TAG_NO || dat_len != 0) {
return status == STATUS_HF_TAG_OK ? STATUS_HF_ERR_STAT : status;
}
return STATUS_HF_TAG_OK;
}
/**
* @brief : Writes the contents of the internal Transfer Buffer to a block
* @param : cmd : Transfer instruction
* addr: block address
* @retval : Status value hf_tag_ok, success
*/
uint8_t pcd_14a_reader_mf1_transfer_value_block(uint8_t addr) {
uint8_t status;
uint16_t dat_len;
uint8_t dat_buff[4] = { PICC_TRANSFER, addr };
// Short data directly MCU calculate
crc_14a_append(dat_buff, 2);
// Then initiate communication
status = pcd_14a_reader_bytes_transfer(PCD_TRANSCEIVE, dat_buff, 4, dat_buff, &dat_len, U8ARR_BIT_LEN(dat_buff));
// The communication fails, the reason is returned directly
if (status != STATUS_HF_TAG_OK) {
return status;
}
// The communication was successful, but the operation was rejected by the card!
if ((dat_len != 4) || ((dat_buff[0] & 0x0F) != 0x0A)) {
// NRF_LOG_INFO("1 status = %d, datalen = %d, data = %02x\n", status, dat_len, dat_buff[0]);
return STATUS_HF_ERR_STAT;
}
return STATUS_HF_TAG_OK;
}
/**
* @brief : Let the card enter the dormant mode
* @param :none
@@ -19,6 +19,11 @@
#define PCD_RESET 0x0F //Restoration
#define PCD_CALCCRC 0x03 //CRC calculation
/**
* flags for pcd_14a_reader_bytes_transfer_flags
*/
#define PCD_TRANSMIT_FLAG_NO_RESET_MF_CRYPTO1_ON 0x01 // do not clear MFCrypto1On when status != STATUS_HF_TAG_OK
/*
* isO14443ACommandWord
*/
@@ -190,6 +195,13 @@ uint8_t pcd_14a_reader_bytes_transfer(uint8_t Command,
uint8_t *pOut,
uint16_t *pOutLenBit,
uint16_t maxOutLenBit);
uint8_t pcd_14a_reader_bytes_transfer_flags(uint8_t Command,
uint8_t *pIn,
uint8_t InLenByte,
uint8_t *pOut,
uint16_t *pOutLenBit,
uint16_t maxOutLenBit,
uint32_t flags);
uint8_t pcd_14a_reader_bits_transfer(uint8_t *pTx,
uint16_t szTxBits,
uint8_t *pTxPar,
@@ -217,6 +229,9 @@ uint8_t pcd_14a_reader_mf1_write(uint8_t addr, uint8_t *pData);
// cardReadingOperation
uint8_t pcd_14a_reader_mf1_read_by_cmd(uint8_t cmd, uint8_t addr, uint8_t *p);
uint8_t pcd_14a_reader_mf1_read(uint8_t addr, uint8_t *pData);
// value block operation
uint8_t pcd_14a_reader_mf1_manipulate_value_block(uint8_t operator, uint8_t addr, int32_t operand);
uint8_t pcd_14a_reader_mf1_transfer_value_block(uint8_t addr);
// Formation card operation
uint8_t pcd_14a_reader_halt_tag(void);
void pcd_14a_reader_fast_halt_tag(void);
+139 -1
View File
@@ -21,7 +21,7 @@ from chameleon_utils import CLITree
from chameleon_utils import CR, CG, CB, CC, CY, C0
from chameleon_enum import Command, Status, SlotNumber, TagSenseType, TagSpecificType
from chameleon_enum import MifareClassicWriteMode, MifareClassicPrngType, MifareClassicDarksideStatus, MfcKeyType
from chameleon_enum import AnimationMode, ButtonType, ButtonPressFunction
from chameleon_enum import AnimationMode, ButtonPressFunction, ButtonType, MfcValueBlockOperator
# NXP IDs based on https://www.nxp.com/docs/en/application-note/AN10833.pdf
type_id_SAK_dict = {0x00: "MIFARE Ultralight Classic/C/EV1/Nano | NTAG 2xx",
@@ -884,6 +884,144 @@ class HFMFWRBL(MF1AuthArgsUnit):
print(f" - {CR}Write fail.{C0}")
@hf_mf.command('value')
class HFMFVALUE(ReaderRequiredUnit):
def args_parser(self) -> ArgumentParserNoExit:
parser = ArgumentParserNoExit()
parser.description = 'MIFARE Classic value block commands'
operator_group = parser.add_mutually_exclusive_group()
operator_group.add_argument('--get', action='store_true', help="get value from src block")
operator_group.add_argument('--set', type=int, required=False, metavar="<dec>",
help="set value X (-2147483647 ~ 2147483647) to src block")
operator_group.add_argument('--inc', type=int, required=False, metavar="<dec>",
help="increment value by X (0 ~ 2147483647) from src to dst")
operator_group.add_argument('--dec', type=int, required=False, metavar="<dec>",
help="decrement value by X (0 ~ 2147483647) from src to dst")
operator_group.add_argument('--res', '--cp', action='store_true', help="copy value from src to dst (Restore and Transfer)")
parser.add_argument('--blk', '--src-block', type=int, required=True, metavar="<dec>",
help="block number of src")
srctype_group = parser.add_mutually_exclusive_group()
srctype_group.add_argument('-a', '-A', action='store_true', help="key of src is A key (default)")
srctype_group.add_argument('-b', '-B', action='store_true', help="key of src is B key")
parser.add_argument('-k', '--src-key', type=str, required=True, metavar="<hex>", help="key of src")
parser.add_argument('--tblk', '--dst-block', type=int, metavar="<dec>",
help="block number of dst (default to src)")
dsttype_group = parser.add_mutually_exclusive_group()
dsttype_group.add_argument('--ta', '--tA', action='store_true', help="key of dst is A key (default to src)")
dsttype_group.add_argument('--tb', '--tB', action='store_true', help="key of dst is B key (default to src)")
parser.add_argument('--tkey', '--dst-key', type=str, metavar="<hex>", help="key of dst (default to src)")
return parser
def on_exec(self, args: argparse.Namespace):
# print(args)
# src
src_blk = args.blk
src_type = MfcKeyType.B if args.b is not False else MfcKeyType.A
src_key = args.src_key
if not re.match(r"^[a-fA-F0-9]{12}$", src_key):
print("src_key must include 12 HEX symbols")
return
src_key = bytearray.fromhex(src_key)
# print(src_blk, src_type, src_key)
if args.get is not False:
self.get_value(src_blk, src_type, src_key)
return
elif args.set is not None:
self.set_value(src_blk, src_type, src_key, args.set)
return
# dst
dst_blk = args.tblk if args.tblk is not None else src_blk
dst_type = MfcKeyType.A if args.ta is not False else (MfcKeyType.B if args.tb is not False else src_type)
dst_key = args.tkey if args.tkey is not None else args.src_key
if not re.match(r"^[a-fA-F0-9]{12}$", dst_key):
print("dst_key must include 12 HEX symbols")
return
dst_key = bytearray.fromhex(dst_key)
# print(dst_blk, dst_type, dst_key)
if args.inc is not None:
self.inc_value(src_blk, src_type, src_key, args.inc, dst_blk, dst_type, dst_key)
return
elif args.dec is not None:
self.dec_value(src_blk, src_type, src_key, args.dec, dst_blk, dst_type, dst_key)
return
elif args.res is not False:
self.res_value(src_blk, src_type, src_key, dst_blk, dst_type, dst_key)
return
else:
raise ArgsParserError("Please specify a value command")
def get_value(self, block, type, key):
resp = self.cmd.mf1_read_one_block(block, type, key)
val1, val2, val3, adr1, adr2, adr3, adr4 = struct.unpack("<iiiBBBB", resp)
# print(f"{val1}, {val2}, {val3}, {adr1}, {adr2}, {adr3}, {adr4}")
if (val1 != val3) or (val1 + val2 != -1):
print(f" - {CR}Invalid value of value block: {resp.hex()}{C0}")
return
if (adr1 != adr3) or (adr2 != adr4) or (adr1 + adr2 != 0xFF):
print(f" - {CR}Invalid address of value block: {resp.hex()}{C0}")
return
print(f" - block[{block}] = {CG}{{ value: {val1}, adr: {adr1} }}{C0}")
def set_value(self, block, type, key, value):
if value < -2147483647 or value > 2147483647:
raise ArgsParserError(f"Set value must be between -2147483647 and 2147483647. Got {value}")
adr_inverted = 0xFF - block
data = struct.pack("<iiiBBBB", value, -value - 1, value, block, adr_inverted, block, adr_inverted)
resp = self.cmd.mf1_write_one_block(block, type, key, data)
if resp:
print(f" - {CG}Set done.{C0}")
self.get_value(block, type, key)
else:
print(f" - {CR}Set fail.{C0}")
def inc_value(self, src_blk, src_type, src_key, value, dst_blk, dst_type, dst_key):
if value < 0 or value > 2147483647:
raise ArgsParserError(f"Increment value must be between 0 and 2147483647. Got {value}")
resp = self.cmd.mf1_manipulate_value_block(
src_blk, src_type, src_key,
MfcValueBlockOperator.INCREMENT, value,
dst_blk, dst_type, dst_key
)
if resp:
print(f" - {CG}Increment done.{C0}")
self.get_value(dst_blk, dst_type, dst_key)
else:
print(f" - {CR}Increment fail.{C0}")
def dec_value(self, src_blk, src_type, src_key, value, dst_blk, dst_type, dst_key):
if value < 0 or value > 2147483647:
raise ArgsParserError(f"Decrement value must be between 0 and 2147483647. Got {value}")
resp = self.cmd.mf1_manipulate_value_block(
src_blk, src_type, src_key,
MfcValueBlockOperator.DECREMENT, value,
dst_blk, dst_type, dst_key
)
if resp:
print(f" - {CG}Decrement done.{C0}")
self.get_value(dst_blk, dst_type, dst_key)
else:
print(f" - {CR}Decrement fail.{C0}")
def res_value(self, src_blk, src_type, src_key, dst_blk, dst_type, dst_key):
resp = self.cmd.mf1_manipulate_value_block(
src_blk, src_type, src_key,
MfcValueBlockOperator.RESTORE, 0,
dst_blk, dst_type, dst_key
)
if resp:
print(f" - {CG}Restore done.{C0}")
self.get_value(dst_blk, dst_type, dst_key)
else:
print(f" - {CR}Restore fail.{C0}")
@hf_mf.command('elog')
class HFMFELog(DeviceRequiredUnit):
detection_log_size = 18
+29 -6
View File
@@ -4,9 +4,9 @@ from typing import Union
import chameleon_com
from chameleon_utils import expect_response
from chameleon_enum import Command, Status, SlotNumber, TagSenseType, TagSpecificType
from chameleon_enum import MifareClassicDarksideStatus
from chameleon_enum import ButtonType, ButtonPressFunction
from chameleon_enum import Command, SlotNumber, Status, TagSenseType, TagSpecificType
from chameleon_enum import ButtonPressFunction, ButtonType, MifareClassicDarksideStatus
from chameleon_enum import MfcKeyType, MfcValueBlockOperator
CURRENT_VERSION_SETTINGS = 5
@@ -187,7 +187,7 @@ class ChameleonCMD:
return resp
@expect_response([Status.HF_TAG_OK, Status.MF_ERR_AUTH])
def mf1_auth_one_key_block(self, block, type_value, key):
def mf1_auth_one_key_block(self, block, type_value: MfcKeyType, key):
"""
Verify the mf1 key, only verify the specified type of key for a single sector.
@@ -202,7 +202,7 @@ class ChameleonCMD:
return resp
@expect_response(Status.HF_TAG_OK)
def mf1_read_one_block(self, block, type_value, key):
def mf1_read_one_block(self, block, type_value: MfcKeyType, key):
"""
Read one mf1 block.
@@ -217,7 +217,7 @@ class ChameleonCMD:
return resp
@expect_response(Status.HF_TAG_OK)
def mf1_write_one_block(self, block, type_value, key, block_data):
def mf1_write_one_block(self, block, type_value: MfcKeyType, key, block_data):
"""
Write mf1 single block.
@@ -277,6 +277,29 @@ class ChameleonCMD:
resp.parsed = resp.data
return resp
@expect_response(Status.HF_TAG_OK)
def mf1_manipulate_value_block(self, src_block, src_type: MfcKeyType, src_key, operator: MfcValueBlockOperator, operand, dst_block, dst_type: MfcKeyType, dst_key):
"""
1. Increment: increments value from source block and write to dest block
2. Decrement: decrements value from source block and write to dest block
3. Restore: copy value from source block and write to dest block
:param src_block:
:param src_type:
:param src_key:
:param operator:
:param operand:
:param dst_block:
:param dst_type:
:param dst_key:
:return:
"""
data = struct.pack('!BB6sBiBB6s', src_type, src_block, src_key, operator, operand, dst_type, dst_block, dst_key)
resp = self.device.send_cmd_sync(Command.MF1_MANIPULATE_VALUE_BLOCK, data)
resp.parsed = resp.status == Status.HF_TAG_OK
return resp
@expect_response(Status.HF_TAG_OK)
def mf1_static_nested_acquire(self, block_known, type_known, key_known, block_target, type_target):
"""
+7
View File
@@ -67,6 +67,7 @@ class Command(enum.IntEnum):
MF1_READ_ONE_BLOCK = 2008
MF1_WRITE_ONE_BLOCK = 2009
HF14A_RAW = 2010
MF1_MANIPULATE_VALUE_BLOCK = 2011
EM410X_SCAN = 3000
EM410X_WRITE_TO_T55XX = 3001
@@ -431,3 +432,9 @@ class ButtonPressFunction(enum.IntEnum):
elif self == ButtonPressFunction.BATTERY:
return "Show Battery Level"
return "None"
@enum.unique
class MfcValueBlockOperator(enum.IntEnum):
DECREMENT = 0xC0
INCREMENT = 0xC1
RESTORE = 0xC2