[FL-2933] Mf Classic initial write, update, detect reader (#1941)
* nfc: introduce nfc write * nfc: add write logic * nfc worker: add write state * nfc: add mfc update logic * nfc: add update success logic * nfc: add custom card for detect reader * nfc: update write logic * nfc: add halt command, add notifications * nfc: add write fail scene * nfc: fixes and clean up * nfc: fix navigation ad notifications * nfc: fix detect reader nfc data setter Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
@@ -73,3 +73,55 @@ uint32_t prng_successor(uint32_t x, uint32_t n) {
|
||||
|
||||
return SWAPENDIAN(x);
|
||||
}
|
||||
|
||||
void crypto1_decrypt(
|
||||
Crypto1* crypto,
|
||||
uint8_t* encrypted_data,
|
||||
uint16_t encrypted_data_bits,
|
||||
uint8_t* decrypted_data) {
|
||||
furi_assert(crypto);
|
||||
furi_assert(encrypted_data);
|
||||
furi_assert(decrypted_data);
|
||||
|
||||
if(encrypted_data_bits < 8) {
|
||||
uint8_t decrypted_byte = 0;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3;
|
||||
decrypted_data[0] = decrypted_byte;
|
||||
} else {
|
||||
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
|
||||
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void crypto1_encrypt(
|
||||
Crypto1* crypto,
|
||||
uint8_t* keystream,
|
||||
uint8_t* plain_data,
|
||||
uint16_t plain_data_bits,
|
||||
uint8_t* encrypted_data,
|
||||
uint8_t* encrypted_parity) {
|
||||
furi_assert(crypto);
|
||||
furi_assert(plain_data);
|
||||
furi_assert(encrypted_data);
|
||||
furi_assert(encrypted_parity);
|
||||
|
||||
if(plain_data_bits < 8) {
|
||||
encrypted_data[0] = 0;
|
||||
for(size_t i = 0; i < plain_data_bits; i++) {
|
||||
encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
|
||||
}
|
||||
} else {
|
||||
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
|
||||
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
|
||||
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
|
||||
plain_data[i];
|
||||
encrypted_parity[i / 8] |=
|
||||
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01)
|
||||
<< (7 - (i & 0x0007)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,3 +21,17 @@ uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted);
|
||||
uint32_t crypto1_filter(uint32_t in);
|
||||
|
||||
uint32_t prng_successor(uint32_t x, uint32_t n);
|
||||
|
||||
void crypto1_decrypt(
|
||||
Crypto1* crypto,
|
||||
uint8_t* encrypted_data,
|
||||
uint16_t encrypted_data_bits,
|
||||
uint8_t* decrypted_data);
|
||||
|
||||
void crypto1_encrypt(
|
||||
Crypto1* crypto,
|
||||
uint8_t* keystream,
|
||||
uint8_t* plain_data,
|
||||
uint16_t plain_data_bits,
|
||||
uint8_t* encrypted_data,
|
||||
uint8_t* encrypted_parity);
|
||||
|
@@ -9,21 +9,8 @@
|
||||
|
||||
#define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U)
|
||||
#define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U)
|
||||
#define MF_CLASSIC_READ_SECT_CMD (0x30)
|
||||
|
||||
typedef enum {
|
||||
MfClassicActionDataRead,
|
||||
MfClassicActionDataWrite,
|
||||
MfClassicActionDataInc,
|
||||
MfClassicActionDataDec,
|
||||
|
||||
MfClassicActionKeyARead,
|
||||
MfClassicActionKeyAWrite,
|
||||
MfClassicActionKeyBRead,
|
||||
MfClassicActionKeyBWrite,
|
||||
MfClassicActionACRead,
|
||||
MfClassicActionACWrite,
|
||||
} MfClassicAction;
|
||||
#define MF_CLASSIC_READ_BLOCK_CMD (0x30)
|
||||
#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0)
|
||||
|
||||
const char* mf_classic_get_type_str(MfClassicType type) {
|
||||
if(type == MfClassicType1k) {
|
||||
@@ -122,6 +109,24 @@ void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassic
|
||||
FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32);
|
||||
}
|
||||
|
||||
bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num) {
|
||||
furi_assert(data);
|
||||
|
||||
uint8_t first_block = mf_classic_get_first_block_num_of_sector(sector_num);
|
||||
uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num);
|
||||
bool data_read = true;
|
||||
for(size_t i = first_block; i < first_block + total_blocks; i++) {
|
||||
data_read &= mf_classic_is_block_read(data, i);
|
||||
}
|
||||
|
||||
return data_read;
|
||||
}
|
||||
|
||||
void mf_classic_set_sector_data_not_read(MfClassicData* data) {
|
||||
furi_assert(data);
|
||||
memset(data->block_read_mask, 0, sizeof(data->block_read_mask));
|
||||
}
|
||||
|
||||
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) {
|
||||
furi_assert(data);
|
||||
|
||||
@@ -190,6 +195,9 @@ void mf_classic_get_read_sectors_and_keys(
|
||||
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);
|
||||
@@ -225,12 +233,12 @@ bool mf_classic_is_card_read(MfClassicData* data) {
|
||||
return card_read;
|
||||
}
|
||||
|
||||
static bool mf_classic_is_allowed_access_sector_trailer(
|
||||
MfClassicEmulator* emulator,
|
||||
bool mf_classic_is_allowed_access_sector_trailer(
|
||||
MfClassicData* data,
|
||||
uint8_t block_num,
|
||||
MfClassicKey key,
|
||||
MfClassicAction action) {
|
||||
uint8_t* sector_trailer = emulator->data.block[block_num].value;
|
||||
uint8_t* sector_trailer = data->block[block_num].value;
|
||||
uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) |
|
||||
((sector_trailer[8] >> 7) & 0x01);
|
||||
switch(action) {
|
||||
@@ -266,13 +274,13 @@ static bool mf_classic_is_allowed_access_sector_trailer(
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mf_classic_is_allowed_access_data_block(
|
||||
MfClassicEmulator* emulator,
|
||||
bool mf_classic_is_allowed_access_data_block(
|
||||
MfClassicData* data,
|
||||
uint8_t block_num,
|
||||
MfClassicKey key,
|
||||
MfClassicAction action) {
|
||||
uint8_t* sector_trailer =
|
||||
emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value;
|
||||
data->block[mf_classic_get_sector_trailer_num_by_block(block_num)].value;
|
||||
|
||||
uint8_t sector_block;
|
||||
if(block_num <= 128) {
|
||||
@@ -336,9 +344,10 @@ static bool mf_classic_is_allowed_access(
|
||||
MfClassicKey key,
|
||||
MfClassicAction action) {
|
||||
if(mf_classic_is_sector_trailer(block_num)) {
|
||||
return mf_classic_is_allowed_access_sector_trailer(emulator, block_num, key, action);
|
||||
return mf_classic_is_allowed_access_sector_trailer(
|
||||
&emulator->data, block_num, key, action);
|
||||
} else {
|
||||
return mf_classic_is_allowed_access_data_block(emulator, block_num, key, action);
|
||||
return mf_classic_is_allowed_access_data_block(&emulator->data, block_num, key, action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,25 +523,17 @@ bool mf_classic_read_block(
|
||||
furi_assert(block);
|
||||
|
||||
bool read_block_success = false;
|
||||
uint8_t plain_cmd[4] = {MF_CLASSIC_READ_SECT_CMD, block_num, 0x00, 0x00};
|
||||
uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00};
|
||||
nfca_append_crc16(plain_cmd, 2);
|
||||
memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data));
|
||||
memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity));
|
||||
|
||||
for(uint8_t i = 0; i < 4; i++) {
|
||||
tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ plain_cmd[i];
|
||||
tx_rx->tx_parity[0] |=
|
||||
((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i);
|
||||
}
|
||||
crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
tx_rx->tx_bits = 4 * 9;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
|
||||
|
||||
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
|
||||
if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) {
|
||||
uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2];
|
||||
for(uint8_t i = 0; i < MF_CLASSIC_BLOCK_SIZE + 2; i++) {
|
||||
block_received[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i];
|
||||
}
|
||||
crypto1_decrypt(crypto, tx_rx->rx_data, tx_rx->rx_bits, block_received);
|
||||
uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE);
|
||||
uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) |
|
||||
block_received[MF_CLASSIC_BLOCK_SIZE];
|
||||
@@ -754,49 +755,6 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data
|
||||
return sectors_read;
|
||||
}
|
||||
|
||||
void mf_crypto1_decrypt(
|
||||
Crypto1* crypto,
|
||||
uint8_t* encrypted_data,
|
||||
uint16_t encrypted_data_bits,
|
||||
uint8_t* decrypted_data) {
|
||||
if(encrypted_data_bits < 8) {
|
||||
uint8_t decrypted_byte = 0;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3;
|
||||
decrypted_data[0] = decrypted_byte;
|
||||
} else {
|
||||
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
|
||||
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mf_crypto1_encrypt(
|
||||
Crypto1* crypto,
|
||||
uint8_t* keystream,
|
||||
uint8_t* plain_data,
|
||||
uint16_t plain_data_bits,
|
||||
uint8_t* encrypted_data,
|
||||
uint8_t* encrypted_parity) {
|
||||
if(plain_data_bits < 8) {
|
||||
encrypted_data[0] = 0;
|
||||
for(size_t i = 0; i < plain_data_bits; i++) {
|
||||
encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
|
||||
}
|
||||
} else {
|
||||
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
|
||||
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
|
||||
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
|
||||
plain_data[i];
|
||||
encrypted_parity[i / 8] |=
|
||||
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01)
|
||||
<< (7 - (i & 0x0007)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) {
|
||||
furi_assert(emulator);
|
||||
furi_assert(tx_rx);
|
||||
@@ -819,7 +777,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
tx_rx->rx_bits);
|
||||
break;
|
||||
}
|
||||
mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
|
||||
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
|
||||
}
|
||||
|
||||
if(plain_data[0] == 0x50 && plain_data[1] == 0x00) {
|
||||
@@ -857,7 +815,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
tx_rx->tx_bits = sizeof(nt) * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
} else {
|
||||
mf_crypto1_encrypt(
|
||||
crypto1_encrypt(
|
||||
&emulator->crypto,
|
||||
nt_keystream,
|
||||
nt,
|
||||
@@ -904,7 +862,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
uint32_t ans = prng_successor(nonce, 96);
|
||||
uint8_t responce[4] = {};
|
||||
nfc_util_num2bytes(ans, 4, responce);
|
||||
mf_crypto1_encrypt(
|
||||
crypto1_encrypt(
|
||||
&emulator->crypto,
|
||||
NULL,
|
||||
responce,
|
||||
@@ -938,7 +896,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
// Send NACK
|
||||
uint8_t nack = 0x04;
|
||||
if(is_encrypted) {
|
||||
mf_crypto1_encrypt(
|
||||
crypto1_encrypt(
|
||||
&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
} else {
|
||||
tx_rx->tx_data[0] = nack;
|
||||
@@ -951,7 +909,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
}
|
||||
nfca_append_crc16(block_data, 16);
|
||||
|
||||
mf_crypto1_encrypt(
|
||||
crypto1_encrypt(
|
||||
&emulator->crypto,
|
||||
NULL,
|
||||
block_data,
|
||||
@@ -967,14 +925,14 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
}
|
||||
// Send ACK
|
||||
uint8_t ack = 0x0A;
|
||||
mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
tx_rx->tx_bits = 4;
|
||||
|
||||
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
|
||||
if(tx_rx->rx_bits != 18 * 8) break;
|
||||
|
||||
mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
|
||||
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
|
||||
uint8_t block_data[16] = {};
|
||||
memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE);
|
||||
if(mf_classic_is_sector_trailer(block)) {
|
||||
@@ -1002,7 +960,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
}
|
||||
// Send ACK
|
||||
ack = 0x0A;
|
||||
mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
|
||||
tx_rx->tx_bits = 4;
|
||||
} else {
|
||||
@@ -1015,8 +973,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
// Send NACK
|
||||
uint8_t nack = 0x04;
|
||||
if(is_encrypted) {
|
||||
mf_crypto1_encrypt(
|
||||
&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
} else {
|
||||
tx_rx->tx_data[0] = nack;
|
||||
}
|
||||
@@ -1027,3 +984,143 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mf_classic_write_block(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
MfClassicBlock* src_block,
|
||||
uint8_t block_num,
|
||||
MfClassicKey key_type,
|
||||
uint64_t key) {
|
||||
furi_assert(tx_rx);
|
||||
furi_assert(src_block);
|
||||
|
||||
Crypto1 crypto = {};
|
||||
uint8_t plain_data[18] = {};
|
||||
uint8_t resp = 0;
|
||||
bool write_success = false;
|
||||
|
||||
do {
|
||||
furi_hal_nfc_sleep();
|
||||
if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto)) {
|
||||
FURI_LOG_D(TAG, "Auth fail");
|
||||
break;
|
||||
}
|
||||
// Send write command
|
||||
plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD;
|
||||
plain_data[1] = block_num;
|
||||
nfca_append_crc16(plain_data, 2);
|
||||
crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
tx_rx->tx_bits = 4 * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
|
||||
|
||||
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
|
||||
if(tx_rx->rx_bits == 4) {
|
||||
crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp);
|
||||
if(resp != 0x0A) {
|
||||
FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Not ACK received");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Failed to send write cmd");
|
||||
break;
|
||||
}
|
||||
|
||||
// Send data
|
||||
memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE);
|
||||
nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE);
|
||||
crypto1_encrypt(
|
||||
&crypto,
|
||||
NULL,
|
||||
plain_data,
|
||||
(MF_CLASSIC_BLOCK_SIZE + 2) * 8,
|
||||
tx_rx->tx_data,
|
||||
tx_rx->tx_parity);
|
||||
tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
|
||||
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
|
||||
if(tx_rx->rx_bits == 4) {
|
||||
crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp);
|
||||
if(resp != 0x0A) {
|
||||
FURI_LOG_D(TAG, "NACK received on sending data");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Not ACK received");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Failed to send data");
|
||||
break;
|
||||
}
|
||||
write_success = true;
|
||||
|
||||
// Send Halt
|
||||
plain_data[0] = 0x50;
|
||||
plain_data[1] = 0x00;
|
||||
nfca_append_crc16(plain_data, 2);
|
||||
crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity);
|
||||
tx_rx->tx_bits = 2 * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
|
||||
// No response is expected
|
||||
furi_hal_nfc_tx_rx(tx_rx, 50);
|
||||
} while(false);
|
||||
|
||||
return write_success;
|
||||
}
|
||||
|
||||
bool mf_classic_write_sector(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
MfClassicData* dest_data,
|
||||
MfClassicData* src_data,
|
||||
uint8_t sec_num) {
|
||||
furi_assert(tx_rx);
|
||||
furi_assert(dest_data);
|
||||
furi_assert(src_data);
|
||||
|
||||
uint8_t first_block = mf_classic_get_first_block_num_of_sector(sec_num);
|
||||
uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num);
|
||||
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(dest_data, sec_num);
|
||||
bool key_a_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyA);
|
||||
bool key_b_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyB);
|
||||
|
||||
bool write_success = true;
|
||||
for(size_t i = first_block; i < first_block + total_blocks; i++) {
|
||||
// Compare blocks
|
||||
if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE)) {
|
||||
bool key_a_write_allowed = mf_classic_is_allowed_access_data_block(
|
||||
dest_data, i, MfClassicKeyA, MfClassicActionDataWrite);
|
||||
bool key_b_write_allowed = mf_classic_is_allowed_access_data_block(
|
||||
dest_data, i, MfClassicKeyB, MfClassicActionDataWrite);
|
||||
|
||||
if(key_a_found && key_a_write_allowed) {
|
||||
FURI_LOG_I(TAG, "Writing block %d with key A", i);
|
||||
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
|
||||
if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) {
|
||||
FURI_LOG_E(TAG, "Failed to write block %d", i);
|
||||
write_success = false;
|
||||
break;
|
||||
}
|
||||
} else if(key_b_found && key_b_write_allowed) {
|
||||
FURI_LOG_I(TAG, "Writing block %d with key A", i);
|
||||
uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6);
|
||||
if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) {
|
||||
FURI_LOG_E(TAG, "Failed to write block %d", i);
|
||||
write_success = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to find key with write access");
|
||||
write_success = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Blocks %d are equal", i);
|
||||
}
|
||||
}
|
||||
|
||||
return write_success;
|
||||
}
|
||||
|
@@ -27,6 +27,20 @@ typedef enum {
|
||||
MfClassicKeyB,
|
||||
} MfClassicKey;
|
||||
|
||||
typedef enum {
|
||||
MfClassicActionDataRead,
|
||||
MfClassicActionDataWrite,
|
||||
MfClassicActionDataInc,
|
||||
MfClassicActionDataDec,
|
||||
|
||||
MfClassicActionKeyARead,
|
||||
MfClassicActionKeyAWrite,
|
||||
MfClassicActionKeyBRead,
|
||||
MfClassicActionKeyBWrite,
|
||||
MfClassicActionACRead,
|
||||
MfClassicActionACWrite,
|
||||
} MfClassicAction;
|
||||
|
||||
typedef struct {
|
||||
uint8_t value[MF_CLASSIC_BLOCK_SIZE];
|
||||
} MfClassicBlock;
|
||||
@@ -90,6 +104,18 @@ bool mf_classic_is_sector_trailer(uint8_t block);
|
||||
|
||||
uint8_t mf_classic_get_sector_by_block(uint8_t block);
|
||||
|
||||
bool mf_classic_is_allowed_access_sector_trailer(
|
||||
MfClassicData* data,
|
||||
uint8_t block_num,
|
||||
MfClassicKey key,
|
||||
MfClassicAction action);
|
||||
|
||||
bool mf_classic_is_allowed_access_data_block(
|
||||
MfClassicData* data,
|
||||
uint8_t block_num,
|
||||
MfClassicKey key,
|
||||
MfClassicAction action);
|
||||
|
||||
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type);
|
||||
|
||||
void mf_classic_set_key_found(
|
||||
@@ -104,6 +130,10 @@ bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num);
|
||||
|
||||
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
|
||||
|
||||
bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num);
|
||||
|
||||
void mf_classic_set_sector_data_not_read(MfClassicData* data);
|
||||
|
||||
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
|
||||
|
||||
bool mf_classic_is_card_read(MfClassicData* data);
|
||||
@@ -145,3 +175,16 @@ uint8_t mf_classic_read_card(
|
||||
uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data);
|
||||
|
||||
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx);
|
||||
|
||||
bool mf_classic_write_block(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
MfClassicBlock* src_block,
|
||||
uint8_t block_num,
|
||||
MfClassicKey key_type,
|
||||
uint64_t key);
|
||||
|
||||
bool mf_classic_write_sector(
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
MfClassicData* dest_data,
|
||||
MfClassicData* src_data,
|
||||
uint8_t sec_num);
|
||||
|
Reference in New Issue
Block a user