diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index e8d630668..a44abf233 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -19,6 +19,7 @@ #include "protocol_gallagher.h" #include "protocol_nexwatch.h" #include "protocol_securakey.h" +#include "protocol_insta_fob.h" const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, @@ -43,4 +44,5 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolGallagher] = &protocol_gallagher, [LFRFIDProtocolNexwatch] = &protocol_nexwatch, [LFRFIDProtocolSecurakey] = &protocol_securakey, + [LFRFIDProtocolInstaFob] = &protocol_insta_fob, }; diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index b9ef5c518..1dfe86ef7 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -30,6 +30,7 @@ typedef enum { LFRFIDProtocolGallagher, LFRFIDProtocolNexwatch, LFRFIDProtocolSecurakey, + LFRFIDProtocolInstaFob, LFRFIDProtocolMax, } LFRFIDProtocol; diff --git a/lib/lfrfid/protocols/protocol_insta_fob.c b/lib/lfrfid/protocols/protocol_insta_fob.c new file mode 100644 index 000000000..2f4868ce6 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_insta_fob.c @@ -0,0 +1,374 @@ +#include +#include + +#include + +#include + +// #include +// #include + +#include "lfrfid_protocols.h" + +#define TAG "InstaFob" + +#define INSTAFOB_DECODED_DATA_SIZE_BYTES (8) + +#define INSTAFOB_ENCODED_DATA_SIZE_BYTES ((4 * 7) + 1) +#define INSTAFOB_ENCODED_DATA_SIZE_BITS ((8 * 4 * 7) + 1) +#define INSTAFOB_ENCODED_DATA_OFFSET (7) + +#define INSTAFOB_BLOCK1 (0x00107060) + +#define LFRFID_FREQUENCY (125000) +#define INSTAFOB_CLOCK_PER_BIT (32) +#define INSTAFOB_READ_LONG_TIME_BASE (1000000 / (LFRFID_FREQUENCY / INSTAFOB_CLOCK_PER_BIT)) +#define INSTAFOB_READ_SHORT_TIME_BASE (INSTAFOB_READ_LONG_TIME_BASE / 2) +#define INSTAFOB_READ_SEQTERM_TIME_BASE \ + (1000000 / (LFRFID_FREQUENCY * 32 / 50 / INSTAFOB_CLOCK_PER_BIT)) +#define INSTAFOB_READ_JITTER_TIME_BASE (INSTAFOB_READ_SHORT_TIME_BASE * 40 / 100) +// #define INSTAFOB_CHECK_AS_WE_GO 1 + +typedef enum { + ProtocolInstaFobSeqTermNone = 0, + ProtocolInstaFobSeqTermS1, + ProtocolInstaFobSeqTermS2, + ProtocolInstaFobSeqTermS3, + ProtocolInstaFobSeqTermS4, + ProtocolInstaFobSeqTermS5, + ProtocolInstaFobSeqTermS6, + ProtocolInstaFobSeqTermSMax, +} ProtocolInstaFobSeqTermState; + +typedef struct { + uint8_t data[INSTAFOB_DECODED_DATA_SIZE_BYTES]; + + uint8_t encoded_data[INSTAFOB_ENCODED_DATA_SIZE_BYTES]; + uint8_t encoded_data_index; + bool encoded_polarity; + ProtocolInstaFobSeqTermState encoded_term_state; + FuriString* debug_string; + + ManchesterState decoder_manchester_state; +} ProtocolInstaFob; + +ProtocolInstaFob* protocol_insta_fob_alloc(void) { + ProtocolInstaFob* protocol = malloc(sizeof(ProtocolInstaFob)); + bit_lib_num_to_bytes_be(INSTAFOB_BLOCK1, 4, protocol->data); + protocol->debug_string = furi_string_alloc(); + return (void*)protocol; +}; + +void protocol_insta_fob_free(ProtocolInstaFob* protocol) { + free(protocol->debug_string); + free(protocol); +}; + +uint8_t* protocol_insta_fob_get_data(ProtocolInstaFob* protocol) { + return protocol->data; +}; + +void protocol_insta_fob_decoder_start(ProtocolInstaFob* protocol) { + memset(protocol->data, 0, INSTAFOB_DECODED_DATA_SIZE_BYTES); + memset(protocol->encoded_data, 0, INSTAFOB_ENCODED_DATA_SIZE_BYTES); + manchester_advance( + protocol->decoder_manchester_state, + ManchesterEventReset, + &protocol->decoder_manchester_state, + NULL); + protocol->encoded_polarity = false; + protocol->encoded_term_state = ProtocolInstaFobSeqTermNone; +}; + +static const char* protocol_insta_fob_get_encoded_data(ProtocolInstaFob* protocol) { + furi_string_reset(protocol->debug_string); + for(size_t i = 0; i < INSTAFOB_ENCODED_DATA_SIZE_BITS; i++) { + furi_string_cat( + protocol->debug_string, bit_lib_get_bit(protocol->encoded_data, i) ? "1" : "0"); + } + return furi_string_get_cstr(protocol->debug_string); +}; + +static bool protocol_insta_fob_can_be_decoded(ProtocolInstaFob* protocol) { + return bit_lib_get_bits_32(protocol->encoded_data, INSTAFOB_ENCODED_DATA_OFFSET, 32) == + INSTAFOB_BLOCK1; +} + +static void protocol_insta_fob_decode(ProtocolInstaFob* protocol) { + bit_lib_copy_bits(protocol->data, 0, 64, protocol->encoded_data, INSTAFOB_ENCODED_DATA_OFFSET); +}; + +static bool protocol_insta_fob_in_range(uint32_t value, uint32_t base, uint32_t jitter) { + return (value > (base - jitter)) && (value < (base + jitter)); +}; + +static ManchesterEvent protocol_insta_fob_manchester_event(bool level, uint32_t duration_us) { + ManchesterEvent event = ManchesterEventReset; + + if(protocol_insta_fob_in_range( + duration_us, INSTAFOB_READ_SHORT_TIME_BASE, INSTAFOB_READ_JITTER_TIME_BASE)) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(protocol_insta_fob_in_range( + duration_us, INSTAFOB_READ_LONG_TIME_BASE, INSTAFOB_READ_JITTER_TIME_BASE)) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + + return event; +}; + +static bool protocol_insta_fob_is_sequence_terminator(uint32_t duration_us) { + return protocol_insta_fob_in_range( + duration_us, INSTAFOB_READ_SEQTERM_TIME_BASE, INSTAFOB_READ_JITTER_TIME_BASE); +}; + +static bool protocol_insta_fob_decoder_seq_terminator( + ProtocolInstaFob* protocol, + bool level, + uint32_t duration_us) { + bool decoded_signal = false; + ManchesterEvent event = protocol_insta_fob_manchester_event(level, duration_us); + + if(level && protocol_insta_fob_is_sequence_terminator(duration_us)) { + protocol->encoded_term_state = ProtocolInstaFobSeqTermS3; + } else if(protocol->encoded_term_state == ProtocolInstaFobSeqTermS3) { + if(event == ManchesterEventShortHigh) { + // We found expected next part of sequence terminator. + protocol->encoded_term_state = ProtocolInstaFobSeqTermS4; + } else { + // Unexpected signal, start scanning the card again. + protocol->encoded_term_state = ProtocolInstaFobSeqTermNone; + } + } else if(protocol->encoded_term_state == ProtocolInstaFobSeqTermS4) { + if(event == ManchesterEventLongLow) { + protocol->encoded_term_state = ProtocolInstaFobSeqTermS6; + } else { + // Unexpected signal, start scanning the card again. + protocol->encoded_term_state = ProtocolInstaFobSeqTermNone; + } + } else if(protocol->encoded_term_state == ProtocolInstaFobSeqTermS6) { + // We found the last part of sequence terminator! + + // Check if our data is valid. + if(protocol_insta_fob_can_be_decoded(protocol)) { + protocol_insta_fob_decode(protocol); + decoded_signal = true; + + FURI_LOG_D(TAG, "Decoded %s", protocol_insta_fob_get_encoded_data(protocol)); + + // Clear the encoded data, reset to the first bit. + memset(protocol->encoded_data, 0, INSTAFOB_ENCODED_DATA_SIZE_BYTES); + protocol->encoded_polarity = false; + } else { + FURI_LOG_D(TAG, "Failed decoding %s", protocol_insta_fob_get_encoded_data(protocol)); + } + + protocol->encoded_term_state = ProtocolInstaFobSeqTermNone; + } else { + // Unknown signal, ignore. + } + + return decoded_signal; +} + +bool protocol_insta_fob_decoder_feed(ProtocolInstaFob* protocol, bool level, uint32_t duration_us) { + bool decoded_signal = false; + ManchesterEvent event = protocol_insta_fob_manchester_event(level, duration_us); + + if(protocol->encoded_term_state || event == ManchesterEventReset) { + return protocol_insta_fob_decoder_seq_terminator(protocol, level, duration_us); + } + + bool data; + bool data_ok = manchester_advance( + protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data); + // If data_ok is false, it means we only decoded the first half of the bit. + // When data_ok is true, we decoded both halves of the bit and "data" variable is set. + + if(data_ok) { + // Mark that we read the second half of signal + protocol->encoded_polarity = false; + + bit_lib_push_bit(protocol->encoded_data, INSTAFOB_ENCODED_DATA_SIZE_BYTES, data); + +#ifdef INSTAFOB_CHECK_AS_WE_GO + if(protocol_insta_fob_can_be_decoded(protocol)) { + protocol_insta_fob_decode(protocol); + decoded_signal = true; + + memset(protocol->encoded_data, 0, INSTAFOB_ENCODED_DATA_SIZE_BYTES); + protocol->encoded_polarity = false; + } +#endif + } else { + // encoded_polarity should be false when we are at the first half of the signal. + // If it is true, then we are actually at the beginning of the first set bit. + + if(protocol->encoded_polarity) { + memset(protocol->encoded_data, 0, INSTAFOB_ENCODED_DATA_SIZE_BYTES); + bit_lib_push_bit(protocol->encoded_data, INSTAFOB_ENCODED_DATA_SIZE_BYTES, 1); + } + + // Mark that we read the first half of signal + protocol->encoded_polarity = true; + } + + return decoded_signal; +}; + +bool protocol_insta_fob_encoder_start(ProtocolInstaFob* protocol) { + uint32_t block1 = bit_lib_get_bits_32(protocol->data, 0, 32); + if(block1 != INSTAFOB_BLOCK1) { + FURI_LOG_E( + TAG, + "Block 1 has wrong data (%08lx). Updating to %08lx.", + block1, + (uint32_t)INSTAFOB_BLOCK1); + + bit_lib_num_to_bytes_be(INSTAFOB_BLOCK1, 4, protocol->data); + } + + // You can add parity or other data manipulation here. + + // Set all of our encoded_data bits to zeros. + memset(protocol->encoded_data, 0, INSTAFOB_ENCODED_DATA_SIZE_BYTES); + + // Copy the data to the beginning of our data onto the encoded_data + bit_lib_copy_bits( + protocol->encoded_data, 0, 8 * INSTAFOB_DECODED_DATA_SIZE_BYTES, protocol->data, 0); + + // Note: For sending we start at bit 0. + protocol->encoded_data_index = 0; + protocol->encoded_polarity = true; + protocol->encoded_term_state = ProtocolInstaFobSeqTermNone; + + return true; +} + +LevelDuration protocol_insta_fob_encoder_yield(ProtocolInstaFob* protocol) { + bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index); + uint32_t duration_cycles = INSTAFOB_CLOCK_PER_BIT / 2; + + if(protocol->encoded_term_state == ProtocolInstaFobSeqTermNone) { + if(protocol->encoded_polarity) { + protocol->encoded_polarity = false; + } else { + level = !level; + + protocol->encoded_polarity = true; + protocol->encoded_data_index++; + if(protocol->encoded_data_index == (INSTAFOB_ENCODED_DATA_SIZE_BITS - 1)) { + // Next yield call will be the start of the sequence terminator. + protocol->encoded_term_state = ProtocolInstaFobSeqTermS1; + } + } + } else { + // States 1-2 will send a "1" bit. This is what I observed on the ProxMark3. + // States 3-4 will send a "." bit due to invalid duration (Really long high then short low). + // States 5-6 will send a "." bit due to invalid duration (A long high). + if(protocol->encoded_term_state == ProtocolInstaFobSeqTermS1) { + level = true; + } else if(protocol->encoded_term_state == ProtocolInstaFobSeqTermS2) { + level = !true; + } else if(protocol->encoded_term_state == ProtocolInstaFobSeqTermS3) { + level = true; + duration_cycles = INSTAFOB_CLOCK_PER_BIT * 50 / 32; + } else if(protocol->encoded_term_state == ProtocolInstaFobSeqTermS4) { + level = false; + } else if(protocol->encoded_term_state == ProtocolInstaFobSeqTermS5) { + level = true; + } else if(protocol->encoded_term_state == ProtocolInstaFobSeqTermS6) { + level = true; + } + + // Move to the next state. + protocol->encoded_term_state++; + + if(protocol->encoded_term_state == ProtocolInstaFobSeqTermSMax) { + // We are sending the end of the sequence terminator. + // Set variables so next yield call will send the first half of the first bit. + protocol->encoded_term_state = ProtocolInstaFobSeqTermNone; + protocol->encoded_data_index = 0; + protocol->encoded_polarity = true; + } + } + + return level_duration_make(level, duration_cycles); +}; + +void protocol_insta_fob_render_data(ProtocolInstaFob* protocol, FuriString* result) { + furi_string_printf( + result, + "InstaFob\nBlk[1]: %08lX\nBlk[2]: %08lX", + bit_lib_get_bits_32(protocol->data, 0, 32), + bit_lib_get_bits_32(protocol->data, 32, 32)); +}; + +void protocol_insta_fob_render_brief_data(ProtocolInstaFob* protocol, FuriString* result) { + furi_string_printf( + result, + "Fob %08lX %08lX", + bit_lib_get_bits_32(protocol->data, 0, 32), + bit_lib_get_bits_32(protocol->data, 32, 32)); +}; + +bool protocol_insta_fob_write_data(ProtocolInstaFob* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Encode data + protocol_insta_fob_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_32 | + LFRFID_T5577_ST_TERMINATOR | (7 << LFRFID_T5577_MAXBLOCK_SHIFT)); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + // Clear the rest of the blocks + for(int i = 3; i < 8; i++) { + request->t5577.block[i] = 0; + } + request->t5577.blocks_to_write = 8; + result = true; + + FURI_LOG_D( + TAG, + "block[0]: %08lx block[1]: %08lx block[2]: %08lx", + request->t5577.block[0], + request->t5577.block[1], + request->t5577.block[2]); + } + return result; +}; + +const ProtocolBase protocol_insta_fob = { + .name = "InstaFob", + .manufacturer = "Hillman Group", + .data_size = INSTAFOB_DECODED_DATA_SIZE_BYTES, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_insta_fob_alloc, + .free = (ProtocolFree)protocol_insta_fob_free, + .get_data = (ProtocolGetData)protocol_insta_fob_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_insta_fob_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_insta_fob_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_insta_fob_encoder_start, + .yield = (ProtocolEncoderYield)protocol_insta_fob_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_insta_fob_render_data, + .render_brief_data = (ProtocolRenderData)protocol_insta_fob_render_brief_data, + .write_data = (ProtocolWriteData)protocol_insta_fob_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_insta_fob.h b/lib/lfrfid/protocols/protocol_insta_fob.h new file mode 100644 index 000000000..f4419e496 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_insta_fob.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_insta_fob; \ No newline at end of file