diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 0d1a7774..b9e0e7a0 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -74,6 +74,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = { &subghz_protocol_mitsubishi_v0, &subghz_protocol_star_line, &subghz_protocol_scher_khan, + &subghz_protocol_sheriff_cfm, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 3d218e88..d58db590 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -76,3 +76,4 @@ #include "mazda_siemens.h" #include "star_line.h" #include "scher_khan.h" +#include "sheriff_cfm.h" diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index 9f574850..b1b8b32b 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -2,8 +2,129 @@ #include "../blocks/custom_btn_i.h" #include "../blocks/generic.h" +#include + #define TAG "SubGhzProtocolScherKhan" +static const uint8_t sk_pi_bytes[146] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89, + 0x45, 0x28, 0x21, 0xE6, 0x38, 0xD0, 0x13, 0x77, + 0xBE, 0x54, 0x66, 0xCF, 0x34, 0xE9, 0x0C, 0x6C, + 0xC0, 0xAC, 0x29, 0xB7, 0xC9, 0x7C, 0x50, 0xDD, + 0x3F, 0x84, 0xD5, 0xB5, 0xB5, 0x47, 0x09, 0x17, + 0x92, 0x16, 0xD5, 0xD9, 0x89, 0x79, 0xFB, 0x1B, + 0xD1, 0x31, 0x0B, 0xA6, 0x98, 0xDF, 0xB5, 0xAC, + 0x2F, 0xFD, 0x72, 0xDB, 0xD0, 0x1A, 0xDF, 0xB7, + 0xB8, 0xE1, 0xAF, 0xED, 0x6A, 0x26, 0x7E, 0x96, + 0xBA, 0x7C, 0x90, 0x45, 0xF1, 0x2C, 0x7F, 0x99, + 0x24, 0xA1, 0x99, 0x47, 0xB3, 0x91, 0x6C, 0xF7, + 0x08, 0x01, 0xF2, 0xE2, 0x85, 0x8E, 0xFC, 0x16, + 0x63, 0x69, 0x20, 0xD8, 0x71, 0x57, 0x4E, 0x69, + 0xA4, 0x58, 0xFE, 0xA3, 0xF4, 0x93, 0x3D, 0x7E, + 0x0D, 0x95, 0x74, 0x8F, 0x72, 0x8E, 0xB6, 0x58, + 0x71, 0x8B, +}; + +static const uint8_t sk_pro1_encoded[64] = { + 0x63, 0x5A, 0x58, 0x98, 0xD6, 0xB3, 0x7E, 0x91, + 0x37, 0x1E, 0xBB, 0x78, 0x43, 0x06, 0x20, 0x65, + 0xC7, 0x4B, 0x3F, 0x73, 0x0D, 0xAE, 0x36, 0x86, + 0x1E, 0x7A, 0xDA, 0xEB, 0xBB, 0x0F, 0x5A, 0xA9, + 0x26, 0x3A, 0x51, 0xB2, 0x7A, 0xE0, 0x44, 0x61, + 0xAC, 0x53, 0x02, 0xFA, 0x42, 0xBD, 0x2D, 0x6F, + 0x85, 0xBB, 0x4A, 0xB5, 0xF9, 0x28, 0x46, 0xAF, + 0x2B, 0xB4, 0xB2, 0x90, 0xD0, 0x05, 0x19, 0x64, +}; + +static const uint8_t sk_pro2_encoded[64] = { + 0x90, 0x27, 0xA3, 0x9C, 0xEA, 0x6B, 0x8B, 0x4F, + 0x91, 0x47, 0x58, 0x87, 0x8A, 0xD8, 0xD1, 0x99, + 0x3B, 0xCD, 0x15, 0xFE, 0xF4, 0x1D, 0xEE, 0xE1, + 0xBF, 0xA4, 0x9E, 0xCB, 0x1C, 0x72, 0x5F, 0x95, + 0xAC, 0x28, 0xB0, 0x36, 0xE3, 0x2B, 0x1B, 0xAC, + 0x30, 0x91, 0xFE, 0x62, 0xD0, 0xD3, 0x6B, 0xA6, + 0x4F, 0x64, 0xC0, 0xF2, 0xD2, 0xCF, 0xCA, 0x36, + 0x53, 0x3D, 0x36, 0xAA, 0x33, 0x67, 0x19, 0x7F, +}; + +static const uint8_t sk_pro_crc_encoded[2] = { + 0x5B, 0x8C, +}; + +static void sk_pi_decode(uint8_t* out, const uint8_t* encoded, size_t offset, size_t len) { + for(size_t i = 0; i < len; i++) { + out[i] = encoded[i] ^ sk_pi_bytes[offset + i]; + } +} + +static uint8_t scher_khan_pro_checksum(const uint8_t* data, uint8_t poly) { + uint8_t cs = 0xFF; + for(uint8_t a = 0; a < 6; a++) { + uint8_t c = data[a]; + for(uint8_t b = 0; b < 8; b++) { + uint8_t _cs = cs; + cs <<= 1; + if((_cs ^ c) & 0x80) cs ^= poly; + c <<= 1; + } + } + return cs; +} + +static void scher_khan_pro_decrypt( + const uint8_t* encrypted, + uint8_t* decrypted, + const uint8_t* key_table) { + memset(decrypted, 0, 7); + decrypted[0] = encrypted[0]; + uint8_t pkt_cnt = encrypted[0] & 0x0F; + + for(int i = 1; i < 6; i++) { + for(int j = 0; j < 4; j++) { + uint8_t cr = key_table[(pkt_cnt << 2) + j]; + uint8_t s = encrypted[i]; + + uint8_t bit = 0x80 >> (cr >> 4); + if((s & bit) == bit) decrypted[i] |= 0x80 >> (j << 1); + + bit = 0x80 >> (cr & 0x0F); + if((s & bit) == bit) decrypted[i] |= 0x80 >> ((j << 1) + 1); + } + pkt_cnt = (pkt_cnt + 1) & 0x0F; + decrypted[i] ^= encrypted[i - 1]; + } +} + +static void scher_khan_pro_encrypt( + const uint8_t* plain, + uint8_t* encrypted, + const uint8_t* key_table) { + uint8_t src[7]; + memcpy(src, plain, 7); + memset(encrypted, 0, 7); + encrypted[0] = src[0]; + uint8_t pkt_cnt = src[0] & 0x0F; + + for(int i = 1; i < 6; i++) { + src[i] ^= encrypted[i - 1]; + + for(int j = 0; j < 4; j++) { + uint8_t cr = key_table[(pkt_cnt << 2) + j]; + uint8_t s = src[i]; + + uint8_t bit = 0x80 >> (j << 1); + if((s & bit) == bit) encrypted[i] |= 0x80 >> (cr >> 4); + + bit = 0x80 >> ((j << 1) + 1); + if((s & bit) == bit) encrypted[i] |= 0x80 >> (cr & 0x0F); + } + pkt_cnt = (pkt_cnt + 1) & 0x0F; + } +} + static const char* scher_khan_btn_name(uint8_t btn) { switch(btn) { case 0x1: return "Lock"; @@ -163,6 +284,52 @@ static bool subghz_protocol_encoder_scher_khan_get_upload( instance->generic.btn = btn; } + if(instance->generic.data_count_bit == 57) { + if((instance->generic.cnt + 1) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += 1; + } + + uint8_t kb[7]; + for(int i = 0; i < 7; i++) { + kb[i] = (uint8_t)((instance->generic.data >> (48 - i * 8)) & 0xFF); + } + + uint8_t crc_polys[2]; + sk_pi_decode(crc_polys, sk_pro_crc_encoded, 144, 2); + + uint8_t key_table[64]; + uint8_t poly; + uint8_t crc1 = scher_khan_pro_checksum(kb, crc_polys[0]); + if(crc1 == kb[6]) { + sk_pi_decode(key_table, sk_pro1_encoded, 0, 64); + poly = crc_polys[0]; + } else { + sk_pi_decode(key_table, sk_pro2_encoded, 64, 64); + poly = crc_polys[1]; + } + + uint8_t decrypted[7]; + scher_khan_pro_decrypt(kb, decrypted, key_table); + + decrypted[3] = (decrypted[3] & 0xE0) | (btn & 0x0F); + decrypted[4] = (uint8_t)(instance->generic.cnt >> 8); + decrypted[5] = (uint8_t)(instance->generic.cnt & 0xFF); + + uint8_t encrypted[7]; + scher_khan_pro_encrypt(decrypted, encrypted, key_table); + + encrypted[6] = scher_khan_pro_checksum(encrypted, poly); + + instance->generic.data = 0; + for(int i = 0; i < 7; i++) { + instance->generic.data |= ((uint64_t)encrypted[i] << (48 - i * 8)); + } + instance->generic.data &= 0x01FFFFFFFFFFFFFFULL; + instance->generic.btn = btn; + } + size_t index = 0; size_t needed = (6 * 2) + (3 * 2) + (1 * 2) + (instance->generic.data_count_bit * 2) + 1; if(needed > instance->encoder.size_upload) { @@ -567,12 +734,53 @@ static void subghz_protocol_scher_khan_check_remote_controller( instance->btn = (instance->data >> 24) & 0x0F; instance->cnt = instance->data & 0xFFFF; break; - case 57: - *protocol_name = "MAGIC CODE PRO/PRO2"; - instance->serial = 0; - instance->btn = 0; - instance->cnt = 0; + case 57: { + uint8_t kb[7]; + for(int i = 0; i < 7; i++) { + kb[i] = (uint8_t)((instance->data >> (48 - i * 8)) & 0xFF); + } + + uint8_t crc_polys[2]; + sk_pi_decode(crc_polys, sk_pro_crc_encoded, 144, 2); + + uint8_t key_table[64]; + uint8_t decrypted[7]; + bool found = false; + + uint8_t computed_crc = scher_khan_pro_checksum(kb, crc_polys[0]); + if(computed_crc == kb[6]) { + sk_pi_decode(key_table, sk_pro1_encoded, 0, 64); + scher_khan_pro_decrypt(kb, decrypted, key_table); + *protocol_name = "MAGIC CODE PRO1"; + found = true; + } + + if(!found) { + computed_crc = scher_khan_pro_checksum(kb, crc_polys[1]); + if(computed_crc == kb[6]) { + sk_pi_decode(key_table, sk_pro2_encoded, 64, 64); + scher_khan_pro_decrypt(kb, decrypted, key_table); + *protocol_name = "MAGIC CODE PRO2"; + found = true; + } + } + + if(found) { + instance->serial = + ((uint32_t)decrypted[0] << 16) | + ((uint32_t)decrypted[1] << 8) | + (uint32_t)decrypted[2]; + instance->btn = decrypted[3] & 0x0F; + instance->cnt = + ((uint16_t)decrypted[4] << 8) | decrypted[5]; + } else { + *protocol_name = "MAGIC CODE PRO/PRO2"; + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + } break; + } case 63: *protocol_name = "MAGIC CODE, Response"; instance->serial = 0; diff --git a/lib/subghz/protocols/sheriff_cfm.c b/lib/subghz/protocols/sheriff_cfm.c new file mode 100644 index 00000000..c8be8682 --- /dev/null +++ b/lib/subghz/protocols/sheriff_cfm.c @@ -0,0 +1,634 @@ +#include "sheriff_cfm.h" +#include "keeloq_common.h" +#include "../blocks/custom_btn_i.h" +#include "../blocks/generic.h" + +#include + +#define TAG "SubGhzProtocolSheriffCfm" + +static const uint8_t cfm_pi_bytes[] = { + 0xA4, 0x58, 0xFE, 0xA3, 0xF4, 0x93, 0x3D, 0x7E, + 0x0D, 0x95, 0x74, 0x8F, 0x72, 0x8E, 0xB6, 0x58, +}; + +static const uint8_t cfm_zx750_encoded[8] = { + 0x32, 0x4D, 0xCB, 0x84, 0x5F, 0xE9, 0x27, 0xCB, +}; + +static const uint8_t cfm_zx930_encoded[8] = { + 0x94, 0x3B, 0x63, 0xA5, 0xE8, 0xF3, 0xAB, 0x60, +}; + +static void cfm_pi_decode(uint8_t* out, const uint8_t* encoded, size_t pi_offset, size_t len) { + for(size_t i = 0; i < len; i++) { + out[i] = encoded[i] ^ cfm_pi_bytes[pi_offset + i]; + } +} + +static uint8_t cfm_rlf(uint8_t in) { + return (uint8_t)((in << 1) | (in >> 7)); +} + +static uint8_t cfm_rrf(uint8_t in) { + return (uint8_t)((in >> 1) | (in << 7)); +} + +static uint8_t cfm_swap(uint8_t in) { + return (uint8_t)((in << 4) | (in >> 4)); +} + +typedef enum { + SheriffCfmModelZX750 = 0, + SheriffCfmModelZX930 = 1, + SheriffCfmModelCount = 2, +} SheriffCfmModel; + +static const char* cfm_model_name(SheriffCfmModel model) { + switch(model) { + case SheriffCfmModelZX750: return "ZX-750"; + case SheriffCfmModelZX930: return "ZX-930"; + default: return "?"; + } +} + +static void cfm_decrypt_transform(uint8_t* hop, SheriffCfmModel model) { + uint8_t temp; + switch(model) { + case SheriffCfmModelZX750: + hop[0] = cfm_swap(hop[0]); + hop[2] = cfm_swap(hop[2]); + break; + + case SheriffCfmModelZX930: + hop[0] = ~hop[0]; + temp = hop[1]; + hop[1] = hop[2]; + hop[2] = temp; + hop[0] = cfm_rrf(hop[0]); + hop[1] = cfm_swap(hop[1]); + hop[1] = cfm_rlf(hop[1]); + hop[1] = cfm_rlf(hop[1]); + break; + + default: + break; + } +} + +static void cfm_encrypt_transform(uint8_t* hop, SheriffCfmModel model) { + uint8_t temp; + switch(model) { + case SheriffCfmModelZX750: + hop[0] = cfm_swap(hop[0]); + hop[2] = cfm_swap(hop[2]); + break; + + case SheriffCfmModelZX930: + hop[1] = cfm_rrf(hop[1]); + hop[1] = cfm_rrf(hop[1]); + hop[1] = cfm_swap(hop[1]); + hop[0] = cfm_rlf(hop[0]); + hop[0] = ~hop[0]; + temp = hop[1]; + hop[1] = hop[2]; + hop[2] = temp; + break; + + default: + break; + } +} + +static const char* cfm_btn_name(uint8_t btn) { + switch(btn) { + case 0x10: return "Lock"; + case 0x20: return "Unlock"; + case 0x40: return "Trunk"; + case 0x80: return "Panic"; + default: return "?"; + } +} + +static uint8_t cfm_btn_to_custom(uint8_t btn) { + switch(btn) { + case 0x10: return SUBGHZ_CUSTOM_BTN_UP; + case 0x20: return SUBGHZ_CUSTOM_BTN_DOWN; + case 0x40: return SUBGHZ_CUSTOM_BTN_LEFT; + case 0x80: return SUBGHZ_CUSTOM_BTN_RIGHT; + default: return SUBGHZ_CUSTOM_BTN_OK; + } +} + +static uint8_t cfm_custom_to_btn(uint8_t custom, uint8_t original_btn) { + if(custom == SUBGHZ_CUSTOM_BTN_OK) return original_btn; + if(custom == SUBGHZ_CUSTOM_BTN_UP) return 0x10; + if(custom == SUBGHZ_CUSTOM_BTN_DOWN) return 0x20; + if(custom == SUBGHZ_CUSTOM_BTN_LEFT) return 0x40; + if(custom == SUBGHZ_CUSTOM_BTN_RIGHT) return 0x80; + return original_btn; +} + +static uint8_t cfm_get_btn_code(uint8_t original_btn) { + return cfm_custom_to_btn(subghz_custom_btn_get(), original_btn); +} + +static const SubGhzBlockConst subghz_protocol_sheriff_cfm_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 64, +}; + +struct SubGhzProtocolDecoderSheriffCfm { + SubGhzProtocolDecoderBase base; + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint16_t header_count; + SheriffCfmModel model; +}; + +struct SubGhzProtocolEncoderSheriffCfm { + SubGhzProtocolEncoderBase base; + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + SheriffCfmModel model; +}; + +typedef enum { + SheriffCfmDecoderStepReset = 0, + SheriffCfmDecoderStepCheckPreambula, + SheriffCfmDecoderStepSaveDuration, + SheriffCfmDecoderStepCheckDuration, +} SheriffCfmDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_sheriff_cfm_decoder = { + .alloc = subghz_protocol_decoder_sheriff_cfm_alloc, + .free = subghz_protocol_decoder_sheriff_cfm_free, + .feed = subghz_protocol_decoder_sheriff_cfm_feed, + .reset = subghz_protocol_decoder_sheriff_cfm_reset, + .get_hash_data = subghz_protocol_decoder_sheriff_cfm_get_hash_data, + .serialize = subghz_protocol_decoder_sheriff_cfm_serialize, + .deserialize = subghz_protocol_decoder_sheriff_cfm_deserialize, + .get_string = subghz_protocol_decoder_sheriff_cfm_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_sheriff_cfm_encoder = { + .alloc = subghz_protocol_encoder_sheriff_cfm_alloc, + .free = subghz_protocol_encoder_sheriff_cfm_free, + .deserialize = subghz_protocol_encoder_sheriff_cfm_deserialize, + .stop = subghz_protocol_encoder_sheriff_cfm_stop, + .yield = subghz_protocol_encoder_sheriff_cfm_yield, +}; + +const SubGhzProtocol subghz_protocol_sheriff_cfm = { + .name = SUBGHZ_PROTOCOL_SHERIFF_CFM_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + .decoder = &subghz_protocol_sheriff_cfm_decoder, + .encoder = &subghz_protocol_sheriff_cfm_encoder, +}; + +static uint64_t cfm_get_mfkey(SheriffCfmModel model) { + uint8_t dkey[8]; + switch(model) { + case SheriffCfmModelZX750: + cfm_pi_decode(dkey, cfm_zx750_encoded, 0, 8); + break; + case SheriffCfmModelZX930: + cfm_pi_decode(dkey, cfm_zx930_encoded, 8, 8); + break; + default: + return 0; + } + uint64_t key = 0; + for(int i = 7; i >= 0; i--) { + key = (key << 8) | dkey[i]; + } + return key; +} + +static bool cfm_try_decrypt( + uint64_t data, + SheriffCfmModel* out_model, + uint8_t* out_btn, + uint32_t* out_serial, + uint16_t* out_cnt) { + + uint32_t hop_encrypted = (uint32_t)(data & 0xFFFFFFFF); + uint32_t fix = (uint32_t)(data >> 32); + + for(SheriffCfmModel model = 0; model < SheriffCfmModelCount; model++) { + uint8_t hop_bytes[4]; + hop_bytes[0] = (uint8_t)(hop_encrypted & 0xFF); + hop_bytes[1] = (uint8_t)((hop_encrypted >> 8) & 0xFF); + hop_bytes[2] = (uint8_t)((hop_encrypted >> 16) & 0xFF); + hop_bytes[3] = (uint8_t)((hop_encrypted >> 24) & 0xFF); + + cfm_decrypt_transform(hop_bytes, model); + + uint32_t hop_transformed = + (uint32_t)hop_bytes[0] | + ((uint32_t)hop_bytes[1] << 8) | + ((uint32_t)hop_bytes[2] << 16) | + ((uint32_t)hop_bytes[3] << 24); + + uint64_t mfkey = cfm_get_mfkey(model); + uint32_t decrypted = subghz_protocol_keeloq_common_decrypt(hop_transformed, mfkey); + + uint16_t dec_serial_lo = (uint16_t)((decrypted >> 16) & 0x3FF); + uint16_t fix_serial_lo = (uint16_t)(fix & 0x3FF); + + uint8_t btn_byte = (uint8_t)((decrypted >> 24) & 0xFF); + bool valid_btn = (btn_byte == 0x10 || btn_byte == 0x20 || + btn_byte == 0x40 || btn_byte == 0x80); + + if(valid_btn && (dec_serial_lo == fix_serial_lo)) { + *out_model = model; + *out_btn = btn_byte; + *out_serial = fix; + *out_cnt = (uint16_t)(decrypted & 0xFFFF); + return true; + } + } + return false; +} + +void* subghz_protocol_encoder_sheriff_cfm_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSheriffCfm* instance = malloc(sizeof(SubGhzProtocolEncoderSheriffCfm)); + instance->base.protocol = &subghz_protocol_sheriff_cfm; + instance->generic.protocol_name = instance->base.protocol->name; + instance->encoder.repeat = 3; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + instance->encoder.front = 0; + instance->model = SheriffCfmModelZX750; + return instance; +} + +void subghz_protocol_encoder_sheriff_cfm_free(void* context) { + furi_check(context); + SubGhzProtocolEncoderSheriffCfm* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static bool subghz_protocol_encoder_sheriff_cfm_get_upload( + SubGhzProtocolEncoderSheriffCfm* instance, + uint8_t btn) { + furi_check(instance); + + if((instance->generic.cnt + 1) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += 1; + } + + uint32_t fix = (uint32_t)(instance->generic.data >> 32); + uint16_t serial_lo = (uint16_t)(fix & 0x3FF); + uint32_t hop_plain = + ((uint32_t)btn << 24) | + ((uint32_t)serial_lo << 16) | + (instance->generic.cnt & 0xFFFF); + + uint64_t mfkey = cfm_get_mfkey(instance->model); + uint32_t hop_encrypted = subghz_protocol_keeloq_common_encrypt(hop_plain, mfkey); + + uint8_t hop_bytes[4]; + hop_bytes[0] = (uint8_t)(hop_encrypted & 0xFF); + hop_bytes[1] = (uint8_t)((hop_encrypted >> 8) & 0xFF); + hop_bytes[2] = (uint8_t)((hop_encrypted >> 16) & 0xFF); + hop_bytes[3] = (uint8_t)((hop_encrypted >> 24) & 0xFF); + cfm_encrypt_transform(hop_bytes, instance->model); + + hop_encrypted = + (uint32_t)hop_bytes[0] | + ((uint32_t)hop_bytes[1] << 8) | + ((uint32_t)hop_bytes[2] << 16) | + ((uint32_t)hop_bytes[3] << 24); + + instance->generic.data = ((uint64_t)fix << 32) | hop_encrypted; + instance->generic.btn = btn; + + size_t index = 0; + + for(uint8_t i = 11; i > 0; i--) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_sheriff_cfm_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_sheriff_cfm_const.te_short); + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_sheriff_cfm_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_sheriff_cfm_const.te_short * 10); + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_sheriff_cfm_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_sheriff_cfm_const.te_long); + } else { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_sheriff_cfm_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_sheriff_cfm_const.te_short); + } + } + + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_sheriff_cfm_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_sheriff_cfm_const.te_short * 40); + + instance->encoder.size_upload = index; + return true; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_sheriff_cfm_deserialize(void* context, FlipperFormat* flipper_format) { + furi_check(context); + SubGhzProtocolEncoderSheriffCfm* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + + do { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_sheriff_cfm_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) break; + + uint32_t model_temp = 0; + if(flipper_format_read_uint32(flipper_format, "Model", &model_temp, 1)) { + instance->model = (SheriffCfmModel)model_temp; + } else { + SheriffCfmModel detected; + uint8_t det_btn; + uint32_t det_serial; + uint16_t det_cnt; + if(cfm_try_decrypt(instance->generic.data, &detected, &det_btn, &det_serial, &det_cnt)) { + instance->model = detected; + instance->generic.btn = det_btn; + instance->generic.serial = det_serial; + instance->generic.cnt = det_cnt; + } + } + + if(!flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1)) { + instance->encoder.repeat = 3; + } + + subghz_custom_btn_set_original(cfm_btn_to_custom(instance->generic.btn)); + subghz_custom_btn_set_max(4); + + uint8_t selected_btn = cfm_get_btn_code(instance->generic.btn); + + if(!subghz_protocol_encoder_sheriff_cfm_get_upload(instance, selected_btn)) { + break; + } + + instance->encoder.is_running = true; + instance->encoder.front = 0; + ret = SubGhzProtocolStatusOk; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_sheriff_cfm_stop(void* context) { + furi_check(context); + SubGhzProtocolEncoderSheriffCfm* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_sheriff_cfm_yield(void* context) { + SubGhzProtocolEncoderSheriffCfm* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_sheriff_cfm_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSheriffCfm* instance = malloc(sizeof(SubGhzProtocolDecoderSheriffCfm)); + instance->base.protocol = &subghz_protocol_sheriff_cfm; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_sheriff_cfm_free(void* context) { + furi_check(context); + SubGhzProtocolDecoderSheriffCfm* instance = context; + free(instance); +} + +void subghz_protocol_decoder_sheriff_cfm_reset(void* context) { + furi_check(context); + SubGhzProtocolDecoderSheriffCfm* instance = context; + instance->decoder.parser_step = SheriffCfmDecoderStepReset; + instance->header_count = 0; +} + +void subghz_protocol_decoder_sheriff_cfm_feed(void* context, bool level, uint32_t duration) { + furi_check(context); + SubGhzProtocolDecoderSheriffCfm* instance = context; + + switch(instance->decoder.parser_step) { + case SheriffCfmDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_sheriff_cfm_const.te_short) < + subghz_protocol_sheriff_cfm_const.te_delta) { + instance->decoder.parser_step = SheriffCfmDecoderStepCheckPreambula; + instance->header_count++; + } + break; + + case SheriffCfmDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_sheriff_cfm_const.te_short) < + subghz_protocol_sheriff_cfm_const.te_delta)) { + instance->decoder.parser_step = SheriffCfmDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_sheriff_cfm_const.te_short * 10) < + subghz_protocol_sheriff_cfm_const.te_delta * 10)) { + instance->decoder.parser_step = SheriffCfmDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = SheriffCfmDecoderStepReset; + instance->header_count = 0; + } + break; + + case SheriffCfmDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = SheriffCfmDecoderStepCheckDuration; + } + break; + + case SheriffCfmDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_sheriff_cfm_const.te_short * 2 + + subghz_protocol_sheriff_cfm_const.te_delta)) { + instance->decoder.parser_step = SheriffCfmDecoderStepReset; + if((instance->decoder.decode_count_bit >= + subghz_protocol_sheriff_cfm_const.min_count_bit_for_found) && + (instance->decoder.decode_count_bit <= + subghz_protocol_sheriff_cfm_const.min_count_bit_for_found + 2)) { + SheriffCfmModel model; + uint8_t btn; + uint32_t serial; + uint16_t cnt; + if(cfm_try_decrypt( + instance->decoder.decode_data, &model, &btn, &serial, &cnt)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = + subghz_protocol_sheriff_cfm_const.min_count_bit_for_found; + instance->model = model; + instance->generic.btn = btn; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_sheriff_cfm_const.te_short) < + subghz_protocol_sheriff_cfm_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_sheriff_cfm_const.te_long) < + subghz_protocol_sheriff_cfm_const.te_delta * 2)) { + if(instance->decoder.decode_count_bit < + subghz_protocol_sheriff_cfm_const.min_count_bit_for_found) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.decode_count_bit++; + } + instance->decoder.parser_step = SheriffCfmDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_sheriff_cfm_const.te_long) < + subghz_protocol_sheriff_cfm_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_sheriff_cfm_const.te_short) < + subghz_protocol_sheriff_cfm_const.te_delta)) { + if(instance->decoder.decode_count_bit < + subghz_protocol_sheriff_cfm_const.min_count_bit_for_found) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + instance->decoder.decode_count_bit++; + } + instance->decoder.parser_step = SheriffCfmDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = SheriffCfmDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = SheriffCfmDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +uint8_t subghz_protocol_decoder_sheriff_cfm_get_hash_data(void* context) { + furi_check(context); + SubGhzProtocolDecoderSheriffCfm* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_sheriff_cfm_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_check(context); + SubGhzProtocolDecoderSheriffCfm* instance = context; + SubGhzProtocolStatus ret = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + if(ret == SubGhzProtocolStatusOk) { + uint32_t model = (uint32_t)instance->model; + flipper_format_insert_or_update_uint32(flipper_format, "Model", &model, 1); + } + return ret; +} + +SubGhzProtocolStatus + subghz_protocol_decoder_sheriff_cfm_deserialize(void* context, FlipperFormat* flipper_format) { + furi_check(context); + SubGhzProtocolDecoderSheriffCfm* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + + do { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_sheriff_cfm_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) break; + + uint32_t model_temp = 0; + if(flipper_format_read_uint32(flipper_format, "Model", &model_temp, 1)) { + instance->model = (SheriffCfmModel)model_temp; + } + + SheriffCfmModel detected; + uint8_t btn; + uint32_t serial; + uint16_t cnt; + if(cfm_try_decrypt(instance->generic.data, &detected, &btn, &serial, &cnt)) { + instance->model = detected; + instance->generic.btn = btn; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + } + } while(false); + + return ret; +} + +void subghz_protocol_decoder_sheriff_cfm_get_string(void* context, FuriString* output) { + furi_check(context); + SubGhzProtocolDecoderSheriffCfm* instance = context; + + subghz_custom_btn_set_original(cfm_btn_to_custom(instance->generic.btn)); + subghz_custom_btn_set_max(4); + + uint8_t selected_btn = cfm_get_btn_code(instance->generic.btn); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:%08lX Btn:[%s]\r\n" + "Cnt:%04lX Model:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.serial, + cfm_btn_name(selected_btn), + (uint32_t)instance->generic.cnt, + cfm_model_name(instance->model)); +} diff --git a/lib/subghz/protocols/sheriff_cfm.h b/lib/subghz/protocols/sheriff_cfm.h new file mode 100644 index 00000000..126cf993 --- /dev/null +++ b/lib/subghz/protocols/sheriff_cfm.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUBGHZ_PROTOCOL_SHERIFF_CFM_NAME "Sheriff CFM" + +typedef struct SubGhzProtocolDecoderSheriffCfm SubGhzProtocolDecoderSheriffCfm; +typedef struct SubGhzProtocolEncoderSheriffCfm SubGhzProtocolEncoderSheriffCfm; + +extern const SubGhzProtocolDecoder subghz_protocol_sheriff_cfm_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_sheriff_cfm_encoder; +extern const SubGhzProtocol subghz_protocol_sheriff_cfm; + +void* subghz_protocol_decoder_sheriff_cfm_alloc(SubGhzEnvironment* environment); +void subghz_protocol_decoder_sheriff_cfm_free(void* context); +void subghz_protocol_decoder_sheriff_cfm_reset(void* context); +void subghz_protocol_decoder_sheriff_cfm_feed(void* context, bool level, uint32_t duration); + +uint8_t subghz_protocol_decoder_sheriff_cfm_get_hash_data(void* context); + +SubGhzProtocolStatus subghz_protocol_decoder_sheriff_cfm_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +SubGhzProtocolStatus + subghz_protocol_decoder_sheriff_cfm_deserialize(void* context, FlipperFormat* flipper_format); + +void subghz_protocol_decoder_sheriff_cfm_get_string(void* context, FuriString* output); + +void* subghz_protocol_encoder_sheriff_cfm_alloc(SubGhzEnvironment* environment); +void subghz_protocol_encoder_sheriff_cfm_free(void* context); +SubGhzProtocolStatus + subghz_protocol_encoder_sheriff_cfm_deserialize(void* context, FlipperFormat* flipper_format); +void subghz_protocol_encoder_sheriff_cfm_stop(void* context); +LevelDuration subghz_protocol_encoder_sheriff_cfm_yield(void* context);