|
|
@@ -1,633 +0,0 @@
|
|
|
-#include "gen2_poller_i.h"
|
|
|
-#include <nfc/helpers/iso14443_crc.h>
|
|
|
-
|
|
|
-#include <bit_lib.h>
|
|
|
-#include "furi_hal_random.h"
|
|
|
-
|
|
|
-#include <furi/furi.h>
|
|
|
-
|
|
|
-#define TAG "GEN2_I"
|
|
|
-
|
|
|
-MfClassicError mf_classic_process_error(Iso14443_3aError error) {
|
|
|
- MfClassicError ret = MfClassicErrorNone;
|
|
|
-
|
|
|
- switch(error) {
|
|
|
- case Iso14443_3aErrorNone:
|
|
|
- ret = MfClassicErrorNone;
|
|
|
- break;
|
|
|
- case Iso14443_3aErrorNotPresent:
|
|
|
- ret = MfClassicErrorNotPresent;
|
|
|
- break;
|
|
|
- case Iso14443_3aErrorColResFailed:
|
|
|
- case Iso14443_3aErrorCommunication:
|
|
|
- case Iso14443_3aErrorWrongCrc:
|
|
|
- ret = MfClassicErrorProtocol;
|
|
|
- break;
|
|
|
- case Iso14443_3aErrorTimeout:
|
|
|
- ret = MfClassicErrorTimeout;
|
|
|
- break;
|
|
|
- default:
|
|
|
- ret = MfClassicErrorProtocol;
|
|
|
- break;
|
|
|
- }
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-Gen2PollerError gen2_poller_process_iso3_error(Iso14443_3aError error) {
|
|
|
- Gen2PollerError ret = Gen2PollerErrorNone;
|
|
|
-
|
|
|
- switch(error) {
|
|
|
- case Iso14443_3aErrorNone:
|
|
|
- ret = Gen2PollerErrorNone;
|
|
|
- break;
|
|
|
- case Iso14443_3aErrorNotPresent:
|
|
|
- ret = Gen2PollerErrorNotPresent;
|
|
|
- break;
|
|
|
- case Iso14443_3aErrorWrongCrc:
|
|
|
- ret = Gen2PollerErrorProtocol;
|
|
|
- break;
|
|
|
- case Iso14443_3aErrorTimeout:
|
|
|
- ret = Gen2PollerErrorTimeout;
|
|
|
- break;
|
|
|
- default:
|
|
|
- ret = Gen2PollerErrorProtocol;
|
|
|
- break;
|
|
|
- }
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-Gen2PollerError gen2_poller_process_mifare_classic_error(MfClassicError error) {
|
|
|
- Gen2PollerError ret = Gen2PollerErrorNone;
|
|
|
-
|
|
|
- switch(error) {
|
|
|
- case MfClassicErrorNone:
|
|
|
- ret = Gen2PollerErrorNone;
|
|
|
- break;
|
|
|
- case MfClassicErrorNotPresent:
|
|
|
- ret = Gen2PollerErrorNotPresent;
|
|
|
- break;
|
|
|
- case MfClassicErrorProtocol:
|
|
|
- ret = Gen2PollerErrorProtocol;
|
|
|
- break;
|
|
|
- case MfClassicErrorAuth:
|
|
|
- ret = Gen2PollerErrorAuth;
|
|
|
- break;
|
|
|
- case MfClassicErrorTimeout:
|
|
|
- ret = Gen2PollerErrorTimeout;
|
|
|
- break;
|
|
|
- default:
|
|
|
- ret = Gen2PollerErrorProtocol;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static Gen2PollerError gen2_poller_get_nt_common(
|
|
|
- Gen2Poller* instance,
|
|
|
- uint8_t block_num,
|
|
|
- MfClassicKeyType key_type,
|
|
|
- MfClassicNt* nt,
|
|
|
- bool is_nested) {
|
|
|
- MfClassicError ret = MfClassicErrorNone;
|
|
|
- Iso14443_3aError error = Iso14443_3aErrorNone;
|
|
|
-
|
|
|
- do {
|
|
|
- uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :
|
|
|
- MF_CLASSIC_CMD_AUTH_KEY_A;
|
|
|
- uint8_t auth_cmd[2] = {auth_type, block_num};
|
|
|
- bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd));
|
|
|
-
|
|
|
- if(is_nested) {
|
|
|
- iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
|
|
|
- crypto1_encrypt(
|
|
|
- instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
|
|
|
- error = iso14443_3a_poller_txrx_custom_parity(
|
|
|
- instance->iso3_poller,
|
|
|
- instance->tx_encrypted_buffer,
|
|
|
- instance->rx_plain_buffer, // NT gets decrypted by mf_classic_async_auth
|
|
|
- GEN2_POLLER_MAX_FWT);
|
|
|
- if(error != Iso14443_3aErrorNone) {
|
|
|
- ret = mf_classic_process_error(error);
|
|
|
- break;
|
|
|
- }
|
|
|
- } else {
|
|
|
- FURI_LOG_D(TAG, "Plain auth cmd");
|
|
|
- error = iso14443_3a_poller_send_standard_frame(
|
|
|
- instance->iso3_poller,
|
|
|
- instance->tx_plain_buffer,
|
|
|
- instance->rx_plain_buffer,
|
|
|
- GEN2_POLLER_MAX_FWT);
|
|
|
- if(error != Iso14443_3aErrorWrongCrc) {
|
|
|
- ret = mf_classic_process_error(error);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if(bit_buffer_get_size_bytes(instance->rx_plain_buffer) != sizeof(MfClassicNt)) {
|
|
|
- ret = MfClassicErrorProtocol;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if(nt) {
|
|
|
- bit_buffer_write_bytes(instance->rx_plain_buffer, nt->data, sizeof(MfClassicNt));
|
|
|
- }
|
|
|
- } while(false);
|
|
|
-
|
|
|
- return gen2_poller_process_mifare_classic_error(ret);
|
|
|
-}
|
|
|
-
|
|
|
-Gen2PollerError gen2_poller_get_nt(
|
|
|
- Gen2Poller* instance,
|
|
|
- uint8_t block_num,
|
|
|
- MfClassicKeyType key_type,
|
|
|
- MfClassicNt* nt) {
|
|
|
- return gen2_poller_get_nt_common(instance, block_num, key_type, nt, false);
|
|
|
-}
|
|
|
-
|
|
|
-Gen2PollerError gen2_poller_get_nt_nested(
|
|
|
- Gen2Poller* instance,
|
|
|
- uint8_t block_num,
|
|
|
- MfClassicKeyType key_type,
|
|
|
- MfClassicNt* nt) {
|
|
|
- return gen2_poller_get_nt_common(instance, block_num, key_type, nt, true);
|
|
|
-}
|
|
|
-
|
|
|
-static Gen2PollerError gen2_poller_auth_common(
|
|
|
- Gen2Poller* instance,
|
|
|
- uint8_t block_num,
|
|
|
- MfClassicKey* key,
|
|
|
- MfClassicKeyType key_type,
|
|
|
- MfClassicAuthContext* data,
|
|
|
- bool is_nested) {
|
|
|
- Gen2PollerError ret = Gen2PollerErrorNone;
|
|
|
- Iso14443_3aError error = Iso14443_3aErrorNone;
|
|
|
-
|
|
|
- do {
|
|
|
- iso14443_3a_copy(instance->data->iso14443_3a_data, nfc_poller_get_data(instance->poller));
|
|
|
-
|
|
|
- MfClassicNt nt = {};
|
|
|
- if(is_nested) {
|
|
|
- ret = gen2_poller_get_nt_nested(instance, block_num, key_type, &nt);
|
|
|
- } else {
|
|
|
- ret = gen2_poller_get_nt(instance, block_num, key_type, &nt);
|
|
|
- }
|
|
|
- if(ret != Gen2PollerErrorNone) break;
|
|
|
- if(data) {
|
|
|
- data->nt = nt;
|
|
|
- }
|
|
|
-
|
|
|
- uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
|
|
|
- uint64_t key_num = bit_lib_bytes_to_num_be(key->data, sizeof(MfClassicKey));
|
|
|
- MfClassicNr nr = {};
|
|
|
- furi_hal_random_fill_buf(nr.data, sizeof(MfClassicNr));
|
|
|
-
|
|
|
- crypto1_encrypt_reader_nonce(
|
|
|
- instance->crypto,
|
|
|
- key_num,
|
|
|
- cuid,
|
|
|
- nt.data,
|
|
|
- nr.data,
|
|
|
- instance->tx_encrypted_buffer,
|
|
|
- is_nested);
|
|
|
- error = iso14443_3a_poller_txrx_custom_parity(
|
|
|
- instance->iso3_poller,
|
|
|
- instance->tx_encrypted_buffer,
|
|
|
- instance->rx_encrypted_buffer,
|
|
|
- GEN2_POLLER_MAX_FWT);
|
|
|
-
|
|
|
- if(error != Iso14443_3aErrorNone) {
|
|
|
- ret = gen2_poller_process_iso3_error(error);
|
|
|
- break;
|
|
|
- }
|
|
|
- if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) != 4) {
|
|
|
- ret = Gen2PollerErrorAuth;
|
|
|
- }
|
|
|
-
|
|
|
- crypto1_word(instance->crypto, 0, 0);
|
|
|
- instance->auth_state = Gen2AuthStatePassed;
|
|
|
-
|
|
|
- if(data) {
|
|
|
- data->nr = nr;
|
|
|
- const uint8_t* nr_ar = bit_buffer_get_data(instance->tx_encrypted_buffer);
|
|
|
- memcpy(data->ar.data, &nr_ar[4], sizeof(MfClassicAr));
|
|
|
- bit_buffer_write_bytes(
|
|
|
- instance->rx_encrypted_buffer, data->at.data, sizeof(MfClassicAt));
|
|
|
- }
|
|
|
- } while(false);
|
|
|
-
|
|
|
- if(ret != Gen2PollerErrorNone) {
|
|
|
- iso14443_3a_poller_halt(instance->iso3_poller);
|
|
|
- }
|
|
|
-
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-Gen2PollerError gen2_poller_auth(
|
|
|
- Gen2Poller* instance,
|
|
|
- uint8_t block_num,
|
|
|
- MfClassicKey* key,
|
|
|
- MfClassicKeyType key_type,
|
|
|
- MfClassicAuthContext* data) {
|
|
|
- return gen2_poller_auth_common(instance, block_num, key, key_type, data, false);
|
|
|
-}
|
|
|
-
|
|
|
-Gen2PollerError gen2_poller_halt(Gen2Poller* instance) {
|
|
|
- Gen2PollerError ret = Gen2PollerErrorNone;
|
|
|
- Iso14443_3aError error = Iso14443_3aErrorNone;
|
|
|
-
|
|
|
- do {
|
|
|
- uint8_t halt_cmd[2] = {MF_CLASSIC_CMD_HALT_MSB, MF_CLASSIC_CMD_HALT_LSB};
|
|
|
- bit_buffer_copy_bytes(instance->tx_plain_buffer, halt_cmd, sizeof(halt_cmd));
|
|
|
- iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
|
|
|
-
|
|
|
- if(instance->auth_state == Gen2AuthStatePassed) {
|
|
|
- // Send an encrypted halt command
|
|
|
- crypto1_encrypt(
|
|
|
- instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
|
|
|
- FURI_LOG_D(TAG, "Send enc halt");
|
|
|
- error = iso14443_3a_poller_txrx_custom_parity(
|
|
|
- instance->iso3_poller,
|
|
|
- instance->tx_encrypted_buffer,
|
|
|
- instance->rx_encrypted_buffer,
|
|
|
- GEN2_POLLER_MAX_FWT);
|
|
|
- }
|
|
|
-
|
|
|
- if(error != Iso14443_3aErrorNone) {
|
|
|
- FURI_LOG_D(TAG, "Enc halt error");
|
|
|
- // Do not break because we still need to halt the iso3 poller
|
|
|
- }
|
|
|
-
|
|
|
- // Send a regular halt command to halt the iso3 poller
|
|
|
- FURI_LOG_D(TAG, "Send reg halt");
|
|
|
- error = iso14443_3a_poller_halt(instance->iso3_poller);
|
|
|
-
|
|
|
- if(error != Iso14443_3aErrorTimeout) {
|
|
|
- FURI_LOG_D(TAG, "Reg halt error");
|
|
|
- // Do not break as well becaue the first halt command might have worked
|
|
|
- // and the card didn't respond because it was already halted
|
|
|
- }
|
|
|
-
|
|
|
- crypto1_reset(instance->crypto);
|
|
|
- instance->auth_state = Gen2AuthStateIdle;
|
|
|
- } while(false);
|
|
|
-
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-Gen2PollerError
|
|
|
- gen2_poller_write_block(Gen2Poller* instance, uint8_t block_num, const MfClassicBlock* data) {
|
|
|
- Gen2PollerError ret = Gen2PollerErrorNone;
|
|
|
- Iso14443_3aError error = Iso14443_3aErrorNone;
|
|
|
-
|
|
|
- do {
|
|
|
- uint8_t write_block_cmd[2] = {MF_CLASSIC_CMD_WRITE_BLOCK, block_num};
|
|
|
- bit_buffer_copy_bytes(instance->tx_plain_buffer, write_block_cmd, sizeof(write_block_cmd));
|
|
|
- iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
|
|
|
-
|
|
|
- crypto1_encrypt(
|
|
|
- instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
|
|
|
-
|
|
|
- error = iso14443_3a_poller_txrx_custom_parity(
|
|
|
- instance->iso3_poller,
|
|
|
- instance->tx_encrypted_buffer,
|
|
|
- instance->rx_encrypted_buffer,
|
|
|
- GEN2_POLLER_MAX_FWT);
|
|
|
- if(error != Iso14443_3aErrorNone) {
|
|
|
- ret = gen2_poller_process_iso3_error(error);
|
|
|
- break;
|
|
|
- }
|
|
|
- if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
|
|
|
- ret = Gen2PollerErrorProtocol;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- crypto1_decrypt(
|
|
|
- instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);
|
|
|
-
|
|
|
- if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {
|
|
|
- FURI_LOG_D(TAG, "NACK received");
|
|
|
- ret = Gen2PollerErrorProtocol;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- bit_buffer_copy_bytes(instance->tx_plain_buffer, data->data, sizeof(MfClassicBlock));
|
|
|
- iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
|
|
|
-
|
|
|
- crypto1_encrypt(
|
|
|
- instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
|
|
|
-
|
|
|
- error = iso14443_3a_poller_txrx_custom_parity(
|
|
|
- instance->iso3_poller,
|
|
|
- instance->tx_encrypted_buffer,
|
|
|
- instance->rx_encrypted_buffer,
|
|
|
- GEN2_POLLER_MAX_FWT);
|
|
|
- if(error != Iso14443_3aErrorNone) {
|
|
|
- ret = gen2_poller_process_iso3_error(error);
|
|
|
- break;
|
|
|
- }
|
|
|
- if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
|
|
|
- ret = Gen2PollerErrorProtocol;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- crypto1_decrypt(
|
|
|
- instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);
|
|
|
-
|
|
|
- if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {
|
|
|
- FURI_LOG_D(TAG, "NACK received");
|
|
|
- ret = Gen2PollerErrorProtocol;
|
|
|
- break;
|
|
|
- }
|
|
|
- } while(false);
|
|
|
-
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-bool gen2_poller_can_write_block(const MfClassicData* mfc_data, uint8_t block_num) {
|
|
|
- furi_assert(mfc_data);
|
|
|
-
|
|
|
- bool can_write = true;
|
|
|
-
|
|
|
- if(block_num == 0 && mfc_data->iso14443_3a_data->uid_len == 7) {
|
|
|
- // 7-byte UID gen2 cards are not supported yet, need further testing
|
|
|
- can_write = false;
|
|
|
- }
|
|
|
-
|
|
|
- if(mf_classic_is_sector_trailer(block_num)) {
|
|
|
- can_write = gen2_poller_can_write_sector_trailer(mfc_data, block_num);
|
|
|
- } else {
|
|
|
- can_write = gen2_poller_can_write_data_block(mfc_data, block_num);
|
|
|
- }
|
|
|
-
|
|
|
- return can_write;
|
|
|
-}
|
|
|
-
|
|
|
-Gen2PollerWriteProblem
|
|
|
- gen2_poller_can_write_data_block(const MfClassicData* mfc_data, uint8_t block_num) {
|
|
|
- // Check whether it's possible to write the block
|
|
|
- furi_assert(mfc_data);
|
|
|
-
|
|
|
- // Check rules:
|
|
|
- // 1. Check if block is read
|
|
|
- // 2. Check if we have any of the keys
|
|
|
- // 3. For each key, check if we can write the block
|
|
|
- // 3.1. If none of the keys can write the block, check whether access conditions can be reset to allow writing
|
|
|
- // 3.2 If the above conditions are not met, return an error code
|
|
|
-
|
|
|
- Gen2PollerWriteProblem can_write = Gen2PollerWriteProblemNone;
|
|
|
-
|
|
|
- bool has_key_a = mf_classic_is_key_found(
|
|
|
- mfc_data, mf_classic_get_sector_by_block(block_num), MfClassicKeyTypeA);
|
|
|
- bool has_key_b = mf_classic_is_key_found(
|
|
|
- mfc_data, mf_classic_get_sector_by_block(block_num), MfClassicKeyTypeB);
|
|
|
-
|
|
|
- if(!mf_classic_is_block_read(mfc_data, block_num)) {
|
|
|
- can_write = Gen2PollerWriteProblemMissingSourceData;
|
|
|
- return can_write;
|
|
|
- }
|
|
|
- if(!has_key_a && !has_key_b) {
|
|
|
- can_write = Gen2PollerWriteProblemMissingTargetKeys;
|
|
|
- return can_write;
|
|
|
- }
|
|
|
- if(!gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeA, MfClassicActionDataWrite) &&
|
|
|
- !gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeB, MfClassicActionDataWrite)) {
|
|
|
- if(!gen2_can_reset_access_conditions(mfc_data, block_num)) {
|
|
|
- can_write = Gen2PollerWriteProblemLockedAccessBits;
|
|
|
- return can_write;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return can_write;
|
|
|
-}
|
|
|
-
|
|
|
-Gen2PollerWriteProblem
|
|
|
- gen2_poller_can_write_sector_trailer(const MfClassicData* mfc_data, uint8_t block_num) {
|
|
|
- // Check whether it's possible to write the sector trailer
|
|
|
- furi_assert(mfc_data);
|
|
|
-
|
|
|
- // Check rules:
|
|
|
- // 1. Check if block is read
|
|
|
- // 2. Check if we have any of the keys
|
|
|
- // 3. For each key, check if we can write the block
|
|
|
- // 3.1 Check that at least one of the keys can write Key A
|
|
|
- // 3.1.1 If none of the keys can write Key A, check whether access conditions can be reset to allow writing
|
|
|
- // 3.2 Check that at least one of the keys can write the Access Conditions
|
|
|
- // 3.3 Check that at least one of the keys can write Key B
|
|
|
- // 3.3.1 If none of the keys can write Key B, check whether access conditions can be reset to allow writing
|
|
|
- // 3.4 If any of the above conditions are not met, return an error code
|
|
|
-
|
|
|
- Gen2PollerWriteProblem can_write = Gen2PollerWriteProblemNone;
|
|
|
-
|
|
|
- bool has_key_a = mf_classic_is_key_found(
|
|
|
- mfc_data, mf_classic_get_sector_by_block(block_num), MfClassicKeyTypeA);
|
|
|
- bool has_key_b = mf_classic_is_key_found(
|
|
|
- mfc_data, mf_classic_get_sector_by_block(block_num), MfClassicKeyTypeB);
|
|
|
- if(!mf_classic_is_block_read(mfc_data, block_num)) {
|
|
|
- can_write = Gen2PollerWriteProblemMissingSourceData;
|
|
|
- return can_write;
|
|
|
- }
|
|
|
- if(!has_key_a && !has_key_b) {
|
|
|
- can_write = Gen2PollerWriteProblemMissingTargetKeys;
|
|
|
- return can_write;
|
|
|
- }
|
|
|
- if(!gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeA, MfClassicActionKeyAWrite) &&
|
|
|
- !gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeB, MfClassicActionKeyAWrite)) {
|
|
|
- if(!gen2_can_reset_access_conditions(mfc_data, block_num)) {
|
|
|
- can_write = Gen2PollerWriteProblemLockedAccessBits;
|
|
|
- return can_write;
|
|
|
- }
|
|
|
- }
|
|
|
- if(!gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeA, MfClassicActionACWrite) &&
|
|
|
- !gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeB, MfClassicActionACWrite)) {
|
|
|
- can_write = Gen2PollerWriteProblemLockedAccessBits;
|
|
|
- return can_write;
|
|
|
- }
|
|
|
- if(!gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeA, MfClassicActionKeyBWrite) &&
|
|
|
- !gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeB, MfClassicActionKeyBWrite)) {
|
|
|
- if(!gen2_can_reset_access_conditions(mfc_data, block_num)) {
|
|
|
- can_write = Gen2PollerWriteProblemLockedAccessBits;
|
|
|
- return can_write;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return can_write;
|
|
|
-}
|
|
|
-
|
|
|
-bool gen2_can_reset_access_conditions(const MfClassicData* mfc_data, uint8_t block_num) {
|
|
|
- // Check whether it's possible to reset the access conditions
|
|
|
- furi_assert(mfc_data);
|
|
|
-
|
|
|
- // Check rules:
|
|
|
- // 1. Check if the sector trailer for this block is read
|
|
|
- // 2. Check if we have any of the keys
|
|
|
- // 3. For each key, check if we can write the access conditions
|
|
|
- // 3.1. If none of the keys can write the access conditions, return false
|
|
|
-
|
|
|
- bool can_reset = false;
|
|
|
-
|
|
|
- bool has_key_a = mf_classic_is_key_found(
|
|
|
- mfc_data, mf_classic_get_sector_by_block(block_num), MfClassicKeyTypeA);
|
|
|
- bool has_key_b = mf_classic_is_key_found(
|
|
|
- mfc_data, mf_classic_get_sector_by_block(block_num), MfClassicKeyTypeB);
|
|
|
- uint8_t sector_tr_num = mf_classic_get_sector_trailer_num_by_block(block_num);
|
|
|
- if(!mf_classic_is_block_read(mfc_data, sector_tr_num)) {
|
|
|
- can_reset = false;
|
|
|
- return can_reset;
|
|
|
- }
|
|
|
- if(!has_key_a && !has_key_b) {
|
|
|
- can_reset = false;
|
|
|
- return can_reset;
|
|
|
- }
|
|
|
- if(gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeA, MfClassicActionACWrite) ||
|
|
|
- gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeB, MfClassicActionACWrite)) {
|
|
|
- can_reset = true;
|
|
|
- }
|
|
|
-
|
|
|
- return can_reset;
|
|
|
-}
|
|
|
-
|
|
|
-MfClassicKeyType
|
|
|
- gen2_poller_get_key_type_to_write(const MfClassicData* mfc_data, uint8_t block_num) {
|
|
|
- // Get the key type to use for writing
|
|
|
- // We assume that at least one of the keys can write the block
|
|
|
- furi_assert(mfc_data);
|
|
|
-
|
|
|
- MfClassicKeyType key_type = MfClassicKeyTypeA;
|
|
|
-
|
|
|
- if(gen2_is_allowed_access(mfc_data, block_num, MfClassicKeyTypeA, MfClassicActionDataWrite)) {
|
|
|
- key_type = MfClassicKeyTypeA;
|
|
|
- } else if(gen2_is_allowed_access(
|
|
|
- mfc_data, block_num, MfClassicKeyTypeB, MfClassicActionDataWrite)) {
|
|
|
- key_type = MfClassicKeyTypeB;
|
|
|
- }
|
|
|
-
|
|
|
- return key_type;
|
|
|
-}
|
|
|
-
|
|
|
-static bool gen2_is_allowed_access_sector_trailer(
|
|
|
- const MfClassicData* data,
|
|
|
- uint8_t block_num,
|
|
|
- MfClassicKeyType key_type,
|
|
|
- MfClassicAction action) {
|
|
|
- uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
|
|
|
- MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num);
|
|
|
- uint8_t* access_bits_arr = sec_tr->access_bits.data;
|
|
|
- uint8_t AC = ((access_bits_arr[1] >> 5) & 0x04) | ((access_bits_arr[2] >> 2) & 0x02) |
|
|
|
- ((access_bits_arr[2] >> 7) & 0x01);
|
|
|
- FURI_LOG_T("NFC", "AC: %02X", AC);
|
|
|
-
|
|
|
- switch(action) {
|
|
|
- case MfClassicActionKeyARead: {
|
|
|
- return false;
|
|
|
- }
|
|
|
- case MfClassicActionKeyAWrite:
|
|
|
- case MfClassicActionKeyBWrite: {
|
|
|
- return (
|
|
|
- (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x01)) ||
|
|
|
- (key_type == MfClassicKeyTypeB &&
|
|
|
- (AC == 0x00 || AC == 0x04 || AC == 0x03 || AC == 0x01)));
|
|
|
- }
|
|
|
- case MfClassicActionKeyBRead: {
|
|
|
- return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x02 || AC == 0x01)) ||
|
|
|
- (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x02 || AC == 0x01));
|
|
|
- }
|
|
|
- case MfClassicActionACRead: {
|
|
|
- return ((key_type == MfClassicKeyTypeA) || (key_type == MfClassicKeyTypeB));
|
|
|
- }
|
|
|
- case MfClassicActionACWrite: {
|
|
|
- return (
|
|
|
- (key_type == MfClassicKeyTypeA && (AC == 0x01)) ||
|
|
|
- (key_type == MfClassicKeyTypeB && (AC == 0x01 || AC == 0x03 || AC == 0x05)));
|
|
|
- }
|
|
|
- default:
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-bool gen2_is_allowed_access_data_block(
|
|
|
- MfClassicSectorTrailer* sec_tr,
|
|
|
- uint8_t block_num,
|
|
|
- MfClassicKeyType key_type,
|
|
|
- MfClassicAction action) {
|
|
|
- // Same as mf_classic_is_allowed_access_data_block but with sector 0 allowed
|
|
|
- furi_assert(sec_tr);
|
|
|
-
|
|
|
- uint8_t* access_bits_arr = sec_tr->access_bits.data;
|
|
|
-
|
|
|
- uint8_t sector_block = 0;
|
|
|
- if(block_num <= 128) {
|
|
|
- sector_block = block_num & 0x03;
|
|
|
- } else {
|
|
|
- sector_block = (block_num & 0x0f) / 5;
|
|
|
- }
|
|
|
-
|
|
|
- uint8_t AC;
|
|
|
- switch(sector_block) {
|
|
|
- case 0x00: {
|
|
|
- AC = ((access_bits_arr[1] >> 2) & 0x04) | ((access_bits_arr[2] << 1) & 0x02) |
|
|
|
- ((access_bits_arr[2] >> 4) & 0x01);
|
|
|
- break;
|
|
|
- }
|
|
|
- case 0x01: {
|
|
|
- AC = ((access_bits_arr[1] >> 3) & 0x04) | ((access_bits_arr[2] >> 0) & 0x02) |
|
|
|
- ((access_bits_arr[2] >> 5) & 0x01);
|
|
|
- break;
|
|
|
- }
|
|
|
- case 0x02: {
|
|
|
- AC = ((access_bits_arr[1] >> 4) & 0x04) | ((access_bits_arr[2] >> 1) & 0x02) |
|
|
|
- ((access_bits_arr[2] >> 6) & 0x01);
|
|
|
- break;
|
|
|
- }
|
|
|
- default:
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- switch(action) {
|
|
|
- case MfClassicActionDataRead: {
|
|
|
- return (
|
|
|
- (key_type == MfClassicKeyTypeA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) ||
|
|
|
- (key_type == MfClassicKeyTypeB && !(AC == 0x07)));
|
|
|
- }
|
|
|
- case MfClassicActionDataWrite: {
|
|
|
- return (
|
|
|
- (key_type == MfClassicKeyTypeA && (AC == 0x00)) ||
|
|
|
- (key_type == MfClassicKeyTypeB &&
|
|
|
- (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03)));
|
|
|
- }
|
|
|
- case MfClassicActionDataInc: {
|
|
|
- return (
|
|
|
- (key_type == MfClassicKeyTypeA && (AC == 0x00)) ||
|
|
|
- (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06)));
|
|
|
- }
|
|
|
- case MfClassicActionDataDec: {
|
|
|
- return (
|
|
|
- (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) ||
|
|
|
- (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06 || AC == 0x01)));
|
|
|
- }
|
|
|
- default:
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-bool gen2_is_allowed_access(
|
|
|
- const MfClassicData* data,
|
|
|
- uint8_t block_num,
|
|
|
- MfClassicKeyType key_type,
|
|
|
- MfClassicAction action) {
|
|
|
- // Same as mf_classic_is_allowed_access but with sector 0 allowed
|
|
|
- furi_assert(data);
|
|
|
-
|
|
|
- bool access_allowed = false;
|
|
|
- if(mf_classic_is_sector_trailer(block_num)) {
|
|
|
- access_allowed = gen2_is_allowed_access_sector_trailer(data, block_num, key_type, action);
|
|
|
- } else {
|
|
|
- uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
|
|
|
- MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num);
|
|
|
- access_allowed = gen2_is_allowed_access_data_block(sec_tr, block_num, key_type, action);
|
|
|
- }
|
|
|
-
|
|
|
- return access_allowed;
|
|
|
-}
|