diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index aec19e37499..d3bcda0423e 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -499,9 +499,6 @@ static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { LFRFIDProtocol protocol = worker->protocol; LFRFIDWriteRequest* request = malloc(sizeof(LFRFIDWriteRequest)); - request->write_type = LFRFIDWriteTypeT5577; - - bool can_be_written = protocol_dict_get_write_data(worker->protocols, protocol, request); uint32_t write_start_time = furi_get_tick(); bool too_long = false; @@ -510,63 +507,88 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { size_t data_size = protocol_dict_get_data_size(worker->protocols, protocol); uint8_t* verify_data = malloc(data_size); uint8_t* read_data = malloc(data_size); - protocol_dict_get_data(worker->protocols, protocol, verify_data, data_size); - if(can_be_written) { - while(!lfrfid_worker_check_for_stop(worker)) { - FURI_LOG_D(TAG, "Data write"); - t5577_write(&request->t5577); - - ProtocolId read_result = PROTOCOL_NO; - LFRFIDWorkerReadState state = lfrfid_worker_read_internal( - worker, - protocol_dict_get_features(worker->protocols, protocol), - LFRFID_WORKER_WRITE_VERIFY_TIME_MS, - &read_result); + protocol_dict_get_data(worker->protocols, protocol, verify_data, data_size); - if(state == LFRFIDWorkerReadOK) { - bool read_success = false; + while(!lfrfid_worker_check_for_stop(worker)) { + FURI_LOG_D(TAG, "Data write"); + uint16_t skips = 0; + for(size_t i = 0; i < LFRFIDWriteTypeMax; i++) { + memset(request, 0, sizeof(LFRFIDWriteRequest)); + LFRFIDWriteType write_type = i; + request->write_type = write_type; - if(read_result == protocol) { - protocol_dict_get_data(worker->protocols, protocol, read_data, data_size); + protocol_dict_set_data(worker->protocols, protocol, verify_data, data_size); - if(memcmp(read_data, verify_data, data_size) == 0) { - read_success = true; - } - } + bool can_be_written = + protocol_dict_get_write_data(worker->protocols, protocol, request); - if(read_success) { + if(!can_be_written) { + skips++; + if(skips == LFRFIDWriteTypeMax) { if(worker->write_cb) { - worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx); + worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx); } break; - } else { - unsuccessful_reads++; + } + continue; + } - if(unsuccessful_reads == LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS) { - if(worker->write_cb) { - worker->write_cb(LFRFIDWorkerWriteFobCannotBeWritten, worker->cb_ctx); - } - } + memset(read_data, 0, data_size); + + if(request->write_type == LFRFIDWriteTypeT5577) { + t5577_write(&request->t5577); + } else if(request->write_type == LFRFIDWriteTypeEM4305) { + em4305_write(&request->em4305); + } else { + furi_crash("Unknown write type"); + } + } + ProtocolId read_result = PROTOCOL_NO; + LFRFIDWorkerReadState state = lfrfid_worker_read_internal( + worker, + protocol_dict_get_features(worker->protocols, protocol), + LFRFID_WORKER_WRITE_VERIFY_TIME_MS, + &read_result); + + if(state == LFRFIDWorkerReadOK) { + bool read_success = false; + + if(read_result == protocol) { + protocol_dict_get_data(worker->protocols, protocol, read_data, data_size); + + if(memcmp(read_data, verify_data, data_size) == 0) { + read_success = true; } - } else if(state == LFRFIDWorkerReadExit) { - break; } - if(!too_long && - (furi_get_tick() - write_start_time) > LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS) { - too_long = true; + if(read_success) { if(worker->write_cb) { - worker->write_cb(LFRFIDWorkerWriteTooLongToWrite, worker->cb_ctx); + worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx); } - } + break; + } else { + unsuccessful_reads++; - lfrfid_worker_delay(worker, LFRFID_WORKER_WRITE_DROP_TIME_MS); + if(unsuccessful_reads == LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS) { + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteFobCannotBeWritten, worker->cb_ctx); + } + } + } + } else if(state == LFRFIDWorkerReadExit) { + break; } - } else { - if(worker->write_cb) { - worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx); + + if(!too_long && + (furi_get_tick() - write_start_time) > LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS) { + too_long = true; + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteTooLongToWrite, worker->cb_ctx); + } } + + lfrfid_worker_delay(worker, LFRFID_WORKER_WRITE_DROP_TIME_MS); } free(request); diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index e9c61616efd..37b7f06cd9b 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -1,6 +1,7 @@ #pragma once #include #include "../tools/t5577.h" +#include "../tools/em4305.h" typedef enum { LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */ @@ -31,6 +32,7 @@ typedef enum { LFRFIDProtocolNexwatch, LFRFIDProtocolSecurakey, LFRFIDProtocolGProxII, + LFRFIDProtocolMax, } LFRFIDProtocol; @@ -38,11 +40,15 @@ extern const ProtocolBase* lfrfid_protocols[]; typedef enum { LFRFIDWriteTypeT5577, + LFRFIDWriteTypeEM4305, + + LFRFIDWriteTypeMax, } LFRFIDWriteType; typedef struct { LFRFIDWriteType write_type; union { LFRFIDT5577 t5577; + LFRFIDEM4305 em4305; }; } LFRFIDWriteRequest; diff --git a/lib/lfrfid/protocols/protocol_electra.c b/lib/lfrfid/protocols/protocol_electra.c index 014c83d1f97..b4e17763f4a 100644 --- a/lib/lfrfid/protocols/protocol_electra.c +++ b/lib/lfrfid/protocols/protocol_electra.c @@ -407,6 +407,24 @@ bool protocol_electra_write_data(ProtocolElectra* protocol, void* data) { request->t5577.blocks_to_write = 5; result = true; } + if(request->write_type == LFRFIDWriteTypeEM4305) { + request->em4305.word[4] = + (EM4x05_MODULATION_MANCHESTER | EM4x05_SET_BITRATE(64) | (8 << EM4x05_MAXBLOCK_SHIFT)); + uint64_t encoded_data_reversed = 0; + uint64_t encoded_epilogue_reversed = 0; + for(uint8_t i = 0; i < 64; i++) { + encoded_data_reversed = (encoded_data_reversed << 1) | + ((protocol->encoded_base_data >> i) & 1); + encoded_epilogue_reversed = (encoded_epilogue_reversed << 1) | + ((protocol->encoded_epilogue >> i) & 1); + } + request->em4305.word[5] = encoded_data_reversed & 0xFFFFFFFF; + request->em4305.word[6] = encoded_data_reversed >> 32; + request->em4305.word[7] = encoded_epilogue_reversed & 0xFFFFFFFF; + request->em4305.word[8] = encoded_epilogue_reversed >> 32; + request->em4305.mask = 0x01F0; + result = true; + } return result; } diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c index 8851a5369cc..83340895c7e 100644 --- a/lib/lfrfid/protocols/protocol_em4100.c +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -69,6 +69,19 @@ uint32_t protocol_em4100_get_t5577_bitrate(ProtocolEM4100* proto) { } } +uint32_t protocol_em4100_get_em4305_bitrate(ProtocolEM4100* proto) { + switch(proto->clock_per_bit) { + case 64: + return EM4x05_SET_BITRATE(64); + case 32: + return EM4x05_SET_BITRATE(32); + case 16: + return EM4x05_SET_BITRATE(16); + default: + return EM4x05_SET_BITRATE(64); + } +} + uint16_t protocol_em4100_get_short_time_low(ProtocolEM4100* proto) { return EM_READ_SHORT_TIME_BASE / protocol_em4100_get_time_divisor(proto) - EM_READ_JITTER_TIME_BASE / protocol_em4100_get_time_divisor(proto); @@ -339,6 +352,19 @@ bool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) { request->t5577.block[2] = protocol->encoded_data; request->t5577.blocks_to_write = 3; result = true; + } else if(request->write_type == LFRFIDWriteTypeEM4305) { + request->em4305.word[4] = + (EM4x05_MODULATION_MANCHESTER | protocol_em4100_get_em4305_bitrate(protocol) | + (6 << EM4x05_MAXBLOCK_SHIFT)); + uint64_t encoded_data_reversed = 0; + for(uint8_t i = 0; i < 64; i++) { + encoded_data_reversed = (encoded_data_reversed << 1) | + ((protocol->encoded_data >> i) & 1); + } + request->em4305.word[5] = encoded_data_reversed; + request->em4305.word[6] = encoded_data_reversed >> 32; + request->em4305.mask = 0x70; + result = true; } return result; } diff --git a/lib/lfrfid/protocols/protocol_gallagher.c b/lib/lfrfid/protocols/protocol_gallagher.c index 9ae0cf80acf..bbed99706cf 100644 --- a/lib/lfrfid/protocols/protocol_gallagher.c +++ b/lib/lfrfid/protocols/protocol_gallagher.c @@ -264,6 +264,20 @@ bool protocol_gallagher_write_data(ProtocolGallagher* protocol, void* data) { request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); request->t5577.blocks_to_write = 4; result = true; + } else if(request->write_type == LFRFIDWriteTypeEM4305) { + request->em4305.word[4] = + (EM4x05_MODULATION_MANCHESTER | EM4x05_SET_BITRATE(32) | (7 << EM4x05_MAXBLOCK_SHIFT)); + uint32_t encoded_data_reversed[3] = {0}; + for(uint8_t i = 0; i < (32 * 3); i++) { + encoded_data_reversed[i / 32] = + (encoded_data_reversed[i / 32] << 1) | + (bit_lib_get_bit(protocol->encoded_data, ((32 * 3) - i)) & 1); + } + request->em4305.word[5] = encoded_data_reversed[2]; + request->em4305.word[6] = encoded_data_reversed[1]; + request->em4305.word[7] = encoded_data_reversed[0]; + request->em4305.mask = 0xF0; + result = true; } return result; } diff --git a/lib/lfrfid/protocols/protocol_viking.c b/lib/lfrfid/protocols/protocol_viking.c index 78499a41587..7e9009fdee9 100644 --- a/lib/lfrfid/protocols/protocol_viking.c +++ b/lib/lfrfid/protocols/protocol_viking.c @@ -171,6 +171,19 @@ bool protocol_viking_write_data(ProtocolViking* protocol, void* data) { request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); request->t5577.blocks_to_write = 3; result = true; + } else if(request->write_type == LFRFIDWriteTypeEM4305) { + request->em4305.word[4] = + (EM4x05_MODULATION_MANCHESTER | EM4x05_SET_BITRATE(32) | (6 << EM4x05_MAXBLOCK_SHIFT)); + uint32_t encoded_data_reversed[2] = {0}; + for(uint8_t i = 0; i < 64; i++) { + encoded_data_reversed[i / 32] = + (encoded_data_reversed[i / 32] << 1) | + (bit_lib_get_bit(protocol->encoded_data, (63 - i)) & 1); + } + request->em4305.word[5] = encoded_data_reversed[1]; + request->em4305.word[6] = encoded_data_reversed[0]; + request->em4305.mask = 0x70; + result = true; } return result; } diff --git a/lib/lfrfid/tools/em4305.c b/lib/lfrfid/tools/em4305.c new file mode 100644 index 00000000000..3b072d38d46 --- /dev/null +++ b/lib/lfrfid/tools/em4305.c @@ -0,0 +1,152 @@ +#include "em4305.h" +#include +#include + +#define TAG "EM4305" + +#define EM4305_TIMING_1 (32) +#define EM4305_TIMING_0_OFF (23) +#define EM4305_TIMING_0_ON (18) + +#define EM4305_FIELD_STOP_OFF_CYCLES (55) +#define EM4305_FIELD_STOP_ON_CYCLES (18) + +#define EM4305_TIMING_POWER_CHECK (1480) +#define EM4305_TIMING_EEPROM_WRITE (9340) + +static bool em4305_line_parity(uint8_t data) { + uint8_t parity = 0; + for(uint8_t i = 0; i < 8; i++) { + parity ^= (data >> i) & 1; + } + return parity; +} + +static uint64_t em4305_prepare_data(uint32_t data) { + uint8_t i, j; + uint64_t data_with_parity = 0; + + // 4 lines of 8 bits of data + // line even parity at bits 8 17 26 35 + // column even parity at bits 36-43 + // bit 44 is always 0 + // final table is 5 lines of 9 bits + + // line parity + for(i = 0; i < 4; i++) { + for(j = 0; j < 8; j++) { + data_with_parity = (data_with_parity << 1) | ((data >> (i * 8 + j)) & 1); + } + data_with_parity = (data_with_parity << 1) | (uint64_t)em4305_line_parity(data >> (i * 8)); + } + + // column parity + for(i = 0; i < 8; i++) { + uint8_t column_parity = 0; + for(j = 0; j < 4; j++) { + column_parity ^= (data >> (j * 8 + i)) & 1; + } + data_with_parity = (data_with_parity << 1) | column_parity; + } + + // bit 44 + data_with_parity = (data_with_parity << 1) | 0; + + return data_with_parity; +} + +static void em4305_start(void) { + furi_hal_rfid_tim_read_start(125000, 0.5); + + // do not ground the antenna + furi_hal_rfid_pin_pull_release(); +} + +static void em4305_stop(void) { + furi_hal_rfid_tim_read_stop(); + furi_hal_rfid_pins_reset(); +} + +static void em4305_write_bit(bool value) { + if(value) { + furi_delay_us(EM4305_TIMING_1 * 8); + } else { + furi_hal_rfid_tim_read_pause(); + furi_delay_us(EM4305_TIMING_0_OFF * 8); + furi_hal_rfid_tim_read_continue(); + furi_delay_us(EM4305_TIMING_0_ON * 8); + } +} + +static void em4305_write_opcode(uint8_t value) { + // 3 bit opcode + for(uint8_t i = 0; i < 3; i++) { + em4305_write_bit((value >> i) & 1); + } + + // parity + bool parity = 0; + for(uint8_t i = 0; i < 3; i++) { + parity ^= (value >> i) & 1; + } + em4305_write_bit(parity); +} + +static void em4305_field_stop() { + furi_hal_rfid_tim_read_pause(); + furi_delay_us(EM4305_FIELD_STOP_OFF_CYCLES * 8); + furi_hal_rfid_tim_read_continue(); + furi_delay_us(EM4305_FIELD_STOP_ON_CYCLES * 8); +} + +static void em4305_write_word(uint8_t address, uint32_t data) { + // parity + uint64_t data_with_parity = em4305_prepare_data(data); + + // power up the tag + furi_delay_us(8000); + + // field stop + em4305_field_stop(); + + // start bit + em4305_write_bit(0); + + // opcode + em4305_write_opcode(EM4x05_OPCODE_WRITE); + + // address + bool address_parity = 0; + for(uint8_t i = 0; i < 4; i++) { + em4305_write_bit((address >> (i)) & 1); + address_parity ^= (address >> (i)) & 1; + } + em4305_write_bit(0); + em4305_write_bit(0); + em4305_write_bit(address_parity); + + // data + for(uint8_t i = 0; i < 45; i++) { + em4305_write_bit((data_with_parity >> (44 - i)) & 1); + } + + // wait for power check and eeprom write + furi_delay_us(EM4305_TIMING_POWER_CHECK); + furi_delay_us(EM4305_TIMING_EEPROM_WRITE); +} + +void em4305_write(LFRFIDEM4305* data) { + furi_check(data); + + em4305_start(); + FURI_CRITICAL_ENTER(); + + for(uint8_t i = 0; i < EM4x05_WORD_COUNT; i++) { + if(data->mask & (1 << i)) { + em4305_write_word(i, data->word[i]); + } + } + + FURI_CRITICAL_EXIT(); + em4305_stop(); +} diff --git a/lib/lfrfid/tools/em4305.h b/lib/lfrfid/tools/em4305.h new file mode 100644 index 00000000000..0cec0025404 --- /dev/null +++ b/lib/lfrfid/tools/em4305.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// EM4305/4205 chip config definitions, thanks proxmark3! +#define EM4x05_GET_BITRATE(x) ((((x) & 0x3F) * 2) + 2) +// Note: only data rates 8, 16, 32, 40(*) and 64 are supported. (*) only with EM4305 330pF +#define EM4x05_SET_BITRATE(x) (((x) - 2) / 2) +#define EM4x05_MODULATION_NRZ (0x00000000) +#define EM4x05_MODULATION_MANCHESTER (0x00000040) +#define EM4x05_MODULATION_BIPHASE (0x00000080) +#define EM4x05_MODULATION_MILLER (0x000000C0) // not supported by all 4x05/4x69 chips +#define EM4x05_MODULATION_PSK1 (0x00000100) // not supported by all 4x05/4x69 chips +#define EM4x05_MODULATION_PSK2 (0x00000140) // not supported by all 4x05/4x69 chips +#define EM4x05_MODULATION_PSK3 (0x00000180) // not supported by all 4x05/4x69 chips +#define EM4x05_MODULATION_FSK1 (0x00000200) // not supported by all 4x05/4x69 chips +#define EM4x05_MODULATION_FSK2 (0x00000240) // not supported by all 4x05/4x69 chips +#define EM4x05_PSK_RF_2 (0) +#define EM4x05_PSK_RF_4 (0x00000400) +#define EM4x05_PSK_RF_8 (0x00000800) +#define EM4x05_MAXBLOCK_SHIFT (14) +#define EM4x05_FIRST_USER_BLOCK (5) +#define EM4x05_SET_NUM_BLOCKS(x) \ + (((x) + 4) << 14) // number of blocks sent during default read mode +#define EM4x05_GET_NUM_BLOCKS(x) ((((x) >> 14) & 0xF) - 4) +#define EM4x05_READ_LOGIN_REQ (1 << 18) +#define EM4x05_READ_HK_LOGIN_REQ (1 << 19) +#define EM4x05_WRITE_LOGIN_REQ (1 << 20) +#define EM4x05_WRITE_HK_LOGIN_REQ (1 << 21) +#define EM4x05_READ_AFTER_WRITE (1 << 22) +#define EM4x05_DISABLE_ALLOWED (1 << 23) +#define EM4x05_READER_TALK_FIRST (1 << 24) +#define EM4x05_INVERT (1 << 25) +#define EM4x05_PIGEON (1 << 26) + +#define EM4x05_WORD_COUNT (16) + +#define EM4x05_OPCODE_LOGIN (0b001) +#define EM4x05_OPCODE_WRITE (0b010) +#define EM4x05_OPCODE_READ (0b100) +#define EM4x05_OPCODE_PROTECT (0b110) +#define EM4x05_OPCODE_DISABLE (0b101) + +typedef struct { + uint32_t word[EM4x05_WORD_COUNT]; /**< Word data to write */ + uint16_t mask; /**< Word mask */ +} LFRFIDEM4305; + +/** Write EM4305 tag data to tag + * + * @param data The data to write (mask is taken from that data) + */ +void em4305_write(LFRFIDEM4305* data); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 29ed764e835..56bfbc2e643 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,79.2,, +Version,+,79.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 4a385d538ae..707acacff66 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,79.2,, +Version,+,79.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -1000,6 +1000,7 @@ Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, size_t" Function,+,elements_text_box,void,"Canvas*, int32_t, int32_t, size_t, size_t, Align, Align, const char*, _Bool" Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" Function,+,elf_symbolname_hash,uint32_t,const char* +Function,+,em4305_write,void,LFRFIDEM4305* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen*