diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 8825a8f44..e6944e0a0 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -110,6 +110,15 @@ App( sources=["plugins/supported_cards/kazan.c"], ) +App( + appid="aime_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="aime_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/aime.c"], +) + App( appid="sonicare_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/aime.c b/applications/main/nfc/plugins/supported_cards/aime.c new file mode 100644 index 000000000..1db89ffd6 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/aime.c @@ -0,0 +1,164 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "Aime" + +static const uint64_t aime_key = 0x574343467632; + +bool aime_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t verify_sector = 0; + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + MfClassicKey key = {}; + nfc_util_num2bytes(aime_key, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_ctx = {}; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx); + + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool aime_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicType1k; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + data->type = type; + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(aime_key, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(aime_key, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool aime_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); + if(key != aime_key) break; + + // Aime Magic is stored at block 1, starts from byte 0, len 4 bytes + const uint8_t* aime_magic = &data->block[1].data[0]; + + // verify aime magic + if(aime_magic[0] != 'S' || aime_magic[1] != 'B' || aime_magic[2] != 'S' || + aime_magic[3] != 'D') + break; + + // Aime checksum is stored at block 1, starts from byte 13, len 3 bytes + // seems like only old games checks this? e.g., old versions of Chunithm + const uint8_t* aime_checksum = &data->block[1].data[13]; + + // Aime access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes + const uint8_t* aime_accesscode = &data->block[2].data[6]; + + char aime_accesscode_str[24 + 1]; + snprintf( + aime_accesscode_str, + sizeof(aime_accesscode_str), + "%02x%02x %02x%02x %02x%02x %02x%02x %02x%02x", + aime_accesscode[0], + aime_accesscode[1], + aime_accesscode[2], + aime_accesscode[3], + aime_accesscode[4], + aime_accesscode[5], + aime_accesscode[6], + aime_accesscode[7], + aime_accesscode[8], + aime_accesscode[9]); + + // validate decimal hex representation + for(int i = 0; i < 24; i++) { + if(aime_accesscode_str[i] == ' ') continue; + if(aime_accesscode_str[i] < '0' || aime_accesscode_str[i] > '9') return false; + } + + // Note: Aime access code has some other self-check algorithms that are not public. + // This parser does not try to verify the number. + + furi_string_printf( + parsed_data, + "\e#Aime Card\nAccess Code: \n%s\nChecksum: %02X%02X%02X\n", + aime_accesscode_str, + aime_checksum[0], + aime_checksum[1], + aime_checksum[2]); + + parsed = true; + + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin aime_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = aime_verify, + .read = aime_read, + .parse = aime_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor aime_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &aime_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* aime_plugin_ep() { + return &aime_plugin_descriptor; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c index 917f7dbb8..ca6f5435e 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c @@ -108,28 +108,30 @@ Iso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_ break; } - // Read blocks: Optional command - simple_array_init(data->block_data, system_info->block_count * system_info->block_size); - ret = iso15693_3_poller_read_blocks( - instance, - simple_array_get_data(data->block_data), - system_info->block_count, - system_info->block_size); - if(ret != Iso15693_3ErrorNone) { - ret = iso15693_3_poller_filter_error(ret); - break; + if(system_info->block_count > 0) { + // Read blocks: Optional command + simple_array_init( + data->block_data, system_info->block_count * system_info->block_size); + ret = iso15693_3_poller_read_blocks( + instance, + simple_array_get_data(data->block_data), + system_info->block_count, + system_info->block_size); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } + + // Get block security status: Optional command + simple_array_init(data->block_security, system_info->block_count); + + ret = iso15693_3_poller_get_blocks_security( + instance, simple_array_get_data(data->block_security), system_info->block_count); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } } - - // Get block security status: Optional command - simple_array_init(data->block_security, system_info->block_count); - - ret = iso15693_3_poller_get_blocks_security( - instance, simple_array_get_data(data->block_security), system_info->block_count); - if(ret != Iso15693_3ErrorNone) { - ret = iso15693_3_poller_filter_error(ret); - break; - } - } while(false); return ret; diff --git a/lib/subghz/protocols/gate_tx.c b/lib/subghz/protocols/gate_tx.c index 51a424fed..2ebd6bb03 100644 --- a/lib/subghz/protocols/gate_tx.c +++ b/lib/subghz/protocols/gate_tx.c @@ -227,7 +227,7 @@ void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t du if(duration >= ((uint32_t)subghz_protocol_gate_tx_const.te_short * 10 + subghz_protocol_gate_tx_const.te_delta)) { instance->decoder.parser_step = GateTXDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_gate_tx_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 464f27ac5..700f1f6d0 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -979,6 +979,13 @@ static void subghz_protocol_keeloq_check_remote_controller( // If we are in BFT / Aprimatic programming mode we will set previous remembered counter and skip mf keys check ProgMode prog_mode = subghz_custom_btn_get_prog_mode(); if(prog_mode == PROG_MODE_OFF) { + if(keystore->mfname == 0x0) { + keystore->mfname = ""; + } + if(*manufacture_name == 0x0) { + *manufacture_name = ""; + } + // Case when we have no mf name means that we are checking for the first time and we have to check all conditions if((strlen(keystore->mfname) < 1) && strlen(*manufacture_name) < 1) { // Check key AN-Motors