Files
ProtoPirate/protocols/fiat_v1.c
T
MX 53e908b1cd
FAP Build / ufbt: Build for Momentum dev (push) Canceled after 0s
FAP Build / ufbt: Build for Momentum release (push) Canceled after 0s
FAP Build / ufbt: Build for OFW dev channel (push) Canceled after 0s
FAP Build / ufbt: Build for OFW release channel (push) Canceled after 0s
FAP Build / ufbt: Build for Unleashed dev (push) Canceled after 0s
FAP Build / ufbt: Build for Unleashed release (push) Canceled after 0s
fbt formatting
2026-09-15 02:15:19 +03:00

1038 lines
36 KiB
C

#include "fiat_v1.h"
#include "protocols_common.h"
#include <string.h>
#define TAG "FiatProtocolV1"
#define FIAT_V1_TE_SHORT 250U
#define FIAT_V1_TE_LONG 500U
#define FIAT_V1_TE_DELTA 100U
#define FIAT_V1_TE_B_SHORT 100U
#define FIAT_V1_TE_B_LONG 200U
#define FIAT_V1_TE_B_DELTA 50U
#define FIAT_V1_WIRE_BITS 104U
#define FIAT_V1_WIRE_BYTES 13U
#define FIAT_V1_WIRE_CELLS (FIAT_V1_WIRE_BITS * 2U)
#define FIAT_V1_LOGICAL_BITS 102U
#define FIAT_V1_VARIANT_COUNT 2U
#define FIAT_V1_BOUNDARY_MIN_US 800U
#define FIAT_V1_DEFAULT_TAIL_BITS 2U
#define FIAT_V1_RAW_FIELD "Raw"
#define FIAT_V1_HOP_FIELD "Hop"
#define FIAT_V1_TAIL_BITS_FIELD "Tail Bits"
#define FIAT_V1_XOR_FIELD "XOR"
#define FIAT_V1_HITAG2_KEY_FIELD "Hitag2 Key"
#define FIAT_V1_HITAG2_EPOCH_FIELD "Hitag2 Epoch"
#define FIAT_V1_KNOWN_KEY_COUNT 9U
#define FIAT_V1_TE_VARIANT_A 0U
#define FIAT_V1_TE_VARIANT_B 1U
#define FIAT_V1_DEFAULT_TE_VARIANT FIAT_V1_TE_VARIANT_B
#define FIAT_V1_ENC_LEAD_A_US 2033U
#define FIAT_V1_ENC_GAP_A_US 3252U
#define FIAT_V1_ENC_LEAD_B_US 850U
#define FIAT_V1_ENC_GAP_B_US 1230U
#define FIAT_V1_ENC_DEFAULT_REPEAT 6U
#define FIAT_V1_UPLOAD_CAPACITY 240U
_Static_assert(
FIAT_V1_UPLOAD_CAPACITY <= PP_SHARED_UPLOAD_CAPACITY,
"FIAT_V1_UPLOAD_CAPACITY exceeds shared upload slab");
static const SubGhzBlockConst subghz_protocol_fiat_v1_const = {
.te_short = FIAT_V1_TE_SHORT,
.te_long = FIAT_V1_TE_LONG,
.te_delta = FIAT_V1_TE_DELTA,
.min_count_bit_for_found = FIAT_V1_LOGICAL_BITS,
};
static const SubGhzBlockConst subghz_protocol_fiat_v1_const_b = {
.te_short = FIAT_V1_TE_B_SHORT,
.te_long = FIAT_V1_TE_B_LONG,
.te_delta = FIAT_V1_TE_B_DELTA,
.min_count_bit_for_found = FIAT_V1_LOGICAL_BITS,
};
static const SubGhzBlockConst* fiat_v1_variant_const(uint8_t variant) {
return (variant == FIAT_V1_TE_VARIANT_A) ? &subghz_protocol_fiat_v1_const :
&subghz_protocol_fiat_v1_const_b;
}
#if PROTOPIRATE_WITH_ENCODER
static uint32_t fiat_v1_enc_lead_us(uint8_t variant) {
return (variant == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_ENC_LEAD_A_US : FIAT_V1_ENC_LEAD_B_US;
}
static uint32_t fiat_v1_enc_gap_us(uint8_t variant) {
return (variant == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_ENC_GAP_A_US : FIAT_V1_ENC_GAP_B_US;
}
static uint32_t fiat_v1_enc_te_us(uint8_t variant) {
return fiat_v1_variant_const(variant)->te_short;
}
#endif
typedef enum {
FiatV1DecoderStepReset = 0,
FiatV1DecoderStepData = 1,
} FiatV1DecoderStep;
struct SubGhzProtocolDecoderFiatV1 {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint8_t cells[FIAT_V1_VARIANT_COUNT][FIAT_V1_WIRE_CELLS];
uint16_t cell_count[FIAT_V1_VARIANT_COUNT];
uint8_t raw_data[FIAT_V1_WIRE_BYTES];
uint8_t last_raw_data[FIAT_V1_WIRE_BYTES];
bool last_raw_valid;
uint32_t uid;
uint32_t hop;
uint8_t family;
uint8_t tail_bits;
uint8_t frame_xor;
uint8_t hitag2_key[6];
uint32_t hitag2_epoch;
bool hitag2_key_valid;
uint8_t te_variant;
};
#if PROTOPIRATE_WITH_ENCODER
struct SubGhzProtocolEncoderFiatV1 {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
uint8_t raw_data[FIAT_V1_WIRE_BYTES];
uint8_t hitag2_key[6];
uint32_t epoch;
uint32_t hop;
uint8_t tail_bits;
uint8_t frame_xor;
uint8_t te_variant;
};
#endif
static bool
fiat_v1_feed_data_pulse(SubGhzProtocolDecoderFiatV1* instance, bool level, uint32_t duration);
static bool fiat_v1_frame_valid(const uint8_t raw[FIAT_V1_WIRE_BYTES]);
static void fiat_v1_build_raw(
uint8_t raw[FIAT_V1_WIRE_BYTES],
uint32_t uid,
uint8_t button,
uint16_t control,
uint32_t auth,
uint8_t tail_bits);
static void fiat_v1_verify_hitag2_key(SubGhzProtocolDecoderFiatV1* instance);
const SubGhzProtocolDecoder subghz_protocol_fiat_v1_decoder = {
.alloc = subghz_protocol_decoder_fiat_v1_alloc,
.free = pp_decoder_free_default,
.feed = subghz_protocol_decoder_fiat_v1_feed,
.reset = subghz_protocol_decoder_fiat_v1_reset,
.get_hash_data = subghz_protocol_decoder_fiat_v1_get_hash_data,
.serialize = subghz_protocol_decoder_fiat_v1_serialize,
.deserialize = subghz_protocol_decoder_fiat_v1_deserialize,
.get_string = subghz_protocol_decoder_fiat_v1_get_string,
};
#if PROTOPIRATE_WITH_ENCODER
const SubGhzProtocolEncoder subghz_protocol_fiat_v1_encoder = {
.alloc = subghz_protocol_encoder_fiat_v1_alloc,
.free = pp_encoder_free,
.deserialize = subghz_protocol_encoder_fiat_v1_deserialize,
.stop = pp_encoder_stop,
.yield = pp_encoder_yield,
};
#else
const SubGhzProtocolEncoder subghz_protocol_fiat_v1_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
#endif
const SubGhzProtocol fiat_v1_protocol = {
.name = FIAT_V1_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save
#if PROTOPIRATE_WITH_ENCODER
| SubGhzProtocolFlag_Send
#endif
,
#if PROTOPIRATE_WITH_DECODER
.decoder = &subghz_protocol_fiat_v1_decoder,
#else
.decoder = NULL,
#endif
#if PROTOPIRATE_WITH_ENCODER
.encoder = &subghz_protocol_fiat_v1_encoder,
#else
.encoder = NULL,
#endif
};
static bool fiat_v1_duration_is_short(uint8_t variant, uint32_t duration) {
return pp_is_short(duration, fiat_v1_variant_const(variant));
}
static bool fiat_v1_duration_is_long(uint8_t variant, uint32_t duration) {
return pp_is_long(duration, fiat_v1_variant_const(variant));
}
static bool fiat_v1_duration_is_pulse(uint32_t duration) {
for(uint8_t variant = 0U; variant < FIAT_V1_VARIANT_COUNT; variant++) {
if(fiat_v1_duration_is_short(variant, duration) ||
fiat_v1_duration_is_long(variant, duration)) {
return true;
}
}
return false;
}
static bool fiat_v1_button_valid(uint8_t button) {
return button == 0x1U || button == 0x2U || button == 0x4U || button == 0x8U;
}
static const char* fiat_v1_button_name(uint8_t button) {
switch(button) {
case 0x8U:
return "Unlock";
case 0x4U:
return "Lock";
case 0x2U:
return "Trunk";
case 0x1U:
return "Close";
default:
return "Unknown";
}
}
static uint8_t fiat_v1_frame_xor(const uint8_t raw[FIAT_V1_WIRE_BYTES]) {
uint8_t value = 0x01U;
for(uint8_t i = 0U; i < FIAT_V1_WIRE_BYTES - 1U; i++) {
value ^= raw[i];
}
return value;
}
static void fiat_v1_build_raw(
uint8_t raw[FIAT_V1_WIRE_BYTES],
uint32_t uid,
uint8_t button,
uint16_t control,
uint32_t auth,
uint8_t tail_bits) {
memset(raw, 0, FIAT_V1_WIRE_BYTES);
raw[1] = 0x01U;
raw[2] = (uint8_t)(uid >> 24U);
raw[3] = (uint8_t)(uid >> 16U);
raw[4] = (uint8_t)(uid >> 8U);
raw[5] = (uint8_t)uid;
raw[6] = (uint8_t)(((button & 0x0FU) << 4U) | ((control >> 6U) & 0x0FU));
raw[7] = (uint8_t)(((control & 0x3FU) << 2U) | ((auth >> 30U) & 0x03U));
raw[8] = (uint8_t)(auth >> 22U);
raw[9] = (uint8_t)(auth >> 14U);
raw[10] = (uint8_t)(auth >> 6U);
raw[11] = (uint8_t)((auth << 2U) | (tail_bits & 0x03U));
raw[12] = fiat_v1_frame_xor(raw);
}
static uint32_t fiat_v1_uid(const uint8_t raw[FIAT_V1_WIRE_BYTES]) {
return ((uint32_t)raw[2] << 24U) | ((uint32_t)raw[3] << 16U) | ((uint32_t)raw[4] << 8U) |
raw[5];
}
static uint32_t fiat_v1_counter(const uint8_t raw[FIAT_V1_WIRE_BYTES]) {
return ((uint32_t)(raw[6] & 0x0FU) << 6U) | (raw[7] >> 2U);
}
static uint32_t fiat_v1_hop(const uint8_t raw[FIAT_V1_WIRE_BYTES]) {
return ((uint32_t)(raw[7] & 0x03U) << 30U) | ((uint32_t)raw[8] << 22U) |
((uint32_t)raw[9] << 14U) | ((uint32_t)raw[10] << 6U) | (raw[11] >> 2U);
}
static bool fiat_v1_frame_valid(const uint8_t raw[FIAT_V1_WIRE_BYTES]) {
if(raw[0] != 0x00U || raw[1] != 0x01U) {
return false;
}
if(fiat_v1_frame_xor(raw) != raw[12]) {
return false;
}
if(!fiat_v1_button_valid(raw[6] >> 4U)) {
return false;
}
const uint32_t uid = fiat_v1_uid(raw);
return uid != 0U && uid != UINT32_MAX;
}
static void fiat_v1_clear_cells(SubGhzProtocolDecoderFiatV1* instance, uint8_t variant) {
instance->cell_count[variant] = 0U;
memset(instance->cells[variant], 0, FIAT_V1_WIRE_CELLS);
}
static void fiat_v1_clear_all_cells(SubGhzProtocolDecoderFiatV1* instance) {
for(uint8_t variant = 0U; variant < FIAT_V1_VARIANT_COUNT; variant++) {
fiat_v1_clear_cells(instance, variant);
}
}
static void fiat_v1_decode_fields(SubGhzProtocolDecoderFiatV1* instance) {
instance->family = instance->raw_data[1];
instance->uid = fiat_v1_uid(instance->raw_data);
instance->generic.serial = instance->uid;
instance->generic.btn = instance->raw_data[6] >> 4U;
instance->generic.cnt = fiat_v1_counter(instance->raw_data);
instance->hop = fiat_v1_hop(instance->raw_data);
instance->tail_bits = instance->raw_data[11] & 0x03U;
instance->frame_xor = instance->raw_data[12];
instance->generic.data = ((uint64_t)instance->generic.serial << 32U) | instance->hop;
instance->generic.data_count_bit = FIAT_V1_LOGICAL_BITS;
instance->decoder.decode_data = instance->generic.data;
instance->decoder.decode_count_bit = instance->generic.data_count_bit;
fiat_v1_verify_hitag2_key(instance);
}
static bool fiat_v1_commit(
SubGhzProtocolDecoderFiatV1* instance,
const uint8_t raw[FIAT_V1_WIRE_BYTES],
uint8_t te_variant) {
if(!fiat_v1_frame_valid(raw)) {
return false;
}
if(instance->last_raw_valid && memcmp(instance->last_raw_data, raw, FIAT_V1_WIRE_BYTES) == 0) {
return true;
}
memcpy(instance->raw_data, raw, FIAT_V1_WIRE_BYTES);
memcpy(instance->last_raw_data, raw, FIAT_V1_WIRE_BYTES);
instance->last_raw_valid = true;
instance->te_variant = (te_variant == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_TE_VARIANT_A :
FIAT_V1_TE_VARIANT_B;
fiat_v1_decode_fields(instance);
FURI_LOG_D(
TAG,
"Accepted UID:%08lX Btn:%02X Cnt:%03lX Auth:%08lX XOR:%02X",
(unsigned long)instance->uid,
instance->generic.btn,
(unsigned long)instance->generic.cnt,
(unsigned long)instance->hop,
instance->frame_xor);
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
return true;
}
static bool
fiat_v1_try_decode_window(SubGhzProtocolDecoderFiatV1* instance, uint8_t variant, bool invert) {
if(instance->cell_count[variant] != FIAT_V1_WIRE_CELLS) {
return false;
}
const uint8_t* cells = instance->cells[variant];
uint8_t raw[FIAT_V1_WIRE_BYTES] = {0};
for(uint8_t bit_index = 0U; bit_index < FIAT_V1_WIRE_BITS; bit_index++) {
const uint8_t first = cells[bit_index * 2U];
const uint8_t second = cells[bit_index * 2U + 1U];
if(first == second) {
return false;
}
bool bit = first != 0U;
if(invert) {
bit = !bit;
}
if(bit) {
raw[bit_index >> 3U] |= (uint8_t)(1U << (7U - (bit_index & 7U)));
}
}
return fiat_v1_commit(instance, raw, variant);
}
static void fiat_v1_try_decode(SubGhzProtocolDecoderFiatV1* instance, uint8_t variant) {
if(fiat_v1_try_decode_window(instance, variant, false)) {
return;
}
(void)fiat_v1_try_decode_window(instance, variant, true);
}
static void fiat_v1_push_cell(SubGhzProtocolDecoderFiatV1* instance, uint8_t variant, bool level) {
uint8_t* cells = instance->cells[variant];
if(instance->cell_count[variant] < FIAT_V1_WIRE_CELLS) {
cells[instance->cell_count[variant]++] = level ? 1U : 0U;
} else {
memmove(cells, &cells[1], FIAT_V1_WIRE_CELLS - 1U);
cells[FIAT_V1_WIRE_CELLS - 1U] = level ? 1U : 0U;
}
fiat_v1_try_decode(instance, variant);
}
static bool
fiat_v1_feed_data_pulse(SubGhzProtocolDecoderFiatV1* instance, bool level, uint32_t duration) {
bool matched = false;
for(uint8_t variant = 0U; variant < FIAT_V1_VARIANT_COUNT; variant++) {
if(fiat_v1_duration_is_short(variant, duration)) {
fiat_v1_push_cell(instance, variant, level);
matched = true;
} else if(fiat_v1_duration_is_long(variant, duration)) {
fiat_v1_push_cell(instance, variant, level);
fiat_v1_push_cell(instance, variant, level);
matched = true;
} else {
if(!level && duration >= FIAT_V1_BOUNDARY_MIN_US) {
fiat_v1_push_cell(instance, variant, false);
}
fiat_v1_clear_cells(instance, variant);
}
}
return matched;
}
static void fiat_v1_rebuild_raw(SubGhzProtocolDecoderFiatV1* instance) {
fiat_v1_build_raw(
instance->raw_data,
instance->generic.serial,
instance->generic.btn,
(uint16_t)(instance->generic.cnt & 0x03FFU),
instance->hop,
instance->tail_bits);
if(instance->family && instance->family != 0x01U) {
instance->raw_data[1] = instance->family;
instance->raw_data[12] = fiat_v1_frame_xor(instance->raw_data);
}
if(instance->frame_xor) {
instance->raw_data[12] = instance->frame_xor;
}
}
#if PROTOPIRATE_WITH_ENCODER || PROTOPIRATE_WITH_DECODER
static uint8_t fiat_v1_truth(uint32_t table, uint8_t index) {
return (uint8_t)((table >> index) & 1U);
}
static uint8_t fiat_v1_filter_index(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
return (uint8_t)((a << 3U) | (b << 2U) | (c << 1U) | d);
}
static uint8_t fiat_v1_byte_bit(uint8_t byte, uint8_t bit) {
return (uint8_t)((byte >> bit) & 1U);
}
static uint8_t fiat_v1_bcm_hitag2_filter(const uint8_t state[6]) {
uint8_t group = 0U;
group |= fiat_v1_truth(
0x2c79U,
fiat_v1_filter_index(
fiat_v1_byte_bit(state[0], 1U),
fiat_v1_byte_bit(state[0], 2U),
fiat_v1_byte_bit(state[0], 4U),
fiat_v1_byte_bit(state[0], 5U)));
group |= (uint8_t)(fiat_v1_truth(
0x6671U,
fiat_v1_filter_index(
fiat_v1_byte_bit(state[1], 0U),
fiat_v1_byte_bit(state[1], 1U),
fiat_v1_byte_bit(state[1], 3U),
fiat_v1_byte_bit(state[1], 7U)))
<< 1U);
group |= (uint8_t)(fiat_v1_truth(
0x6671U,
fiat_v1_filter_index(
fiat_v1_byte_bit(state[3], 5U),
fiat_v1_byte_bit(state[2], 0U),
fiat_v1_byte_bit(state[2], 2U),
fiat_v1_byte_bit(state[2], 6U)))
<< 2U);
group |= (uint8_t)(fiat_v1_truth(
0x6671U,
fiat_v1_filter_index(
fiat_v1_byte_bit(state[4], 6U),
fiat_v1_byte_bit(state[3], 0U),
fiat_v1_byte_bit(state[3], 2U),
fiat_v1_byte_bit(state[3], 3U)))
<< 3U);
group |= (uint8_t)(fiat_v1_truth(
0x2c79U,
fiat_v1_filter_index(
fiat_v1_byte_bit(state[5], 1U),
fiat_v1_byte_bit(state[5], 3U),
fiat_v1_byte_bit(state[5], 4U),
fiat_v1_byte_bit(state[4], 5U)))
<< 4U);
return fiat_v1_truth(0x7907287bUL, group);
}
static uint8_t fiat_v1_parity8(uint8_t value) {
value ^= (uint8_t)(value >> 4U);
value ^= (uint8_t)(value >> 2U);
value ^= (uint8_t)(value >> 1U);
return value & 1U;
}
static uint8_t fiat_v1_bcm_hitag2_feedback(const uint8_t state[6]) {
static const uint8_t masks[6] = {0xb3U, 0x80U, 0x83U, 0x22U, 0x00U, 0x73U};
uint8_t feedback = 0U;
for(uint8_t i = 0U; i < 6U; i++) {
feedback ^= fiat_v1_parity8((uint8_t)(state[i] & masks[i]));
}
return feedback & 1U;
}
static void fiat_v1_bcm_hitag2_shift(uint8_t state[6], uint8_t input) {
for(uint8_t i = 0U; i < 5U; i++) {
state[i] = (uint8_t)((state[i] << 1U) | (state[i + 1U] >> 7U));
}
state[5] = (uint8_t)((state[5] << 1U) | (input & 1U));
}
static uint8_t fiat_v1_input_bit_u32_be(uint32_t value, uint8_t index) {
return (uint8_t)((value >> (31U - index)) & 1U);
}
static uint8_t fiat_v1_input_bit_bytes_be(const uint8_t* bytes, uint8_t index) {
return (uint8_t)((bytes[index >> 3U] >> (7U - (index & 7U))) & 1U);
}
static uint32_t fiat_v1_bcm_generate_authenticator(
uint32_t uid,
uint8_t button,
uint16_t control,
const uint8_t key[6],
uint32_t epoch) {
uint8_t state[6] = {
(uint8_t)(uid >> 24U),
(uint8_t)(uid >> 16U),
(uint8_t)(uid >> 8U),
(uint8_t)uid,
key[4],
key[5],
};
const uint32_t iv = ((epoch & 0x3FFFFUL) << 14U) | (((uint32_t)control & 0x03FFUL) << 4U) |
((uint32_t)button & 0x0FUL);
for(uint8_t i = 0U; i < 32U; i++) {
const uint8_t input = fiat_v1_input_bit_u32_be(iv, i) ^
fiat_v1_input_bit_bytes_be(key, i) ^
fiat_v1_bcm_hitag2_filter(state);
fiat_v1_bcm_hitag2_shift(state, input);
}
uint32_t authenticator = 0U;
for(uint8_t i = 0U; i < 32U; i++) {
authenticator = (authenticator << 1U) | fiat_v1_bcm_hitag2_filter(state);
fiat_v1_bcm_hitag2_shift(state, fiat_v1_bcm_hitag2_feedback(state));
}
return authenticator;
}
#endif
static const uint8_t fiat_v1_known_keys[FIAT_V1_KNOWN_KEY_COUNT][6] = {
{0xB7U, 0x92U, 0x80U, 0xAEU, 0xCCU, 0x37U},
{0xD4U, 0x24U, 0x28U, 0xF7U, 0xD9U, 0x66U},
{0x4DU, 0x34U, 0x3FU, 0xD4U, 0xE7U, 0xB6U},
{0x6DU, 0x6BU, 0xF2U, 0x1DU, 0x3AU, 0x1AU},
{0xA3U, 0xF3U, 0xACU, 0xF7U, 0xB9U, 0x10U},
{0x4DU, 0x49U, 0x4BU, 0x52U, 0x4FU, 0x4EU},
{0xCDU, 0x49U, 0x4BU, 0x52U, 0x4FU, 0x4EU},
{0x33U, 0xFAU, 0x2FU, 0xCDU, 0xC3U, 0x3BU},
{0xF6U, 0x1AU, 0xEFU, 0x9CU, 0xD0U, 0x1BU},
};
static bool fiat_v1_key_matches(
uint32_t uid,
uint8_t button,
uint16_t control,
uint32_t hop,
const uint8_t key[6],
uint32_t epoch) {
return fiat_v1_bcm_generate_authenticator(uid, button, control, key, epoch) == hop;
}
static bool fiat_v1_key_matches_any_button(
uint32_t uid,
uint16_t control,
uint32_t hop,
const uint8_t key[6],
uint32_t epoch) {
static const uint8_t buttons[] = {0x1U, 0x2U, 0x4U, 0x8U};
for(size_t i = 0; i < COUNT_OF(buttons); i++) {
if(fiat_v1_key_matches(uid, buttons[i], control, hop, key, epoch)) {
return true;
}
}
return false;
}
static bool fiat_v1_resolve_hitag2_key(
FlipperFormat* ff,
uint32_t uid,
uint8_t button,
uint16_t control,
uint32_t hop,
uint8_t key_out[6],
uint32_t* epoch_out) {
if(!key_out) return false;
uint8_t key[6] = {0};
uint32_t epoch = 0U;
if(ff) {
flipper_format_rewind(ff);
if(flipper_format_read_hex(ff, FIAT_V1_HITAG2_KEY_FIELD, key, sizeof(key))) {
flipper_format_rewind(ff);
if(flipper_format_read_uint32(ff, FIAT_V1_HITAG2_EPOCH_FIELD, &epoch, 1U)) {
epoch &= 0x3FFFFUL;
}
if(fiat_v1_key_matches(uid, button, control, hop, key, epoch) ||
fiat_v1_key_matches_any_button(uid, control, hop, key, epoch)) {
memcpy(key_out, key, sizeof(key));
if(epoch_out) *epoch_out = epoch;
return true;
}
}
}
for(uint8_t i = 0U; i < FIAT_V1_KNOWN_KEY_COUNT; i++) {
if(fiat_v1_key_matches(uid, button, control, hop, fiat_v1_known_keys[i], 0U) ||
fiat_v1_key_matches_any_button(uid, control, hop, fiat_v1_known_keys[i], 0U)) {
memcpy(key_out, fiat_v1_known_keys[i], sizeof(key));
if(epoch_out) *epoch_out = 0U;
return true;
}
}
return false;
}
static void fiat_v1_verify_hitag2_key(SubGhzProtocolDecoderFiatV1* instance) {
instance->hitag2_key_valid = false;
instance->hitag2_epoch = 0U;
memset(instance->hitag2_key, 0, sizeof(instance->hitag2_key));
instance->hitag2_key_valid = fiat_v1_resolve_hitag2_key(
NULL,
instance->uid,
instance->generic.btn,
(uint16_t)(instance->generic.cnt & 0x03FFU),
instance->hop,
instance->hitag2_key,
&instance->hitag2_epoch);
}
#if PROTOPIRATE_WITH_ENCODER
static bool fiat_v1_encoder_build_upload(SubGhzProtocolEncoderFiatV1* instance) {
furi_check(instance);
LevelDuration* upload = instance->encoder.upload;
if(!upload) {
return false;
}
const uint8_t variant = instance->te_variant;
const uint32_t te = fiat_v1_enc_te_us(variant);
size_t index = 0U;
const size_t cap = FIAT_V1_UPLOAD_CAPACITY;
index = pp_emit_merge(upload, index, cap, true, fiat_v1_enc_lead_us(variant));
for(uint8_t bit_index = 0U; bit_index < FIAT_V1_WIRE_BITS; bit_index++) {
const bool bit = ((instance->raw_data[bit_index >> 3U] >> (7U - (bit_index & 7U))) & 1U) !=
0U;
index = pp_emit_merge(upload, index, cap, bit, te);
index = pp_emit_merge(upload, index, cap, !bit, te);
}
index = pp_emit_merge(upload, index, cap, false, fiat_v1_enc_gap_us(variant));
furi_check(index <= cap);
instance->encoder.size_upload = index;
instance->encoder.front = 0U;
return true;
}
void* subghz_protocol_encoder_fiat_v1_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderFiatV1* instance = calloc(1, sizeof(SubGhzProtocolEncoderFiatV1));
furi_check(instance);
instance->base.protocol = &fiat_v1_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
instance->tail_bits = FIAT_V1_DEFAULT_TAIL_BITS;
instance->te_variant = FIAT_V1_DEFAULT_TE_VARIANT;
instance->encoder.repeat = FIAT_V1_ENC_DEFAULT_REPEAT;
return instance;
}
SubGhzProtocolStatus
subghz_protocol_encoder_fiat_v1_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
furi_check(flipper_format);
SubGhzProtocolEncoderFiatV1* instance = context;
instance->encoder.is_running = false;
instance->encoder.front = 0U;
flipper_format_rewind(flipper_format);
if(pp_verify_protocol_name(flipper_format, instance->base.protocol->name) !=
SubGhzProtocolStatusOk) {
return SubGhzProtocolStatusErrorParserOthers;
}
uint32_t bit_count = 0U;
static const uint16_t allowed_bits[] = {FIAT_V1_LOGICAL_BITS};
if(pp_encoder_read_bit(flipper_format, allowed_bits, 1U, &bit_count) !=
SubGhzProtocolStatusOk) {
return SubGhzProtocolStatusErrorValueBitCount;
}
instance->generic.data_count_bit = bit_count;
uint32_t key_serial = 0U;
uint32_t key_button = 0U;
uint32_t key_control = 0U;
uint32_t captured_hop = 0U;
uint8_t raw_from_file[FIAT_V1_WIRE_BYTES] = {0};
bool have_raw = false;
flipper_format_rewind(flipper_format);
if(flipper_format_read_hex(
flipper_format, FIAT_V1_RAW_FIELD, raw_from_file, sizeof(raw_from_file)) &&
fiat_v1_frame_valid(raw_from_file)) {
have_raw = true;
key_serial = fiat_v1_uid(raw_from_file);
key_button = raw_from_file[6] >> 4U;
key_control = fiat_v1_counter(raw_from_file);
captured_hop = fiat_v1_hop(raw_from_file);
instance->tail_bits = raw_from_file[11] & 0x03U;
} else {
SubGhzBlockGeneric generic = {0};
flipper_format_rewind(flipper_format);
if(subghz_block_generic_deserialize_check_count_bit(
&generic, flipper_format, subghz_protocol_fiat_v1_const.min_count_bit_for_found) ==
SubGhzProtocolStatusOk) {
key_serial = (uint32_t)(generic.data >> 32U);
key_button = generic.btn;
key_control = generic.cnt;
captured_hop = (uint32_t)generic.data;
}
flipper_format_rewind(flipper_format);
uint32_t hop_field = 0U;
if(flipper_format_read_uint32(flipper_format, FIAT_V1_HOP_FIELD, &hop_field, 1U)) {
captured_hop = hop_field;
}
}
uint32_t serial = key_serial;
uint32_t button = key_button;
uint32_t control = key_control;
uint32_t type = FIAT_V1_DEFAULT_TE_VARIANT;
pp_encoder_read_fields(flipper_format, &serial, &button, &control, &type);
if(!have_raw) {
key_serial = serial;
key_control = control;
if(fiat_v1_button_valid((uint8_t)key_button) == false) {
key_button = button;
}
}
if(serial == 0U || serial == UINT32_MAX || !fiat_v1_button_valid((uint8_t)button)) {
return SubGhzProtocolStatusErrorParserOthers;
}
flipper_format_rewind(flipper_format);
uint32_t tail_bits = instance->tail_bits;
if(flipper_format_read_uint32(flipper_format, FIAT_V1_TAIL_BITS_FIELD, &tail_bits, 1U)) {
instance->tail_bits = (uint8_t)(tail_bits & 0x03U);
}
instance->te_variant = (type == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_TE_VARIANT_A :
FIAT_V1_TE_VARIANT_B;
if(!fiat_v1_resolve_hitag2_key(
flipper_format,
key_serial ? key_serial : serial,
(uint8_t)(key_button ? key_button : button),
(uint16_t)(key_control & 0x03FFU),
captured_hop,
instance->hitag2_key,
&instance->epoch)) {
return SubGhzProtocolStatusErrorParserOthers;
}
control &= 0x03FFU;
button &= 0x0FU;
instance->generic.serial = serial;
instance->generic.btn = (uint8_t)button;
instance->generic.cnt = control;
instance->hop = fiat_v1_bcm_generate_authenticator(
serial, (uint8_t)button, (uint16_t)control, instance->hitag2_key, instance->epoch);
instance->generic.data = ((uint64_t)serial << 32U) | instance->hop;
instance->generic.data_count_bit = FIAT_V1_LOGICAL_BITS;
fiat_v1_build_raw(
instance->raw_data,
serial,
(uint8_t)button,
(uint16_t)control,
instance->hop,
instance->tail_bits);
instance->frame_xor = instance->raw_data[12];
instance->encoder.repeat = pp_encoder_read_repeat(flipper_format, FIAT_V1_ENC_DEFAULT_REPEAT);
if(instance->encoder.repeat == 0U) {
instance->encoder.repeat = FIAT_V1_ENC_DEFAULT_REPEAT;
}
pp_encoder_buffer_ensure(instance, FIAT_V1_UPLOAD_CAPACITY);
if(!fiat_v1_encoder_build_upload(instance)) {
return SubGhzProtocolStatusErrorParserOthers;
}
instance->encoder.is_running = true;
FURI_LOG_I(
TAG,
"TX UID:%08lX Btn:%02lX Cnt:%03lX Auth:%08lX Epoch:%05lX TE:%u XOR:%02X",
(unsigned long)serial,
(unsigned long)button,
(unsigned long)control,
(unsigned long)instance->hop,
(unsigned long)instance->epoch,
instance->te_variant,
instance->frame_xor);
return SubGhzProtocolStatusOk;
}
#endif
void* subghz_protocol_decoder_fiat_v1_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderFiatV1* instance = calloc(1, sizeof(SubGhzProtocolDecoderFiatV1));
furi_check(instance);
instance->base.protocol = &fiat_v1_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
subghz_protocol_decoder_fiat_v1_reset(instance);
return instance;
}
void subghz_protocol_decoder_fiat_v1_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderFiatV1* instance = context;
memset(instance->raw_data, 0, sizeof(instance->raw_data));
memset(instance->last_raw_data, 0, sizeof(instance->last_raw_data));
instance->decoder.parser_step = FiatV1DecoderStepReset;
instance->decoder.decode_data = 0U;
instance->decoder.decode_count_bit = 0U;
instance->last_raw_valid = false;
instance->generic.data = 0U;
instance->generic.data_count_bit = 0U;
instance->generic.serial = 0U;
instance->generic.btn = 0U;
instance->generic.cnt = 0U;
instance->uid = 0U;
instance->hop = 0U;
instance->family = 0U;
instance->tail_bits = FIAT_V1_DEFAULT_TAIL_BITS;
instance->frame_xor = 0U;
instance->hitag2_key_valid = false;
instance->hitag2_epoch = 0U;
instance->te_variant = FIAT_V1_DEFAULT_TE_VARIANT;
memset(instance->hitag2_key, 0, sizeof(instance->hitag2_key));
fiat_v1_clear_all_cells(instance);
}
void subghz_protocol_decoder_fiat_v1_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderFiatV1* instance = context;
switch(instance->decoder.parser_step) {
case FiatV1DecoderStepReset:
if(fiat_v1_duration_is_pulse(duration)) {
fiat_v1_clear_all_cells(instance);
instance->decoder.parser_step = FiatV1DecoderStepData;
(void)fiat_v1_feed_data_pulse(instance, level, duration);
}
break;
case FiatV1DecoderStepData:
if(!fiat_v1_feed_data_pulse(instance, level, duration)) {
instance->decoder.parser_step = FiatV1DecoderStepReset;
}
break;
}
}
uint8_t subghz_protocol_decoder_fiat_v1_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderFiatV1* instance = context;
SubGhzBlockDecoder decoder = {
.decode_data = instance->generic.data,
.decode_count_bit = 64U,
};
return subghz_protocol_blocks_get_hash_data(&decoder, 8U) ^
(uint8_t)(instance->generic.cnt >> 8U) ^ (uint8_t)instance->generic.cnt ^
instance->generic.btn ^ instance->frame_xor;
}
SubGhzProtocolStatus subghz_protocol_decoder_fiat_v1_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderFiatV1* instance = context;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(ret != SubGhzProtocolStatusOk) {
return ret;
}
flipper_format_rewind(flipper_format);
flipper_format_delete_key(flipper_format, FIAT_V1_RAW_FIELD);
pp_flipper_update_or_insert_u32(flipper_format, FF_SERIAL, instance->generic.serial);
pp_flipper_update_or_insert_u32(flipper_format, FF_BTN, instance->generic.btn);
pp_flipper_update_or_insert_u32(flipper_format, FF_CNT, instance->generic.cnt);
pp_flipper_update_or_insert_u32(flipper_format, FF_TYPE, instance->te_variant);
pp_flipper_update_or_insert_u32(flipper_format, FIAT_V1_HOP_FIELD, instance->hop);
pp_flipper_update_or_insert_u32(flipper_format, FIAT_V1_XOR_FIELD, instance->frame_xor);
pp_flipper_update_or_insert_u32(flipper_format, FIAT_V1_TAIL_BITS_FIELD, instance->tail_bits);
if(!flipper_format_insert_or_update_hex(
flipper_format, FIAT_V1_RAW_FIELD, instance->raw_data, FIAT_V1_WIRE_BYTES)) {
return SubGhzProtocolStatusErrorParserOthers;
}
if(instance->hitag2_key_valid) {
uint32_t epoch = instance->hitag2_epoch & 0x3FFFFUL;
if(!flipper_format_insert_or_update_hex(
flipper_format, FIAT_V1_HITAG2_KEY_FIELD, instance->hitag2_key, 6U)) {
return SubGhzProtocolStatusErrorParserOthers;
}
pp_flipper_update_or_insert_u32(flipper_format, FIAT_V1_HITAG2_EPOCH_FIELD, epoch);
}
return pp_write_display(
flipper_format,
instance->generic.protocol_name,
fiat_v1_button_name(instance->generic.btn));
}
static void
fiat_v1_load_hitag2_key(SubGhzProtocolDecoderFiatV1* instance, FlipperFormat* flipper_format) {
instance->hitag2_key_valid = fiat_v1_resolve_hitag2_key(
flipper_format,
instance->uid,
instance->generic.btn,
(uint16_t)(instance->generic.cnt & 0x03FFU),
instance->hop,
instance->hitag2_key,
&instance->hitag2_epoch);
}
SubGhzProtocolStatus
subghz_protocol_decoder_fiat_v1_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderFiatV1* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, subghz_protocol_fiat_v1_const.min_count_bit_for_found);
if(ret != SubGhzProtocolStatusOk) {
return ret;
}
if(instance->generic.data_count_bit != FIAT_V1_LOGICAL_BITS) {
return SubGhzProtocolStatusErrorValueBitCount;
}
flipper_format_rewind(flipper_format);
uint32_t value = 0U;
instance->te_variant = FIAT_V1_DEFAULT_TE_VARIANT;
if(flipper_format_read_uint32(flipper_format, FF_TYPE, &value, 1)) {
instance->te_variant = (value == FIAT_V1_TE_VARIANT_A) ? FIAT_V1_TE_VARIANT_A :
FIAT_V1_TE_VARIANT_B;
}
flipper_format_rewind(flipper_format);
if(flipper_format_read_hex(
flipper_format, FIAT_V1_RAW_FIELD, instance->raw_data, FIAT_V1_WIRE_BYTES)) {
if(!fiat_v1_frame_valid(instance->raw_data)) {
return SubGhzProtocolStatusErrorParserOthers;
}
fiat_v1_decode_fields(instance);
fiat_v1_load_hitag2_key(instance, flipper_format);
return SubGhzProtocolStatusOk;
}
instance->generic.serial = (uint32_t)(instance->generic.data >> 32U);
instance->hop = (uint32_t)instance->generic.data;
instance->family = 0x01U;
instance->uid = instance->generic.serial;
instance->tail_bits = FIAT_V1_DEFAULT_TAIL_BITS;
if(flipper_format_read_uint32(flipper_format, FF_SERIAL, &value, 1)) {
instance->generic.serial = value;
}
if(flipper_format_read_uint32(flipper_format, FF_BTN, &value, 1)) {
instance->generic.btn = (uint8_t)value;
}
if(flipper_format_read_uint32(flipper_format, FF_CNT, &value, 1)) {
instance->generic.cnt = value;
}
if(flipper_format_read_uint32(flipper_format, FIAT_V1_HOP_FIELD, &value, 1)) {
instance->hop = value;
}
if(flipper_format_read_uint32(flipper_format, FIAT_V1_XOR_FIELD, &value, 1)) {
instance->frame_xor = (uint8_t)value;
}
if(flipper_format_read_uint32(flipper_format, FIAT_V1_TAIL_BITS_FIELD, &value, 1)) {
instance->tail_bits = (uint8_t)(value & 0x03U);
}
fiat_v1_rebuild_raw(instance);
if(!fiat_v1_frame_valid(instance->raw_data)) {
return SubGhzProtocolStatusErrorParserOthers;
}
fiat_v1_decode_fields(instance);
fiat_v1_load_hitag2_key(instance, flipper_format);
return SubGhzProtocolStatusOk;
}
void subghz_protocol_decoder_fiat_v1_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderFiatV1* instance = context;
furi_string_cat_printf(
output,
"%s %ubit %s\r\n"
"%08lX %03lX%01X %08lX\r\n"
"Sync:%02X UID:%08lX Auth:%08lX\r\n"
"Btn:%02X [%s] Ctrl:%03lX\r\n"
"TE:%u Tail:%u XOR:%02X\r\n",
instance->generic.protocol_name,
FIAT_V1_LOGICAL_BITS,
instance->hitag2_key_valid ? "KEY:OK" : "KEY:??",
(unsigned long)instance->generic.serial,
(unsigned long)instance->generic.cnt,
instance->generic.btn,
(unsigned long)instance->hop,
instance->family,
(unsigned long)instance->uid,
(unsigned long)instance->hop,
instance->generic.btn,
fiat_v1_button_name(instance->generic.btn),
(unsigned long)instance->generic.cnt,
instance->te_variant,
instance->tail_bits,
instance->frame_xor);
}