diff --git a/helpers/protopirate_storage.c b/helpers/protopirate_storage.c index 3bf47ed..90fad64 100644 --- a/helpers/protopirate_storage.c +++ b/helpers/protopirate_storage.c @@ -147,6 +147,17 @@ static bool protopirate_storage_write_capture_data( if(flipper_format_read_uint32(flipper_format, "Type", &uint32_value, 1)) flipper_format_write_uint32(save_file, "Type", &uint32_value, 1); + // VAG Key2 field + flipper_format_rewind(flipper_format); + uint8_t key2_buf[8]; + if(flipper_format_read_hex(flipper_format, "Key2", key2_buf, 8)) + flipper_format_write_hex(save_file, "Key2", key2_buf, 8); + + // VAG KeyIdx field (which key was used) + flipper_format_rewind(flipper_format); + if(flipper_format_read_uint32(flipper_format, "KeyIdx", &uint32_value, 1)) + flipper_format_write_uint32(save_file, "KeyIdx", &uint32_value, 1); + flipper_format_rewind(flipper_format); if(flipper_format_read_uint32(flipper_format, "Seed", &uint32_value, 1)) flipper_format_write_uint32(save_file, "Seed", &uint32_value, 1); diff --git a/protocols/vag.c b/protocols/vag.c index bc1166c..75aa351 100644 --- a/protocols/vag.c +++ b/protocols/vag.c @@ -102,6 +102,7 @@ typedef struct SubGhzProtocolDecoderVAG { uint32_t cnt; uint8_t btn; uint8_t check_byte; + uint8_t key_idx; bool decrypted; } SubGhzProtocolDecoderVAG; @@ -125,6 +126,15 @@ static void vag_tea_decrypt(uint32_t* v0, uint32_t* v1, const uint32_t* key_sche } } +static void vag_tea_encrypt(uint32_t* v0, uint32_t* v1, const uint32_t* key_schedule) { + uint32_t sum = 0; + for(int i = 0; i < VAG_TEA_ROUNDS; i++) { + *v0 += (((*v1 << 4) ^ (*v1 >> 5)) + *v1) ^ (sum + key_schedule[sum & 3]); + sum += VAG_TEA_DELTA; + *v1 += (((*v0 << 4) ^ (*v0 >> 5)) + *v0) ^ (sum + key_schedule[(sum >> 11) & 3]); + } +} + static bool vag_dispatch_type_1_2(uint8_t dispatch) { return (dispatch == 0x2A || dispatch == 0x1C || dispatch == 0x46); } @@ -264,6 +274,7 @@ static void vag_parse_data(SubGhzProtocolDecoderVAG* instance) { ((uint32_t)block_copy[6] << 16); instance->btn = block_copy[7]; instance->check_byte = dispatch_byte; + instance->key_idx = key_idx; instance->decrypted = true; FURI_LOG_I( TAG, @@ -320,6 +331,7 @@ static void vag_parse_data(SubGhzProtocolDecoderVAG* instance) { } vag_fill_from_decrypted(instance, tea_dec, dispatch_byte); + instance->key_idx = 0xFF; FURI_LOG_I( TAG, @@ -338,6 +350,7 @@ static void vag_parse_data(SubGhzProtocolDecoderVAG* instance) { memcpy(block_copy, block, 8); if(vag_aut64_decrypt(block_copy, 2) && vag_button_valid(block_copy)) { instance->vag_type = 4; + instance->key_idx = 2; vag_fill_from_decrypted(instance, block_copy, dispatch_byte); FURI_LOG_I( TAG, @@ -350,6 +363,7 @@ static void vag_parse_data(SubGhzProtocolDecoderVAG* instance) { memcpy(block_copy, block, 8); if(vag_aut64_decrypt(block_copy, 1) && vag_button_valid(block_copy)) { + instance->key_idx = 1; vag_fill_from_decrypted(instance, block_copy, dispatch_byte); FURI_LOG_I( TAG, @@ -362,6 +376,7 @@ static void vag_parse_data(SubGhzProtocolDecoderVAG* instance) { memcpy(block_copy, block, 8); if(vag_aut64_decrypt(block_copy, 0) && vag_button_valid(block_copy)) { + instance->key_idx = 0; vag_fill_from_decrypted(instance, block_copy, dispatch_byte); FURI_LOG_I( TAG, @@ -400,6 +415,7 @@ static void vag_parse_data(SubGhzProtocolDecoderVAG* instance) { dispatch_byte); break; } + instance->key_idx = 2; vag_fill_from_decrypted(instance, block_copy, dispatch_byte); FURI_LOG_I( TAG, @@ -434,13 +450,21 @@ const SubGhzProtocolDecoder subghz_protocol_vag_decoder = { .get_string = subghz_protocol_decoder_vag_get_string, }; +const SubGhzProtocolEncoder subghz_protocol_vag_encoder = { + .alloc = subghz_protocol_encoder_vag_alloc, + .free = subghz_protocol_encoder_vag_free, + .deserialize = subghz_protocol_encoder_vag_deserialize, + .stop = subghz_protocol_encoder_vag_stop, + .yield = subghz_protocol_encoder_vag_yield, +}; + const SubGhzProtocol vag_protocol = { .name = VAG_PROTOCOL_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_vag_decoder, - .encoder = NULL, + .encoder = &subghz_protocol_vag_encoder, }; void* subghz_protocol_decoder_vag_alloc(SubGhzEnvironment* environment) { @@ -453,6 +477,7 @@ void* subghz_protocol_decoder_vag_alloc(SubGhzEnvironment* environment) { instance->cnt = 0; instance->btn = 0; instance->check_byte = 0; + instance->key_idx = 0xFF; protocol_vag_load_keys(APP_ASSETS_PATH("vag")); @@ -474,6 +499,7 @@ void subghz_protocol_decoder_vag_reset(void* context) { instance->cnt = 0; instance->btn = 0; instance->check_byte = 0; + instance->key_idx = 0xFF; } void subghz_protocol_decoder_vag_feed(void* context, bool level, uint32_t duration) { @@ -878,13 +904,41 @@ SubGhzProtocolStatus subghz_protocol_decoder_vag_serialize( furi_assert(context); SubGhzProtocolDecoderVAG* instance = context; + FURI_LOG_I(TAG, "=== VAG SERIALIZE START ==="); + FURI_LOG_I( + TAG, + "Before parse: decrypted=%d serial=%08lX cnt=%06lX btn=%02X type=%d", + instance->decrypted, + (unsigned long)instance->serial, + (unsigned long)instance->cnt, + instance->btn, + instance->vag_type); + if(!instance->decrypted && instance->data_count_bit >= 80) { + FURI_LOG_I(TAG, "Not decrypted yet, calling vag_parse_data..."); vag_parse_data(instance); } + FURI_LOG_I( + TAG, + "After parse: decrypted=%d serial=%08lX cnt=%06lX btn=%02X type=%d", + instance->decrypted, + (unsigned long)instance->serial, + (unsigned long)instance->cnt, + instance->btn, + instance->vag_type); + uint64_t key1 = ((uint64_t)instance->key1_high << 32) | instance->key1_low; uint64_t key2 = ((uint64_t)instance->key2_high << 32) | instance->key2_low; + FURI_LOG_I( + TAG, + "Keys: Key1=%08lX%08lX Key2=%08lX%08lX", + (unsigned long)instance->key1_high, + (unsigned long)instance->key1_low, + (unsigned long)instance->key2_high, + (unsigned long)instance->key2_low); + instance->generic.data = key1; instance->generic.data_count_bit = instance->data_count_bit; @@ -892,17 +946,42 @@ SubGhzProtocolStatus subghz_protocol_decoder_vag_serialize( instance->generic.serial = instance->serial; instance->generic.cnt = instance->cnt; instance->generic.btn = instance->btn; + FURI_LOG_I(TAG, "Decrypted - setting generic fields for serialize"); + } else { + FURI_LOG_W(TAG, "NOT decrypted - Serial/Cnt/Btn will be 0 in saved file!"); } SubGhzProtocolStatus ret = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + FURI_LOG_I(TAG, "Generic serialize returned: %d", ret); + if(ret == SubGhzProtocolStatusOk) { flipper_format_write_hex(flipper_format, "Key2", (uint8_t*)&key2, 8); + FURI_LOG_I(TAG, "Wrote Key2"); + uint32_t type = instance->vag_type; flipper_format_write_uint32(flipper_format, "Type", &type, 1); + FURI_LOG_I(TAG, "Wrote Type: %d", instance->vag_type); + + if(instance->decrypted) { + flipper_format_write_uint32(flipper_format, "Serial", &instance->serial, 1); + FURI_LOG_I(TAG, "Wrote Serial: %08lX", (unsigned long)instance->serial); + + uint32_t btn_temp = instance->btn; + flipper_format_write_uint32(flipper_format, "Btn", &btn_temp, 1); + FURI_LOG_I(TAG, "Wrote Btn: %02X", instance->btn); + + flipper_format_write_uint32(flipper_format, "Cnt", &instance->cnt, 1); + FURI_LOG_I(TAG, "Wrote Cnt: %06lX", (unsigned long)instance->cnt); + + uint32_t key_idx_temp = instance->key_idx; + flipper_format_write_uint32(flipper_format, "KeyIdx", &key_idx_temp, 1); + FURI_LOG_I(TAG, "Wrote KeyIdx: %d", instance->key_idx); + } } + FURI_LOG_I(TAG, "=== VAG SERIALIZE END (ret=%d) ===", ret); return ret; } @@ -941,6 +1020,538 @@ SubGhzProtocolStatus return ret; } +typedef struct SubGhzProtocolEncoderVAG { + SubGhzProtocolEncoderBase base; + SubGhzBlockGeneric generic; + + uint32_t key1_low; + uint32_t key1_high; + uint32_t key2_low; + uint32_t key2_high; + uint32_t serial; + uint32_t cnt; + uint8_t vag_type; + uint8_t btn; + uint8_t dispatch_byte; + uint8_t key_idx; + + size_t repeat; + size_t front; + size_t size_upload; + LevelDuration* upload; + bool is_running; +} SubGhzProtocolEncoderVAG; + +static bool vag_aut64_encrypt(uint8_t* block, int key_index) { + struct aut64_key* key = protocol_vag_get_key(key_index + 1); + if(!key) { + FURI_LOG_E(TAG, "Key not found for encryption: %d", key_index + 1); + return false; + } + aut64_encrypt(*key, block); + return true; +} + +static uint8_t vag_get_dispatch_byte(uint8_t btn, uint8_t vag_type) { + if(vag_type == 1 || vag_type == 2) { + switch(btn) { + case 0x20: + case 2: + return 0x2A; + case 0x40: + case 4: + return 0x46; + case 0x10: + case 1: + return 0x1C; + default: + return 0x2A; + } + } else { + switch(btn) { + case 0x20: + case 2: + return 0x2B; + case 0x40: + case 4: + return 0x47; + case 0x10: + case 1: + return 0x1D; + default: + return 0x2B; + } + } +} + +static uint8_t vag_btn_to_byte(uint8_t btn, uint8_t vag_type) { + if(vag_type == 1) { + return btn; + } else { + switch(btn) { + case 1: + return 0x10; + case 2: + return 0x20; + case 4: + return 0x40; + default: + return 0x20; + } + } +} + +static void vag_encoder_build_type1(SubGhzProtocolEncoderVAG* instance) { + FURI_LOG_I(TAG, "=== Building Type 1 upload (300us, AUT64) ==="); + + size_t index = 0; + LevelDuration* upload = instance->upload; + + uint8_t btn_byte = vag_btn_to_byte(instance->btn, 1); + uint8_t dispatch = vag_get_dispatch_byte(btn_byte, 1); + instance->dispatch_byte = dispatch; + + FURI_LOG_D(TAG, "btn=%02X -> btn_byte=%02X, dispatch=%02X", instance->btn, btn_byte, dispatch); + + uint8_t block[8]; + uint8_t type_byte = (uint8_t)(instance->key1_high >> 24); + + block[0] = (uint8_t)(instance->serial >> 24); + block[1] = (uint8_t)(instance->serial >> 16); + block[2] = (uint8_t)(instance->serial >> 8); + block[3] = (uint8_t)(instance->serial); + + block[4] = (uint8_t)(instance->cnt); + block[5] = (uint8_t)(instance->cnt >> 8); + block[6] = (uint8_t)(instance->cnt >> 16); + + block[7] = btn_byte; + + FURI_LOG_I( + TAG, + "Plaintext: %02X %02X %02X %02X %02X %02X %02X %02X (ser=%08lX cnt=%06lX btn=%02X)", + block[0], + block[1], + block[2], + block[3], + block[4], + block[5], + block[6], + block[7], + (unsigned long)instance->serial, + (unsigned long)instance->cnt, + btn_byte); + + int key_idx = (instance->key_idx != 0xFF) ? instance->key_idx : 0; + FURI_LOG_D(TAG, "Encrypting with AUT64 key index %d (saved=%d)", key_idx, instance->key_idx); + + if(!vag_aut64_encrypt(block, key_idx)) { + FURI_LOG_E(TAG, "Type1 AUT64 encryption failed! Key not loaded?"); + instance->size_upload = 0; + return; + } + + FURI_LOG_I( + TAG, + "Encrypted: %02X %02X %02X %02X %02X %02X %02X %02X", + block[0], + block[1], + block[2], + block[3], + block[4], + block[5], + block[6], + block[7]); + + instance->key1_high = + ((uint32_t)type_byte << 24) | ((uint32_t)block[0] << 16) | ((uint32_t)block[1] << 8) | + (uint32_t)block[2]; + instance->key1_low = ((uint32_t)block[3] << 24) | ((uint32_t)block[4] << 16) | + ((uint32_t)block[5] << 8) | (uint32_t)block[6]; + instance->key2_low = ((uint32_t)block[7] << 8) | dispatch; + instance->key2_high = 0; + + for(int i = 0; i < 220; i++) { + upload[index++] = level_duration_make(true, 300); + upload[index++] = level_duration_make(false, 300); + } + upload[index++] = level_duration_make(false, 300); + upload[index++] = level_duration_make(true, 300); + + FURI_LOG_D(TAG, "Preamble: %zu pulses (220 cycles + 2 sync)", index); + + uint16_t prefix = 0xAF3F; + size_t prefix_start = index; + for(int i = 15; i >= 0; i--) { + bool bit = (prefix >> i) & 1; + if(bit) { + upload[index++] = level_duration_make(true, 300); + upload[index++] = level_duration_make(false, 300); + } else { + upload[index++] = level_duration_make(false, 300); + upload[index++] = level_duration_make(true, 300); + } + } + FURI_LOG_D(TAG, "Prefix 0x%04X: %zu pulses", prefix, index - prefix_start); + + uint64_t key1 = ((uint64_t)instance->key1_high << 32) | instance->key1_low; + uint64_t key1_inv = ~key1; + FURI_LOG_D(TAG, "Key1: %08lX%08lX -> inverted: %08lX%08lX", + (unsigned long)(key1 >> 32), (unsigned long)(key1 & 0xFFFFFFFF), + (unsigned long)(key1_inv >> 32), (unsigned long)(key1_inv & 0xFFFFFFFF)); + + size_t key1_start = index; + for(int i = 63; i >= 0; i--) { + bool bit = (key1_inv >> i) & 1; + if(bit) { + upload[index++] = level_duration_make(true, 300); + upload[index++] = level_duration_make(false, 300); + } else { + upload[index++] = level_duration_make(false, 300); + upload[index++] = level_duration_make(true, 300); + } + } + FURI_LOG_D(TAG, "Key1: %zu pulses (64 bits)", index - key1_start); + + uint16_t key2 = (uint16_t)(instance->key2_low & 0xFFFF); + uint16_t key2_inv = ~key2; + FURI_LOG_D(TAG, "Key2: %04X -> inverted: %04X", key2, key2_inv); + + size_t key2_start = index; + for(int i = 15; i >= 0; i--) { + bool bit = (key2_inv >> i) & 1; + if(bit) { + upload[index++] = level_duration_make(true, 300); + upload[index++] = level_duration_make(false, 300); + } else { + upload[index++] = level_duration_make(false, 300); + upload[index++] = level_duration_make(true, 300); + } + } + FURI_LOG_D(TAG, "Key2: %zu pulses (16 bits)", index - key2_start); + + upload[index++] = level_duration_make(false, 6000); + + instance->size_upload = index; + FURI_LOG_I(TAG, "Type1 upload built: %zu pulses (expected: 635)", index); + + if(index != 635) { + FURI_LOG_W(TAG, "WARNING: Pulse count %zu != expected 635!", index); + } +} + +static void vag_encoder_build_type2(SubGhzProtocolEncoderVAG* instance) { + FURI_LOG_I(TAG, "=== Building Type 2 upload (300us, TEA) ==="); + + size_t index = 0; + LevelDuration* upload = instance->upload; + + uint8_t btn_byte = vag_btn_to_byte(instance->btn, 2); + uint8_t dispatch = vag_get_dispatch_byte(btn_byte, 2); + instance->dispatch_byte = dispatch; + + FURI_LOG_D(TAG, "btn=%02X -> btn_byte=%02X, dispatch=%02X", instance->btn, btn_byte, dispatch); + + uint8_t type_byte = (uint8_t)(instance->key1_high >> 24); + + uint8_t block[8]; + block[0] = (uint8_t)(instance->serial >> 24); + block[1] = (uint8_t)(instance->serial >> 16); + block[2] = (uint8_t)(instance->serial >> 8); + block[3] = (uint8_t)(instance->serial); + + block[4] = (uint8_t)(instance->cnt); + block[5] = (uint8_t)(instance->cnt >> 8); + block[6] = (uint8_t)(instance->cnt >> 16); + + block[7] = btn_byte; + + FURI_LOG_I( + TAG, + "Plaintext: %02X %02X %02X %02X %02X %02X %02X %02X (ser=%08lX cnt=%06lX btn=%02X)", + block[0], + block[1], + block[2], + block[3], + block[4], + block[5], + block[6], + block[7], + (unsigned long)instance->serial, + (unsigned long)instance->cnt, + btn_byte); + + uint32_t v0 = ((uint32_t)block[0] << 24) | ((uint32_t)block[1] << 16) | + ((uint32_t)block[2] << 8) | (uint32_t)block[3]; + uint32_t v1 = ((uint32_t)block[4] << 24) | ((uint32_t)block[5] << 16) | + ((uint32_t)block[6] << 8) | (uint32_t)block[7]; + + FURI_LOG_D(TAG, "TEA input: v0=%08lX v1=%08lX", (unsigned long)v0, (unsigned long)v1); + + vag_tea_encrypt(&v0, &v1, vag_tea_key_schedule); + + FURI_LOG_D(TAG, "TEA output: v0=%08lX v1=%08lX", (unsigned long)v0, (unsigned long)v1); + + block[0] = (uint8_t)(v0 >> 24); + block[1] = (uint8_t)(v0 >> 16); + block[2] = (uint8_t)(v0 >> 8); + block[3] = (uint8_t)(v0); + block[4] = (uint8_t)(v1 >> 24); + block[5] = (uint8_t)(v1 >> 16); + block[6] = (uint8_t)(v1 >> 8); + block[7] = (uint8_t)(v1); + + FURI_LOG_I( + TAG, + "Encrypted: %02X %02X %02X %02X %02X %02X %02X %02X", + block[0], + block[1], + block[2], + block[3], + block[4], + block[5], + block[6], + block[7]); + + instance->key1_high = + ((uint32_t)type_byte << 24) | ((uint32_t)block[0] << 16) | ((uint32_t)block[1] << 8) | + (uint32_t)block[2]; + instance->key1_low = ((uint32_t)block[3] << 24) | ((uint32_t)block[4] << 16) | + ((uint32_t)block[5] << 8) | (uint32_t)block[6]; + instance->key2_low = ((uint32_t)block[7] << 8) | dispatch; + instance->key2_high = 0; + + for(int i = 0; i < 220; i++) { + upload[index++] = level_duration_make(true, 300); + upload[index++] = level_duration_make(false, 300); + } + upload[index++] = level_duration_make(false, 300); + upload[index++] = level_duration_make(true, 300); + + FURI_LOG_D(TAG, "Preamble: %zu pulses (220 cycles + 2 sync)", index); + + uint16_t prefix = 0xAF1C; + size_t prefix_start = index; + for(int i = 15; i >= 0; i--) { + bool bit = (prefix >> i) & 1; + if(bit) { + upload[index++] = level_duration_make(true, 300); + upload[index++] = level_duration_make(false, 300); + } else { + upload[index++] = level_duration_make(false, 300); + upload[index++] = level_duration_make(true, 300); + } + } + FURI_LOG_D(TAG, "Prefix 0x%04X: %zu pulses", prefix, index - prefix_start); + + uint64_t key1 = ((uint64_t)instance->key1_high << 32) | instance->key1_low; + uint64_t key1_inv = ~key1; + FURI_LOG_D(TAG, "Key1: %08lX%08lX -> inverted: %08lX%08lX", + (unsigned long)(key1 >> 32), (unsigned long)(key1 & 0xFFFFFFFF), + (unsigned long)(key1_inv >> 32), (unsigned long)(key1_inv & 0xFFFFFFFF)); + + size_t key1_start = index; + for(int i = 63; i >= 0; i--) { + bool bit = (key1_inv >> i) & 1; + if(bit) { + upload[index++] = level_duration_make(true, 300); + upload[index++] = level_duration_make(false, 300); + } else { + upload[index++] = level_duration_make(false, 300); + upload[index++] = level_duration_make(true, 300); + } + } + FURI_LOG_D(TAG, "Key1: %zu pulses", index - key1_start); + + uint16_t key2 = (uint16_t)(instance->key2_low & 0xFFFF); + uint16_t key2_inv = ~key2; + FURI_LOG_D(TAG, "Key2: %04X -> inverted: %04X", key2, key2_inv); + + size_t key2_start = index; + for(int i = 15; i >= 0; i--) { + bool bit = (key2_inv >> i) & 1; + if(bit) { + upload[index++] = level_duration_make(true, 300); + upload[index++] = level_duration_make(false, 300); + } else { + upload[index++] = level_duration_make(false, 300); + upload[index++] = level_duration_make(true, 300); + } + } + FURI_LOG_D(TAG, "Key2: %zu pulses", index - key2_start); + + upload[index++] = level_duration_make(false, 6000); + + instance->size_upload = index; + FURI_LOG_I(TAG, "Type2 upload built: %zu pulses (expected: 635)", index); +} + +static void vag_encoder_build_type3_4(SubGhzProtocolEncoderVAG* instance) { + FURI_LOG_I(TAG, "=== Building Type %d upload (500us, AUT64) ===", instance->vag_type); + + size_t index = 0; + LevelDuration* upload = instance->upload; + + uint8_t btn_byte = vag_btn_to_byte(instance->btn, instance->vag_type); + uint8_t dispatch = vag_get_dispatch_byte(btn_byte, instance->vag_type); + instance->dispatch_byte = dispatch; + + FURI_LOG_D(TAG, "btn=%02X -> btn_byte=%02X, dispatch=%02X", instance->btn, btn_byte, dispatch); + + uint8_t type_byte = (uint8_t)(instance->key1_high >> 24); + + uint8_t block[8]; + block[0] = (uint8_t)(instance->serial >> 24); + block[1] = (uint8_t)(instance->serial >> 16); + block[2] = (uint8_t)(instance->serial >> 8); + block[3] = (uint8_t)(instance->serial); + block[4] = (uint8_t)(instance->cnt); + block[5] = (uint8_t)(instance->cnt >> 8); + block[6] = (uint8_t)(instance->cnt >> 16); + block[7] = btn_byte; + + FURI_LOG_I( + TAG, + "Plaintext: %02X %02X %02X %02X %02X %02X %02X %02X (ser=%08lX cnt=%06lX btn=%02X)", + block[0], + block[1], + block[2], + block[3], + block[4], + block[5], + block[6], + block[7], + (unsigned long)instance->serial, + (unsigned long)instance->cnt, + btn_byte); + + int key_idx; + if(instance->key_idx != 0xFF) { + key_idx = instance->key_idx; + } else { + key_idx = (instance->vag_type == 4) ? 2 : 1; + } + FURI_LOG_D(TAG, "Encrypting with AUT64 key index %d (saved=%d)", key_idx, instance->key_idx); + + if(!vag_aut64_encrypt(block, key_idx)) { + FURI_LOG_E(TAG, "Type%d AUT64 encryption failed! Key not loaded?", instance->vag_type); + instance->size_upload = 0; + return; + } + + FURI_LOG_I( + TAG, + "Encrypted: %02X %02X %02X %02X %02X %02X %02X %02X", + block[0], + block[1], + block[2], + block[3], + block[4], + block[5], + block[6], + block[7]); + + instance->key1_high = + ((uint32_t)type_byte << 24) | ((uint32_t)block[0] << 16) | ((uint32_t)block[1] << 8) | + (uint32_t)block[2]; + instance->key1_low = ((uint32_t)block[3] << 24) | ((uint32_t)block[4] << 16) | + ((uint32_t)block[5] << 8) | (uint32_t)block[6]; + instance->key2_low = ((uint32_t)block[7] << 8) | dispatch; + instance->key2_high = 0; + + uint64_t key1 = ((uint64_t)instance->key1_high << 32) | instance->key1_low; + uint16_t key2 = (uint16_t)(instance->key2_low & 0xFFFF); + FURI_LOG_D(TAG, "Key1: %08lX%08lX (NOT inverted for Type 3/4)", + (unsigned long)(key1 >> 32), (unsigned long)(key1 & 0xFFFFFFFF)); + FURI_LOG_D(TAG, "Key2: %04X (NOT inverted for Type 3/4)", key2); + + uint8_t key1_byte6 = (key1 >> 8) & 0xFF; + uint8_t key1_byte7 = key1 & 0xFF; + FURI_LOG_D(TAG, "Key1 last 2 bytes: %02X %02X (bits: %d%d%d%d%d%d%d%d %d%d%d%d%d%d%d%d)", + key1_byte6, key1_byte7, + (key1_byte6>>7)&1, (key1_byte6>>6)&1, (key1_byte6>>5)&1, (key1_byte6>>4)&1, + (key1_byte6>>3)&1, (key1_byte6>>2)&1, (key1_byte6>>1)&1, (key1_byte6>>0)&1, + (key1_byte7>>7)&1, (key1_byte7>>6)&1, (key1_byte7>>5)&1, (key1_byte7>>4)&1, + (key1_byte7>>3)&1, (key1_byte7>>2)&1, (key1_byte7>>1)&1, (key1_byte7>>0)&1); + + for(int repeat = 0; repeat < 2; repeat++) { + size_t repeat_start = index; + + for(int i = 0; i < 45; i++) { + upload[index++] = level_duration_make(true, 500); + upload[index++] = level_duration_make(false, 500); + } + FURI_LOG_D(TAG, "Repeat %d: Preamble %zu pulses (45 cycles)", repeat + 1, index - repeat_start); + + upload[index++] = level_duration_make(true, 1000); + upload[index++] = level_duration_make(false, 500); + + for(int i = 0; i < 3; i++) { + upload[index++] = level_duration_make(true, 750); + upload[index++] = level_duration_make(false, 750); + } + + size_t key1_start = index; + uint8_t consecutive_same = 0; + bool prev_level = true; + + for(int i = 63; i >= 0; i--) { + bool bit = (key1 >> i) & 1; + bool first_level = bit ? true : false; + + if(first_level == prev_level) { + consecutive_same++; + } + + if(bit) { + upload[index++] = level_duration_make(true, 500); + upload[index++] = level_duration_make(false, 500); + prev_level = false; + } else { + upload[index++] = level_duration_make(false, 500); + upload[index++] = level_duration_make(true, 500); + prev_level = true; + } + } + FURI_LOG_D(TAG, "Repeat %d: Key1 %zu pulses (64 bits), %u double-width transitions", + repeat + 1, index - key1_start, consecutive_same); + + size_t key2_start = index; + bool last_level = false; + for(int i = 15; i >= 0; i--) { + bool bit = (key2 >> i) & 1; + if(bit) { + upload[index++] = level_duration_make(true, 500); + upload[index++] = level_duration_make(false, 500); + last_level = false; + } else { + upload[index++] = level_duration_make(false, 500); + upload[index++] = level_duration_make(true, 500); + last_level = true; + } + } + FURI_LOG_D(TAG, "Repeat %d: Key2 %zu pulses (16 bits), ends %s", + repeat + 1, index - key2_start, last_level ? "HIGH" : "LOW"); + + if(!last_level) { + upload[index++] = level_duration_make(false, 10000); + FURI_LOG_D(TAG, "Repeat %d: Gap 10000us LOW (consecutive with data)", repeat + 1); + } else { + upload[index++] = level_duration_make(false, 10000); + FURI_LOG_D(TAG, "Repeat %d: Gap 10000us LOW (after HIGH)", repeat + 1); + } + + FURI_LOG_D(TAG, "Repeat %d: Total %zu pulses", repeat + 1, index - repeat_start); + } + + instance->size_upload = index; + FURI_LOG_I(TAG, "Type%d upload built: %zu pulses (expected: 518)", instance->vag_type, index); + + if(index != 518) { + FURI_LOG_W(TAG, "WARNING: Pulse count %zu != expected 518!", index); + } +} + void subghz_protocol_decoder_vag_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderVAG* instance = context; @@ -975,9 +1586,6 @@ void subghz_protocol_decoder_vag_get_string(void* context, FuriString* output) { break; } - //instance->generic.protocol_name = vehicle_name; - // Do not rename protocol ? - if(instance->decrypted) { furi_string_cat_printf( output, @@ -1006,3 +1614,316 @@ void subghz_protocol_decoder_vag_get_string(void* context, FuriString* output) { key2); } } + +#define VAG_ENCODER_UPLOAD_MAX_SIZE 2560 + +void* subghz_protocol_encoder_vag_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + FURI_LOG_I(TAG, "VAG encoder alloc"); + + SubGhzProtocolEncoderVAG* instance = malloc(sizeof(SubGhzProtocolEncoderVAG)); + instance->base.protocol = &vag_protocol; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->upload = malloc(VAG_ENCODER_UPLOAD_MAX_SIZE * sizeof(LevelDuration)); + instance->size_upload = 0; + instance->repeat = 10; + instance->front = 0; + instance->is_running = false; + + instance->key1_low = 0; + instance->key1_high = 0; + instance->key2_low = 0; + instance->key2_high = 0; + instance->serial = 0; + instance->cnt = 0; + instance->vag_type = 0; + instance->btn = 0; + instance->dispatch_byte = 0; + instance->key_idx = 0xFF; + + protocol_vag_load_keys(APP_ASSETS_PATH("vag")); + + FURI_LOG_I(TAG, "VAG encoder alloc complete, keys loaded: %d", protocol_vag_keys_loaded); + + return instance; +} + +void subghz_protocol_encoder_vag_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderVAG* instance = context; + free(instance->upload); + free(instance); +} + +void subghz_protocol_encoder_vag_stop(void* context) { + furi_assert(context); + SubGhzProtocolEncoderVAG* instance = context; + FURI_LOG_I(TAG, "VAG encoder stop (was_running=%d)", instance->is_running); + instance->is_running = false; +} + +LevelDuration subghz_protocol_encoder_vag_yield(void* context) { + furi_assert(context); + SubGhzProtocolEncoderVAG* instance = context; + + if(!instance->is_running || instance->repeat == 0) { + if(instance->is_running) { + FURI_LOG_I(TAG, "VAG encoder transmission complete"); + } + instance->is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->upload[instance->front]; + instance->front++; + + if(instance->front >= instance->size_upload) { + instance->front = 0; + instance->repeat--; + FURI_LOG_D(TAG, "VAG encoder repeat cycle, %zu remaining", instance->repeat); + } + + return ret; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_vag_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderVAG* instance = context; + + FURI_LOG_I(TAG, "=== VAG ENCODER DESERIALIZE START ==="); + + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + + do { + ret = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { + FURI_LOG_E(TAG, "Encoder deserialize: generic failed (ret=%d)", ret); + break; + } + + uint64_t key1 = instance->generic.data; + instance->key1_low = (uint32_t)key1; + instance->key1_high = (uint32_t)(key1 >> 32); + FURI_LOG_I( + TAG, + "Loaded Key1: %08lX%08lX", + (unsigned long)instance->key1_high, + (unsigned long)instance->key1_low); + + uint64_t key2 = 0; + flipper_format_rewind(flipper_format); + if(!flipper_format_read_hex(flipper_format, "Key2", (uint8_t*)&key2, 8)) { + FURI_LOG_E(TAG, "Encoder deserialize: Key2 not found in file"); + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + instance->key2_low = (uint32_t)key2; + instance->key2_high = (uint32_t)(key2 >> 32); + FURI_LOG_I( + TAG, + "Loaded Key2: %08lX%08lX", + (unsigned long)instance->key2_high, + (unsigned long)instance->key2_low); + + uint32_t type = 0; + flipper_format_rewind(flipper_format); + if(!flipper_format_read_uint32(flipper_format, "Type", &type, 1)) { + FURI_LOG_W(TAG, "Type not found in file, will try to detect"); + type = 0; + } + instance->vag_type = (uint8_t)type; + FURI_LOG_I(TAG, "Loaded Type: %d", instance->vag_type); + + uint32_t file_serial = 0, file_cnt = 0, file_btn = 0, file_key_idx = 0xFF; + + flipper_format_rewind(flipper_format); + bool has_serial = flipper_format_read_uint32(flipper_format, "Serial", &file_serial, 1); + + flipper_format_rewind(flipper_format); + bool has_cnt = flipper_format_read_uint32(flipper_format, "Cnt", &file_cnt, 1); + + flipper_format_rewind(flipper_format); + bool has_btn = flipper_format_read_uint32(flipper_format, "Btn", &file_btn, 1); + + flipper_format_rewind(flipper_format); + bool has_key_idx = flipper_format_read_uint32(flipper_format, "KeyIdx", &file_key_idx, 1); + + FURI_LOG_I( + TAG, + "Direct file read: Serial=%08lX(%s) Cnt=%06lX(%s) Btn=%02lX(%s) KeyIdx=%d(%s)", + (unsigned long)file_serial, + has_serial ? "found" : "MISSING", + (unsigned long)file_cnt, + has_cnt ? "found" : "MISSING", + (unsigned long)file_btn, + has_btn ? "found" : "MISSING", + (int)file_key_idx, + has_key_idx ? "found" : "MISSING"); + + FURI_LOG_I( + TAG, + "Generic values: Ser=%08lX Cnt=%06lX Btn=%02X", + (unsigned long)instance->generic.serial, + (unsigned long)instance->generic.cnt, + instance->generic.btn); + + instance->serial = has_serial ? file_serial : instance->generic.serial; + instance->cnt = has_cnt ? file_cnt : instance->generic.cnt; + instance->btn = has_btn ? (uint8_t)file_btn : instance->generic.btn; + instance->key_idx = has_key_idx ? (uint8_t)file_key_idx : 0xFF; + + FURI_LOG_I( + TAG, + "Final values: Ser=%08lX Cnt=%06lX Btn=%02X KeyIdx=%d", + (unsigned long)instance->serial, + (unsigned long)instance->cnt, + instance->btn, + instance->key_idx); + + if(instance->key_idx == 0xFF) { + FURI_LOG_I(TAG, "KeyIdx not found in file, decoding original signal to find correct key..."); + + SubGhzProtocolDecoderVAG decoder; + memset(&decoder, 0, sizeof(decoder)); + decoder.key1_low = instance->key1_low; + decoder.key1_high = instance->key1_high; + decoder.key2_low = instance->key2_low; + decoder.key2_high = instance->key2_high; + decoder.vag_type = instance->vag_type; + decoder.data_count_bit = 80; + decoder.key_idx = 0xFF; + vag_parse_data(&decoder); + + if(decoder.decrypted) { + instance->key_idx = decoder.key_idx; + FURI_LOG_I(TAG, "Decoded key_idx=%d from original signal", instance->key_idx); + + if(instance->serial == 0 && instance->cnt == 0) { + instance->serial = decoder.serial; + instance->cnt = decoder.cnt; + instance->btn = decoder.btn; + FURI_LOG_I( + TAG, + "Also decoded: Ser=%08lX Cnt=%06lX Btn=%02X", + (unsigned long)instance->serial, + (unsigned long)instance->cnt, + instance->btn); + } + } else { + FURI_LOG_W(TAG, "Could not decode original signal - check if keys are loaded and Type is correct"); + } + } + + uint32_t old_cnt = instance->cnt; + instance->cnt = (instance->cnt + 1) & 0xFFFFFF; + FURI_LOG_I( + TAG, + "Counter incremented: %06lX -> %06lX", + (unsigned long)old_cnt, + (unsigned long)instance->cnt); + + FURI_LOG_I(TAG, "Building upload for type %d...", instance->vag_type); + switch(instance->vag_type) { + case 1: + vag_encoder_build_type1(instance); + break; + case 2: + vag_encoder_build_type2(instance); + break; + case 3: + case 4: + vag_encoder_build_type3_4(instance); + break; + default: + FURI_LOG_W(TAG, "Unknown type %d, defaulting to type 1", instance->vag_type); + instance->vag_type = 1; + vag_encoder_build_type1(instance); + break; + } + + if(instance->size_upload == 0) { + FURI_LOG_E(TAG, "Failed to build upload buffer!"); + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + + FURI_LOG_I( + TAG, + "Upload built: %zu pulses, Key1=%08lX%08lX Key2=%04lX", + instance->size_upload, + (unsigned long)instance->key1_high, + (unsigned long)instance->key1_low, + (unsigned long)(instance->key2_low & 0xFFFF)); + + flipper_format_rewind(flipper_format); + uint8_t key1_bytes[8]; + key1_bytes[0] = (uint8_t)(instance->key1_high >> 24); + key1_bytes[1] = (uint8_t)(instance->key1_high >> 16); + key1_bytes[2] = (uint8_t)(instance->key1_high >> 8); + key1_bytes[3] = (uint8_t)(instance->key1_high); + key1_bytes[4] = (uint8_t)(instance->key1_low >> 24); + key1_bytes[5] = (uint8_t)(instance->key1_low >> 16); + key1_bytes[6] = (uint8_t)(instance->key1_low >> 8); + key1_bytes[7] = (uint8_t)(instance->key1_low); + if(!flipper_format_update_hex(flipper_format, "Key", key1_bytes, 8)) { + FURI_LOG_W(TAG, "Failed to update Key in file (non-fatal)"); + } + + flipper_format_rewind(flipper_format); + uint64_t new_key2 = ((uint64_t)instance->key2_high << 32) | instance->key2_low; + if(!flipper_format_update_hex(flipper_format, "Key2", (uint8_t*)&new_key2, 8)) { + FURI_LOG_W(TAG, "Failed to update Key2 in file (non-fatal)"); + } + + flipper_format_rewind(flipper_format); + uint32_t serial32 = instance->serial; + if(!flipper_format_update_uint32(flipper_format, "Serial", &serial32, 1)) { + flipper_format_rewind(flipper_format); + flipper_format_insert_or_update_uint32(flipper_format, "Serial", &serial32, 1); + } + + flipper_format_rewind(flipper_format); + uint32_t cnt32 = instance->cnt; + if(!flipper_format_update_uint32(flipper_format, "Cnt", &cnt32, 1)) { + flipper_format_rewind(flipper_format); + flipper_format_insert_or_update_uint32(flipper_format, "Cnt", &cnt32, 1); + } + + flipper_format_rewind(flipper_format); + uint32_t btn32 = instance->btn; + if(!flipper_format_update_uint32(flipper_format, "Btn", &btn32, 1)) { + flipper_format_rewind(flipper_format); + flipper_format_insert_or_update_uint32(flipper_format, "Btn", &btn32, 1); + } + + flipper_format_rewind(flipper_format); + uint32_t key_idx32 = instance->key_idx; + if(!flipper_format_update_uint32(flipper_format, "KeyIdx", &key_idx32, 1)) { + flipper_format_rewind(flipper_format); + flipper_format_insert_or_update_uint32(flipper_format, "KeyIdx", &key_idx32, 1); + } + + FURI_LOG_I( + TAG, + "Updated file: Serial=%08lX Cnt=%06lX Btn=%02X KeyIdx=%d", + (unsigned long)instance->serial, + (unsigned long)instance->cnt, + instance->btn, + instance->key_idx); + + instance->repeat = 10; + instance->front = 0; + instance->is_running = true; + + FURI_LOG_I(TAG, "=== VAG ENCODER READY (repeat=%zu) ===", instance->repeat); + ret = SubGhzProtocolStatusOk; + } while(false); + + if(ret != SubGhzProtocolStatusOk) { + FURI_LOG_E(TAG, "=== VAG ENCODER DESERIALIZE FAILED (ret=%d) ===", ret); + } + + return ret; +} diff --git a/protocols/vag.h b/protocols/vag.h index 7eb0bf3..776390e 100644 --- a/protocols/vag.h +++ b/protocols/vag.h @@ -15,6 +15,7 @@ extern const SubGhzProtocol vag_protocol; +// Decoder functions void* subghz_protocol_decoder_vag_alloc(SubGhzEnvironment* environment); void subghz_protocol_decoder_vag_free(void* context); void subghz_protocol_decoder_vag_reset(void* context); @@ -27,3 +28,11 @@ SubGhzProtocolStatus subghz_protocol_decoder_vag_serialize( SubGhzProtocolStatus subghz_protocol_decoder_vag_deserialize(void* context, FlipperFormat* flipper_format); void subghz_protocol_decoder_vag_get_string(void* context, FuriString* output); + +// Encoder functions +void* subghz_protocol_encoder_vag_alloc(SubGhzEnvironment* environment); +void subghz_protocol_encoder_vag_free(void* context); +SubGhzProtocolStatus + subghz_protocol_encoder_vag_deserialize(void* context, FlipperFormat* flipper_format); +void subghz_protocol_encoder_vag_stop(void* context); +LevelDuration subghz_protocol_encoder_vag_yield(void* context); diff --git a/scenes/protopirate_scene_saved_info.c b/scenes/protopirate_scene_saved_info.c index 86d28dc..0737771 100644 --- a/scenes/protopirate_scene_saved_info.c +++ b/scenes/protopirate_scene_saved_info.c @@ -107,9 +107,7 @@ void protopirate_scene_saved_info_on_enter(void* context) { if(flipper_format_read_string(ff, "Protocol", temp_str)) { furi_string_cat_printf(info_str, "Protocol: %s\n", furi_string_get_cstr(temp_str)); } - if(furi_string_cmp_str(temp_str, "VAG") == 0) { - is_emu_off = true; - } else if(furi_string_cmp_str(temp_str, "Scher-Khan") == 0) { + if(furi_string_cmp_str(temp_str, "Scher-Khan") == 0) { is_emu_off = true; } else if(furi_string_cmp_str(temp_str, "Kia V5") == 0) { is_emu_off = true; @@ -163,6 +161,11 @@ void protopirate_scene_saved_info_on_enter(void* context) { furi_string_cat_printf(info_str, "Type: %02X\n", (uint8_t)temp_data); } + flipper_format_rewind(ff); + if(flipper_format_read_uint32(ff, "KeyIdx", &temp_data, 1)) { + furi_string_cat_printf(info_str, "KeyIdx: %d\n", (uint8_t)temp_data); + } + flipper_format_rewind(ff); if(flipper_format_read_string(ff, "Key", temp_str)) { furi_string_cat_printf(info_str, "Key1: %s\n", furi_string_get_cstr(temp_str));