"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring. Starring: - @gornekich - NFC refactoring project lead, architect, senior developer - @gsurkov - architect, senior developer - @RebornedBrain - senior developer Supporting roles: - @skotopes, @DrZlo13, @hedger - general architecture advisors, code review - @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance Special thanks: @bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
		
			
				
	
	
		
			387 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "mf_classic_poller_i.h"
 | 
						|
 | 
						|
#include <furi.h>
 | 
						|
#include <furi_hal_random.h>
 | 
						|
 | 
						|
#include <nfc/helpers/iso14443_crc.h>
 | 
						|
 | 
						|
#define TAG "MfCLassicPoller"
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
MfClassicError mf_classic_async_get_nt(
 | 
						|
    MfClassicPoller* instance,
 | 
						|
    uint8_t block_num,
 | 
						|
    MfClassicKeyType key_type,
 | 
						|
    MfClassicNt* nt) {
 | 
						|
    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));
 | 
						|
 | 
						|
        error = iso14443_3a_poller_send_standard_frame(
 | 
						|
            instance->iso14443_3a_poller,
 | 
						|
            instance->tx_plain_buffer,
 | 
						|
            instance->rx_plain_buffer,
 | 
						|
            MF_CLASSIC_FWT_FC);
 | 
						|
        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 ret;
 | 
						|
}
 | 
						|
 | 
						|
MfClassicError mf_classic_async_auth(
 | 
						|
    MfClassicPoller* instance,
 | 
						|
    uint8_t block_num,
 | 
						|
    MfClassicKey* key,
 | 
						|
    MfClassicKeyType key_type,
 | 
						|
    MfClassicAuthContext* data) {
 | 
						|
    MfClassicError ret = MfClassicErrorNone;
 | 
						|
    Iso14443_3aError error = Iso14443_3aErrorNone;
 | 
						|
 | 
						|
    do {
 | 
						|
        iso14443_3a_copy(
 | 
						|
            instance->data->iso14443_3a_data,
 | 
						|
            iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
 | 
						|
 | 
						|
        MfClassicNt nt = {};
 | 
						|
        ret = mf_classic_async_get_nt(instance, block_num, key_type, &nt);
 | 
						|
        if(ret != MfClassicErrorNone) break;
 | 
						|
        if(data) {
 | 
						|
            data->nt = nt;
 | 
						|
        }
 | 
						|
 | 
						|
        uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
 | 
						|
        uint64_t key_num = nfc_util_bytes2num(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);
 | 
						|
        error = iso14443_3a_poller_txrx_custom_parity(
 | 
						|
            instance->iso14443_3a_poller,
 | 
						|
            instance->tx_encrypted_buffer,
 | 
						|
            instance->rx_encrypted_buffer,
 | 
						|
            MF_CLASSIC_FWT_FC);
 | 
						|
 | 
						|
        if(error != Iso14443_3aErrorNone) {
 | 
						|
            ret = mf_classic_process_error(error);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) != 4) {
 | 
						|
            ret = MfClassicErrorAuth;
 | 
						|
        }
 | 
						|
 | 
						|
        crypto1_word(instance->crypto, 0, 0);
 | 
						|
        instance->auth_state = MfClassicAuthStatePassed;
 | 
						|
 | 
						|
        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 != MfClassicErrorNone) {
 | 
						|
        iso14443_3a_poller_halt(instance->iso14443_3a_poller);
 | 
						|
    }
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
MfClassicError mf_classic_async_halt(MfClassicPoller* instance) {
 | 
						|
    MfClassicError ret = MfClassicErrorNone;
 | 
						|
    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);
 | 
						|
 | 
						|
        crypto1_encrypt(
 | 
						|
            instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
 | 
						|
 | 
						|
        error = iso14443_3a_poller_txrx_custom_parity(
 | 
						|
            instance->iso14443_3a_poller,
 | 
						|
            instance->tx_encrypted_buffer,
 | 
						|
            instance->rx_encrypted_buffer,
 | 
						|
            MF_CLASSIC_FWT_FC);
 | 
						|
        if(error != Iso14443_3aErrorTimeout) {
 | 
						|
            ret = mf_classic_process_error(error);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        instance->auth_state = MfClassicAuthStateIdle;
 | 
						|
        instance->iso14443_3a_poller->state = Iso14443_3aPollerStateIdle;
 | 
						|
    } while(false);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
MfClassicError mf_classic_async_read_block(
 | 
						|
    MfClassicPoller* instance,
 | 
						|
    uint8_t block_num,
 | 
						|
    MfClassicBlock* data) {
 | 
						|
    MfClassicError ret = MfClassicErrorNone;
 | 
						|
    Iso14443_3aError error = Iso14443_3aErrorNone;
 | 
						|
 | 
						|
    do {
 | 
						|
        uint8_t read_block_cmd[2] = {MF_CLASSIC_CMD_READ_BLOCK, block_num};
 | 
						|
        bit_buffer_copy_bytes(instance->tx_plain_buffer, read_block_cmd, sizeof(read_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->iso14443_3a_poller,
 | 
						|
            instance->tx_encrypted_buffer,
 | 
						|
            instance->rx_encrypted_buffer,
 | 
						|
            MF_CLASSIC_FWT_FC);
 | 
						|
        if(error != Iso14443_3aErrorNone) {
 | 
						|
            ret = mf_classic_process_error(error);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) !=
 | 
						|
           (sizeof(MfClassicBlock) + 2)) {
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        crypto1_decrypt(
 | 
						|
            instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);
 | 
						|
 | 
						|
        if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_plain_buffer)) {
 | 
						|
            FURI_LOG_D(TAG, "CRC error");
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        iso14443_crc_trim(instance->rx_plain_buffer);
 | 
						|
        bit_buffer_write_bytes(instance->rx_plain_buffer, data->data, sizeof(MfClassicBlock));
 | 
						|
    } while(false);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
MfClassicError mf_classic_async_write_block(
 | 
						|
    MfClassicPoller* instance,
 | 
						|
    uint8_t block_num,
 | 
						|
    MfClassicBlock* data) {
 | 
						|
    MfClassicError ret = MfClassicErrorNone;
 | 
						|
    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->iso14443_3a_poller,
 | 
						|
            instance->tx_encrypted_buffer,
 | 
						|
            instance->rx_encrypted_buffer,
 | 
						|
            MF_CLASSIC_FWT_FC);
 | 
						|
        if(error != Iso14443_3aErrorNone) {
 | 
						|
            ret = mf_classic_process_error(error);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            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, "Not ACK received");
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            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->iso14443_3a_poller,
 | 
						|
            instance->tx_encrypted_buffer,
 | 
						|
            instance->rx_encrypted_buffer,
 | 
						|
            MF_CLASSIC_FWT_FC);
 | 
						|
        if(error != Iso14443_3aErrorNone) {
 | 
						|
            ret = mf_classic_process_error(error);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            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, "Not ACK received");
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    } while(false);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
MfClassicError mf_classic_async_value_cmd(
 | 
						|
    MfClassicPoller* instance,
 | 
						|
    uint8_t block_num,
 | 
						|
    MfClassicValueCommand cmd,
 | 
						|
    int32_t data) {
 | 
						|
    MfClassicError ret = MfClassicErrorNone;
 | 
						|
    Iso14443_3aError error = Iso14443_3aErrorNone;
 | 
						|
 | 
						|
    do {
 | 
						|
        uint8_t cmd_value = 0;
 | 
						|
        if(cmd == MfClassicValueCommandDecrement) {
 | 
						|
            cmd_value = MF_CLASSIC_CMD_VALUE_DEC;
 | 
						|
        } else if(cmd == MfClassicValueCommandIncrement) {
 | 
						|
            cmd_value = MF_CLASSIC_CMD_VALUE_INC;
 | 
						|
        } else {
 | 
						|
            cmd_value = MF_CLASSIC_CMD_VALUE_RESTORE;
 | 
						|
        }
 | 
						|
        uint8_t value_cmd[2] = {cmd_value, block_num};
 | 
						|
        bit_buffer_copy_bytes(instance->tx_plain_buffer, value_cmd, sizeof(value_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->iso14443_3a_poller,
 | 
						|
            instance->tx_encrypted_buffer,
 | 
						|
            instance->rx_encrypted_buffer,
 | 
						|
            MF_CLASSIC_FWT_FC);
 | 
						|
        if(error != Iso14443_3aErrorNone) {
 | 
						|
            ret = mf_classic_process_error(error);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            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, "Not ACK received");
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        bit_buffer_copy_bytes(instance->tx_plain_buffer, (uint8_t*)&data, sizeof(data));
 | 
						|
        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->iso14443_3a_poller,
 | 
						|
            instance->tx_encrypted_buffer,
 | 
						|
            instance->rx_encrypted_buffer,
 | 
						|
            MF_CLASSIC_FWT_FC);
 | 
						|
 | 
						|
        // Command processed if tag doesn't respond
 | 
						|
        if(error != Iso14443_3aErrorTimeout) {
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        ret = MfClassicErrorNone;
 | 
						|
    } while(false);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
MfClassicError mf_classic_async_value_transfer(MfClassicPoller* instance, uint8_t block_num) {
 | 
						|
    MfClassicError ret = MfClassicErrorNone;
 | 
						|
    Iso14443_3aError error = Iso14443_3aErrorNone;
 | 
						|
 | 
						|
    do {
 | 
						|
        uint8_t transfer_cmd[2] = {MF_CLASSIC_CMD_VALUE_TRANSFER, block_num};
 | 
						|
        bit_buffer_copy_bytes(instance->tx_plain_buffer, transfer_cmd, sizeof(transfer_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->iso14443_3a_poller,
 | 
						|
            instance->tx_encrypted_buffer,
 | 
						|
            instance->rx_encrypted_buffer,
 | 
						|
            MF_CLASSIC_FWT_FC);
 | 
						|
        if(error != Iso14443_3aErrorNone) {
 | 
						|
            ret = mf_classic_process_error(error);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            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, "Not ACK received");
 | 
						|
            ret = MfClassicErrorProtocol;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
    } while(false);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 |