Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/lfrfid/protocols/lfrfid_protocols.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "protocol_h10301.h"
#include "protocol_idteck.h"
#include "protocol_indala26.h"
#include "protocol_indala224.h"
#include "protocol_io_prox_xsf.h"
#include "protocol_awid.h"
#include "protocol_fdx_a.h"
Expand All @@ -30,6 +31,7 @@ const ProtocolBase* const lfrfid_protocols[] = {
[LFRFIDProtocolH10301] = &protocol_h10301,
[LFRFIDProtocolIdteck] = &protocol_idteck,
[LFRFIDProtocolIndala26] = &protocol_indala26,
[LFRFIDProtocolIndala224] = &protocol_indala224,
[LFRFIDProtocolIOProxXSF] = &protocol_io_prox_xsf,
[LFRFIDProtocolAwid] = &protocol_awid,
[LFRFIDProtocolFDXA] = &protocol_fdx_a,
Expand Down
1 change: 1 addition & 0 deletions lib/lfrfid/protocols/lfrfid_protocols.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ typedef enum {
LFRFIDProtocolH10301,
LFRFIDProtocolIdteck,
LFRFIDProtocolIndala26,
LFRFIDProtocolIndala224,
LFRFIDProtocolIOProxXSF,
LFRFIDProtocolAwid,
LFRFIDProtocolFDXA,
Expand Down
348 changes: 348 additions & 0 deletions lib/lfrfid/protocols/protocol_indala224.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
#include <furi.h>
#include <toolbox/protocols/protocol.h>
#include <lfrfid/tools/bit_lib.h>
#include "lfrfid_protocols.h"

//----------------------------------------------------------------
// Parameter Definitions
//----------------------------------------------------------------

#define INDALA224_PREAMBLE_BIT_SIZE (32)
#define INDALA224_PREAMBLE_DATA_SIZE (4)

#define INDALA224_ENCODED_BIT_SIZE (224)
#define INDALA224_ENCODED_DATA_SIZE \
(((INDALA224_ENCODED_BIT_SIZE) / 8) + INDALA224_PREAMBLE_DATA_SIZE)
#define INDALA224_ENCODED_DATA_LAST ((INDALA224_ENCODED_BIT_SIZE) / 8)

#define INDALA224_DECODED_BIT_SIZE (224)
#define INDALA224_DECODED_DATA_SIZE (28)

#define INDALA224_US_PER_BIT (255)
#define INDALA224_ENCODER_PULSES_PER_BIT (16)

//----------------------------------------------------------------
// Struct Definitions
//----------------------------------------------------------------

typedef struct {
uint8_t data_index;
uint8_t bit_clock_index;
bool last_bit;
bool current_polarity;
bool pulse_phase;
} ProtocolIndala224Encoder;

typedef struct {
uint8_t encoded_data[INDALA224_ENCODED_DATA_SIZE];
uint8_t negative_encoded_data[INDALA224_ENCODED_DATA_SIZE];
uint8_t corrupted_encoded_data[INDALA224_ENCODED_DATA_SIZE];
uint8_t corrupted_negative_encoded_data[INDALA224_ENCODED_DATA_SIZE];

uint8_t data[INDALA224_DECODED_DATA_SIZE];
ProtocolIndala224Encoder encoder;
} ProtocolIndala224;

//----------------------------------------------------------------
// Utility Functions
//----------------------------------------------------------------

// Neater aliases for getting and setting bits

bool gb(const uint8_t* data, size_t position) {
return bit_lib_get_bit(data, position);
}

void sb(uint8_t* data, size_t position, bool bit) {
bit_lib_set_bit(data, position, bit);
}

void psk1_to_psk2(uint8_t* data, size_t size) {
bool lastbit = gb(data, 0);
size_t num_bits = size * 8;

for(size_t i = 1; i < num_bits; i++) {
if(lastbit != gb(data, i)) {
lastbit = gb(data, i);
sb(data, i, 1);
} else {
sb(data, i, 0);
}
}
}

void psk2_to_psk1(uint8_t* data, size_t size) {
bool phase = 0;
size_t num_bits = size * 8;

for(size_t i = 0; i < num_bits; i++) {
if(gb(data, i) == 1) {
phase ^= 1;
}
sb(data, i, phase);
}
}

//----------------------------------------------------------------
// Indala 224 Functions
//----------------------------------------------------------------

ProtocolIndala224* protocol_indala224_alloc(void) {
ProtocolIndala224* protocol = malloc(sizeof(ProtocolIndala224));
return protocol;
};

void protocol_indala224_free(ProtocolIndala224* protocol) {
free(protocol);
};

uint8_t* protocol_indala224_get_data(ProtocolIndala224* protocol) {
return protocol->data;
};

void protocol_indala224_decoder_start(ProtocolIndala224* protocol) {
memset(protocol->encoded_data, 0, INDALA224_ENCODED_DATA_SIZE);
memset(protocol->negative_encoded_data, 0, INDALA224_ENCODED_DATA_SIZE);
memset(protocol->corrupted_encoded_data, 0, INDALA224_ENCODED_DATA_SIZE);
memset(protocol->corrupted_negative_encoded_data, 0, INDALA224_ENCODED_DATA_SIZE);
};

static bool protocol_indala224_check_preamble(uint8_t* data, size_t bit_index) {
// Preamble 10000000__00000000__00000000__00000001
if(data[bit_index / 8] != 0b10000000) return false;
if(data[(bit_index + 1) / 8] != 0b00000000) return false;
if(data[(bit_index + 2) / 8] != 0b00000000) return false;
if(data[(bit_index + 3) / 8] != 0b00000001) return false;
Comment on lines +112 to +115
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There's a bug in how byte indices are calculated for the preamble check. (bit_index + 1) / 8 will not give you the next byte index if bit_index is a multiple of 8. For example, if bit_index is 0, (0+1)/8 is 0, (0+2)/8 is 0, etc. You are checking data[0] multiple times against different values. You should calculate the base byte index and then add offsets to check subsequent bytes.

    if(data[bit_index / 8] != 0b10000000) return false;
    if(data[bit_index / 8 + 1] != 0b00000000) return false;
    if(data[bit_index / 8 + 2] != 0b00000000) return false;
    if(data[bit_index / 8 + 3] != 0b00000001) return false;

Comment on lines +110 to +115
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preamble check indexes same byte, decoder always fails

In protocol_indala224_check_preamble the four preamble bytes are read with data[bit_index/8], data[(bit_index+1)/8], etc., which all resolve to the same byte when bit_index is 0 or 224. That compares the first byte against 0x80, 0x00, 0x00, and 0x01 simultaneously, which can never succeed, so protocol_indala224_can_be_decoded always returns false and the decoder never reports a valid Indala 224 frame.

Useful? React with 👍 / 👎.

return true;
}

static bool protocol_indala224_can_be_decoded(uint8_t* data) {
// Use PSK2 Demodulated version
uint8_t temp_data[INDALA224_ENCODED_DATA_SIZE];
memcpy(temp_data, data, INDALA224_ENCODED_DATA_SIZE);
psk1_to_psk2(temp_data, INDALA224_ENCODED_DATA_SIZE);

if(!protocol_indala224_check_preamble(temp_data, 0)) return false;
if(!protocol_indala224_check_preamble(temp_data, 224)) return false;
return true;
}

static bool protocol_indala224_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) {
time += (INDALA224_US_PER_BIT / 2);

size_t bit_count = (time / INDALA224_US_PER_BIT);
bool result = false;

if(bit_count < INDALA224_ENCODED_BIT_SIZE) {
for(size_t i = 0; i < bit_count; i++) {
bit_lib_push_bit(data, INDALA224_ENCODED_DATA_SIZE, polarity);
if(protocol_indala224_can_be_decoded(data)) {
result = true;
break;
}
}
}

return result;
}

static void protocol_indala224_decoder_save(uint8_t* data_to, const uint8_t* data_from) {
bit_lib_copy_bits(data_to, 0, 32, data_from, 0); // UID 1
bit_lib_copy_bits(data_to, 32, 32, data_from, 0 + 32); // UID 2
bit_lib_copy_bits(data_to, 64, 32, data_from, 0 + 64); // UID 3
bit_lib_copy_bits(data_to, 96, 32, data_from, 0 + 96); // UID 4
bit_lib_copy_bits(data_to, 128, 32, data_from, 0 + 128); // UID 5
bit_lib_copy_bits(data_to, 160, 32, data_from, 0 + 160); // UID 6
bit_lib_copy_bits(data_to, 192, 32, data_from, 0 + 192); // UID 7
Comment on lines +150 to +156
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These consecutive bit_lib_copy_bits calls can be replaced by a single call to copy all 224 bits at once. This is more efficient and readable. The 0 + ... pattern is also redundant.

    bit_lib_copy_bits(data_to, 0, 224, data_from, 0); // UID


// Convert from PSK1 demodulation to PSK2 demodulation
psk1_to_psk2(data_to, INDALA224_DECODED_DATA_SIZE);
}

bool protocol_indala224_decoder_feed(ProtocolIndala224* protocol, bool level, uint32_t duration) {
bool result = false;

if(duration > (INDALA224_US_PER_BIT / 2)) {
if(protocol_indala224_decoder_feed_internal(level, duration, protocol->encoded_data)) {
protocol_indala224_decoder_save(protocol->data, protocol->encoded_data);
FURI_LOG_D("Indala224", "Positive");
result = true;
return result;
}

if(protocol_indala224_decoder_feed_internal(
!level, duration, protocol->negative_encoded_data)) {
protocol_indala224_decoder_save(protocol->data, protocol->negative_encoded_data);
FURI_LOG_D("Indala224", "Negative");
result = true;
return result;
}
}

if(duration > (INDALA224_US_PER_BIT / 4)) {
// Try to decode wrong phase synced data
if(level) {
duration += 120;
} else {
if(duration > 120) {
duration -= 120;
}
}
Comment on lines +184 to +190
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The value 120 is a magic number used for phase adjustment. It's better to define it as a constant, for example #define INDALA224_PHASE_ADJUST_US 120, to improve code clarity and maintainability. This value seems to be close to half of INDALA224_US_PER_BIT.


if(protocol_indala224_decoder_feed_internal(
level, duration, protocol->corrupted_encoded_data)) {
protocol_indala224_decoder_save(protocol->data, protocol->corrupted_encoded_data);
FURI_LOG_D("Indala224", "Positive Corrupted");

result = true;
return result;
}

if(protocol_indala224_decoder_feed_internal(
!level, duration, protocol->corrupted_negative_encoded_data)) {
protocol_indala224_decoder_save(
protocol->data, protocol->corrupted_negative_encoded_data);
FURI_LOG_D("Indala224", "Negative Corrupted");

result = true;
return result;
}
}

return result;
};

bool protocol_indala224_encoder_start(ProtocolIndala224* protocol) {
memset(protocol->encoded_data, 0, INDALA224_ENCODED_DATA_SIZE);
bit_lib_copy_bits(protocol->encoded_data, 0, 32, protocol->data, 0); // UID 1
bit_lib_copy_bits(protocol->encoded_data, 0 + 32, 32, protocol->data, 32); // UID 2
bit_lib_copy_bits(protocol->encoded_data, 0 + 64, 32, protocol->data, 64); // UID 3
bit_lib_copy_bits(protocol->encoded_data, 0 + 96, 32, protocol->data, 96); // UID 4
bit_lib_copy_bits(protocol->encoded_data, 0 + 128, 32, protocol->data, 128); // UID 5
bit_lib_copy_bits(protocol->encoded_data, 0 + 160, 32, protocol->data, 160); // UID 6
bit_lib_copy_bits(protocol->encoded_data, 0 + 192, 32, protocol->data, 192); // UID 7
Comment on lines +217 to +223
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These consecutive bit_lib_copy_bits calls can be simplified into a single call. This improves readability and is slightly more efficient. The 0 + ... pattern is also redundant.

    bit_lib_copy_bits(protocol->encoded_data, 0, 224, protocol->data, 0); // UID


// Convert from PSK2 Demodulation to PSK1 Demodulation
psk2_to_psk1(protocol->encoded_data, INDALA224_ENCODED_DATA_SIZE);

protocol->encoder.last_bit =
bit_lib_get_bit(protocol->encoded_data, INDALA224_ENCODED_BIT_SIZE - 1);
protocol->encoder.data_index = 0;
protocol->encoder.current_polarity = true;
protocol->encoder.pulse_phase = true;
protocol->encoder.bit_clock_index = 0;

return true;
};

LevelDuration protocol_indala224_encoder_yield(ProtocolIndala224* protocol) {
LevelDuration level_duration;
ProtocolIndala224Encoder* encoder = &protocol->encoder;

if(encoder->pulse_phase) {
level_duration = level_duration_make(encoder->current_polarity, 1);
encoder->pulse_phase = false;
} else {
level_duration = level_duration_make(!encoder->current_polarity, 1);
encoder->pulse_phase = true;

encoder->bit_clock_index++;
if(encoder->bit_clock_index >= INDALA224_ENCODER_PULSES_PER_BIT) {
encoder->bit_clock_index = 0;

bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index);

if(current_bit != encoder->last_bit) {
encoder->current_polarity = !encoder->current_polarity;
}

encoder->last_bit = current_bit;

bit_lib_increment_index(encoder->data_index, INDALA224_ENCODED_BIT_SIZE);
}
}

return level_duration;
};

void protocol_indala224_render_data_internal(
ProtocolIndala224* protocol,
FuriString* result,
bool brief) {
const uint32_t uid1 = bit_lib_get_bits_32(protocol->data, 0, 32);
const uint32_t uid2 = bit_lib_get_bits_32(protocol->data, 32, 32);
const uint32_t uid3 = bit_lib_get_bits_32(protocol->data, 64, 32);
const uint32_t uid4 = bit_lib_get_bits_32(protocol->data, 96, 32);
const uint32_t uid5 = bit_lib_get_bits_32(protocol->data, 128, 32);
const uint32_t uid6 = bit_lib_get_bits_32(protocol->data, 160, 32);
const uint32_t uid7 = bit_lib_get_bits_32(protocol->data, 192, 32);

if(brief) {
furi_string_printf(result, "UID: %u%u...", (unsigned int)uid1, (unsigned int)uid2);
} else {
furi_string_printf(
result,
"UID: %u%u%u%u%u%u%u",
(unsigned int)uid1,
(unsigned int)uid2,
(unsigned int)uid3,
(unsigned int)uid4,
(unsigned int)uid5,
(unsigned int)uid6,
(unsigned int)uid7);
}
Comment on lines +280 to +293
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The UID is 224 bits long. Printing it as a concatenated decimal string will result in a very large, unreadable number. It's standard practice to display long identifiers like this in hexadecimal format. Other Indala protocols in this codebase also use hex.

Additionally, using %u for uint32_t might not be portable, as unsigned int is not guaranteed to be 32 bits. Using %lX with a cast to unsigned long is safer and more conventional in this codebase for printing 32-bit hex values.

    if(brief) {
        furi_string_printf(
            result, "UID: %08lX%08lX...", (unsigned long)uid1, (unsigned long)uid2);
    } else {
        furi_string_printf(
            result,
            "UID: %08lX%08lX%08lX%08lX%08lX%08lX%08lX",
            (unsigned long)uid1,
            (unsigned long)uid2,
            (unsigned long)uid3,
            (unsigned long)uid4,
            (unsigned long)uid5,
            (unsigned long)uid6,
            (unsigned long)uid7);
    }

}
void protocol_indala224_render_data(ProtocolIndala224* protocol, FuriString* result) {
protocol_indala224_render_data_internal(protocol, result, false);
}
void protocol_indala224_render_brief_data(ProtocolIndala224* protocol, FuriString* result) {
protocol_indala224_render_data_internal(protocol, result, true);
}

bool protocol_indala224_write_data(ProtocolIndala224* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;

if(request->write_type == LFRFIDWriteTypeT5577) {
request->t5577.block[0] = LFRFID_T5577_BITRATE_RF_32 | LFRFID_T5577_MODULATION_PSK2 |
(7 << LFRFID_T5577_MAXBLOCK_SHIFT);
request->t5577.block[1] = bit_lib_get_bits_32(protocol->data, 0, 32);
request->t5577.block[2] = bit_lib_get_bits_32(protocol->data, 32, 32);
request->t5577.block[3] = bit_lib_get_bits_32(protocol->data, 32, 32);
request->t5577.block[4] = bit_lib_get_bits_32(protocol->data, 32, 32);
request->t5577.block[5] = bit_lib_get_bits_32(protocol->data, 32, 32);
request->t5577.block[6] = bit_lib_get_bits_32(protocol->data, 32, 32);
request->t5577.block[7] = bit_lib_get_bits_32(protocol->data, 32, 32);
Comment on lines +311 to +315
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There seems to be a copy-paste error here. Blocks 3 through 7 are all reading data from the same offset 32. This will cause incorrect data to be written to the T5577 tag. The offsets should be incremented by 32 for each block.

        request->t5577.block[3] = bit_lib_get_bits_32(protocol->data, 64, 32);
        request->t5577.block[4] = bit_lib_get_bits_32(protocol->data, 96, 32);
        request->t5577.block[5] = bit_lib_get_bits_32(protocol->data, 128, 32);
        request->t5577.block[6] = bit_lib_get_bits_32(protocol->data, 160, 32);
        request->t5577.block[7] = bit_lib_get_bits_32(protocol->data, 192, 32);

Comment on lines +309 to +315
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge T5577 write repeats one 32‑bit word for all payload blocks

When writing an Indala224 tag to a T5577 (protocol_indala224_write_data), blocks 3–7 are all filled with bit_lib_get_bits_32(protocol->data, 32, 32), i.e., the same 32‑bit slice is duplicated five times instead of using offsets 64, 96, 128, 160, and 192 for the remaining words. Any cloned tag will therefore contain only the second word repeated, discarding most of the 224‑bit payload.

Useful? React with 👍 / 👎.

request->t5577.blocks_to_write = 8;
result = true;
}
return result;
};

//----------------------------------------------------------------
// Indala 224 Protocol
//----------------------------------------------------------------

const ProtocolBase protocol_indala224 = {
.name = "Indala224",
.manufacturer = "Motorola",
.data_size = INDALA224_DECODED_DATA_SIZE,
.features = LFRFIDFeaturePSK,
.validate_count = 6,
.alloc = (ProtocolAlloc)protocol_indala224_alloc,
.free = (ProtocolFree)protocol_indala224_free,
.get_data = (ProtocolGetData)protocol_indala224_get_data,
.decoder =
{
.start = (ProtocolDecoderStart)protocol_indala224_decoder_start,
.feed = (ProtocolDecoderFeed)protocol_indala224_decoder_feed,
},
.encoder =
{
.start = (ProtocolEncoderStart)protocol_indala224_encoder_start,
.yield = (ProtocolEncoderYield)protocol_indala224_encoder_yield,
},
.render_data = (ProtocolRenderData)protocol_indala224_render_data,
.render_brief_data = (ProtocolRenderData)protocol_indala224_render_brief_data,
.write_data = (ProtocolWriteData)protocol_indala224_write_data,
};
4 changes: 4 additions & 0 deletions lib/lfrfid/protocols/protocol_indala224.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once
#include <toolbox/protocols/protocol.h>

extern const ProtocolBase protocol_indala224;