mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-10-22 20:11:18 +00:00
Kia V4 Decode working now
This commit is contained in:
1 parent
9722ea7f95
commit
80591cf1a7
3 files changed
+106
-110
No files matched your search
@@ -1,7 +1,7 @@
|
||||
# ProtoPirate
|
||||
## for Flipper Zero
|
||||
|
||||
Work in progress by The Pirates' Plunder crew... currently only decoding Kia V0, V1, V2, and V5.
|
||||
Work in progress by The Pirates' Plunder crew... currently only decoding Kia V0, V1, V2, V4, and V5.
|
||||
but will eventually be a drop-in app (fap) for decoding, storing, and replicating all automotive key fobs and any other protocols which utilize rolling code.
|
||||
If you contribute significantly you will be added to the app credits.
|
||||
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+105
-109
@@ -16,7 +16,10 @@ struct SubGhzProtocolDecoderKiaV4 {
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint16_t header_count;
|
||||
uint8_t short_pulse_count;
|
||||
|
||||
uint8_t raw_bits[32];
|
||||
uint16_t raw_bit_count;
|
||||
|
||||
uint32_t encrypted;
|
||||
uint32_t decrypted;
|
||||
};
|
||||
@@ -30,8 +33,7 @@ struct SubGhzProtocolEncoderKiaV4 {
|
||||
typedef enum {
|
||||
KiaV4DecoderStepReset = 0,
|
||||
KiaV4DecoderStepCheckPreamble,
|
||||
KiaV4DecoderStepSaveDuration,
|
||||
KiaV4DecoderStepCheckDuration,
|
||||
KiaV4DecoderStepCollectRawBits,
|
||||
} KiaV4DecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder kia_protocol_v4_decoder = {
|
||||
@@ -61,66 +63,87 @@ const SubGhzProtocol kia_protocol_v4 = {
|
||||
.encoder = &kia_protocol_v4_encoder,
|
||||
};
|
||||
|
||||
static const uint32_t keeloq_nlf = 0x3A5C742E;
|
||||
|
||||
static uint32_t keeloq_decrypt(uint32_t data, uint64_t key) {
|
||||
// KeeLoq decrypt
|
||||
static uint32_t keeloq_common_decrypt(uint32_t data, uint64_t key) {
|
||||
uint32_t block = data;
|
||||
uint64_t tkey = key;
|
||||
for(int i = 0; i < 528; i++) {
|
||||
uint8_t nlf_idx = ((data >> 1) & 1) | ((data >> 8) & 2) | ((data >> 18) & 4) |
|
||||
((data >> 21) & 8) | ((data >> 27) & 16);
|
||||
uint8_t nlf_bit = (keeloq_nlf >> nlf_idx) & 1;
|
||||
|
||||
uint8_t key_bit = (key >> (15 - (i % 64))) & 1;
|
||||
uint8_t msb = (data >> 31) & 1;
|
||||
uint8_t bit0 = (data >> 0) & 1;
|
||||
uint8_t bit16 = (data >> 16) & 1;
|
||||
|
||||
uint8_t new_bit = nlf_bit ^ msb ^ bit0 ^ bit16 ^ key_bit;
|
||||
|
||||
data = (data >> 1) | (new_bit << 31);
|
||||
int lutkey = ((block >> 0) & 1) | ((block >> 7) & 2) |
|
||||
((block >> 17) & 4) | ((block >> 22) & 8) | ((block >> 26) & 16);
|
||||
int lsb = ((block >> 31) ^ ((block >> 15) & 1) ^
|
||||
((0x3A5C742E >> lutkey) & 1) ^ ((tkey >> 15) & 1));
|
||||
block = ((block & 0x7FFFFFFF) << 1) | lsb;
|
||||
tkey = ((tkey & 0x7FFFFFFFFFFFFFFFULL) << 1) | (tkey >> 63);
|
||||
}
|
||||
return data;
|
||||
return block;
|
||||
}
|
||||
|
||||
static uint8_t kia_v4_reverse8(uint8_t byte) {
|
||||
static uint8_t reverse8(uint8_t byte) {
|
||||
byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;
|
||||
byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;
|
||||
byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;
|
||||
return byte;
|
||||
}
|
||||
|
||||
static void kia_v4_extract_fields(SubGhzProtocolDecoderKiaV4* instance) {
|
||||
uint64_t inverted = ~instance->decoder.decode_data;
|
||||
|
||||
instance->generic.data = inverted;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
|
||||
uint8_t b[8];
|
||||
for(int i = 0; i < 8; i++) {
|
||||
b[i] = (instance->generic.data >> (56 - i * 8)) & 0xFF;
|
||||
static void kia_v4_add_raw_bit(SubGhzProtocolDecoderKiaV4* instance, bool bit) {
|
||||
if(instance->raw_bit_count < 256) {
|
||||
uint16_t byte_idx = instance->raw_bit_count / 8;
|
||||
uint8_t bit_idx = 7 - (instance->raw_bit_count % 8);
|
||||
if(bit) {
|
||||
instance->raw_bits[byte_idx] |= (1 << bit_idx);
|
||||
} else {
|
||||
instance->raw_bits[byte_idx] &= ~(1 << bit_idx);
|
||||
}
|
||||
instance->raw_bit_count++;
|
||||
}
|
||||
}
|
||||
|
||||
static bool kia_v4_process_buffer(SubGhzProtocolDecoderKiaV4* instance) {
|
||||
if(instance->raw_bit_count < 64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
instance->generic.serial =
|
||||
((uint32_t)kia_v4_reverse8(b[7] & 0xF0) << 24) |
|
||||
((uint32_t)kia_v4_reverse8(b[6]) << 16) |
|
||||
((uint32_t)kia_v4_reverse8(b[5]) << 8) |
|
||||
(uint32_t)kia_v4_reverse8(b[4]);
|
||||
uint8_t* b = instance->raw_bits;
|
||||
|
||||
instance->generic.btn = (kia_v4_reverse8(b[7]) & 0xF0) >> 4;
|
||||
// Extract fields
|
||||
uint32_t encrypted = ((uint32_t)reverse8(b[3]) << 24) |
|
||||
((uint32_t)reverse8(b[2]) << 16) |
|
||||
((uint32_t)reverse8(b[1]) << 8) |
|
||||
(uint32_t)reverse8(b[0]);
|
||||
|
||||
instance->encrypted =
|
||||
((uint32_t)kia_v4_reverse8(b[3]) << 24) |
|
||||
((uint32_t)kia_v4_reverse8(b[2]) << 16) |
|
||||
((uint32_t)kia_v4_reverse8(b[1]) << 8) |
|
||||
(uint32_t)kia_v4_reverse8(b[0]);
|
||||
uint32_t serial = ((uint32_t)reverse8(b[7] & 0xF0) << 24) |
|
||||
((uint32_t)reverse8(b[6]) << 16) |
|
||||
((uint32_t)reverse8(b[5]) << 8) |
|
||||
(uint32_t)reverse8(b[4]);
|
||||
|
||||
instance->decrypted = keeloq_decrypt(instance->encrypted, kia_v4_mf_key);
|
||||
instance->generic.cnt = instance->decrypted & 0xFFFF;
|
||||
uint8_t btn = (reverse8(b[7]) & 0xF0) >> 4;
|
||||
|
||||
FURI_LOG_D(TAG, "Raw: %016llX Bits: %u Inv: %016llX",
|
||||
instance->decoder.decode_data, instance->decoder.decode_count_bit, inverted);
|
||||
FURI_LOG_D(TAG, "Sn: %07lX Btn: %X Enc: %08lX Dec: %08lX Cnt: %04lX",
|
||||
instance->generic.serial, instance->generic.btn, instance->encrypted,
|
||||
instance->decrypted, instance->generic.cnt);
|
||||
// Decrypt with master key
|
||||
uint32_t decrypted = keeloq_common_decrypt(encrypted, kia_v4_mf_key);
|
||||
uint8_t dec_btn = (decrypted >> 28) & 0x0F;
|
||||
uint8_t dec_serial_lsb = (decrypted >> 16) & 0xFF;
|
||||
uint8_t our_serial_lsb = serial & 0xFF;
|
||||
|
||||
// Validate
|
||||
if(dec_btn != btn || dec_serial_lsb != our_serial_lsb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid decode
|
||||
instance->encrypted = encrypted;
|
||||
instance->decrypted = decrypted;
|
||||
instance->generic.serial = serial;
|
||||
instance->generic.btn = btn;
|
||||
instance->generic.cnt = decrypted & 0xFFFF;
|
||||
|
||||
uint64_t key_data = ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) |
|
||||
((uint64_t)b[2] << 40) | ((uint64_t)b[3] << 32) |
|
||||
((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
|
||||
((uint64_t)b[6] << 8) | (uint64_t)b[7];
|
||||
instance->generic.data = key_data;
|
||||
instance->generic.data_count_bit = 64;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* kia_protocol_decoder_v4_alloc(SubGhzEnvironment* environment) {
|
||||
@@ -128,9 +151,6 @@ void* kia_protocol_decoder_v4_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolDecoderKiaV4* instance = malloc(sizeof(SubGhzProtocolDecoderKiaV4));
|
||||
instance->base.protocol = &kia_protocol_v4;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->encrypted = 0;
|
||||
instance->decrypted = 0;
|
||||
instance->short_pulse_count = 0;
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -144,12 +164,9 @@ void kia_protocol_decoder_v4_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKiaV4* instance = context;
|
||||
instance->decoder.parser_step = KiaV4DecoderStepReset;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->header_count = 0;
|
||||
instance->short_pulse_count = 0;
|
||||
instance->encrypted = 0;
|
||||
instance->decrypted = 0;
|
||||
instance->raw_bit_count = 0;
|
||||
memset(instance->raw_bits, 0, sizeof(instance->raw_bits));
|
||||
}
|
||||
|
||||
void kia_protocol_decoder_v4_feed(void* context, bool level, uint32_t duration) {
|
||||
@@ -158,75 +175,65 @@ void kia_protocol_decoder_v4_feed(void* context, bool level, uint32_t duration)
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case KiaV4DecoderStepReset:
|
||||
if(level) {
|
||||
if(DURATION_DIFF(duration, kia_protocol_v4_const.te_long) < kia_protocol_v4_const.te_delta) {
|
||||
instance->decoder.parser_step = KiaV4DecoderStepCheckPreamble;
|
||||
instance->header_count = 1;
|
||||
instance->short_pulse_count = 0;
|
||||
}
|
||||
if(level && DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) {
|
||||
instance->decoder.parser_step = KiaV4DecoderStepCheckPreamble;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case KiaV4DecoderStepCheckPreamble:
|
||||
if(level) {
|
||||
if(DURATION_DIFF(duration, kia_protocol_v4_const.te_long) < kia_protocol_v4_const.te_delta) {
|
||||
instance->header_count++;
|
||||
instance->short_pulse_count = 0;
|
||||
} else if(DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) {
|
||||
instance->short_pulse_count++;
|
||||
if(DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else if(duration > 1000 && duration < 1500) {
|
||||
if(instance->header_count >= 6 && instance->short_pulse_count >= 2) {
|
||||
FURI_LOG_D(TAG, "Sync: %lu (hdr=%u, short=%u)",
|
||||
duration, instance->header_count, instance->short_pulse_count);
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->decoder.parser_step = KiaV4DecoderStepSaveDuration;
|
||||
// Sync pulse
|
||||
if(instance->header_count >= 8) {
|
||||
instance->decoder.parser_step = KiaV4DecoderStepCollectRawBits;
|
||||
instance->raw_bit_count = 0;
|
||||
memset(instance->raw_bits, 0, sizeof(instance->raw_bits));
|
||||
} else {
|
||||
instance->decoder.parser_step = KiaV4DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = KiaV4DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if(DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta &&
|
||||
DURATION_DIFF(instance->decoder.te_last, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) {
|
||||
instance->header_count++;
|
||||
} else if(duration > 1500) {
|
||||
instance->decoder.parser_step = KiaV4DecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KiaV4DecoderStepSaveDuration:
|
||||
case KiaV4DecoderStepCollectRawBits:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = KiaV4DecoderStepCheckDuration;
|
||||
} else {
|
||||
if(duration > 1500) {
|
||||
if(instance->decoder.decode_count_bit >= kia_protocol_v4_const.min_count_bit_for_found) {
|
||||
kia_v4_extract_fields(instance);
|
||||
// HIGH pulse - PWM data bit (short=0, long=1)
|
||||
if(duration > 1000 && duration < 1500) {
|
||||
// Next sync pulse - end this packet
|
||||
if(kia_v4_process_buffer(instance)) {
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
instance->decoder.parser_step = KiaV4DecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KiaV4DecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if(DURATION_DIFF(instance->decoder.te_last, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) {
|
||||
instance->decoder.decode_data <<= 1;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = KiaV4DecoderStepSaveDuration;
|
||||
} else if(DURATION_DIFF(instance->decoder.te_last, kia_protocol_v4_const.te_long) < kia_protocol_v4_const.te_delta) {
|
||||
instance->decoder.decode_data <<= 1;
|
||||
instance->decoder.decode_data |= 1;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = KiaV4DecoderStepSaveDuration;
|
||||
} else if(DURATION_DIFF(duration, kia_protocol_v4_const.te_short) < kia_protocol_v4_const.te_delta) {
|
||||
kia_v4_add_raw_bit(instance, false);
|
||||
} else if(DURATION_DIFF(duration, kia_protocol_v4_const.te_long) < kia_protocol_v4_const.te_delta) {
|
||||
kia_v4_add_raw_bit(instance, true);
|
||||
} else {
|
||||
if(instance->decoder.decode_count_bit >= kia_protocol_v4_const.min_count_bit_for_found) {
|
||||
kia_v4_extract_fields(instance);
|
||||
instance->decoder.parser_step = KiaV4DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
// LOW pulse - check for end gap
|
||||
if(duration > 1500) {
|
||||
if(kia_v4_process_buffer(instance)) {
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
instance->decoder.parser_step = KiaV4DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = KiaV4DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -259,26 +266,15 @@ void kia_protocol_decoder_v4_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKiaV4* instance = context;
|
||||
|
||||
uint8_t b[8];
|
||||
for(int i = 0; i < 8; i++) {
|
||||
b[i] = (instance->generic.data >> (56 - i * 8)) & 0xFF;
|
||||
}
|
||||
uint64_t yek = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
yek |= ((uint64_t)kia_v4_reverse8(b[7-i]) << (56 - i * 8));
|
||||
}
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%016llX\r\n"
|
||||
"Yek:%016llX\r\n"
|
||||
"Sn:%07lX Btn:%X Cnt:%04lX\r\n"
|
||||
"Enc:%08lX Dec:%08lX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
instance->generic.data,
|
||||
yek,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->generic.cnt,
|
||||
|
||||
Reference in new issue
Block a user