Merge pull request #80 from zero-mega/main

Add PSA Encoder
This commit is contained in:
MMX
2026-01-27 03:26:55 +03:00
committed by GitHub
4 changed files with 735 additions and 50 deletions

View File

@@ -147,6 +147,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);

View File

@@ -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);

View File

@@ -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

View File

@@ -148,6 +148,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(info_str, "Manufacture: %s\n", furi_string_get_cstr(temp_str));