diff --git a/README.md b/README.md index 744f6ef..f645ea6 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/dist/proto_pirate.fap b/dist/proto_pirate.fap index 6b6f78b..0320a13 100644 Binary files a/dist/proto_pirate.fap and b/dist/proto_pirate.fap differ diff --git a/protocols/kia_v4.c b/protocols/kia_v4.c index 490ca70..c561ea8 100644 --- a/protocols/kia_v4.c +++ b/protocols/kia_v4.c @@ -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,