mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-05-25 05:55:49 +00:00
379 lines
13 KiB
C
379 lines
13 KiB
C
#include "kia_v5.h"
|
|
#include "../protopirate_app_i.h"
|
|
#include "keys.h"
|
|
|
|
#define TAG "KiaV5"
|
|
|
|
static const SubGhzBlockConst kia_protocol_v5_const = {
|
|
.te_short = 400,
|
|
.te_long = 800,
|
|
.te_delta = 150,
|
|
.min_count_bit_for_found = 64,
|
|
};
|
|
|
|
static void build_keystore_from_mfkey(uint8_t* result) {
|
|
uint64_t ky = get_kia_v5_key();
|
|
for(int i = 0; i < 8; i++) {
|
|
result[i] = (ky >> ((7 - i) * 8)) & 0xFF;
|
|
}
|
|
}
|
|
|
|
static uint8_t keystore_bytes[8] = {0};
|
|
|
|
static uint16_t mixer_decode(uint32_t encrypted) {
|
|
uint8_t s0 = (encrypted & 0xFF);
|
|
uint8_t s1 = (encrypted >> 8) & 0xFF;
|
|
uint8_t s2 = (encrypted >> 16) & 0xFF;
|
|
uint8_t s3 = (encrypted >> 24) & 0xFF;
|
|
|
|
// Prepare key
|
|
build_keystore_from_mfkey(keystore_bytes);
|
|
|
|
int round_index = 1;
|
|
for(size_t i = 0; i < 18; i++) {
|
|
uint8_t r = keystore_bytes[round_index] & 0xFF;
|
|
int steps = 8;
|
|
while(steps > 0) {
|
|
uint8_t base;
|
|
if((s3 & 0x40) == 0) {
|
|
base = (s3 & 0x02) == 0 ? 0x74 : 0x2E;
|
|
} else {
|
|
base = (s3 & 0x02) == 0 ? 0x3A : 0x5C;
|
|
}
|
|
|
|
if(s2 & 0x08) {
|
|
base = (((base >> 4) & 0x0F) | ((base & 0x0F) << 4)) & 0xFF;
|
|
}
|
|
if(s1 & 0x01) {
|
|
base = ((base & 0x3F) << 2) & 0xFF;
|
|
}
|
|
if(s0 & 0x01) {
|
|
base = (base << 1) & 0xFF;
|
|
}
|
|
|
|
uint8_t temp = (s3 ^ s1) & 0xFF;
|
|
s3 = ((s3 & 0x7F) << 1) & 0xFF;
|
|
if(s2 & 0x80) {
|
|
s3 |= 0x01;
|
|
}
|
|
s2 = ((s2 & 0x7F) << 1) & 0xFF;
|
|
if(s1 & 0x80) {
|
|
s2 |= 0x01;
|
|
}
|
|
s1 = ((s1 & 0x7F) << 1) & 0xFF;
|
|
if(s0 & 0x80) {
|
|
s1 |= 0x01;
|
|
}
|
|
s0 = ((s0 & 0x7F) << 1) & 0xFF;
|
|
|
|
uint8_t chk = (base ^ (r ^ temp)) & 0xFF;
|
|
if(chk & 0x80) {
|
|
s0 |= 0x01;
|
|
}
|
|
r = ((r & 0x7F) << 1) & 0xFF;
|
|
steps--;
|
|
}
|
|
round_index = (round_index - 1) & 0x7;
|
|
}
|
|
return (s0 + (s1 << 8)) & 0xFFFF;
|
|
}
|
|
|
|
struct SubGhzProtocolDecoderKiaV5 {
|
|
SubGhzProtocolDecoderBase base;
|
|
SubGhzBlockDecoder decoder;
|
|
SubGhzBlockGeneric generic;
|
|
uint16_t header_count;
|
|
|
|
ManchesterState manchester_state;
|
|
uint64_t decoded_data;
|
|
uint64_t saved_key;
|
|
uint8_t bit_count;
|
|
uint64_t yek;
|
|
uint8_t crc;
|
|
};
|
|
|
|
struct SubGhzProtocolEncoderKiaV5 {
|
|
SubGhzProtocolEncoderBase base;
|
|
SubGhzProtocolBlockEncoder encoder;
|
|
SubGhzBlockGeneric generic;
|
|
};
|
|
|
|
typedef enum {
|
|
KiaV5DecoderStepReset = 0,
|
|
KiaV5DecoderStepCheckPreamble,
|
|
KiaV5DecoderStepData,
|
|
} KiaV5DecoderStep;
|
|
|
|
const SubGhzProtocolDecoder kia_protocol_v5_decoder = {
|
|
.alloc = kia_protocol_decoder_v5_alloc,
|
|
.free = kia_protocol_decoder_v5_free,
|
|
.feed = kia_protocol_decoder_v5_feed,
|
|
.reset = kia_protocol_decoder_v5_reset,
|
|
.get_hash_data = kia_protocol_decoder_v5_get_hash_data,
|
|
.serialize = kia_protocol_decoder_v5_serialize,
|
|
.deserialize = kia_protocol_decoder_v5_deserialize,
|
|
.get_string = kia_protocol_decoder_v5_get_string,
|
|
};
|
|
|
|
const SubGhzProtocolEncoder kia_protocol_v5_encoder = {
|
|
.alloc = NULL,
|
|
.free = NULL,
|
|
.deserialize = NULL,
|
|
.stop = NULL,
|
|
.yield = NULL,
|
|
};
|
|
|
|
const SubGhzProtocol kia_protocol_v5 = {
|
|
.name = KIA_PROTOCOL_V5_NAME,
|
|
.type = SubGhzProtocolTypeDynamic,
|
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,
|
|
.decoder = &kia_protocol_v5_decoder,
|
|
.encoder = &kia_protocol_v5_encoder,
|
|
};
|
|
|
|
static void kia_v5_add_bit(SubGhzProtocolDecoderKiaV5* instance, bool bit) {
|
|
instance->decoded_data = (instance->decoded_data << 1) | (bit ? 1 : 0);
|
|
instance->bit_count++;
|
|
}
|
|
|
|
void* kia_protocol_decoder_v5_alloc(SubGhzEnvironment* environment) {
|
|
UNUSED(environment);
|
|
SubGhzProtocolDecoderKiaV5* instance = malloc(sizeof(SubGhzProtocolDecoderKiaV5));
|
|
instance->base.protocol = &kia_protocol_v5;
|
|
instance->generic.protocol_name = instance->base.protocol->name;
|
|
return instance;
|
|
}
|
|
|
|
void kia_protocol_decoder_v5_free(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderKiaV5* instance = context;
|
|
free(instance);
|
|
}
|
|
|
|
void kia_protocol_decoder_v5_reset(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderKiaV5* instance = context;
|
|
instance->decoder.parser_step = KiaV5DecoderStepReset;
|
|
instance->header_count = 0;
|
|
instance->bit_count = 0;
|
|
instance->decoded_data = 0;
|
|
instance->saved_key = 0;
|
|
instance->yek = 0;
|
|
instance->crc = 0;
|
|
instance->manchester_state = ManchesterStateMid1;
|
|
}
|
|
|
|
void kia_protocol_decoder_v5_feed(void* context, bool level, uint32_t duration) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderKiaV5* instance = context;
|
|
|
|
switch(instance->decoder.parser_step) {
|
|
case KiaV5DecoderStepReset:
|
|
if((level) && (DURATION_DIFF(duration, kia_protocol_v5_const.te_short) <
|
|
kia_protocol_v5_const.te_delta)) {
|
|
instance->decoder.parser_step = KiaV5DecoderStepCheckPreamble;
|
|
instance->decoder.te_last = duration;
|
|
instance->header_count = 1;
|
|
instance->bit_count = 0;
|
|
instance->decoded_data = 0;
|
|
manchester_advance(
|
|
instance->manchester_state,
|
|
ManchesterEventReset,
|
|
&instance->manchester_state,
|
|
NULL);
|
|
}
|
|
break;
|
|
|
|
case KiaV5DecoderStepCheckPreamble:
|
|
if(level) {
|
|
if(DURATION_DIFF(duration, kia_protocol_v5_const.te_long) <
|
|
kia_protocol_v5_const.te_delta) {
|
|
if(instance->header_count > 40) {
|
|
instance->decoder.parser_step = KiaV5DecoderStepData;
|
|
instance->bit_count = 0;
|
|
instance->decoded_data = 0;
|
|
instance->saved_key = 0;
|
|
instance->header_count = 0;
|
|
} else {
|
|
instance->decoder.te_last = duration;
|
|
}
|
|
} else if(
|
|
DURATION_DIFF(duration, kia_protocol_v5_const.te_short) <
|
|
kia_protocol_v5_const.te_delta) {
|
|
instance->decoder.te_last = duration;
|
|
} else {
|
|
instance->decoder.parser_step = KiaV5DecoderStepReset;
|
|
}
|
|
} else {
|
|
if((DURATION_DIFF(duration, kia_protocol_v5_const.te_short) <
|
|
kia_protocol_v5_const.te_delta) &&
|
|
(DURATION_DIFF(instance->decoder.te_last, kia_protocol_v5_const.te_short) <
|
|
kia_protocol_v5_const.te_delta)) {
|
|
instance->header_count++;
|
|
} else if(
|
|
(DURATION_DIFF(duration, kia_protocol_v5_const.te_long) <
|
|
kia_protocol_v5_const.te_delta) &&
|
|
(DURATION_DIFF(instance->decoder.te_last, kia_protocol_v5_const.te_short) <
|
|
kia_protocol_v5_const.te_delta)) {
|
|
instance->header_count++;
|
|
} else if(
|
|
DURATION_DIFF(instance->decoder.te_last, kia_protocol_v5_const.te_long) <
|
|
kia_protocol_v5_const.te_delta) {
|
|
instance->header_count++;
|
|
} else {
|
|
instance->decoder.parser_step = KiaV5DecoderStepReset;
|
|
}
|
|
instance->decoder.te_last = duration;
|
|
}
|
|
break;
|
|
|
|
case KiaV5DecoderStepData: {
|
|
ManchesterEvent event;
|
|
|
|
if(DURATION_DIFF(duration, kia_protocol_v5_const.te_short) <
|
|
kia_protocol_v5_const.te_delta) {
|
|
event = level ? ManchesterEventShortHigh : ManchesterEventShortLow;
|
|
} else if(
|
|
DURATION_DIFF(duration, kia_protocol_v5_const.te_long) <
|
|
kia_protocol_v5_const.te_delta) {
|
|
event = level ? ManchesterEventLongHigh : ManchesterEventLongLow;
|
|
} else {
|
|
if(instance->bit_count >= kia_protocol_v5_const.min_count_bit_for_found) {
|
|
instance->generic.data = instance->saved_key;
|
|
instance->generic.data_count_bit =
|
|
(instance->bit_count > 67) ? 67 : instance->bit_count;
|
|
|
|
instance->crc = (uint8_t)(instance->decoded_data & 0x07);
|
|
|
|
instance->yek = 0;
|
|
for(int i = 0; i < 8; i++) {
|
|
uint8_t byte = (instance->generic.data >> (i * 8)) & 0xFF;
|
|
uint8_t reversed = 0;
|
|
for(int b = 0; b < 8; b++) {
|
|
if(byte & (1 << b)) reversed |= (1 << (7 - b));
|
|
}
|
|
instance->yek |= ((uint64_t)reversed << ((7 - i) * 8));
|
|
}
|
|
|
|
instance->generic.serial = (uint32_t)((instance->yek >> 32) & 0x0FFFFFFF);
|
|
instance->generic.btn = (uint8_t)((instance->yek >> 60) & 0x0F);
|
|
|
|
uint32_t encrypted = (uint32_t)(instance->yek & 0xFFFFFFFF);
|
|
instance->generic.cnt = mixer_decode(encrypted);
|
|
|
|
FURI_LOG_I(
|
|
TAG,
|
|
"Key=%08lX%08lX Sn=%07lX Btn=%X Cnt=%04lX CRC=%X",
|
|
(uint32_t)(instance->generic.data >> 32),
|
|
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
|
|
instance->generic.serial,
|
|
instance->generic.btn,
|
|
instance->generic.cnt,
|
|
instance->crc);
|
|
|
|
if(instance->base.callback)
|
|
instance->base.callback(&instance->base, instance->base.context);
|
|
}
|
|
|
|
instance->decoder.parser_step = KiaV5DecoderStepReset;
|
|
break;
|
|
}
|
|
|
|
bool data_bit;
|
|
if(instance->bit_count <= 66 &&
|
|
manchester_advance(
|
|
instance->manchester_state, event, &instance->manchester_state, &data_bit)) {
|
|
kia_v5_add_bit(instance, data_bit);
|
|
|
|
if(instance->bit_count == 64) {
|
|
instance->saved_key = instance->decoded_data;
|
|
instance->decoded_data = 0;
|
|
}
|
|
}
|
|
|
|
instance->decoder.te_last = duration;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t kia_protocol_decoder_v5_get_hash_data(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderKiaV5* instance = context;
|
|
return subghz_protocol_blocks_get_hash_data(
|
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
|
}
|
|
|
|
SubGhzProtocolStatus kia_protocol_decoder_v5_serialize(
|
|
void* context,
|
|
FlipperFormat* flipper_format,
|
|
SubGhzRadioPreset* preset) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderKiaV5* instance = context;
|
|
|
|
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
|
|
|
ret = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
|
|
|
if(ret == SubGhzProtocolStatusOk) {
|
|
// Save decoded fields
|
|
flipper_format_write_uint32(flipper_format, "Serial", &instance->generic.serial, 1);
|
|
|
|
uint32_t temp = instance->generic.btn;
|
|
flipper_format_write_uint32(flipper_format, "Btn", &temp, 1);
|
|
|
|
flipper_format_write_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
|
|
|
|
uint32_t crc_temp = instance->crc;
|
|
flipper_format_write_uint32(flipper_format, "CRC", &crc_temp, 1);
|
|
|
|
// Save raw bit data for exact reproduction (since V5 has complex bit reversal)
|
|
uint32_t raw_high = (uint32_t)(instance->generic.data >> 32);
|
|
uint32_t raw_low = (uint32_t)(instance->generic.data & 0xFFFFFFFF);
|
|
flipper_format_write_uint32(flipper_format, "DataHi", &raw_high, 1);
|
|
flipper_format_write_uint32(flipper_format, "DataLo", &raw_low, 1);
|
|
uint32_t yek_high = (uint32_t)(instance->yek >> 32);
|
|
uint32_t yek_low = (uint32_t)(instance->yek & 0xFFFFFFFF);
|
|
flipper_format_write_uint32(flipper_format, "YekHi", &yek_high, 1);
|
|
flipper_format_write_uint32(flipper_format, "YekLo", &yek_low, 1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
SubGhzProtocolStatus
|
|
kia_protocol_decoder_v5_deserialize(void* context, FlipperFormat* flipper_format) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderKiaV5* instance = context;
|
|
return subghz_block_generic_deserialize_check_count_bit(
|
|
&instance->generic, flipper_format, kia_protocol_v5_const.min_count_bit_for_found);
|
|
}
|
|
|
|
void kia_protocol_decoder_v5_get_string(void* context, FuriString* output) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderKiaV5* instance = context;
|
|
|
|
uint32_t code_found_hi = instance->generic.data >> 32;
|
|
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
|
uint32_t yek_hi = (uint32_t)(instance->yek >> 32);
|
|
uint32_t yek_lo = (uint32_t)(instance->yek & 0xFFFFFFFF);
|
|
|
|
furi_string_cat_printf(
|
|
output,
|
|
"%s %dbit\r\n"
|
|
"Key:%08lX%08lX\r\n"
|
|
"Yek:%08lX%08lX\r\n"
|
|
"Sn:%07lX Btn:%X Cnt:%04lX\r\n"
|
|
"CRC:%X\r\n",
|
|
instance->generic.protocol_name,
|
|
instance->generic.data_count_bit,
|
|
code_found_hi,
|
|
code_found_lo,
|
|
yek_hi,
|
|
yek_lo,
|
|
instance->generic.serial,
|
|
instance->generic.btn,
|
|
instance->generic.cnt,
|
|
instance->crc);
|
|
}
|