 c10c45616d
			
		
	
	
		c10c45616d
		
			
		
	
	
	
	
		
			
			* improve digital_signal for longer packets, also clean up code * added SLIX2 specific features like signature and unknown keys (for issue #2781), added WRITE_PASSWORD handling * fix NfcV AFI selection * when NFCV_CMD_READ_MULTI_BLOCK reads beyond memory end, return the maximum possible block's content * added SLIX2 reading * fix NXP SYSTEMINFO response check size * capture the first received password if none was set before * clear stored data before reading SLIX details renamed slix2_dump functions to slix2_read * display card block size values as decimal Co-authored-by: あく <alleteam@gmail.com>
		
			
				
	
	
		
			785 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			785 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
 | |
| #include <limits.h>
 | |
| #include "nfcv.h"
 | |
| #include "slix.h"
 | |
| #include "nfc_util.h"
 | |
| #include <furi.h>
 | |
| #include "furi_hal_nfc.h"
 | |
| #include <furi_hal_random.h>
 | |
| 
 | |
| #define TAG "SLIX"
 | |
| 
 | |
| ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
 | |
|     furi_assert(nfc_data);
 | |
|     furi_assert(nfcv_data);
 | |
| 
 | |
|     uint8_t rxBuf[32];
 | |
|     uint16_t received = 0;
 | |
|     ReturnCode ret = ERR_NONE;
 | |
| 
 | |
|     FURI_LOG_D(TAG, "Read NXP SYSTEM INFORMATION...");
 | |
| 
 | |
|     for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
 | |
|         uint8_t cmd[] = {};
 | |
|         uint8_t uid[NFCV_UID_LENGTH];
 | |
| 
 | |
|         /* UID is stored reversed in requests */
 | |
|         for(int pos = 0; pos < nfc_data->uid_len; pos++) {
 | |
|             uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos];
 | |
|         }
 | |
| 
 | |
|         ReturnCode ret = rfalNfcvPollerTransceiveReq(
 | |
|             NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION,
 | |
|             RFAL_NFCV_REQ_FLAG_DEFAULT,
 | |
|             NFCV_MANUFACTURER_NXP,
 | |
|             uid,
 | |
|             cmd,
 | |
|             sizeof(cmd),
 | |
|             rxBuf,
 | |
|             sizeof(rxBuf),
 | |
|             &received);
 | |
| 
 | |
|         if(ret == ERR_NONE) {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if(ret != ERR_NONE || received != 8) { //-V560
 | |
|         FURI_LOG_D(TAG, "Failed: %d, %d", ret, received);
 | |
|         return ret;
 | |
|     }
 | |
|     FURI_LOG_D(TAG, "Success...");
 | |
| 
 | |
|     NfcVSlixData* slix = &nfcv_data->sub_data.slix;
 | |
|     slix->pp_pointer = rxBuf[1];
 | |
|     slix->pp_condition = rxBuf[2];
 | |
| 
 | |
|     /* convert NXP's to our internal lock bits format */
 | |
|     nfcv_data->security_status[0] = 0;
 | |
|     nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitDsfid) ? NfcVLockBitDsfid : 0;
 | |
|     nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitAfi) ? NfcVLockBitAfi : 0;
 | |
|     nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitEas) ? NfcVLockBitEas : 0;
 | |
|     nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitPpl) ? NfcVLockBitPpl : 0;
 | |
| 
 | |
|     return ERR_NONE;
 | |
| }
 | |
| 
 | |
| ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
 | |
|     furi_assert(nfc_data);
 | |
|     furi_assert(nfcv_data);
 | |
| 
 | |
|     uint8_t rxBuf[64];
 | |
|     uint16_t received = 0;
 | |
|     ReturnCode ret = ERR_NONE;
 | |
| 
 | |
|     FURI_LOG_D(TAG, "Read SIGNATURE...");
 | |
| 
 | |
|     for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
 | |
|         uint8_t cmd[] = {};
 | |
|         uint8_t uid[NFCV_UID_LENGTH];
 | |
| 
 | |
|         /* UID is stored reversed in requests */
 | |
|         for(int pos = 0; pos < nfc_data->uid_len; pos++) {
 | |
|             uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos];
 | |
|         }
 | |
| 
 | |
|         ReturnCode ret = rfalNfcvPollerTransceiveReq(
 | |
|             NFCV_CMD_NXP_READ_SIGNATURE,
 | |
|             RFAL_NFCV_REQ_FLAG_DEFAULT,
 | |
|             NFCV_MANUFACTURER_NXP,
 | |
|             uid,
 | |
|             cmd,
 | |
|             sizeof(cmd),
 | |
|             rxBuf,
 | |
|             sizeof(rxBuf),
 | |
|             &received);
 | |
| 
 | |
|         if(ret == ERR_NONE) {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if(ret != ERR_NONE || received != 33) { //-V560
 | |
|         FURI_LOG_D(TAG, "Failed: %d, %d", ret, received);
 | |
|         return ret;
 | |
|     }
 | |
|     FURI_LOG_D(TAG, "Success...");
 | |
| 
 | |
|     NfcVSlixData* slix = &nfcv_data->sub_data.slix;
 | |
|     memcpy(slix->signature, &rxBuf[1], 32);
 | |
| 
 | |
|     return ERR_NONE;
 | |
| }
 | |
| 
 | |
| ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
 | |
|     ReturnCode ret = ERR_NONE;
 | |
| 
 | |
|     ret = slix2_read_nxp_sysinfo(nfc_data, nfcv_data);
 | |
|     if(ret != ERR_NONE) {
 | |
|         return ret;
 | |
|     }
 | |
|     ret = slix2_read_signature(nfc_data, nfcv_data);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static uint32_t slix_read_be(uint8_t* data, uint32_t length) {
 | |
|     uint32_t value = 0;
 | |
| 
 | |
|     for(uint32_t pos = 0; pos < length; pos++) {
 | |
|         value <<= 8;
 | |
|         value |= data[pos];
 | |
|     }
 | |
| 
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| uint8_t slix_get_ti(FuriHalNfcDevData* nfc_data) {
 | |
|     return (nfc_data->uid[3] >> 3) & 3;
 | |
| }
 | |
| 
 | |
| bool slix_check_card_type(FuriHalNfcDevData* nfc_data) {
 | |
|     if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) &&
 | |
|        slix_get_ti(nfc_data) == 2) {
 | |
|         return true;
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool slix2_check_card_type(FuriHalNfcDevData* nfc_data) {
 | |
|     if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) &&
 | |
|        slix_get_ti(nfc_data) == 1) {
 | |
|         return true;
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data) {
 | |
|     if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x02)) {
 | |
|         return true;
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) {
 | |
|     if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x03)) {
 | |
|         return true;
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| ReturnCode slix_get_random(NfcVData* data) {
 | |
|     uint16_t received = 0;
 | |
|     uint8_t rxBuf[32];
 | |
| 
 | |
|     ReturnCode ret = rfalNfcvPollerTransceiveReq(
 | |
|         NFCV_CMD_NXP_GET_RANDOM_NUMBER,
 | |
|         RFAL_NFCV_REQ_FLAG_DEFAULT,
 | |
|         NFCV_MANUFACTURER_NXP,
 | |
|         NULL,
 | |
|         NULL,
 | |
|         0,
 | |
|         rxBuf,
 | |
|         sizeof(rxBuf),
 | |
|         &received);
 | |
| 
 | |
|     if(ret == ERR_NONE) {
 | |
|         if(received != 3) {
 | |
|             return ERR_PROTO;
 | |
|         }
 | |
|         if(data != NULL) {
 | |
|             data->sub_data.slix.rand[0] = rxBuf[2];
 | |
|             data->sub_data.slix.rand[1] = rxBuf[1];
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) {
 | |
|     furi_assert(rand);
 | |
| 
 | |
|     uint16_t received = 0;
 | |
|     uint8_t rxBuf[32];
 | |
|     uint8_t cmd_set_pass[] = {
 | |
|         password_id,
 | |
|         data->sub_data.slix.rand[1],
 | |
|         data->sub_data.slix.rand[0],
 | |
|         data->sub_data.slix.rand[1],
 | |
|         data->sub_data.slix.rand[0]};
 | |
|     uint8_t* password = NULL;
 | |
| 
 | |
|     switch(password_id) {
 | |
|     case SLIX_PASS_READ:
 | |
|         password = data->sub_data.slix.key_read;
 | |
|         break;
 | |
|     case SLIX_PASS_WRITE:
 | |
|         password = data->sub_data.slix.key_write;
 | |
|         break;
 | |
|     case SLIX_PASS_PRIVACY:
 | |
|         password = data->sub_data.slix.key_privacy;
 | |
|         break;
 | |
|     case SLIX_PASS_DESTROY:
 | |
|         password = data->sub_data.slix.key_destroy;
 | |
|         break;
 | |
|     case SLIX_PASS_EASAFI:
 | |
|         password = data->sub_data.slix.key_eas;
 | |
|         break;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     if(!password) {
 | |
|         return ERR_NOTSUPP;
 | |
|     }
 | |
| 
 | |
|     for(int pos = 0; pos < 4; pos++) {
 | |
|         cmd_set_pass[1 + pos] ^= password[3 - pos];
 | |
|     }
 | |
| 
 | |
|     ReturnCode ret = rfalNfcvPollerTransceiveReq(
 | |
|         NFCV_CMD_NXP_SET_PASSWORD,
 | |
|         RFAL_NFCV_REQ_FLAG_DATA_RATE,
 | |
|         NFCV_MANUFACTURER_NXP,
 | |
|         NULL,
 | |
|         cmd_set_pass,
 | |
|         sizeof(cmd_set_pass),
 | |
|         rxBuf,
 | |
|         sizeof(rxBuf),
 | |
|         &received);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static void slix_generic_pass_infos(
 | |
|     uint8_t password_id,
 | |
|     NfcVSlixData* slix,
 | |
|     uint8_t** password,
 | |
|     uint32_t* flag_valid,
 | |
|     uint32_t* flag_set) {
 | |
|     switch(password_id) {
 | |
|     case SLIX_PASS_READ:
 | |
|         *password = slix->key_read;
 | |
|         *flag_valid = NfcVSlixDataFlagsValidKeyRead;
 | |
|         *flag_set = NfcVSlixDataFlagsHasKeyRead;
 | |
|         break;
 | |
|     case SLIX_PASS_WRITE:
 | |
|         *password = slix->key_write;
 | |
|         *flag_valid = NfcVSlixDataFlagsValidKeyWrite;
 | |
|         *flag_set = NfcVSlixDataFlagsHasKeyWrite;
 | |
|         break;
 | |
|     case SLIX_PASS_PRIVACY:
 | |
|         *password = slix->key_privacy;
 | |
|         *flag_valid = NfcVSlixDataFlagsValidKeyPrivacy;
 | |
|         *flag_set = NfcVSlixDataFlagsHasKeyPrivacy;
 | |
|         break;
 | |
|     case SLIX_PASS_DESTROY:
 | |
|         *password = slix->key_destroy;
 | |
|         *flag_valid = NfcVSlixDataFlagsValidKeyDestroy;
 | |
|         *flag_set = NfcVSlixDataFlagsHasKeyDestroy;
 | |
|         break;
 | |
|     case SLIX_PASS_EASAFI:
 | |
|         *password = slix->key_eas;
 | |
|         *flag_valid = NfcVSlixDataFlagsValidKeyEas;
 | |
|         *flag_set = NfcVSlixDataFlagsHasKeyEas;
 | |
|         break;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| bool slix_generic_protocol_filter(
 | |
|     FuriHalNfcTxRxContext* tx_rx,
 | |
|     FuriHalNfcDevData* nfc_data,
 | |
|     void* nfcv_data_in,
 | |
|     uint32_t password_supported) {
 | |
|     furi_assert(tx_rx);
 | |
|     furi_assert(nfc_data);
 | |
|     furi_assert(nfcv_data_in);
 | |
| 
 | |
|     NfcVData* nfcv_data = (NfcVData*)nfcv_data_in;
 | |
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
 | |
|     NfcVSlixData* slix = &nfcv_data->sub_data.slix;
 | |
| 
 | |
|     if((slix->flags & NfcVSlixDataFlagsPrivacy) &&
 | |
|        ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER &&
 | |
|        ctx->command != NFCV_CMD_NXP_SET_PASSWORD) {
 | |
|         snprintf(
 | |
|             nfcv_data->last_command,
 | |
|             sizeof(nfcv_data->last_command),
 | |
|             "command 0x%02X ignored, privacy mode",
 | |
|             ctx->command);
 | |
|         FURI_LOG_D(TAG, "%s", nfcv_data->last_command);
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool handled = false;
 | |
| 
 | |
|     switch(ctx->command) {
 | |
|     case NFCV_CMD_NXP_GET_RANDOM_NUMBER: {
 | |
|         slix->rand[0] = furi_hal_random_get();
 | |
|         slix->rand[1] = furi_hal_random_get();
 | |
| 
 | |
|         ctx->response_buffer[0] = NFCV_NOERROR;
 | |
|         ctx->response_buffer[1] = slix->rand[1];
 | |
|         ctx->response_buffer[2] = slix->rand[0];
 | |
| 
 | |
|         nfcv_emu_send(
 | |
|             tx_rx, nfcv_data, ctx->response_buffer, 3, ctx->response_flags, ctx->send_time);
 | |
|         snprintf(
 | |
|             nfcv_data->last_command,
 | |
|             sizeof(nfcv_data->last_command),
 | |
|             "GET_RANDOM_NUMBER -> 0x%02X%02X",
 | |
|             slix->rand[0],
 | |
|             slix->rand[1]);
 | |
| 
 | |
|         handled = true;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     case NFCV_CMD_NXP_SET_PASSWORD: {
 | |
|         /* the password to be set is the first parameter */
 | |
|         uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
 | |
|         /* right after that is the XORed password */
 | |
|         uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
 | |
| 
 | |
|         /* only handle if the password type is supported */
 | |
|         if(!(password_id & password_supported)) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         /* fetch the last RAND value */
 | |
|         uint8_t* rand = slix->rand;
 | |
| 
 | |
|         /* first calc the password that has been sent */
 | |
|         uint8_t password_rcv[4];
 | |
|         for(int pos = 0; pos < 4; pos++) {
 | |
|             password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
 | |
|         }
 | |
|         uint32_t pass_received = slix_read_be(password_rcv, 4);
 | |
| 
 | |
|         /* then determine the password type (or even update if not set yet) */
 | |
|         uint8_t* password = NULL;
 | |
|         uint32_t flag_valid = 0;
 | |
|         uint32_t flag_set = 0;
 | |
| 
 | |
|         slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set);
 | |
| 
 | |
|         /* when the password is not supported, return silently */
 | |
|         if(!password) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         /* check if the password is known */
 | |
|         bool pass_valid = false;
 | |
|         uint32_t pass_expect = 0;
 | |
| 
 | |
|         if(slix->flags & flag_set) {
 | |
|             /* if so, fetch the stored password and compare */
 | |
|             pass_expect = slix_read_be(password, 4);
 | |
|             pass_valid = (pass_expect == pass_received);
 | |
|         } else {
 | |
|             /* if not known, just accept it and store that password */
 | |
|             memcpy(password, password_rcv, 4);
 | |
|             nfcv_data->modified = true;
 | |
|             slix->flags |= flag_set;
 | |
| 
 | |
|             pass_valid = true;
 | |
|         }
 | |
| 
 | |
|         /* if the pass was valid or accepted for other reasons, continue */
 | |
|         if(pass_valid) {
 | |
|             slix->flags |= flag_valid;
 | |
| 
 | |
|             /* handle actions when a correct password was given, aside of setting the flag */
 | |
|             switch(password_id) {
 | |
|             case SLIX_PASS_PRIVACY:
 | |
|                 slix->flags &= ~NfcVSlixDataFlagsPrivacy;
 | |
|                 nfcv_data->modified = true;
 | |
|                 break;
 | |
|             case SLIX_PASS_DESTROY:
 | |
|                 slix->flags |= NfcVSlixDataFlagsDestroyed;
 | |
|                 FURI_LOG_D(TAG, "Pooof! Got destroyed");
 | |
|                 break;
 | |
|             default:
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             ctx->response_buffer[0] = NFCV_NOERROR;
 | |
|             nfcv_emu_send(
 | |
|                 tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
 | |
|             snprintf(
 | |
|                 nfcv_data->last_command,
 | |
|                 sizeof(nfcv_data->last_command),
 | |
|                 "SET_PASSWORD #%02X 0x%08lX OK",
 | |
|                 password_id,
 | |
|                 pass_received);
 | |
|         } else {
 | |
|             snprintf(
 | |
|                 nfcv_data->last_command,
 | |
|                 sizeof(nfcv_data->last_command),
 | |
|                 "SET_PASSWORD #%02X 0x%08lX/%08lX FAIL",
 | |
|                 password_id,
 | |
|                 pass_received,
 | |
|                 pass_expect);
 | |
|         }
 | |
|         handled = true;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     case NFCV_CMD_NXP_WRITE_PASSWORD: {
 | |
|         uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
 | |
| 
 | |
|         if(!(password_id & password_supported)) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         uint8_t* new_password = &nfcv_data->frame[ctx->payload_offset + 1];
 | |
|         uint8_t* password = NULL;
 | |
|         uint32_t flag_valid = 0;
 | |
|         uint32_t flag_set = 0;
 | |
| 
 | |
|         slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set);
 | |
| 
 | |
|         /* when the password is not supported, return silently */
 | |
|         if(!password) {
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         bool pass_valid = (slix->flags & flag_valid);
 | |
|         if(!(slix->flags & flag_set)) {
 | |
|             pass_valid = true;
 | |
|         }
 | |
| 
 | |
|         if(pass_valid) {
 | |
|             slix->flags |= flag_valid;
 | |
|             slix->flags |= flag_set;
 | |
| 
 | |
|             memcpy(password, new_password, 4);
 | |
| 
 | |
|             ctx->response_buffer[0] = NFCV_NOERROR;
 | |
|             nfcv_emu_send(
 | |
|                 tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
 | |
|             snprintf(
 | |
|                 nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD OK");
 | |
|         } else {
 | |
|             snprintf(
 | |
|                 nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD FAIL");
 | |
|         }
 | |
|         handled = true;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     case NFCV_CMD_NXP_ENABLE_PRIVACY: {
 | |
|         ctx->response_buffer[0] = NFCV_NOERROR;
 | |
| 
 | |
|         nfcv_emu_send(
 | |
|             tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
 | |
|         snprintf(
 | |
|             nfcv_data->last_command,
 | |
|             sizeof(nfcv_data->last_command),
 | |
|             "NFCV_CMD_NXP_ENABLE_PRIVACY");
 | |
| 
 | |
|         slix->flags |= NfcVSlixDataFlagsPrivacy;
 | |
|         handled = true;
 | |
|         break;
 | |
|     }
 | |
|     }
 | |
| 
 | |
|     return handled;
 | |
| }
 | |
| 
 | |
| bool slix_l_protocol_filter(
 | |
|     FuriHalNfcTxRxContext* tx_rx,
 | |
|     FuriHalNfcDevData* nfc_data,
 | |
|     void* nfcv_data_in) {
 | |
|     furi_assert(tx_rx);
 | |
|     furi_assert(nfc_data);
 | |
|     furi_assert(nfcv_data_in);
 | |
| 
 | |
|     bool handled = false;
 | |
| 
 | |
|     /* many SLIX share some of the functions, place that in a generic handler */
 | |
|     if(slix_generic_protocol_filter(
 | |
|            tx_rx,
 | |
|            nfc_data,
 | |
|            nfcv_data_in,
 | |
|            SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI)) {
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     return handled;
 | |
| }
 | |
| 
 | |
| void slix_l_prepare(NfcVData* nfcv_data) {
 | |
|     FURI_LOG_D(
 | |
|         TAG, "  Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
 | |
|     FURI_LOG_D(
 | |
|         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
 | |
|     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
 | |
|     FURI_LOG_D(
 | |
|         TAG,
 | |
|         "  Privacy mode: %s",
 | |
|         (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
 | |
| 
 | |
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
 | |
|     ctx->emu_protocol_filter = &slix_l_protocol_filter;
 | |
| }
 | |
| 
 | |
| bool slix_s_protocol_filter(
 | |
|     FuriHalNfcTxRxContext* tx_rx,
 | |
|     FuriHalNfcDevData* nfc_data,
 | |
|     void* nfcv_data_in) {
 | |
|     furi_assert(tx_rx);
 | |
|     furi_assert(nfc_data);
 | |
|     furi_assert(nfcv_data_in);
 | |
| 
 | |
|     bool handled = false;
 | |
| 
 | |
|     /* many SLIX share some of the functions, place that in a generic handler */
 | |
|     if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) {
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     return handled;
 | |
| }
 | |
| 
 | |
| void slix_s_prepare(NfcVData* nfcv_data) {
 | |
|     FURI_LOG_D(
 | |
|         TAG, "  Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
 | |
|     FURI_LOG_D(
 | |
|         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
 | |
|     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
 | |
|     FURI_LOG_D(
 | |
|         TAG,
 | |
|         "  Privacy mode: %s",
 | |
|         (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
 | |
| 
 | |
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
 | |
|     ctx->emu_protocol_filter = &slix_s_protocol_filter;
 | |
| }
 | |
| 
 | |
| bool slix_protocol_filter(
 | |
|     FuriHalNfcTxRxContext* tx_rx,
 | |
|     FuriHalNfcDevData* nfc_data,
 | |
|     void* nfcv_data_in) {
 | |
|     furi_assert(tx_rx);
 | |
|     furi_assert(nfc_data);
 | |
|     furi_assert(nfcv_data_in);
 | |
| 
 | |
|     bool handled = false;
 | |
| 
 | |
|     /* many SLIX share some of the functions, place that in a generic handler */
 | |
|     if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_EASAFI)) {
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     return handled;
 | |
| }
 | |
| 
 | |
| void slix_prepare(NfcVData* nfcv_data) {
 | |
|     FURI_LOG_D(
 | |
|         TAG, "  Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
 | |
|     FURI_LOG_D(
 | |
|         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
 | |
|     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
 | |
|     FURI_LOG_D(
 | |
|         TAG,
 | |
|         "  Privacy mode: %s",
 | |
|         (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
 | |
| 
 | |
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
 | |
|     ctx->emu_protocol_filter = &slix_protocol_filter;
 | |
| }
 | |
| 
 | |
| bool slix2_protocol_filter( // -V524
 | |
|     FuriHalNfcTxRxContext* tx_rx,
 | |
|     FuriHalNfcDevData* nfc_data,
 | |
|     void* nfcv_data_in) {
 | |
|     furi_assert(tx_rx);
 | |
|     furi_assert(nfc_data);
 | |
|     furi_assert(nfcv_data_in);
 | |
| 
 | |
|     NfcVData* nfcv_data = (NfcVData*)nfcv_data_in;
 | |
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
 | |
|     NfcVSlixData* slix = &nfcv_data->sub_data.slix;
 | |
| 
 | |
|     bool handled = false;
 | |
| 
 | |
|     /* many SLIX share some of the functions, place that in a generic handler */
 | |
|     if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) {
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     switch(ctx->command) {
 | |
|     /* override WRITE BLOCK for block 79 (16 bit counter)  */
 | |
|     case NFCV_CMD_WRITE_BLOCK:
 | |
|     case NFCV_CMD_WRITE_MULTI_BLOCK: {
 | |
|         uint8_t resp_len = 1;
 | |
|         uint8_t blocks = 1;
 | |
|         uint8_t block = nfcv_data->frame[ctx->payload_offset];
 | |
|         uint8_t data_pos = ctx->payload_offset + 1;
 | |
| 
 | |
|         if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) {
 | |
|             blocks = nfcv_data->frame[data_pos] + 1;
 | |
|             data_pos++;
 | |
|         }
 | |
| 
 | |
|         uint8_t* data = &nfcv_data->frame[data_pos];
 | |
|         uint32_t data_len = nfcv_data->block_size * blocks;
 | |
| 
 | |
|         if((block + blocks) <= nfcv_data->block_num &&
 | |
|            (data_pos + data_len + 2) == nfcv_data->frame_length) {
 | |
|             ctx->response_buffer[0] = NFCV_NOERROR;
 | |
| 
 | |
|             for(int block_num = block; block_num < block + blocks; block_num++) {
 | |
|                 /* special case, 16-bit counter */
 | |
|                 if(block_num == 79) {
 | |
|                     uint32_t dest;
 | |
|                     uint32_t ctr_old;
 | |
| 
 | |
|                     memcpy(&dest, &nfcv_data->frame[data_pos], 4);
 | |
|                     memcpy(&ctr_old, &nfcv_data->data[nfcv_data->block_size * block_num], 4);
 | |
| 
 | |
|                     uint32_t ctr_new = ctr_old;
 | |
|                     bool allowed = true;
 | |
| 
 | |
|                     /* increment counter */
 | |
|                     if(dest == 1) {
 | |
|                         ctr_new = (ctr_old & 0xFFFF0000) | ((ctr_old + 1) & 0xFFFF);
 | |
| 
 | |
|                         /* protection flag set? */
 | |
|                         if(ctr_old & 0x01000000) { //-V1051
 | |
|                             allowed = nfcv_data->sub_data.slix.flags &
 | |
|                                       NfcVSlixDataFlagsValidKeyRead;
 | |
|                         }
 | |
|                     } else {
 | |
|                         ctr_new = dest;
 | |
|                         allowed = nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsValidKeyWrite;
 | |
|                     }
 | |
| 
 | |
|                     if(allowed) {
 | |
|                         memcpy( //-V1086
 | |
|                             &nfcv_data->data[nfcv_data->block_size * block_num],
 | |
|                             &ctr_new,
 | |
|                             4);
 | |
|                     } else {
 | |
|                         /* incorrect read or write password */
 | |
|                         ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
 | |
|                         ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
 | |
|                         resp_len = 2;
 | |
|                     }
 | |
|                 } else {
 | |
|                     memcpy(
 | |
|                         &nfcv_data->data[nfcv_data->block_size * block_num],
 | |
|                         &nfcv_data->frame[data_pos],
 | |
|                         nfcv_data->block_size);
 | |
|                 }
 | |
|                 data_pos += nfcv_data->block_size;
 | |
|             }
 | |
|             nfcv_data->modified = true;
 | |
| 
 | |
|         } else {
 | |
|             ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
 | |
|             ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
 | |
|             resp_len = 2;
 | |
|         }
 | |
| 
 | |
|         bool respond = (ctx->response_buffer[0] == NFCV_NOERROR) ||
 | |
|                        (ctx->addressed || ctx->selected);
 | |
| 
 | |
|         if(respond) {
 | |
|             nfcv_emu_send(
 | |
|                 tx_rx,
 | |
|                 nfcv_data,
 | |
|                 ctx->response_buffer,
 | |
|                 resp_len,
 | |
|                 ctx->response_flags,
 | |
|                 ctx->send_time);
 | |
|         }
 | |
| 
 | |
|         if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) {
 | |
|             snprintf(
 | |
|                 nfcv_data->last_command,
 | |
|                 sizeof(nfcv_data->last_command),
 | |
|                 "WRITE MULTI BLOCK %d, %d blocks",
 | |
|                 block,
 | |
|                 blocks);
 | |
|         } else {
 | |
|             snprintf(
 | |
|                 nfcv_data->last_command,
 | |
|                 sizeof(nfcv_data->last_command),
 | |
|                 "WRITE BLOCK %d <- %02X %02X %02X %02X",
 | |
|                 block,
 | |
|                 data[0],
 | |
|                 data[1],
 | |
|                 data[2],
 | |
|                 data[3]);
 | |
|         }
 | |
|         handled = true;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     case NFCV_CMD_NXP_READ_SIGNATURE: {
 | |
|         uint32_t len = 0;
 | |
|         ctx->response_buffer[len++] = NFCV_NOERROR;
 | |
|         memcpy(&ctx->response_buffer[len], slix->signature, sizeof(slix->signature));
 | |
|         len += sizeof(slix->signature);
 | |
| 
 | |
|         nfcv_emu_send(
 | |
|             tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time);
 | |
|         snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_SIGNATURE");
 | |
| 
 | |
|         handled = true;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     case NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION: {
 | |
|         uint32_t len = 0;
 | |
|         uint8_t lock_bits = 0;
 | |
| 
 | |
|         /* convert our internal lock bits format into NXP's */
 | |
|         lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? SlixLockBitDsfid : 0;
 | |
|         lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitAfi) ? SlixLockBitAfi : 0;
 | |
|         lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitEas) ? SlixLockBitEas : 0;
 | |
|         lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitPpl) ? SlixLockBitPpl : 0;
 | |
| 
 | |
|         ctx->response_buffer[len++] = NFCV_NOERROR;
 | |
|         ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_pointer;
 | |
|         ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_condition;
 | |
|         ctx->response_buffer[len++] = lock_bits;
 | |
|         ctx->response_buffer[len++] = 0x7F; /* features LSB */
 | |
|         ctx->response_buffer[len++] = 0x35; /* features */
 | |
|         ctx->response_buffer[len++] = 0; /* features */
 | |
|         ctx->response_buffer[len++] = 0; /* features MSB */
 | |
| 
 | |
|         nfcv_emu_send(
 | |
|             tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time);
 | |
|         snprintf(
 | |
|             nfcv_data->last_command,
 | |
|             sizeof(nfcv_data->last_command),
 | |
|             "GET_NXP_SYSTEM_INFORMATION");
 | |
| 
 | |
|         handled = true;
 | |
|         break;
 | |
|     }
 | |
|     }
 | |
| 
 | |
|     return handled;
 | |
| }
 | |
| 
 | |
| void slix2_prepare(NfcVData* nfcv_data) {
 | |
|     FURI_LOG_D(
 | |
|         TAG, "  Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4));
 | |
|     FURI_LOG_D(
 | |
|         TAG, "  Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
 | |
|     FURI_LOG_D(TAG, "  EAS     pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
 | |
|     FURI_LOG_D(
 | |
|         TAG,
 | |
|         "  Privacy mode: %s",
 | |
|         (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
 | |
| 
 | |
|     NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
 | |
|     ctx->emu_protocol_filter = &slix2_protocol_filter;
 | |
| }
 |