diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..e892a2fe --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +# The docker compose file can build firmware in docker image. +# +# - Pull the latest docker image: `docker compose pull` +# - Build ultra fw: `docker compose up build-ultra` +# - Build lite fw: `docker compose up build-lite` + +services: + # docker compose up build-ultra + build-ultra: + image: ghcr.io/rfidresearchgroup/chameleonultra-fw-builder:main + platform: linux/amd64 + volumes: + - '.:/workdir:rw' + environment: + CURRENT_DEVICE_TYPE: ultra + command: ./firmware/build.sh + # docker compose up build-lite + build-lite: + image: ghcr.io/rfidresearchgroup/chameleonultra-fw-builder:main + platform: linux/amd64 + volumes: + - '.:/workdir:rw' + environment: + CURRENT_DEVICE_TYPE: lite + command: ./firmware/build.sh \ No newline at end of file diff --git a/docs/protocol.md b/docs/protocol.md index 0be18720..df7f82da 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -290,6 +290,14 @@ Notes: * `reserved`:2 * Response: data sent by the card * CLI: cf `hf 14a raw` +### 2011: MF1_MANIPULATE_VALUE_BLOCK +* Command: 8 bytes: `type|block|key[6]|operator|operand[4]`. 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` +### 2012: MF1_TRANSFER_VALUE_BLOCK +* Command: 8 bytes: `type|block|key[6]`. Key as 6 bytes. Type=`0x60` for key A, `0x61` for key B. +* Response: no data +* CLI: cf `hf mf value --transfer` ### 3000: EM410X_SCAN * Command: no data * Response: 5 bytes. `id[5]`. ID as 5 bytes. diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index 00e3f54e..99a34eb9 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -447,6 +447,54 @@ 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 type; + uint8_t block; + uint8_t key[6]; + uint8_t operator; + uint32_t operand; + } PACKED payload_t; + if (length != sizeof(payload_t)) { + return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL); + } + + payload_t *payload = (payload_t *)data; + + status = auth_key_use_522_hw(payload->block, payload->type, payload->key); + if (status != STATUS_HF_TAG_OK) { + return data_frame_make(cmd, status, 0, NULL); + } + + status = pcd_14a_reader_mf1_manipulate_value_block(payload->operator, payload->block, (int32_t) U32NTOHL(payload->operand)); + 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_mf1_transfer_value_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + typedef struct { + uint8_t type; + uint8_t block; + uint8_t 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; + status = auth_key_use_522_hw(payload->block, payload->type, payload->key); + if (status != STATUS_HF_TAG_OK) { + return data_frame_make(cmd, status, 0, NULL); + } + status = pcd_14a_reader_mf1_transfer_value_block(payload->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 +1063,8 @@ 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_MF1_TRANSFER_VALUE_BLOCK, before_hf_reader_run, cmd_processor_mf1_transfer_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 }, diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index b0ad7511..6bb04709 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -66,6 +66,8 @@ #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) +#define DATA_CMD_MF1_TRANSFER_VALUE_BLOCK (2012) // // ****************************************************************** diff --git a/firmware/application/src/rfid/reader/hf/rc522.c b/firmware/application/src/rfid/reader/hf/rc522.c index 7fc615f4..35330683 100644 --- a/firmware/application/src/rfid/reader/hf/rc522.c +++ b/firmware/application/src/rfid/reader/hf/rc522.c @@ -487,64 +487,49 @@ uint8_t pcd_14a_reader_bits_transfer(uint8_t *pTx, uint16_t szTxBits, uint8_t * * @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 +963,97 @@ 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, 16 bytes + * @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 write a card, 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 operation + // 1. Copy data and calculate CRC + memcpy(dat_buff, &operand, 4); + crc_14a_calculate(dat_buff, 4, &dat_buff[4]); + + // NRF_LOG_INFO_hex("Will send: ", (uint8_t *)p, 4); + // NRF_LOG_INFO("\n"); + + // 2. Transfer the operand to complete the value block manipulation + // Increment, Decrement and Restore part 2 does not acknowledge + status = pcd_14a_reader_bytes_transfer(PCD_TRANSCEIVE, dat_buff, 6, NULL, &dat_len, U8ARR_BIT_LEN(dat_buff)); + // The communication fails, the reason is returned directly + if (status != STATUS_HF_TAG_OK) { + return 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_by_cmd(uint8_t cmd, uint8_t addr) { + uint8_t status; + uint16_t dat_len; + uint8_t dat_buff[4] = { cmd, 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 : Writes the contents of the internal Transfer Buffer to a block +* @param : Addr: block address +* @retval : Status value hf_tag_ok, success +*/ +uint8_t pcd_14a_reader_mf1_transfer_value_block(uint8_t addr) { + // Standard M1 Card Transfer + return pcd_14a_reader_mf1_transfer_value_block_by_cmd(PICC_TRANSFER, addr); +} + /** * @brief : Let the card enter the dormant mode * @param :none diff --git a/firmware/application/src/rfid/reader/hf/rc522.h b/firmware/application/src/rfid/reader/hf/rc522.h index 96d6652c..cf3dfbb7 100644 --- a/firmware/application/src/rfid/reader/hf/rc522.h +++ b/firmware/application/src/rfid/reader/hf/rc522.h @@ -217,6 +217,10 @@ 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_by_cmd(uint8_t cmd, uint8_t addr); +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); diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index 1e6b4732..cd306007 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -21,7 +21,7 @@ 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,106 @@ def on_exec(self, args: argparse.Namespace): print(f" - {CR}Write fail.{C0}") +@hf_mf.command('value') +class HFMFVALUE(MF1AuthArgsUnit): + def args_parser(self) -> ArgumentParserNoExit: + parser = super().args_parser() + parser.description = 'MIFARE Classic value data commands' + + group = parser.add_mutually_exclusive_group() + group.add_argument('--get', action='store_true', help="Get value from block") + group.add_argument('--set', type=int, required=False, metavar="", + help="Set value to X (-2147483647 ~ 2147483647)") + group.add_argument('--inc', type=int, required=False, metavar="", + help="Increment value by X (0 ~ 2147483647) and stores to Transfer Buffer") + group.add_argument('--dec', type=int, required=False, metavar="", + help="Decrement value by X (0 ~ 2147483647) and stores to Transfer Buffer") + group.add_argument('--res', action='store_true', help="Load value to Transfer Buffer") + group.add_argument('--transfer', action='store_true', help="Save value from Transfer Buffer to block") + + return parser + + def on_exec(self, args: argparse.Namespace): + param = self.get_param(args) + + if args.get: + self.get_value(param.block, param.type, param.key) + return + elif args.set: + self.set_value(param.block, param.type, param.key, args.set) + return + elif args.inc: + self.inc_value(param.block, param.type, param.key, args.inc) + return + elif args.dec: + self.dec_value(param.block, param.type, param.key, args.dec) + return + elif args.res: + self.res_value(param.block, param.type, param.key) + return + elif args.transfer: + self.transfer_value(param.block, param.type, param.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(" 2147483647: + raise ArgsParserError(f"Set value must be between -2147483647 and 2147483647. Got {value}") + adr_inverted = 0xFF - block + data = struct.pack(" 2147483647: + raise ArgsParserError(f"Increment value must be between 0 and 2147483647. Got {value}") + resp = self.cmd.mf1_manipulate_value_block(block, type, key, MfcValueBlockOperator.INCREMENT, value) + if resp: + print(f" - {CG}Increment done.{C0}") + else: + print(f" - {CR}Increment fail.{C0}") + + def dec_value(self, block, type, key, value): + 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(block, type, key, MfcValueBlockOperator.DECREMENT, value) + if resp: + print(f" - {CG}Decrement done.{C0}") + else: + print(f" - {CR}Decrement fail.{C0}") + + def res_value(self, block, type, key): + resp = self.cmd.mf1_manipulate_value_block(block, type, key, MfcValueBlockOperator.RESTORE, 0) + if resp: + print(f" - {CG}Restore done.{C0}") + else: + print(f" - {CR}Restore fail.{C0}") + + def transfer_value(self, block, type, key): + resp = self.cmd.mf1_transfer_value_block(block, type, key) + if resp: + print(f" - {CG}Transfer done.{C0}") + else: + print(f" - {CR}Transfer fail.{C0}") + + @hf_mf.command('elog') class HFMFELog(DeviceRequiredUnit): detection_log_size = 18 diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 15356feb..7ae747fd 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -4,9 +4,9 @@ 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 @@ def mf1_darkside_acquire(self, block_target, type_target, first_recover: Union[i 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 @@ def mf1_auth_one_key_block(self, block, type_value, key): 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 @@ def mf1_read_one_block(self, block, type_value, key): 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,42 @@ class CStruct(ctypes.BigEndianStructure): resp.parsed = resp.data return resp + @expect_response(Status.HF_TAG_OK) + def mf1_manipulate_value_block(self, block, type_value: MfcKeyType, key, operator: MfcValueBlockOperator, operand): + """ + 1. Increment: increments the contents of a block and stores the result in the internal Transfer Buffer + 2. Decrement: decrements the contents of a block and stores the result in the internal Transfer Buffer + 3. Restore: reads the contents of a block into the internal Transfer Buffer + + + :param block: + :param type_value: + :param key: + :param operator: + :param operand: + :return: + """ + data = struct.pack('!BB6sBi', type_value, block, key, operator, operand) + resp = self.device.send_cmd_sync(Command.MF1_MANIPULATE_VALUE_BLOCK, data) + resp.data = resp.status == Status.HF_TAG_OK + return resp + + @expect_response(Status.HF_TAG_OK) + def mf1_transfer_value_block(self, block, type_value: MfcKeyType, key): + """ + Writes the contents of the internal Transfer Buffer to a block + + + :param block: + :param type_value: + :param key: + :return: + """ + data = struct.pack('!BB6s', type_value, block, key) + resp = self.device.send_cmd_sync(Command.MF1_TRANSFER_VALUE_BLOCK, data) + resp.data = 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): """ diff --git a/software/script/chameleon_enum.py b/software/script/chameleon_enum.py index b0e8d53b..fe0ab4d4 100644 --- a/software/script/chameleon_enum.py +++ b/software/script/chameleon_enum.py @@ -67,6 +67,8 @@ class Command(enum.IntEnum): MF1_READ_ONE_BLOCK = 2008 MF1_WRITE_ONE_BLOCK = 2009 HF14A_RAW = 2010 + MF1_MANIPULATE_VALUE_BLOCK = 2011 + MF1_TRANSFER_VALUE_BLOCK = 2012 EM410X_SCAN = 3000 EM410X_WRITE_TO_T55XX = 3001 @@ -431,3 +433,9 @@ def __str__(self): elif self == ButtonPressFunction.BATTERY: return "Show Battery Level" return "None" + +@enum.unique +class MfcValueBlockOperator(enum.IntEnum): + DECREMENT = 0xC0 + INCREMENT = 0xC1 + RESTORE = 0xC2 \ No newline at end of file