[FL-3686] Mifare Classic fixes (#3221)
* Update Mifare Classic generators to create more accuate data * Check the transfer buffer validity for NACK * Fix the AC issues * CRC errors don't really affect emulation, checking for them isn't worth it * Make ATQA logic a bit easier to understand * mf classic: change log level * mf classic: fix log level Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									1c3cbec661
								
							
						
					
					
						commit
						f9101d8084
					
				| @ -329,9 +329,23 @@ static void nfc_generate_mf_classic_uid(uint8_t* uid, uint8_t length) { | |||||||
| static void | static void | ||||||
|     nfc_generate_mf_classic_common(MfClassicData* data, uint8_t uid_len, MfClassicType type) { |     nfc_generate_mf_classic_common(MfClassicData* data, uint8_t uid_len, MfClassicType type) { | ||||||
|     data->iso14443_3a_data->uid_len = uid_len; |     data->iso14443_3a_data->uid_len = uid_len; | ||||||
|     data->iso14443_3a_data->atqa[0] = 0x44; |     data->iso14443_3a_data->atqa[0] = 0x00; | ||||||
|     data->iso14443_3a_data->atqa[1] = 0x00; |     data->iso14443_3a_data->atqa[1] = 0x00; | ||||||
|  |     data->iso14443_3a_data->sak = 0x00; | ||||||
|  |     // Calculate the proper ATQA and SAK
 | ||||||
|  |     if(uid_len == 7) { | ||||||
|  |         data->iso14443_3a_data->atqa[0] |= 0x40; | ||||||
|  |     } | ||||||
|  |     if(type == MfClassicType1k) { | ||||||
|  |         data->iso14443_3a_data->atqa[0] |= 0x04; | ||||||
|         data->iso14443_3a_data->sak = 0x08; |         data->iso14443_3a_data->sak = 0x08; | ||||||
|  |     } else if(type == MfClassicType4k) { | ||||||
|  |         data->iso14443_3a_data->atqa[0] |= 0x02; | ||||||
|  |         data->iso14443_3a_data->sak = 0x18; | ||||||
|  |     } else if(type == MfClassicTypeMini) { | ||||||
|  |         data->iso14443_3a_data->atqa[0] |= 0x08; | ||||||
|  |         data->iso14443_3a_data->sak = 0x09; | ||||||
|  |     } | ||||||
|     data->type = type; |     data->type = type; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -343,6 +357,11 @@ static void nfc_generate_mf_classic_sector_trailer(MfClassicData* data, uint8_t | |||||||
|     sec_tr->access_bits.data[2] = 0x80; |     sec_tr->access_bits.data[2] = 0x80; | ||||||
|     sec_tr->access_bits.data[3] = 0x69; // Nice
 |     sec_tr->access_bits.data[3] = 0x69; // Nice
 | ||||||
| 
 | 
 | ||||||
|  |     for(int i = 0; i < 6; i++) { | ||||||
|  |         sec_tr->key_a.data[i] = 0xFF; | ||||||
|  |         sec_tr->key_b.data[i] = 0xFF; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     mf_classic_set_block_read(data, block, &data->block[block]); |     mf_classic_set_block_read(data, block, &data->block[block]); | ||||||
|     mf_classic_set_key_found( |     mf_classic_set_key_found( | ||||||
|         data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeA, 0xFFFFFFFFFFFF); |         data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeA, 0xFFFFFFFFFFFF); | ||||||
| @ -396,41 +415,35 @@ static void nfc_generate_mf_classic(NfcDevice* nfc_device, uint8_t uid_len, MfCl | |||||||
| 
 | 
 | ||||||
|     uint16_t block_num = mf_classic_get_total_block_num(type); |     uint16_t block_num = mf_classic_get_total_block_num(type); | ||||||
|     if(type == MfClassicType4k) { |     if(type == MfClassicType4k) { | ||||||
|         // Set every block to 0xFF
 |         // Set every block to 0x00
 | ||||||
|         for(uint16_t i = 1; i < block_num; i++) { |         for(uint16_t i = 1; i < block_num; i++) { | ||||||
|             if(mf_classic_is_sector_trailer(i)) { |             if(mf_classic_is_sector_trailer(i)) { | ||||||
|                 nfc_generate_mf_classic_sector_trailer(mfc_data, i); |                 nfc_generate_mf_classic_sector_trailer(mfc_data, i); | ||||||
|             } else { |             } else { | ||||||
|                 memset(&mfc_data->block[i].data, 0xFF, 16); |                 memset(&mfc_data->block[i].data, 0x00, 16); | ||||||
|             } |             } | ||||||
|             mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); |             mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); | ||||||
|         } |         } | ||||||
|         // Set SAK to 18
 |  | ||||||
|         mfc_data->iso14443_3a_data->sak = 0x18; |  | ||||||
|     } else if(type == MfClassicType1k) { |     } else if(type == MfClassicType1k) { | ||||||
|         // Set every block to 0xFF
 |         // Set every block to 0x00
 | ||||||
|         for(uint16_t i = 1; i < block_num; i++) { |         for(uint16_t i = 1; i < block_num; i++) { | ||||||
|             if(mf_classic_is_sector_trailer(i)) { |             if(mf_classic_is_sector_trailer(i)) { | ||||||
|                 nfc_generate_mf_classic_sector_trailer(mfc_data, i); |                 nfc_generate_mf_classic_sector_trailer(mfc_data, i); | ||||||
|             } else { |             } else { | ||||||
|                 memset(&mfc_data->block[i].data, 0xFF, 16); |                 memset(&mfc_data->block[i].data, 0x00, 16); | ||||||
|             } |             } | ||||||
|             mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); |             mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); | ||||||
|         } |         } | ||||||
|         // Set SAK to 08
 |  | ||||||
|         mfc_data->iso14443_3a_data->sak = 0x08; |  | ||||||
|     } else if(type == MfClassicTypeMini) { |     } else if(type == MfClassicTypeMini) { | ||||||
|         // Set every block to 0xFF
 |         // Set every block to 0x00
 | ||||||
|         for(uint16_t i = 1; i < block_num; i++) { |         for(uint16_t i = 1; i < block_num; i++) { | ||||||
|             if(mf_classic_is_sector_trailer(i)) { |             if(mf_classic_is_sector_trailer(i)) { | ||||||
|                 nfc_generate_mf_classic_sector_trailer(mfc_data, i); |                 nfc_generate_mf_classic_sector_trailer(mfc_data, i); | ||||||
|             } else { |             } else { | ||||||
|                 memset(&mfc_data->block[i].data, 0xFF, 16); |                 memset(&mfc_data->block[i].data, 0x00, 16); | ||||||
|             } |             } | ||||||
|             mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); |             mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); | ||||||
|         } |         } | ||||||
|         // Set SAK to 09
 |  | ||||||
|         mfc_data->iso14443_3a_data->sak = 0x09; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     nfc_generate_mf_classic_block_0( |     nfc_generate_mf_classic_block_0( | ||||||
|  | |||||||
| @ -606,6 +606,7 @@ static bool mf_classic_is_allowed_access_sector_trailer( | |||||||
|     uint8_t* access_bits_arr = sec_tr->access_bits.data; |     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) | |     uint8_t AC = ((access_bits_arr[1] >> 5) & 0x04) | ((access_bits_arr[2] >> 2) & 0x02) | | ||||||
|                  ((access_bits_arr[2] >> 7) & 0x01); |                  ((access_bits_arr[2] >> 7) & 0x01); | ||||||
|  |     FURI_LOG_T("NFC", "AC: %02X", AC); | ||||||
| 
 | 
 | ||||||
|     switch(action) { |     switch(action) { | ||||||
|     case MfClassicActionKeyARead: { |     case MfClassicActionKeyARead: { | ||||||
| @ -615,20 +616,20 @@ static bool mf_classic_is_allowed_access_sector_trailer( | |||||||
|     case MfClassicActionKeyBWrite: { |     case MfClassicActionKeyBWrite: { | ||||||
|         return ( |         return ( | ||||||
|             (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x01)) || |             (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x01)) || | ||||||
|             (key_type == MfClassicKeyTypeB && (AC == 0x04 || AC == 0x03))); |             (key_type == MfClassicKeyTypeB && | ||||||
|  |              (AC == 0x00 || AC == 0x04 || AC == 0x03 || AC == 0x01))); | ||||||
|     } |     } | ||||||
|     case MfClassicActionKeyBRead: { |     case MfClassicActionKeyBRead: { | ||||||
|         return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); |         return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x02 || AC == 0x01)) || | ||||||
|  |                (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x02 || AC == 0x01)); | ||||||
|     } |     } | ||||||
|     case MfClassicActionACRead: { |     case MfClassicActionACRead: { | ||||||
|         return ( |         return ((key_type == MfClassicKeyTypeA) || (key_type == MfClassicKeyTypeB)); | ||||||
|             (key_type == MfClassicKeyTypeA) || |  | ||||||
|             (key_type == MfClassicKeyTypeB && !(AC == 0x00 || AC == 0x02 || AC == 0x01))); |  | ||||||
|     } |     } | ||||||
|     case MfClassicActionACWrite: { |     case MfClassicActionACWrite: { | ||||||
|         return ( |         return ( | ||||||
|             (key_type == MfClassicKeyTypeA && (AC == 0x01)) || |             (key_type == MfClassicKeyTypeA && (AC == 0x01)) || | ||||||
|             (key_type == MfClassicKeyTypeB && (AC == 0x03 || AC == 0x05))); |             (key_type == MfClassicKeyTypeB && (AC == 0x01 || AC == 0x03 || AC == 0x05))); | ||||||
|     } |     } | ||||||
|     default: |     default: | ||||||
|         return false; |         return false; | ||||||
|  | |||||||
| @ -19,6 +19,8 @@ extern "C" { | |||||||
| #define MF_CLASSIC_CMD_HALT_LSB (0x00) | #define MF_CLASSIC_CMD_HALT_LSB (0x00) | ||||||
| #define MF_CLASSIC_CMD_ACK (0x0A) | #define MF_CLASSIC_CMD_ACK (0x0A) | ||||||
| #define MF_CLASSIC_CMD_NACK (0x00) | #define MF_CLASSIC_CMD_NACK (0x00) | ||||||
|  | #define MF_CLASSIC_CMD_NACK_TRANSFER_INVALID (0x04) | ||||||
|  | #define MF_CLASSIC_CMD_NACK_TRANSFER_CRC_ERROR (0x01) | ||||||
| 
 | 
 | ||||||
| #define MF_CLASSIC_TOTAL_SECTORS_MAX (40) | #define MF_CLASSIC_TOTAL_SECTORS_MAX (40) | ||||||
| #define MF_CLASSIC_TOTAL_BLOCKS_MAX (256) | #define MF_CLASSIC_TOTAL_BLOCKS_MAX (256) | ||||||
|  | |||||||
| @ -34,6 +34,7 @@ static void mf_classic_listener_reset_state(MfClassicListener* instance) { | |||||||
|     instance->cmd_in_progress = false; |     instance->cmd_in_progress = false; | ||||||
|     instance->current_cmd_handler_idx = 0; |     instance->current_cmd_handler_idx = 0; | ||||||
|     instance->transfer_value = 0; |     instance->transfer_value = 0; | ||||||
|  |     instance->transfer_valid = false; | ||||||
|     instance->value_cmd = MfClassicValueCommandInvalid; |     instance->value_cmd = MfClassicValueCommandInvalid; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -154,7 +155,7 @@ static MfClassicListenerCommand | |||||||
|         uint32_t nt_num = nfc_util_bytes2num(instance->auth_context.nt.data, sizeof(MfClassicNt)); |         uint32_t nt_num = nfc_util_bytes2num(instance->auth_context.nt.data, sizeof(MfClassicNt)); | ||||||
|         uint32_t secret_poller = ar_num ^ crypto1_word(instance->crypto, 0, 0); |         uint32_t secret_poller = ar_num ^ crypto1_word(instance->crypto, 0, 0); | ||||||
|         if(secret_poller != prng_successor(nt_num, 64)) { |         if(secret_poller != prng_successor(nt_num, 64)) { | ||||||
|             FURI_LOG_D( |             FURI_LOG_T( | ||||||
|                 TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64)); |                 TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64)); | ||||||
|             mf_classic_listener_reset_state(instance); |             mf_classic_listener_reset_state(instance); | ||||||
|             break; |             break; | ||||||
| @ -272,6 +273,17 @@ static MfClassicListenerCommand mf_classic_listener_write_block_second_part_hand | |||||||
| 
 | 
 | ||||||
|         if(mf_classic_is_sector_trailer(block_num)) { |         if(mf_classic_is_sector_trailer(block_num)) { | ||||||
|             MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)█ |             MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)█ | ||||||
|  | 
 | ||||||
|  |             // Check if any writing is allowed
 | ||||||
|  |             if(!mf_classic_is_allowed_access( | ||||||
|  |                    instance->data, block_num, key_type, MfClassicActionKeyAWrite) && | ||||||
|  |                !mf_classic_is_allowed_access( | ||||||
|  |                    instance->data, block_num, key_type, MfClassicActionKeyBWrite) && | ||||||
|  |                !mf_classic_is_allowed_access( | ||||||
|  |                    instance->data, block_num, key_type, MfClassicActionACWrite)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if(mf_classic_is_allowed_access( |             if(mf_classic_is_allowed_access( | ||||||
|                    instance->data, block_num, key_type, MfClassicActionKeyAWrite)) { |                    instance->data, block_num, key_type, MfClassicActionKeyAWrite)) { | ||||||
|                 bit_buffer_write_bytes_mid(buff, sec_tr->key_a.data, 0, sizeof(MfClassicKey)); |                 bit_buffer_write_bytes_mid(buff, sec_tr->key_a.data, 0, sizeof(MfClassicKey)); | ||||||
| @ -338,6 +350,7 @@ static MfClassicListenerCommand | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         instance->transfer_valid = true; | ||||||
|         instance->cmd_in_progress = true; |         instance->cmd_in_progress = true; | ||||||
|         instance->current_cmd_handler_idx++; |         instance->current_cmd_handler_idx++; | ||||||
|         command = MfClassicListenerCommandAck; |         command = MfClassicListenerCommandAck; | ||||||
| @ -382,6 +395,7 @@ static MfClassicListenerCommand | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         instance->transfer_value += data; |         instance->transfer_value += data; | ||||||
|  |         instance->transfer_valid = true; | ||||||
| 
 | 
 | ||||||
|         instance->cmd_in_progress = true; |         instance->cmd_in_progress = true; | ||||||
|         instance->current_cmd_handler_idx++; |         instance->current_cmd_handler_idx++; | ||||||
| @ -411,6 +425,7 @@ static MfClassicListenerCommand | |||||||
|         mf_classic_value_to_block( |         mf_classic_value_to_block( | ||||||
|             instance->transfer_value, block_num, &instance->data->block[block_num]); |             instance->transfer_value, block_num, &instance->data->block[block_num]); | ||||||
|         instance->transfer_value = 0; |         instance->transfer_value = 0; | ||||||
|  |         instance->transfer_valid = false; | ||||||
| 
 | 
 | ||||||
|         command = MfClassicListenerCommandAck; |         command = MfClassicListenerCommandAck; | ||||||
|     } while(false); |     } while(false); | ||||||
| @ -581,7 +596,13 @@ NfcCommand mf_classic_listener_run(NfcGenericEvent event, void* context) { | |||||||
|         if(mfc_command == MfClassicListenerCommandAck) { |         if(mfc_command == MfClassicListenerCommandAck) { | ||||||
|             mf_classic_listener_send_short_frame(instance, MF_CLASSIC_CMD_ACK); |             mf_classic_listener_send_short_frame(instance, MF_CLASSIC_CMD_ACK); | ||||||
|         } else if(mfc_command == MfClassicListenerCommandNack) { |         } else if(mfc_command == MfClassicListenerCommandNack) { | ||||||
|             mf_classic_listener_send_short_frame(instance, MF_CLASSIC_CMD_NACK); |             // Calculate nack based on the transfer buffer validity
 | ||||||
|  |             uint8_t nack = MF_CLASSIC_CMD_NACK; | ||||||
|  |             if(!instance->transfer_valid) { | ||||||
|  |                 nack += MF_CLASSIC_CMD_NACK_TRANSFER_INVALID; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             mf_classic_listener_send_short_frame(instance, nack); | ||||||
|         } else if(mfc_command == MfClassicListenerCommandSilent) { |         } else if(mfc_command == MfClassicListenerCommandSilent) { | ||||||
|             command = NfcCommandReset; |             command = NfcCommandReset; | ||||||
|         } else if(mfc_command == MfClassicListenerCommandSleep) { |         } else if(mfc_command == MfClassicListenerCommandSleep) { | ||||||
|  | |||||||
| @ -42,6 +42,7 @@ struct MfClassicListener { | |||||||
| 
 | 
 | ||||||
|     // Value operation data
 |     // Value operation data
 | ||||||
|     int32_t transfer_value; |     int32_t transfer_value; | ||||||
|  |     bool transfer_valid; | ||||||
|     MfClassicValueCommand value_cmd; |     MfClassicValueCommand value_cmd; | ||||||
| 
 | 
 | ||||||
|     NfcGenericEvent generic_event; |     NfcGenericEvent generic_event; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Astra
						Astra