From b76d1e7dbcabe7e940ae9d7e9c3e87e3caf368f8 Mon Sep 17 00:00:00 2001 From: zero-mega Date: Mon, 26 Jan 2026 18:54:57 +0100 Subject: [PATCH] Add PSA Encoder --- helpers/protopirate_storage.c | 23 + protocols/psa.c | 662 +++++++++++++++++++++-- scenes/protopirate_scene_receiver_info.c | 80 ++- scenes/protopirate_scene_saved_info.c | 20 + 4 files changed, 735 insertions(+), 50 deletions(-) diff --git a/helpers/protopirate_storage.c b/helpers/protopirate_storage.c index a739dbd..f2affef 100644 --- a/helpers/protopirate_storage.c +++ b/helpers/protopirate_storage.c @@ -155,6 +155,29 @@ 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); + 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); + + flipper_format_rewind(flipper_format); + uint8_t val_field[2]; + if(flipper_format_read_hex(flipper_format, "ValidationField", val_field, 2)) { + flipper_format_write_hex(save_file, "ValidationField", val_field, 2); + } else { + flipper_format_rewind(flipper_format); + if(flipper_format_read_uint32(flipper_format, "ValidationField", &uint32_value, 1)) + flipper_format_write_uint32(save_file, "ValidationField", &uint32_value, 1); + } + + flipper_format_rewind(flipper_format); + if(flipper_format_read_string(flipper_format, "Key_2", string_value)) + flipper_format_write_string(save_file, "Key_2", string_value); + + flipper_format_rewind(flipper_format); + uint8_t key1_buf[8]; + if(flipper_format_read_hex(flipper_format, "Key1", key1_buf, 8)) + flipper_format_write_hex(save_file, "Key1", key1_buf, 8); + flipper_format_rewind(flipper_format); if(flipper_format_read_uint32(flipper_format, "Check", &uint32_value, 1)) flipper_format_write_uint32(save_file, "Check", &uint32_value, 1); diff --git a/protocols/psa.c b/protocols/psa.c index f91d62a..b7115e9 100644 --- a/protocols/psa.c +++ b/protocols/psa.c @@ -93,6 +93,19 @@ struct SubGhzProtocolEncoderPSA { SubGhzProtocolEncoderBase base; SubGhzProtocolBlockEncoder encoder; SubGhzBlockGeneric generic; + + uint32_t key1_low; + uint32_t key1_high; + uint16_t validation_field; + uint32_t key2_low; + uint32_t counter; + uint8_t button; + uint8_t type; + uint8_t seed; + uint8_t mode; + uint32_t serial; + uint16_t crc; + bool is_running; }; const SubGhzProtocolDecoder subghz_protocol_psa_decoder = { @@ -107,11 +120,11 @@ const SubGhzProtocolDecoder subghz_protocol_psa_decoder = { }; const SubGhzProtocolEncoder subghz_protocol_psa_encoder = { - .alloc = NULL, - .free = NULL, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, + .alloc = subghz_protocol_encoder_psa_alloc, + .free = subghz_protocol_encoder_psa_free, + .deserialize = subghz_protocol_encoder_psa_deserialize, + .stop = subghz_protocol_encoder_psa_stop, + .yield = subghz_protocol_encoder_psa_yield, }; const SubGhzProtocol psa_protocol = { @@ -123,29 +136,529 @@ const SubGhzProtocol psa_protocol = { .encoder = &subghz_protocol_psa_encoder, }; +static void psa_setup_byte_buffer(uint8_t* buffer, uint32_t key1_low, uint32_t key1_high, uint32_t key2_low); +static void psa_calculate_checksum(uint8_t* buffer); +static uint8_t psa_calculate_tea_crc(uint32_t v0, uint32_t v1); +static void psa_tea_encrypt(uint32_t* v0, uint32_t* v1, const uint32_t* key); +static void psa_unpack_tea_result_to_buffer(uint8_t* buffer, uint32_t v0, uint32_t v1); + +static void psa_second_stage_xor_encrypt(uint8_t* buffer) { + + uint8_t E6 = buffer[8]; + uint8_t E7 = buffer[9]; + + uint8_t P[6]; + P[0] = buffer[2]; + P[1] = buffer[3]; + P[2] = buffer[4]; + P[3] = buffer[5]; + P[4] = buffer[6]; + P[5] = buffer[7]; + + uint8_t E5 = P[5] ^ E7 ^ E6; + uint8_t E0 = P[2] ^ E5; + uint8_t E2 = P[4] ^ E0; + uint8_t E4 = P[3] ^ E2; + uint8_t E3 = P[0] ^ E5; + uint8_t E1 = P[1] ^ E3; + + buffer[2] = E0; + buffer[3] = E1; + buffer[4] = E2; + buffer[5] = E3; + buffer[6] = E4; + buffer[7] = E5; +} + +static void psa_build_buffer_mode23(SubGhzProtocolEncoderPSA* instance, uint8_t* buffer, uint8_t* preserve_buffer01) { + + FURI_LOG_I(TAG, "=== MODE 0x23 ENCRYPTION ==="); + FURI_LOG_I(TAG, "Input: Ser:%06lX Cnt:%08lX CRC:%02X Btn:%01X", + (unsigned long)instance->serial, (unsigned long)instance->counter, + (unsigned int)instance->crc, (unsigned int)instance->button); + + memset(buffer, 0, 48); + + buffer[2] = (uint8_t)((instance->serial >> 16) & 0xFF); + buffer[3] = (uint8_t)((instance->serial >> 8) & 0xFF); + buffer[4] = (uint8_t)(instance->serial & 0xFF); + buffer[5] = (uint8_t)((instance->counter >> 8) & 0xFF); + buffer[6] = (uint8_t)(instance->counter & 0xFF); + buffer[7] = (uint8_t)(instance->crc & 0xFF); + buffer[8] = (uint8_t)(instance->button & 0xF); + + uint8_t original_buffer9 = 0; + uint8_t original_buffer8 = 0; + bool has_original_key2 = (instance->key2_low != 0); + if(has_original_key2) { + original_buffer9 = (uint8_t)(instance->key2_low & 0xFF); + original_buffer8 = (uint8_t)((instance->key2_low >> 8) & 0xFF); + buffer[9] = original_buffer9; + FURI_LOG_D(TAG, "Original Key2: 0x%04X, buffer[8]=0x%02X buffer[9]=0x%02X, preserving buffer[9]", + (unsigned int)instance->key2_low, original_buffer8, original_buffer9); + } else { + FURI_LOG_D(TAG, "No original Key2, will find valid buffer[9]"); + } + + FURI_LOG_D(TAG, "Plaintext buffer[2-9]: %02X %02X %02X %02X %02X %02X %02X %02X", + buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9]); + + uint8_t initial_plaintext[6]; + initial_plaintext[0] = buffer[2]; + initial_plaintext[1] = buffer[3]; + initial_plaintext[2] = buffer[4]; + initial_plaintext[3] = buffer[5]; + initial_plaintext[4] = buffer[6]; + initial_plaintext[5] = buffer[7]; + uint8_t initial_button = buffer[8] & 0xF; + + bool found = false; + uint8_t buffer9_to_use = has_original_key2 ? original_buffer9 : 0; + uint8_t buffer9_end = has_original_key2 ? original_buffer9 + 1 : 255; + + for(uint8_t buffer9_try = buffer9_to_use; buffer9_try < buffer9_end && !found; buffer9_try++) { + for(uint8_t buffer8_high_try = 0; buffer8_high_try < 16 && !found; buffer8_high_try++) { + + buffer[2] = initial_plaintext[0]; + buffer[3] = initial_plaintext[1]; + buffer[4] = initial_plaintext[2]; + buffer[5] = initial_plaintext[3]; + buffer[6] = initial_plaintext[4]; + buffer[7] = initial_plaintext[5]; + buffer[8] = initial_button | (buffer8_high_try << 4); + buffer[9] = buffer9_try; + + psa_second_stage_xor_encrypt(buffer); + + psa_calculate_checksum(buffer); + uint8_t checksum_after = buffer[11]; + uint8_t key2_high_after = checksum_after & 0xF0; + + uint8_t validation = (checksum_after ^ buffer[8]) & 0xF0; + if(validation == 0) { + buffer[8] = (buffer[8] & 0x0F) | key2_high_after; + buffer[13] = buffer[9] ^ buffer[8]; + found = true; + FURI_LOG_D(TAG, "Found valid Key2: buffer[8]=0x%02X buffer[9]=0x%02X", buffer[8], buffer[9]); + break; + } + } + } + + if(!found) { + FURI_LOG_W(TAG, "Brute force failed, using default approach"); + buffer[2] = initial_plaintext[0]; + buffer[3] = initial_plaintext[1]; + buffer[4] = initial_plaintext[2]; + buffer[5] = initial_plaintext[3]; + buffer[6] = initial_plaintext[4]; + buffer[7] = initial_plaintext[5]; + buffer[8] = initial_button; + buffer[9] = has_original_key2 ? original_buffer9 : 0x23; + + psa_second_stage_xor_encrypt(buffer); + psa_calculate_checksum(buffer); + uint8_t checksum_after = buffer[11]; + uint8_t key2_high_after = checksum_after & 0xF0; + buffer[8] = (buffer[8] & 0x0F) | key2_high_after; + buffer[13] = buffer[9] ^ buffer[8]; + } + + if(preserve_buffer01 != NULL) { + buffer[0] = preserve_buffer01[0]; + buffer[1] = preserve_buffer01[1]; + FURI_LOG_D(TAG, "Preserved buffer[0-1] from original Key1: %02X %02X", buffer[0], buffer[1]); + } else { + buffer[0] = buffer[2] ^ buffer[6]; + buffer[1] = buffer[3] ^ buffer[7]; + FURI_LOG_D(TAG, "Derived buffer[0-1]: %02X %02X", buffer[0], buffer[1]); + } + + FURI_LOG_I(TAG, "Encrypted buffer[0-9]: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9]); +} + +static void psa_build_buffer_mode36(SubGhzProtocolEncoderPSA* instance, uint8_t* buffer, uint8_t* preserve_buffer01) { + FURI_LOG_I(TAG, "=== MODE 0x36 ENCRYPTION ==="); + FURI_LOG_I(TAG, "Input: Ser:%06lX Cnt:%08lX CRC:%02X Btn:%01X", + (unsigned long)instance->serial, (unsigned long)instance->counter, + (unsigned int)instance->crc, (unsigned int)instance->button); + + memset(buffer, 0, 48); + + uint32_t v0 = ((instance->serial & 0xFFFFFF) << 8) | + ((instance->counter >> 24) & 0xFF); + uint32_t v1 = ((instance->counter & 0xFFFFFF) << 8) | + ((instance->button & 0xF) << 4) | + (instance->crc & 0xFF); + + FURI_LOG_D(TAG, "Packed v0:0x%08lX v1:0x%08lX (before CRC)", (unsigned long)v0, (unsigned long)v1); + + uint8_t crc = psa_calculate_tea_crc(v0, v1); + v1 = (v1 & 0xFFFFFF00) | crc; + + FURI_LOG_D(TAG, "Calculated CRC: 0x%02X, v1 after CRC: 0x%08lX", (unsigned int)crc, (unsigned long)v1); + + uint32_t bf_counter = PSA_BF1_START | (instance->serial & 0xFFFFFF); + FURI_LOG_D(TAG, "BF counter: 0x%08lX (BF1_START | serial)", (unsigned long)bf_counter); + + uint32_t working_key[4]; + + uint32_t wk2 = PSA_BF1_CONST_U4; + uint32_t wk3 = bf_counter; + psa_tea_encrypt(&wk2, &wk3, PSA_BF1_KEY_SCHEDULE); + + uint32_t wk0 = (bf_counter << 8) | 0x0E; + uint32_t wk1 = PSA_BF1_CONST_U5; + psa_tea_encrypt(&wk0, &wk1, PSA_BF1_KEY_SCHEDULE); + + working_key[0] = wk0; + working_key[1] = wk1; + working_key[2] = wk2; + working_key[3] = wk3; + + psa_tea_encrypt(&v0, &v1, working_key); + + FURI_LOG_D(TAG, "TEA encrypted v0:0x%08lX v1:0x%08lX", (unsigned long)v0, (unsigned long)v1); + FURI_LOG_D(TAG, "Working key: %08lX %08lX %08lX %08lX", + (unsigned long)working_key[0], (unsigned long)working_key[1], + (unsigned long)working_key[2], (unsigned long)working_key[3]); + + psa_unpack_tea_result_to_buffer(buffer, v0, v1); + + if(preserve_buffer01 != NULL) { + buffer[0] = preserve_buffer01[0]; + buffer[1] = preserve_buffer01[1]; + FURI_LOG_D(TAG, "Preserved buffer[0-1] from original Key1: %02X %02X", buffer[0], buffer[1]); + } else { + buffer[0] = buffer[2] ^ buffer[6]; + buffer[1] = buffer[3] ^ buffer[7]; + FURI_LOG_D(TAG, "Derived buffer[0-1]: %02X %02X", buffer[0], buffer[1]); + } + + FURI_LOG_I(TAG, "Encrypted buffer[0-9]: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9]); +} + +static void psa_encoder_build_upload(SubGhzProtocolEncoderPSA* instance) { + furi_assert(instance); + + FURI_LOG_I(TAG, "=== ENCODER BUILD UPLOAD ==="); + FURI_LOG_I(TAG, "Mode: 0x%02X, Serial:%06lX Counter:%08lX CRC:%02X Button:%01X", + (unsigned int)instance->mode, (unsigned long)instance->serial, + (unsigned long)instance->counter, (unsigned int)instance->crc, + (unsigned int)instance->button); + + uint8_t buffer[48] = {0}; + + uint8_t preserve_buffer01[2] = {0}; + uint8_t* preserve_ptr = NULL; + + if(instance->key1_low != 0 || instance->key1_high != 0) { + uint8_t orig_buffer[48] = {0}; + psa_setup_byte_buffer(orig_buffer, instance->key1_low, instance->key1_high, instance->key2_low); + preserve_buffer01[0] = orig_buffer[0]; + preserve_buffer01[1] = orig_buffer[1]; + preserve_ptr = preserve_buffer01; + FURI_LOG_D(TAG, "Original Key1: %08lX%08lX, preserving buffer[0-1]: %02X %02X", + (unsigned long)instance->key1_high, (unsigned long)instance->key1_low, + preserve_buffer01[0], preserve_buffer01[1]); + } else { + FURI_LOG_D(TAG, "No original Key1, will derive buffer[0-1]"); + } + + if(instance->mode == 0x23) { + psa_build_buffer_mode23(instance, buffer, preserve_ptr); + } else if(instance->mode == 0x36) { + psa_build_buffer_mode36(instance, buffer, preserve_ptr); + } else { + FURI_LOG_E(TAG, "Unknown mode: 0x%02X", instance->mode); + return; + } + + uint32_t key1_high = ((uint32_t)buffer[0] << 24) | ((uint32_t)buffer[1] << 16) | + ((uint32_t)buffer[2] << 8) | (uint32_t)buffer[3]; + uint32_t key1_low = ((uint32_t)buffer[4] << 24) | ((uint32_t)buffer[5] << 16) | + ((uint32_t)buffer[6] << 8) | (uint32_t)buffer[7]; + uint16_t validation_field = ((uint16_t)buffer[8] << 8) | (uint16_t)buffer[9]; + + FURI_LOG_I(TAG, "=== ENCRYPTED KEYS ==="); + FURI_LOG_I(TAG, "Key1: %08lX%08lX", (unsigned long)key1_high, (unsigned long)key1_low); + FURI_LOG_I(TAG, "Key2: %04X", (unsigned int)validation_field); + + size_t index = 0; + uint32_t te = PSA_TE_LONG_250; + + for(int i = 0; i < 80; i++) { + if(index >= instance->encoder.size_upload - 2) break; + instance->encoder.upload[index++] = level_duration_make(true, te); + instance->encoder.upload[index++] = level_duration_make(false, te); + } + + uint32_t te_long_transition = subghz_protocol_psa_const.te_long; + if(index < instance->encoder.size_upload - 3) { + instance->encoder.upload[index++] = level_duration_make(false, te); + instance->encoder.upload[index++] = level_duration_make(true, te_long_transition); + instance->encoder.upload[index++] = level_duration_make(false, te); + } + + uint64_t key1_data = ((uint64_t)key1_high << 32) | key1_low; + for(int bit = 63; bit >= 0; bit--) { + if(index >= instance->encoder.size_upload - 2) break; + bool bit_value = (key1_data >> bit) & 1; + if(bit_value) { + instance->encoder.upload[index++] = level_duration_make(true, te); + instance->encoder.upload[index++] = level_duration_make(false, te); + } else { + instance->encoder.upload[index++] = level_duration_make(false, te); + instance->encoder.upload[index++] = level_duration_make(true, te); + } + } + + for(int bit = 15; bit >= 0; bit--) { + if(index >= instance->encoder.size_upload - 2) break; + bool bit_value = (validation_field >> bit) & 1; + if(bit_value) { + instance->encoder.upload[index++] = level_duration_make(true, te); + instance->encoder.upload[index++] = level_duration_make(false, te); + } else { + instance->encoder.upload[index++] = level_duration_make(false, te); + instance->encoder.upload[index++] = level_duration_make(true, te); + } + } + + uint32_t end_duration = PSA_TE_END_1000; + if(index < instance->encoder.size_upload - 1) { + instance->encoder.upload[index++] = level_duration_make(true, end_duration); + instance->encoder.upload[index++] = level_duration_make(false, end_duration); + } + + instance->encoder.size_upload = index; + instance->encoder.front = 0; + instance->encoder.repeat = 10; + + FURI_LOG_I(TAG, "=== TRANSMISSION PARAMETERS ==="); + FURI_LOG_I(TAG, "TE: %lu, TE_LONG_TRANS: %lu, TE_END: %lu", + (unsigned long)te, (unsigned long)te_long_transition, (unsigned long)end_duration); + FURI_LOG_I(TAG, "Upload size: %zu levels, Repeat: %d", + instance->encoder.size_upload, instance->encoder.repeat); + FURI_LOG_I(TAG, "Key1 data (64-bit): %016llX", (unsigned long long)key1_data); + FURI_LOG_I(TAG, "Key2 data (16-bit): %04X", (unsigned int)validation_field); + FURI_LOG_I(TAG, "=== END ENCODER BUILD ==="); +} + void* subghz_protocol_encoder_psa_alloc(SubGhzEnvironment* environment) { UNUSED(environment); - return NULL; + SubGhzProtocolEncoderPSA* instance = malloc(sizeof(SubGhzProtocolEncoderPSA)); + + if(instance) { + memset(instance, 0, sizeof(SubGhzProtocolEncoderPSA)); + instance->base.protocol = &psa_protocol; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.size_upload = 600; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.repeat = 10; + instance->encoder.front = 0; + instance->encoder.is_running = false; + instance->is_running = false; + } + + return instance; } void subghz_protocol_encoder_psa_free(void* context) { - UNUSED(context); + furi_assert(context); + SubGhzProtocolEncoderPSA* instance = context; + + if(instance->encoder.upload) { + free(instance->encoder.upload); + } + free(instance); } SubGhzProtocolStatus subghz_protocol_encoder_psa_deserialize(void* context, FlipperFormat* flipper_format) { - UNUSED(context); - UNUSED(flipper_format); - return SubGhzProtocolStatusError; + furi_assert(context); + SubGhzProtocolEncoderPSA* instance = context; + + FURI_LOG_I(TAG, "=== ENCODER DESERIALIZE ==="); + + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + FuriString* temp_str = furi_string_alloc(); + uint8_t hex_buffer[8]; + + do { + if(!flipper_format_read_string(flipper_format, "Key", temp_str)) { + if(!flipper_format_read_hex(flipper_format, "Key1", hex_buffer, 8)) { + FURI_LOG_E(TAG, "Failed to read Key1"); + break; + } + instance->key1_low = ((uint32_t)hex_buffer[0] << 24) | ((uint32_t)hex_buffer[1] << 16) | + ((uint32_t)hex_buffer[2] << 8) | (uint32_t)hex_buffer[3]; + instance->key1_high = ((uint32_t)hex_buffer[4] << 24) | ((uint32_t)hex_buffer[5] << 16) | + ((uint32_t)hex_buffer[6] << 8) | (uint32_t)hex_buffer[7]; + } else { + const char* key_str = furi_string_get_cstr(temp_str); + uint64_t key1 = 0; + size_t str_len = strlen(key_str); + for(size_t i = 0; i < str_len && i < 16; i++) { + char c = key_str[i]; + if(c == ' ') continue; + uint8_t nibble; + if(c >= '0' && c <= '9') { + nibble = c - '0'; + } else if(c >= 'A' && c <= 'F') { + nibble = c - 'A' + 10; + } else if(c >= 'a' && c <= 'f') { + nibble = c - 'a' + 10; + } else { + break; + } + key1 = (key1 << 4) | nibble; + } + instance->key1_low = (uint32_t)(key1 & 0xFFFFFFFF); + instance->key1_high = (uint32_t)((key1 >> 32) & 0xFFFFFFFF); + } + + flipper_format_rewind(flipper_format); + if(flipper_format_read_string(flipper_format, "Key_2", temp_str)) { + const char* key2_str = furi_string_get_cstr(temp_str); + uint64_t key2 = 0; + size_t str_len = strlen(key2_str); + for(size_t i = 0; i < str_len && i < 16; i++) { + char c = key2_str[i]; + if(c == ' ') continue; + uint8_t nibble; + if(c >= '0' && c <= '9') { + nibble = c - '0'; + } else if(c >= 'A' && c <= 'F') { + nibble = c - 'A' + 10; + } else if(c >= 'a' && c <= 'f') { + nibble = c - 'a' + 10; + } else { + break; + } + key2 = (key2 << 4) | nibble; + } + instance->key2_low = (uint32_t)(key2 & 0xFFFFFFFF); + instance->validation_field = (uint16_t)(key2 & 0xFFFF); + } else { + uint32_t val_field_val; + if(flipper_format_read_uint32(flipper_format, "ValidationField", &val_field_val, 1)) { + instance->validation_field = (uint16_t)(val_field_val & 0xFFFF); + instance->key2_low = instance->validation_field; + } else { + uint8_t val_field[2]; + if(flipper_format_read_hex(flipper_format, "ValidationField", val_field, 2)) { + instance->validation_field = ((uint16_t)val_field[0] << 8) | val_field[1]; + instance->key2_low = instance->validation_field; + } else { + FURI_LOG_E(TAG, "ValidationField not found"); + break; + } + } + } + + flipper_format_rewind(flipper_format); + if(!flipper_format_read_uint32(flipper_format, "Serial", &instance->serial, 1)) { + FURI_LOG_E(TAG, "Serial not found"); + break; + } + + flipper_format_rewind(flipper_format); + if(!flipper_format_read_uint32(flipper_format, "Cnt", &instance->counter, 1)) { + FURI_LOG_E(TAG, "Counter not found"); + break; + } + + flipper_format_rewind(flipper_format); + uint32_t btn_val = 0; + if(!flipper_format_read_uint32(flipper_format, "Btn", &btn_val, 1)) { + FURI_LOG_E(TAG, "Button not found"); + break; + } + instance->button = (uint8_t)(btn_val & 0xFF); + + flipper_format_rewind(flipper_format); + uint32_t type_val = 0; + if(!flipper_format_read_uint32(flipper_format, "Type", &type_val, 1)) { + FURI_LOG_E(TAG, "Type not found"); + break; + } + instance->type = (uint8_t)(type_val & 0xFF); + + flipper_format_rewind(flipper_format); + uint32_t crc_val = 0; + if(!flipper_format_read_uint32(flipper_format, "CRC", &crc_val, 1)) { + FURI_LOG_E(TAG, "CRC not found"); + break; + } + instance->crc = (uint16_t)(crc_val & 0xFFFF); + + flipper_format_rewind(flipper_format); + uint32_t seed_val = 0; + if(!flipper_format_read_uint32(flipper_format, "Seed", &seed_val, 1)) { + FURI_LOG_E(TAG, "Seed not found"); + break; + } + instance->seed = (uint8_t)(seed_val & 0xFF); + + instance->mode = instance->type; + if(instance->mode == 0x23 || instance->mode == 0) { + instance->mode = 0x23; + } else if(instance->mode == 0x36) { + instance->mode = 0x36; + } else { + instance->mode = 0x23; + } + + FURI_LOG_I(TAG, "=== LOADED VALUES ==="); + FURI_LOG_I(TAG, "Key1: %08lX%08lX", (unsigned long)instance->key1_high, (unsigned long)instance->key1_low); + FURI_LOG_I(TAG, "Key2: %04X (validation_field: %04X)", + (unsigned int)instance->key2_low, (unsigned int)instance->validation_field); + FURI_LOG_I(TAG, "Serial: %06lX, Counter: %08lX, CRC: %02X, Button: %01X, Type/Mode: 0x%02X", + (unsigned long)instance->serial, (unsigned long)instance->counter, + (unsigned int)instance->crc, (unsigned int)instance->button, (unsigned int)instance->mode); + + psa_encoder_build_upload(instance); + + instance->is_running = true; + ret = SubGhzProtocolStatusOk; + } while(false); + + furi_string_free(temp_str); + return ret; } void subghz_protocol_encoder_psa_stop(void* context) { - UNUSED(context); + furi_assert(context); + SubGhzProtocolEncoderPSA* instance = context; + instance->is_running = false; + instance->encoder.is_running = false; } LevelDuration subghz_protocol_encoder_psa_yield(void* context) { - UNUSED(context); - return level_duration_reset(); + furi_assert(context); + SubGhzProtocolEncoderPSA* instance = context; + + if(!instance->is_running || instance->encoder.size_upload == 0) { + instance->is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + instance->encoder.front++; + + if(instance->encoder.front >= instance->encoder.size_upload) { + instance->encoder.front = 0; + instance->encoder.repeat--; + if(instance->encoder.repeat <= 0) { + instance->is_running = false; + } + } + + return ret; } static uint32_t psa_abs_diff(uint32_t a, uint32_t b) { @@ -529,6 +1042,7 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati return; } new_state = PSADecoderState3; + FURI_LOG_I(TAG, "[State0->State3] Detected te=125 pattern (Type 0x23)"); } else { new_state = PSADecoderState1; } @@ -590,6 +1104,8 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati } if(long_diff < 100) { if(instance->pattern_counter > PSA_PATTERN_THRESHOLD_1) { + FURI_LOG_I(TAG, "[State1->State2] Transition detected with pattern_cnt=%lu > %d", + (unsigned long)instance->pattern_counter, PSA_PATTERN_THRESHOLD_1); new_state = PSADecoderState2; instance->decode_data_low = 0; instance->decode_data_high = 0; @@ -597,6 +1113,11 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati manchester_advance(instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); instance->state = new_state; + } else { + FURI_LOG_D(TAG, "[State1] LONG(%lu) seen but pattern_cnt=%lu <= %d", + (unsigned long)duration, + (unsigned long)instance->pattern_counter, + PSA_PATTERN_THRESHOLD_1); } instance->pattern_counter = 0; instance->prev_duration = duration; @@ -829,50 +1350,75 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati break; case PSADecoderState3: - if(level) { + if(duration >= PSA_TE_LONG_250 && duration < 0x12c) { + if(instance->pattern_counter > PSA_PATTERN_THRESHOLD_2) { + FURI_LOG_I(TAG, "[State3->State4] Transition detected with pattern_cnt=%lu > %d", + (unsigned long)instance->pattern_counter, PSA_PATTERN_THRESHOLD_2); + new_state = PSADecoderState4; + instance->decode_data_low = 0; + instance->decode_data_high = 0; + instance->decode_count_bit = 0; + manchester_advance(instance->manchester_state, ManchesterEventReset, + &instance->manchester_state, NULL); + instance->state = new_state; + } else { + FURI_LOG_W(TAG, "[State3] %s(%lu) seen but pattern_cnt=%lu <= %d", + level ? "HIGH" : "LOW", + (unsigned long)duration, + (unsigned long)instance->pattern_counter, + PSA_PATTERN_THRESHOLD_2); + } + instance->pattern_counter = 0; + instance->prev_duration = duration; return; } if(duration < PSA_TE_SHORT_125) { tolerance = PSA_TE_SHORT_125 - duration; - if(tolerance < PSA_TOLERANCE_50) { - uint32_t prev_diff = psa_abs_diff(prev_dur, PSA_TE_SHORT_125); - if(prev_diff <= PSA_TOLERANCE_49) { - instance->pattern_counter++; - } else { - instance->pattern_counter = 0; - } - instance->prev_duration = duration; - return; - } } else { tolerance = duration - PSA_TE_SHORT_125; - if(tolerance < PSA_TOLERANCE_50) { - uint32_t prev_diff = psa_abs_diff(prev_dur, PSA_TE_SHORT_125); - if(prev_diff <= PSA_TOLERANCE_49) { - instance->pattern_counter++; - } else { - instance->pattern_counter = 0; - } - instance->prev_duration = duration; - return; - } else if(duration >= PSA_TE_LONG_250 && duration < 0x12c) { - if(instance->pattern_counter > PSA_PATTERN_THRESHOLD_2) { - new_state = PSADecoderState4; - instance->decode_data_low = 0; - instance->decode_data_high = 0; - instance->decode_count_bit = 0; - manchester_advance(instance->manchester_state, ManchesterEventReset, - &instance->manchester_state, NULL); - instance->state = new_state; - } - instance->pattern_counter = 0; - instance->prev_duration = duration; - return; - } } - new_state = PSADecoderState0; + if(tolerance < PSA_TOLERANCE_50) { + uint32_t prev_diff = psa_abs_diff(prev_dur, PSA_TE_SHORT_125); + if(prev_diff <= PSA_TOLERANCE_49) { + instance->pattern_counter++; + FURI_LOG_D(TAG, "[State3] %s(%lu) pattern_cnt=%lu (prev_diff=%lu)", + level ? "HIGH" : "LOW", + (unsigned long)duration, + (unsigned long)instance->pattern_counter, + (unsigned long)prev_diff); + } else { + if(instance->pattern_counter < 10) { + FURI_LOG_D(TAG, "[State3] %s(%lu) reset pattern_cnt (prev_diff=%lu > %d, cnt=%lu)", + level ? "HIGH" : "LOW", + (unsigned long)duration, + (unsigned long)prev_diff, + PSA_TOLERANCE_49, + (unsigned long)instance->pattern_counter); + instance->pattern_counter = 0; + } else { + FURI_LOG_D(TAG, "[State3] %s(%lu) pattern_cnt kept at %lu (prev_diff=%lu, slight variation)", + level ? "HIGH" : "LOW", + (unsigned long)duration, + (unsigned long)instance->pattern_counter, + (unsigned long)prev_diff); + } + } + instance->prev_duration = duration; + return; + } + + if(duration > 500) { + new_state = PSADecoderState0; + } else { + FURI_LOG_D(TAG, "[State3] %s(%lu) ignored (tolerance=%lu >= %d), staying in State3", + level ? "HIGH" : "LOW", + (unsigned long)duration, + (unsigned long)tolerance, + PSA_TOLERANCE_50); + return; + } break; case PSADecoderState4: @@ -1031,6 +1577,26 @@ SubGhzProtocolStatus subghz_protocol_decoder_psa_serialize( snprintf(key2_str, sizeof(key2_str), "%016llX", key2); if(!flipper_format_write_string_cstr(flipper_format, "Key_2", key2_str)) break; + uint32_t val_field = instance->validation_field; + flipper_format_write_uint32(flipper_format, "ValidationField", &val_field, 1); + + if(instance->decrypted == 0x50 && instance->decrypted_type != 0) { + flipper_format_write_uint32(flipper_format, "Serial", &instance->decrypted_serial, 1); + + uint32_t btn_temp = instance->decrypted_button; + flipper_format_write_uint32(flipper_format, "Btn", &btn_temp, 1); + + flipper_format_write_uint32(flipper_format, "Cnt", &instance->decrypted_counter, 1); + + uint32_t crc_temp = instance->decrypted_crc; + flipper_format_write_uint32(flipper_format, "CRC", &crc_temp, 1); + + uint32_t type_temp = instance->decrypted_type; + flipper_format_write_uint32(flipper_format, "Type", &type_temp, 1); + + flipper_format_write_uint32(flipper_format, "Seed", &instance->decrypted_seed, 1); + } + ret = SubGhzProtocolStatusOk; } while(false); diff --git a/scenes/protopirate_scene_receiver_info.c b/scenes/protopirate_scene_receiver_info.c index 570f821..4641772 100644 --- a/scenes/protopirate_scene_receiver_info.c +++ b/scenes/protopirate_scene_receiver_info.c @@ -40,6 +40,19 @@ void protopirate_scene_receiver_info_on_enter(void* context) { furi_string_reset(text); protopirate_history_get_text_item(app->txrx->history, text, app->txrx->idx_menu_chosen); + bool is_psa = false; + FlipperFormat* ff = protopirate_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen); + if(ff) { + FuriString* protocol = furi_string_alloc(); + flipper_format_rewind(ff); + if(flipper_format_read_string(ff, "Protocol", protocol)) { + if(furi_string_cmp_str(protocol, "PSA") == 0) { + is_psa = true; + } + } + furi_string_free(protocol); + } + // Skip the first line (protocol name + Xbits) since it's already shown as the title const char* text_str = furi_string_get_cstr(text); const char* first_newline = strchr(text_str, '\r'); @@ -57,8 +70,71 @@ void protopirate_scene_receiver_info_on_enter(void* context) { } } - widget_add_string_multiline_element( - app->widget, 0, 11, AlignLeft, AlignTop, FontSecondary, text_str); + if(is_psa) { + FuriString* reformatted = furi_string_alloc(); + const char* current = text_str; + + while(*current) { + + const char* line_end = strchr(current, '\r'); + if(!line_end) { + line_end = strchr(current, '\n'); + } + if(!line_end) { + line_end = current + strlen(current); + } + + if(strncmp(current, "Ser:", 4) == 0) { + + size_t ser_len = line_end - current; + furi_string_cat_printf(reformatted, "%.*s", (int)ser_len, current); + + const char* next_line = line_end; + if(*next_line == '\r') next_line++; + if(*next_line == '\n') next_line++; + + if(strncmp(next_line, "Cnt:", 4) == 0) { + + const char* cnt_end = strchr(next_line, '\r'); + if(!cnt_end) { + cnt_end = strchr(next_line, '\n'); + } + if(!cnt_end) { + cnt_end = next_line + strlen(next_line); + } + + size_t cnt_len = cnt_end - next_line; + furi_string_cat_printf(reformatted, " %.*s\r\n", (int)cnt_len, next_line); + + current = cnt_end; + if(*current == '\r') current++; + if(*current == '\n') current++; + } else { + + furi_string_cat_printf(reformatted, "\r\n"); + current = line_end; + if(*current == '\r') current++; + if(*current == '\n') current++; + } + } else { + + size_t line_len = line_end - current; + furi_string_cat_printf(reformatted, "%.*s\r\n", (int)line_len, current); + current = line_end; + if(*current == '\r') current++; + if(*current == '\n') current++; + } + + if(*current == '\0') break; + } + + widget_add_string_multiline_element( + app->widget, 0, 11, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(reformatted)); + furi_string_free(reformatted); + } else { + widget_add_string_multiline_element( + app->widget, 0, 11, AlignLeft, AlignTop, FontSecondary, text_str); + } #ifdef ENABLE_EMULATE_FEATURE // Add emulate button on the left diff --git a/scenes/protopirate_scene_saved_info.c b/scenes/protopirate_scene_saved_info.c index 2369921..f512ac0 100644 --- a/scenes/protopirate_scene_saved_info.c +++ b/scenes/protopirate_scene_saved_info.c @@ -90,6 +90,26 @@ 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_string(ff, "Key", temp_str)) { + furi_string_cat_printf(info_str, "Key1: %s\n", furi_string_get_cstr(temp_str)); + } + + flipper_format_rewind(ff); + if(flipper_format_read_string(ff, "Key_2", temp_str)) { + furi_string_cat_printf(info_str, "Key2: %s\n", furi_string_get_cstr(temp_str)); + } + + flipper_format_rewind(ff); + if(flipper_format_read_uint32(ff, "ValidationField", &temp_data, 1)) { + furi_string_cat_printf(info_str, "ValField: %04X\n", (uint16_t)temp_data); + } + + flipper_format_rewind(ff); + if(flipper_format_read_uint32(ff, "Seed", &temp_data, 1)) { + furi_string_cat_printf(info_str, "Seed: %02X\n", (uint8_t)temp_data); + } + flipper_format_rewind(ff); if(flipper_format_read_string(ff, "Manufacture", temp_str)) { furi_string_cat_printf(