#include "mf_classic.h"
 
#include <furi/furi.h>
#include <toolbox/hex.h>
 
#include <lib/nfc/helpers/nfc_util.h>
 
#define MF_CLASSIC_PROTOCOL_NAME "Mifare Classic"
 
typedef struct {
    uint8_t sectors_total;
    uint16_t blocks_total;
    const char* full_name;
    const char* type_name;
} MfClassicFeatures;
 
static const uint32_t mf_classic_data_format_version = 2;
 
static const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = {
    [MfClassicTypeMini] =
        {
            .sectors_total = 5,
            .blocks_total = 20,
            .full_name = "Mifare Classic Mini 0.3K",
            .type_name = "MINI",
        },
    [MfClassicType1k] =
        {
            .sectors_total = 16,
            .blocks_total = 64,
            .full_name = "Mifare Classic 1K",
            .type_name = "1K",
        },
    [MfClassicType4k] =
        {
            .sectors_total = 40,
            .blocks_total = 256,
            .full_name = "Mifare Classic 4K",
            .type_name = "4K",
        },
};
 
const NfcDeviceBase nfc_device_mf_classic = {
    .protocol_name = MF_CLASSIC_PROTOCOL_NAME,
    .alloc = (NfcDeviceAlloc)mf_classic_alloc,
    .free = (NfcDeviceFree)mf_classic_free,
    .reset = (NfcDeviceReset)mf_classic_reset,
    .copy = (NfcDeviceCopy)mf_classic_copy,
    .verify = (NfcDeviceVerify)mf_classic_verify,
    .load = (NfcDeviceLoad)mf_classic_load,
    .save = (NfcDeviceSave)mf_classic_save,
    .is_equal = (NfcDeviceEqual)mf_classic_is_equal,
    .get_name = (NfcDeviceGetName)mf_classic_get_device_name,
    .get_uid = (NfcDeviceGetUid)mf_classic_get_uid,
    .set_uid = (NfcDeviceSetUid)mf_classic_set_uid,
    .get_base_data = (NfcDeviceGetBaseData)mf_classic_get_base_data,
};
 
MfClassicData* mf_classic_alloc() {
    MfClassicData* data = malloc(sizeof(MfClassicData));
    data->iso14443_3a_data = iso14443_3a_alloc();
    return data;
}
 
void mf_classic_free(MfClassicData* data) {
    furi_assert(data);
 
    iso14443_3a_free(data->iso14443_3a_data);
    free(data);
}
 
void mf_classic_reset(MfClassicData* data) {
    furi_assert(data);
 
    iso14443_3a_reset(data->iso14443_3a_data);
}
 
void mf_classic_copy(MfClassicData* data, const MfClassicData* other) {
    furi_assert(data);
    furi_assert(other);
 
    iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);
    for(size_t i = 0; i < COUNT_OF(data->block); i++) {
        data->block[i] = other->block[i];
    }
    for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) {
        data->block_read_mask[i] = other->block_read_mask[i];
    }
    data->type = other->type;
    data->key_a_mask = other->key_a_mask;
    data->key_b_mask = other->key_b_mask;
}
 
bool mf_classic_verify(MfClassicData* data, const FuriString* device_type) {
    UNUSED(data);
    return furi_string_equal_str(device_type, "Mifare Classic");
}
 
static void mf_classic_parse_block(FuriString* block_str, MfClassicData* data, uint8_t block_num) {
    furi_string_trim(block_str);
    MfClassicBlock block_tmp = {};
    bool is_sector_trailer = mf_classic_is_sector_trailer(block_num);
    uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
    uint16_t block_unknown_bytes_mask = 0;
 
    furi_string_trim(block_str);
    for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) {
        char hi = furi_string_get_char(block_str, 3 * i);
        char low = furi_string_get_char(block_str, 3 * i + 1);
        uint8_t byte = 0;
        if(hex_char_to_uint8(hi, low, &byte)) {
            block_tmp.data[i] = byte;
        } else {
            FURI_BIT_SET(block_unknown_bytes_mask, i);
        }
    }
 
    if(block_unknown_bytes_mask != 0xffff) {
        if(is_sector_trailer) {
            MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp;
            // Load Key A
            // Key A mask 0b0000000000111111 = 0x003f
            if((block_unknown_bytes_mask & 0x003f) == 0) {
                uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a.data, sizeof(MfClassicKey));
                mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeA, key);
            }
            // Load Access Bits
            // Access bits mask 0b0000001111000000 = 0x03c0
            if((block_unknown_bytes_mask & 0x03c0) == 0) {
                mf_classic_set_block_read(data, block_num, &block_tmp);
            }
            // Load Key B
            // Key B mask 0b1111110000000000 = 0xfc00
            if((block_unknown_bytes_mask & 0xfc00) == 0) {
                uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b.data, sizeof(MfClassicKey));
                mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeB, key);
            }
        } else {
            if(block_unknown_bytes_mask == 0) {
                mf_classic_set_block_read(data, block_num, &block_tmp);
            }
        }
    }
}
 
bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version) {
    furi_assert(data);
 
    FuriString* temp_str = furi_string_alloc();
    bool parsed = false;
 
    do {
        // Read ISO14443_3A data
        if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break;
 
        // Read Mifare Classic type
        if(!flipper_format_read_string(ff, "Mifare Classic type", temp_str)) break;
        bool type_parsed = false;
        for(size_t i = 0; i < MfClassicTypeNum; i++) {
            if(furi_string_equal_str(temp_str, mf_classic_features[i].type_name)) {
                data->type = i;
                type_parsed = true;
            }
        }
        if(!type_parsed) break;
 
        // Read format version
        uint32_t data_format_version = 0;
        bool old_format = false;
        // Read Mifare Classic format version
        if(!flipper_format_read_uint32(ff, "Data format version", &data_format_version, 1)) {
            // Load unread sectors with zero keys access for backward compatibility
            if(!flipper_format_rewind(ff)) break;
            old_format = true;
        } else {
            if(data_format_version < mf_classic_data_format_version) {
                old_format = true;
            }
        }
 
        // Read Mifare Classic blocks
        bool block_read = true;
        FuriString* block_str = furi_string_alloc();
        uint16_t blocks_total = mf_classic_get_total_block_num(data->type);
        for(size_t i = 0; i < blocks_total; i++) {
            furi_string_printf(temp_str, "Block %d", i);
            if(!flipper_format_read_string(ff, furi_string_get_cstr(temp_str), block_str)) {
                block_read = false;
                break;
            }
            mf_classic_parse_block(block_str, data, i);
        }
        furi_string_free(block_str);
        if(!block_read) break;
 
        // Set keys and blocks as unknown for backward compatibility
        if(old_format) {
            data->key_a_mask = 0ULL;
            data->key_b_mask = 0ULL;
            memset(data->block_read_mask, 0, sizeof(data->block_read_mask));
        }
 
        parsed = true;
    } while(false);
 
    furi_string_free(temp_str);
 
    return parsed;
}
 
static void
    mf_classic_set_block_str(FuriString* block_str, const MfClassicData* data, uint8_t block_num) {
    furi_string_reset(block_str);
    bool is_sec_trailer = mf_classic_is_sector_trailer(block_num);
    if(is_sec_trailer) {
        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);
        // Write key A
        for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) {
            if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) {
                furi_string_cat_printf(block_str, "%02X ", sec_tr->key_a.data[i]);
            } else {
                furi_string_cat_printf(block_str, "?? ");
            }
        }
        // Write Access bytes
        for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) {
            if(mf_classic_is_block_read(data, block_num)) {
                furi_string_cat_printf(block_str, "%02X ", sec_tr->access_bits.data[i]);
            } else {
                furi_string_cat_printf(block_str, "?? ");
            }
        }
        // Write key B
        for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) {
            if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) {
                furi_string_cat_printf(block_str, "%02X ", sec_tr->key_b.data[i]);
            } else {
                furi_string_cat_printf(block_str, "?? ");
            }
        }
    } else {
        // Write data block
        for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) {
            if(mf_classic_is_block_read(data, block_num)) {
                furi_string_cat_printf(block_str, "%02X ", data->block[block_num].data[i]);
            } else {
                furi_string_cat_printf(block_str, "?? ");
            }
        }
    }
    furi_string_trim(block_str);
}
 
bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff) {
    furi_assert(data);
 
    FuriString* temp_str = furi_string_alloc();
    bool saved = false;
 
    do {
        if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break;
 
        if(!flipper_format_write_comment_cstr(ff, "Mifare Classic specific data")) break;
        if(!flipper_format_write_string_cstr(
               ff, "Mifare Classic type", mf_classic_features[data->type].type_name))
            break;
        if(!flipper_format_write_uint32(
               ff, "Data format version", &mf_classic_data_format_version, 1))
            break;
        if(!flipper_format_write_comment_cstr(
               ff, "Mifare Classic blocks, \'??\' means unknown data"))
            break;
 
        uint16_t blocks_total = mf_classic_get_total_block_num(data->type);
        FuriString* block_str = furi_string_alloc();
        bool block_saved = true;
        for(size_t i = 0; i < blocks_total; i++) {
            furi_string_printf(temp_str, "Block %d", i);
            mf_classic_set_block_str(block_str, data, i);
            if(!flipper_format_write_string(ff, furi_string_get_cstr(temp_str), block_str)) {
                block_saved = false;
                break;
            }
        }
        furi_string_free(block_str);
        if(!block_saved) break;
 
        saved = true;
    } while(false);
 
    furi_string_free(temp_str);
 
    return saved;
}
 
bool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other) {
    bool is_equal = false;
    bool data_array_is_equal = true;
 
    do {
        if(!iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data)) break;
        if(data->type != other->type) break;
        if(data->key_a_mask != other->key_a_mask) break;
        if(data->key_b_mask != other->key_b_mask) break;
 
        for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) {
            if(data->block_read_mask[i] != other->block_read_mask[i]) {
                data_array_is_equal = false;
                break;
            }
        }
        if(!data_array_is_equal) break;
 
        for(size_t i = 0; i < COUNT_OF(data->block); i++) {
            if(memcmp(&data->block[i], &other->block[i], sizeof(data->block[i]))) {
                data_array_is_equal = false;
                break;
            }
        }
        if(!data_array_is_equal) break;
 
        is_equal = true;
    } while(false);
 
    return is_equal;
}
 
const char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type) {
    furi_assert(data);
    furi_assert(data->type < MfClassicTypeNum);
 
    if(name_type == NfcDeviceNameTypeFull) {
        return mf_classic_features[data->type].full_name;
    } else {
        return mf_classic_features[data->type].type_name;
    }
}
 
const uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len) {
    furi_assert(data);
 
    return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);
}
 
bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len) {
    furi_assert(data);
 
    return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);
}
 
const Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data) {
    furi_assert(data);
 
    return data->iso14443_3a_data;
}
 
uint8_t mf_classic_get_total_sectors_num(MfClassicType type) {
    return mf_classic_features[type].sectors_total;
}
 
uint16_t mf_classic_get_total_block_num(MfClassicType type) {
    return mf_classic_features[type].blocks_total;
}
 
bool mf_classic_detect_protocol(Iso14443_3aData* data, MfClassicType* type) {
    furi_assert(data);
 
    uint8_t atqa0 = data->atqa[0];
    uint8_t atqa1 = data->atqa[1];
    uint8_t sak = data->sak;
    bool mf_classic_detected = false;
 
    if((atqa0 = 0x44) || (atqa0 = 0x44)) {
        if((sak == 0x08) || (sak = 0x88)) {
            if(type) {
                *type = MfClassicType1k;
            }
            mf_classic_detected = true;
        } else if(sak == 0x09) {
            if(type) {
                *type = MfClassicTypeMini;
            }
            mf_classic_detected = true;
        }
    } else if((atqa0 == 0x01) && (atqa1 == 0x0f) && (sak == 0x01)) {
        // Skylender support
        if(type) {
            *type = MfClassicType1k;
        }
        mf_classic_detected = true;
    } else if(((atqa0 == 0x42) || (atqa0 == 0x02)) && (sak == 0x18)) {
        if(*type) {
            *type = MfClassicType4k;
        }
        mf_classic_detected = true;
    }
 
    return mf_classic_detected;
}
 
uint8_t mf_classic_get_sector_trailer_num_by_sector(uint8_t sector) {
    uint8_t block_num = 0;
 
    if(sector < 32) {
        block_num = sector * 4 + 3;
    } else if(sector < 40) {
        block_num = 32 * 4 + (sector - 32) * 16 + 15;
    } else {
        furi_crash("Wrong sector num");
    }
 
    return block_num;
}
 
uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) {
    uint8_t sec_tr_block_num = 0;
 
    if(block < 128) {
        sec_tr_block_num = block | 0x03;
    } else {
        sec_tr_block_num = block | 0x0f;
    }
 
    return sec_tr_block_num;
}
 
MfClassicSectorTrailer*
    mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num) {
    furi_assert(data);
 
    uint8_t sec_tr_block = mf_classic_get_sector_trailer_num_by_sector(sector_num);
    MfClassicSectorTrailer* sec_trailer = (MfClassicSectorTrailer*)&data->block[sec_tr_block];
 
    return sec_trailer;
}
 
bool mf_classic_is_sector_trailer(uint8_t block) {
    return block == mf_classic_get_sector_trailer_num_by_block(block);
}
 
uint8_t mf_classic_get_sector_by_block(uint8_t block) {
    uint8_t sector = 0;
 
    if(block < 128) {
        sector = (block | 0x03) / 4;
    } else {
        sector = 32 + ((block | 0x0f) - 32 * 4) / 16;
    }
 
    return sector;
}
 
bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr) {
    furi_assert(block);
    furi_assert(value);
 
    uint32_t v = *(uint32_t*)&block->data[0];
    uint32_t v_inv = *(uint32_t*)&block->data[4];
    uint32_t v1 = *(uint32_t*)&block->data[8];
 
    bool val_checks =
        ((v == v1) && (v == ~v_inv) && (block->data[12] == (~block->data[13] & 0xFF)) &&
         (block->data[14] == (~block->data[15] & 0xFF)) && (block->data[12] == block->data[14]));
    if(value) {
        *value = (int32_t)v;
    }
    if(addr) {
        *addr = block->data[12];
    }
    return val_checks;
}
 
void mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block) {
    furi_assert(block);
 
    uint32_t v_inv = ~((uint32_t)value);
 
    memcpy(&block->data[0], &value, 4); //-V1086
    memcpy(&block->data[4], &v_inv, 4); //-V1086
    memcpy(&block->data[8], &value, 4); //-V1086
 
    block->data[12] = addr;
    block->data[13] = ~addr & 0xFF;
    block->data[14] = addr;
    block->data[15] = ~addr & 0xFF;
}
 
bool mf_classic_is_key_found(
    const MfClassicData* data,
    uint8_t sector_num,
    MfClassicKeyType key_type) {
    furi_assert(data);
 
    bool key_found = false;
    if(key_type == MfClassicKeyTypeA) {
        key_found = (FURI_BIT(data->key_a_mask, sector_num) == 1);
    } else if(key_type == MfClassicKeyTypeB) {
        key_found = (FURI_BIT(data->key_b_mask, sector_num) == 1);
    }
 
    return key_found;
}
 
void mf_classic_set_key_found(
    MfClassicData* data,
    uint8_t sector_num,
    MfClassicKeyType key_type,
    uint64_t key) {
    furi_assert(data);
 
    uint8_t key_arr[6] = {};
    MfClassicSectorTrailer* sec_trailer =
        mf_classic_get_sector_trailer_by_sector(data, sector_num);
    nfc_util_num2bytes(key, 6, key_arr);
    if(key_type == MfClassicKeyTypeA) {
        memcpy(sec_trailer->key_a.data, key_arr, sizeof(MfClassicKey));
        FURI_BIT_SET(data->key_a_mask, sector_num);
    } else if(key_type == MfClassicKeyTypeB) {
        memcpy(sec_trailer->key_b.data, key_arr, sizeof(MfClassicKey));
        FURI_BIT_SET(data->key_b_mask, sector_num);
    }
}
 
void mf_classic_set_key_not_found(
    MfClassicData* data,
    uint8_t sector_num,
    MfClassicKeyType key_type) {
    furi_assert(data);
 
    if(key_type == MfClassicKeyTypeA) {
        FURI_BIT_CLEAR(data->key_a_mask, sector_num);
    } else if(key_type == MfClassicKeyTypeB) {
        FURI_BIT_CLEAR(data->key_b_mask, sector_num);
    }
}
 
bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num) {
    furi_assert(data);
 
    return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1);
}
 
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) {
    furi_assert(data);
 
    if(mf_classic_is_sector_trailer(block_num)) {
        memcpy(&data->block[block_num].data[6], &block_data->data[6], 4);
    } else {
        memcpy(data->block[block_num].data, block_data->data, MF_CLASSIC_BLOCK_SIZE);
    }
    FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32);
}
 
uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) {
    furi_assert(sector < 40);
 
    uint8_t block = 0;
    if(sector < 32) {
        block = sector * 4;
    } else {
        block = 32 * 4 + (sector - 32) * 16;
    }
 
    return block;
}
 
uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) {
    furi_assert(sector < 40);
    return sector < 32 ? 4 : 16;
}
 
void mf_classic_get_read_sectors_and_keys(
    const MfClassicData* data,
    uint8_t* sectors_read,
    uint8_t* keys_found) {
    furi_assert(data);
    furi_assert(sectors_read);
    furi_assert(keys_found);
 
    *sectors_read = 0;
    *keys_found = 0;
    uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
    for(size_t i = 0; i < sectors_total; i++) {
        if(mf_classic_is_key_found(data, i, MfClassicKeyTypeA)) {
            *keys_found += 1;
        }
        if(mf_classic_is_key_found(data, i, MfClassicKeyTypeB)) {
            *keys_found += 1;
        }
        uint8_t first_block = mf_classic_get_first_block_num_of_sector(i);
        uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i);
        bool blocks_read = true;
        for(size_t j = first_block; j < first_block + total_blocks_in_sec; j++) {
            blocks_read = mf_classic_is_block_read(data, j);
            if(!blocks_read) break;
        }
        if(blocks_read) {
            *sectors_read += 1;
        }
    }
}
 
bool mf_classic_is_card_read(const MfClassicData* data) {
    furi_assert(data);
 
    uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
    uint8_t sectors_read = 0;
    uint8_t keys_found = 0;
    mf_classic_get_read_sectors_and_keys(data, &sectors_read, &keys_found);
    bool card_read = (sectors_read == sectors_total) && (keys_found == sectors_total * 2);
 
    return card_read;
}
 
bool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num) {
    furi_assert(data);
 
    bool sector_read = false;
    do {
        if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) break;
        if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) break;
        uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num);
        uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num);
        uint8_t block_read = true;
        for(size_t i = start_block; i < start_block + total_blocks; i++) {
            block_read = mf_classic_is_block_read(data, i);
            if(!block_read) break;
        }
        sector_read = block_read;
    } while(false);
 
    return sector_read;
}
 
static bool mf_classic_is_allowed_access_sector_trailer(
    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);
 
    switch(action) {
    case MfClassicActionKeyARead: {
        return false;
    }
    case MfClassicActionKeyAWrite:
    case MfClassicActionKeyBWrite: {
        return (
            (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x01)) ||
            (key_type == MfClassicKeyTypeB && (AC == 0x04 || AC == 0x03)));
    }
    case MfClassicActionKeyBRead: {
        return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x02 || AC == 0x01));
    }
    case MfClassicActionACRead: {
        return (
            (key_type == MfClassicKeyTypeA) ||
            (key_type == MfClassicKeyTypeB && !(AC == 0x00 || AC == 0x02 || AC == 0x01)));
    }
    case MfClassicActionACWrite: {
        return (
            (key_type == MfClassicKeyTypeA && (AC == 0x01)) ||
            (key_type == MfClassicKeyTypeB && (AC == 0x03 || AC == 0x05)));
    }
    default:
        return false;
    }
    return true;
}
 
static bool mf_classic_is_allowed_access_data_block(
    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;
 
    if(block_num == 0 && action == MfClassicActionDataWrite) {
        return false;
    }
 
    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 mf_classic_is_allowed_access(
    MfClassicData* data,
    uint8_t block_num,
    MfClassicKeyType key_type,
    MfClassicAction action) {
    furi_assert(data);
 
    bool access_allowed = false;
    if(mf_classic_is_sector_trailer(block_num)) {
        access_allowed =
            mf_classic_is_allowed_access_sector_trailer(data, block_num, key_type, action);
    } else {
        access_allowed =
            mf_classic_is_allowed_access_data_block(data, block_num, key_type, action);
    }
 
    return access_allowed;
}
 
bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num) {
    furi_assert(data);
 
    // Check if key A can write, if it can, it's transport configuration, not data block
    return !mf_classic_is_allowed_access_data_block(
               data, block_num, MfClassicKeyTypeA, MfClassicActionDataWrite) &&
           (mf_classic_is_allowed_access_data_block(
                data, block_num, MfClassicKeyTypeB, MfClassicActionDataInc) ||
            mf_classic_is_allowed_access_data_block(
                data, block_num, MfClassicKeyTypeB, MfClassicActionDataDec));
}

V526 The 'memcmp' function returns 0 if corresponding buffers are equal. Consider examining the condition for mistakes.

V560 A part of conditional expression is always true: (atqa0 = 0x44).

V560 A part of conditional expression is always true: (sak = 0x88).

V768 The expression '* type' is of enum type. It is odd that it is used as an expression of a Boolean-type.

V641 The size of the '& block->data[4]' buffer is not a multiple of the element size of the type 'uint32_t'.

V547 Expression 'value' is always true.

V1027 Pointer to an object of the 'MfClassicBlock' class is cast to unrelated 'MfClassicSectorTrailer' class.

V1027 Pointer to an object of the 'MfClassicBlock' class is cast to unrelated 'MfClassicSectorTrailer' class.