SubGhz: Сreating and delivering Security+ 2.0 (#1273)
* SubGhz: Security+ 2.0 "Add manually" option * SubGhz: fix message error * Unit_test: add Security+ 2.0 encoder * Applications: remove obsolete code * SubGhz: save menu position in "Add Manually" menu Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									5c45250dd2
								
							
						
					
					
						commit
						67fbefbe63
					
				| @ -420,14 +420,6 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { | |||||||
|      .flags = FlipperApplicationFlagDefault}, |      .flags = FlipperApplicationFlagDefault}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_SCENED |  | ||||||
|     {.app = scened_app, |  | ||||||
|      .name = "Templated Scene", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_LF_RFID | #ifdef APP_LF_RFID | ||||||
|     {.app = lfrfid_debug_app, |     {.app = lfrfid_debug_app, | ||||||
|      .name = "LF-RFID Debug", |      .name = "LF-RFID Debug", | ||||||
|  | |||||||
| @ -1,35 +0,0 @@ | |||||||
| #include "scened_app_scene_byte_input.h" |  | ||||||
| 
 |  | ||||||
| void ScenedAppSceneByteInput::on_enter(ScenedApp* app, bool /* need_restore */) { |  | ||||||
|     ByteInputVM* byte_input = app->view_controller; |  | ||||||
|     auto callback = cbc::obtain_connector(this, &ScenedAppSceneByteInput::result_callback); |  | ||||||
| 
 |  | ||||||
|     byte_input->set_result_callback(callback, NULL, app, data, 4); |  | ||||||
|     byte_input->set_header_text("Enter the key"); |  | ||||||
| 
 |  | ||||||
|     app->view_controller.switch_to<ByteInputVM>(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool ScenedAppSceneByteInput::on_event(ScenedApp* app, ScenedApp::Event* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event->type == ScenedApp::EventType::ByteEditResult) { |  | ||||||
|         app->scene_controller.switch_to_previous_scene(); |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ScenedAppSceneByteInput::on_exit(ScenedApp* app) { |  | ||||||
|     app->view_controller.get<ByteInputVM>()->clean(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ScenedAppSceneByteInput::result_callback(void* context) { |  | ||||||
|     ScenedApp* app = static_cast<ScenedApp*>(context); |  | ||||||
|     ScenedApp::Event event; |  | ||||||
| 
 |  | ||||||
|     event.type = ScenedApp::EventType::ByteEditResult; |  | ||||||
| 
 |  | ||||||
|     app->view_controller.send_event(&event); |  | ||||||
| } |  | ||||||
| @ -1,19 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "../scened_app.h" |  | ||||||
| 
 |  | ||||||
| class ScenedAppSceneByteInput : public GenericScene<ScenedApp> { |  | ||||||
| public: |  | ||||||
|     void on_enter(ScenedApp* app, bool need_restore) final; |  | ||||||
|     bool on_event(ScenedApp* app, ScenedApp::Event* event) final; |  | ||||||
|     void on_exit(ScenedApp* app) final; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void result_callback(void* context); |  | ||||||
| 
 |  | ||||||
|     uint8_t data[4] = { |  | ||||||
|         0x01, |  | ||||||
|         0xA2, |  | ||||||
|         0xF4, |  | ||||||
|         0xD3, |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| @ -1,47 +0,0 @@ | |||||||
| #include "scened_app_scene_start.h" |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     SubmenuByteInput, |  | ||||||
| } SubmenuIndex; |  | ||||||
| 
 |  | ||||||
| void ScenedAppSceneStart::on_enter(ScenedApp* app, bool need_restore) { |  | ||||||
|     auto submenu = app->view_controller.get<SubmenuVM>(); |  | ||||||
|     auto callback = cbc::obtain_connector(this, &ScenedAppSceneStart::submenu_callback); |  | ||||||
| 
 |  | ||||||
|     submenu->add_item("Byte Input", SubmenuByteInput, callback, app); |  | ||||||
| 
 |  | ||||||
|     if(need_restore) { |  | ||||||
|         submenu->set_selected_item(submenu_item_selected); |  | ||||||
|     } |  | ||||||
|     app->view_controller.switch_to<SubmenuVM>(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool ScenedAppSceneStart::on_event(ScenedApp* app, ScenedApp::Event* event) { |  | ||||||
|     bool consumed = false; |  | ||||||
| 
 |  | ||||||
|     if(event->type == ScenedApp::EventType::MenuSelected) { |  | ||||||
|         submenu_item_selected = event->payload.menu_index; |  | ||||||
|         switch(event->payload.menu_index) { |  | ||||||
|         case SubmenuByteInput: |  | ||||||
|             app->scene_controller.switch_to_next_scene(ScenedApp::SceneType::ByteInputScene); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         consumed = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return consumed; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ScenedAppSceneStart::on_exit(ScenedApp* app) { |  | ||||||
|     app->view_controller.get<SubmenuVM>()->clean(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ScenedAppSceneStart::submenu_callback(void* context, uint32_t index) { |  | ||||||
|     ScenedApp* app = static_cast<ScenedApp*>(context); |  | ||||||
|     ScenedApp::Event event; |  | ||||||
| 
 |  | ||||||
|     event.type = ScenedApp::EventType::MenuSelected; |  | ||||||
|     event.payload.menu_index = index; |  | ||||||
| 
 |  | ||||||
|     app->view_controller.send_event(&event); |  | ||||||
| } |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "../scened_app.h" |  | ||||||
| 
 |  | ||||||
| class ScenedAppSceneStart : public GenericScene<ScenedApp> { |  | ||||||
| public: |  | ||||||
|     void on_enter(ScenedApp* app, bool need_restore) final; |  | ||||||
|     bool on_event(ScenedApp* app, ScenedApp::Event* event) final; |  | ||||||
|     void on_exit(ScenedApp* app) final; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     void submenu_callback(void* context, uint32_t index); |  | ||||||
|     uint32_t submenu_item_selected = 0; |  | ||||||
| }; |  | ||||||
| @ -1,20 +0,0 @@ | |||||||
| #include "scened_app.h" |  | ||||||
| #include "scene/scened_app_scene_start.h" |  | ||||||
| #include "scene/scened_app_scene_byte_input.h" |  | ||||||
| 
 |  | ||||||
| ScenedApp::ScenedApp() |  | ||||||
|     : scene_controller{this} |  | ||||||
|     , text_store{128} |  | ||||||
|     , notification{"notification"} { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ScenedApp::~ScenedApp() { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ScenedApp::run() { |  | ||||||
|     scene_controller.add_scene(SceneType::Start, new ScenedAppSceneStart()); |  | ||||||
|     scene_controller.add_scene(SceneType::ByteInputScene, new ScenedAppSceneByteInput()); |  | ||||||
| 
 |  | ||||||
|     notification_message(notification, &sequence_blink_green_10); |  | ||||||
|     scene_controller.process(100); |  | ||||||
| } |  | ||||||
| @ -1,47 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| 
 |  | ||||||
| #include <generic_scene.hpp> |  | ||||||
| #include <scene_controller.hpp> |  | ||||||
| #include <view_controller.hpp> |  | ||||||
| #include <record_controller.hpp> |  | ||||||
| #include <text_store.h> |  | ||||||
| 
 |  | ||||||
| #include <view_modules/submenu_vm.h> |  | ||||||
| #include <view_modules/byte_input_vm.h> |  | ||||||
| 
 |  | ||||||
| #include <notification/notification_messages.h> |  | ||||||
| 
 |  | ||||||
| class ScenedApp { |  | ||||||
| public: |  | ||||||
|     enum class EventType : uint8_t { |  | ||||||
|         GENERIC_EVENT_ENUM_VALUES, |  | ||||||
|         MenuSelected, |  | ||||||
|         ByteEditResult, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     enum class SceneType : uint8_t { |  | ||||||
|         GENERIC_SCENE_ENUM_VALUES, |  | ||||||
|         ByteInputScene, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     class Event { |  | ||||||
|     public: |  | ||||||
|         union { |  | ||||||
|             int32_t menu_index; |  | ||||||
|         } payload; |  | ||||||
| 
 |  | ||||||
|         EventType type; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     SceneController<GenericScene<ScenedApp>, ScenedApp> scene_controller; |  | ||||||
|     TextStore text_store; |  | ||||||
|     ViewController<ScenedApp, SubmenuVM, ByteInputVM> view_controller; |  | ||||||
|     RecordController<NotificationApp> notification; |  | ||||||
| 
 |  | ||||||
|     ~ScenedApp(); |  | ||||||
|     ScenedApp(); |  | ||||||
| 
 |  | ||||||
|     void run(); |  | ||||||
| }; |  | ||||||
| @ -1,11 +0,0 @@ | |||||||
| #include "scened_app.h" |  | ||||||
| 
 |  | ||||||
| // app enter function
 |  | ||||||
| extern "C" int32_t scened_app(void* p) { |  | ||||||
|     UNUSED(p); |  | ||||||
|     ScenedApp* app = new ScenedApp(); |  | ||||||
|     app->run(); |  | ||||||
|     delete app; |  | ||||||
| 
 |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| @ -1,6 +1,7 @@ | |||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
| #include <lib/subghz/protocols/keeloq.h> | #include <lib/subghz/protocols/keeloq.h> | ||||||
| #include <lib/subghz/protocols/secplus_v1.h> | #include <lib/subghz/protocols/secplus_v1.h> | ||||||
|  | #include <lib/subghz/protocols/secplus_v2.h> | ||||||
| #include <lib/subghz/blocks/math.h> | #include <lib/subghz/blocks/math.h> | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| #include <flipper_format/flipper_format_i.h> | #include <flipper_format/flipper_format_i.h> | ||||||
| @ -24,6 +25,9 @@ enum SubmenuIndex { | |||||||
|     SubmenuIndexFirefly_300_00, |     SubmenuIndexFirefly_300_00, | ||||||
|     SubmenuIndexLiftMaster_315_00, |     SubmenuIndexLiftMaster_315_00, | ||||||
|     SubmenuIndexLiftMaster_390_00, |     SubmenuIndexLiftMaster_390_00, | ||||||
|  |     SubmenuIndexSecPlus_v2_310_00, | ||||||
|  |     SubmenuIndexSecPlus_v2_315_00, | ||||||
|  |     SubmenuIndexSecPlus_v2_390_00, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool subghz_scene_set_type_submenu_gen_data_protocol( | bool subghz_scene_set_type_submenu_gen_data_protocol( | ||||||
| @ -157,6 +161,24 @@ void subghz_scene_set_type_on_enter(void* context) { | |||||||
|         SubmenuIndexLiftMaster_390_00, |         SubmenuIndexLiftMaster_390_00, | ||||||
|         subghz_scene_set_type_submenu_callback, |         subghz_scene_set_type_submenu_callback, | ||||||
|         subghz); |         subghz); | ||||||
|  |     submenu_add_item( | ||||||
|  |         subghz->submenu, | ||||||
|  |         "Security+2.0_310", | ||||||
|  |         SubmenuIndexSecPlus_v2_310_00, | ||||||
|  |         subghz_scene_set_type_submenu_callback, | ||||||
|  |         subghz); | ||||||
|  |     submenu_add_item( | ||||||
|  |         subghz->submenu, | ||||||
|  |         "Security+2.0_315", | ||||||
|  |         SubmenuIndexSecPlus_v2_315_00, | ||||||
|  |         subghz_scene_set_type_submenu_callback, | ||||||
|  |         subghz); | ||||||
|  |     submenu_add_item( | ||||||
|  |         subghz->submenu, | ||||||
|  |         "Security+2.0_390", | ||||||
|  |         SubmenuIndexSecPlus_v2_390_00, | ||||||
|  |         subghz_scene_set_type_submenu_callback, | ||||||
|  |         subghz); | ||||||
| 
 | 
 | ||||||
|     submenu_set_selected_item( |     submenu_set_selected_item( | ||||||
|         subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType)); |         subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType)); | ||||||
| @ -330,7 +352,6 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|             while(!subghz_protocol_secplus_v1_check_fixed(key)) { |             while(!subghz_protocol_secplus_v1_check_fixed(key)) { | ||||||
|                 key = subghz_random_serial(); |                 key = subghz_random_serial(); | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |             if(subghz_scene_set_type_submenu_gen_data_protocol( | ||||||
|                    subghz, |                    subghz, | ||||||
|                    SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, |                    SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, | ||||||
| @ -345,7 +366,6 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|             while(!subghz_protocol_secplus_v1_check_fixed(key)) { |             while(!subghz_protocol_secplus_v1_check_fixed(key)) { | ||||||
|                 key = subghz_random_serial(); |                 key = subghz_random_serial(); | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             if(subghz_scene_set_type_submenu_gen_data_protocol( |             if(subghz_scene_set_type_submenu_gen_data_protocol( | ||||||
|                    subghz, |                    subghz, | ||||||
|                    SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, |                    SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, | ||||||
| @ -356,17 +376,70 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 generated_protocol = true; |                 generated_protocol = true; | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
| 
 |         case SubmenuIndexSecPlus_v2_310_00: | ||||||
|  |             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||||
|  |                 subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); | ||||||
|  |             if(subghz->txrx->transmitter) { | ||||||
|  |                 subghz_protocol_secplus_v2_create_data( | ||||||
|  |                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), | ||||||
|  |                     subghz->txrx->fff_data, | ||||||
|  |                     key, | ||||||
|  |                     0x68, | ||||||
|  |                     0xE500000, | ||||||
|  |                     310000000, | ||||||
|  |                     FuriHalSubGhzPresetOok650Async); | ||||||
|  |                 generated_protocol = true; | ||||||
|  |             } else { | ||||||
|  |                 generated_protocol = false; | ||||||
|  |             } | ||||||
|  |             subghz_transmitter_free(subghz->txrx->transmitter); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexSecPlus_v2_315_00: | ||||||
|  |             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||||
|  |                 subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); | ||||||
|  |             if(subghz->txrx->transmitter) { | ||||||
|  |                 subghz_protocol_secplus_v2_create_data( | ||||||
|  |                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), | ||||||
|  |                     subghz->txrx->fff_data, | ||||||
|  |                     key, | ||||||
|  |                     0x68, | ||||||
|  |                     0xE500000, | ||||||
|  |                     315000000, | ||||||
|  |                     FuriHalSubGhzPresetOok650Async); | ||||||
|  |                 generated_protocol = true; | ||||||
|  |             } else { | ||||||
|  |                 generated_protocol = false; | ||||||
|  |             } | ||||||
|  |             subghz_transmitter_free(subghz->txrx->transmitter); | ||||||
|  |             break; | ||||||
|  |         case SubmenuIndexSecPlus_v2_390_00: | ||||||
|  |             subghz->txrx->transmitter = subghz_transmitter_alloc_init( | ||||||
|  |                 subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); | ||||||
|  |             if(subghz->txrx->transmitter) { | ||||||
|  |                 subghz_protocol_secplus_v2_create_data( | ||||||
|  |                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), | ||||||
|  |                     subghz->txrx->fff_data, | ||||||
|  |                     key, | ||||||
|  |                     0x68, | ||||||
|  |                     0xE500000, | ||||||
|  |                     390000000, | ||||||
|  |                     FuriHalSubGhzPresetOok650Async); | ||||||
|  |                 generated_protocol = true; | ||||||
|  |             } else { | ||||||
|  |                 generated_protocol = false; | ||||||
|  |             } | ||||||
|  |             subghz_transmitter_free(subghz->txrx->transmitter); | ||||||
|  |             break; | ||||||
|         default: |         default: | ||||||
|             return false; |             return false; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSetType, event.event); | ||||||
|  | 
 | ||||||
|         if(generated_protocol) { |         if(generated_protocol) { | ||||||
|             subghz_file_name_clear(subghz); |             subghz_file_name_clear(subghz); | ||||||
|             DOLPHIN_DEED(DolphinDeedSubGhzAddManually); |             DOLPHIN_DEED(DolphinDeedSubGhzAddManually); | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet); |  | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -422,6 +422,12 @@ MU_TEST(subghz_encoder_secplus_v1_test) { | |||||||
|         "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n"); |         "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | MU_TEST(subghz_encoder_secplus_v2_test) { | ||||||
|  |     mu_assert( | ||||||
|  |         subghz_encoder_test("/ext/unit_tests/subghz/security_pls_2_0.sub"), | ||||||
|  |         "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| MU_TEST(subghz_random_test) { | MU_TEST(subghz_random_test) { | ||||||
|     mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); |     mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); | ||||||
| } | } | ||||||
| @ -464,6 +470,7 @@ MU_TEST_SUITE(subghz) { | |||||||
|     MU_RUN_TEST(subghz_encoder_megacode_test); |     MU_RUN_TEST(subghz_encoder_megacode_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_holtek_test); |     MU_RUN_TEST(subghz_encoder_holtek_test); | ||||||
|     MU_RUN_TEST(subghz_encoder_secplus_v1_test); |     MU_RUN_TEST(subghz_encoder_secplus_v1_test); | ||||||
|  |     MU_RUN_TEST(subghz_encoder_secplus_v2_test); | ||||||
| 
 | 
 | ||||||
|     MU_RUN_TEST(subghz_random_test); |     MU_RUN_TEST(subghz_random_test); | ||||||
|     subghz_test_deinit(); |     subghz_test_deinit(); | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								assets/unit_tests/subghz/security_pls_2_0.sub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								assets/unit_tests/subghz/security_pls_2_0.sub
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | Filetype: Flipper SubGhz Key File | ||||||
|  | Version: 1 | ||||||
|  | Frequency: 315000000 | ||||||
|  | Preset: FuriHalSubGhzPresetOok650Async | ||||||
|  | Protocol: Security+ 2.0 | ||||||
|  | Bit: 62 | ||||||
|  | Key: 00 00 3D 29 0F B9 BE EE | ||||||
|  | Secplus_packet_1: 00 00 3C 01 6B 19 DD 60 | ||||||
| @ -145,8 +145,7 @@ static LevelDuration | |||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     default: |     default: | ||||||
|         FURI_LOG_E(TAG, "DO CRASH HERE."); |         furi_crash("SugGhz: ManchesterEncoderResult is incorrect."); | ||||||
|         furi_crash(NULL); |  | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     return level_duration_make(data.level, data.duration); |     return level_duration_make(data.level, data.duration); | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #include "secplus_v2.h" | #include "secplus_v2.h" | ||||||
| #include <lib/toolbox/manchester_decoder.h> | #include <lib/toolbox/manchester_decoder.h> | ||||||
|  | #include <lib/toolbox/manchester_encoder.h> | ||||||
| #include "../blocks/const.h" | #include "../blocks/const.h" | ||||||
| #include "../blocks/decoder.h" | #include "../blocks/decoder.h" | ||||||
| #include "../blocks/encoder.h" | #include "../blocks/encoder.h" | ||||||
| @ -42,6 +43,7 @@ struct SubGhzProtocolEncoderSecPlus_v2 { | |||||||
| 
 | 
 | ||||||
|     SubGhzProtocolBlockEncoder encoder; |     SubGhzProtocolBlockEncoder encoder; | ||||||
|     SubGhzBlockGeneric generic; |     SubGhzBlockGeneric generic; | ||||||
|  |     uint64_t secplus_packet_1; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -63,23 +65,555 @@ const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder = { | const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder = { | ||||||
|     .alloc = NULL, |     .alloc = subghz_protocol_encoder_secplus_v2_alloc, | ||||||
|     .free = NULL, |     .free = subghz_protocol_encoder_secplus_v2_free, | ||||||
| 
 | 
 | ||||||
|     .deserialize = NULL, |     .deserialize = subghz_protocol_encoder_secplus_v2_deserialize, | ||||||
|     .stop = NULL, |     .stop = subghz_protocol_encoder_secplus_v2_stop, | ||||||
|     .yield = NULL, |     .yield = subghz_protocol_encoder_secplus_v2_yield, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const SubGhzProtocol subghz_protocol_secplus_v2 = { | const SubGhzProtocol subghz_protocol_secplus_v2 = { | ||||||
|     .name = SUBGHZ_PROTOCOL_SECPLUS_V2_NAME, |     .name = SUBGHZ_PROTOCOL_SECPLUS_V2_NAME, | ||||||
|     .type = SubGhzProtocolTypeDynamic, |     .type = SubGhzProtocolTypeDynamic, | ||||||
|     .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, |     .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | | ||||||
|  |             SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send, | ||||||
| 
 | 
 | ||||||
|     .decoder = &subghz_protocol_secplus_v2_decoder, |     .decoder = &subghz_protocol_secplus_v2_decoder, | ||||||
|     .encoder = &subghz_protocol_secplus_v2_encoder, |     .encoder = &subghz_protocol_secplus_v2_encoder, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment) { | ||||||
|  |     UNUSED(environment); | ||||||
|  |     SubGhzProtocolEncoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v2)); | ||||||
|  | 
 | ||||||
|  |     instance->base.protocol = &subghz_protocol_secplus_v2; | ||||||
|  |     instance->generic.protocol_name = instance->base.protocol->name; | ||||||
|  | 
 | ||||||
|  |     instance->encoder.repeat = 10; | ||||||
|  |     instance->encoder.size_upload = 256; | ||||||
|  |     instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); | ||||||
|  |     instance->encoder.is_runing = false; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_protocol_encoder_secplus_v2_free(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhzProtocolEncoderSecPlus_v2* instance = context; | ||||||
|  |     free(instance->encoder.upload); | ||||||
|  |     free(instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool subghz_protocol_secplus_v2_mix_invet(uint8_t invert, uint16_t p[]) { | ||||||
|  |     // selectively invert buffers
 | ||||||
|  |     switch(invert) { | ||||||
|  |     case 0x00: // 0b0000 (True, True, False),
 | ||||||
|  |         p[0] = ~p[0] & 0x03FF; | ||||||
|  |         p[1] = ~p[1] & 0x03FF; | ||||||
|  |         break; | ||||||
|  |     case 0x01: // 0b0001 (False, True, False),
 | ||||||
|  |         p[1] = ~p[1] & 0x03FF; | ||||||
|  |         break; | ||||||
|  |     case 0x02: // 0b0010 (False, False, True),
 | ||||||
|  |         p[2] = ~p[2] & 0x03FF; | ||||||
|  |         break; | ||||||
|  |     case 0x04: // 0b0100 (True, True, True),
 | ||||||
|  |         p[0] = ~p[0] & 0x03FF; | ||||||
|  |         p[1] = ~p[1] & 0x03FF; | ||||||
|  |         p[2] = ~p[2] & 0x03FF; | ||||||
|  |         break; | ||||||
|  |     case 0x05: // 0b0101 (True, False, True),
 | ||||||
|  |     case 0x0a: // 0b1010 (True, False, True),
 | ||||||
|  |         p[0] = ~p[0] & 0x03FF; | ||||||
|  |         p[2] = ~p[2] & 0x03FF; | ||||||
|  |         break; | ||||||
|  |     case 0x06: // 0b0110 (False, True, True),
 | ||||||
|  |         p[1] = ~p[1] & 0x03FF; | ||||||
|  |         p[2] = ~p[2] & 0x03FF; | ||||||
|  |         break; | ||||||
|  |     case 0x08: // 0b1000 (True, False, False),
 | ||||||
|  |         p[0] = ~p[0] & 0x03FF; | ||||||
|  |         break; | ||||||
|  |     case 0x09: // 0b1001 (False, False, False),
 | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         FURI_LOG_E(TAG, "Invert FAIL"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t p[]) { | ||||||
|  |     uint16_t a = p[0], b = p[1], c = p[2]; | ||||||
|  | 
 | ||||||
|  |     // selectively reorder buffers
 | ||||||
|  |     switch(order) { | ||||||
|  |     case 0x06: // 0b0110  2, 1, 0],
 | ||||||
|  |     case 0x09: // 0b1001  2, 1, 0],
 | ||||||
|  |         p[2] = a; | ||||||
|  |         p[1] = b; | ||||||
|  |         p[0] = c; | ||||||
|  |         break; | ||||||
|  |     case 0x08: // 0b1000  1, 2, 0],
 | ||||||
|  |     case 0x04: // 0b0100  1, 2, 0],
 | ||||||
|  |         p[1] = a; | ||||||
|  |         p[2] = b; | ||||||
|  |         p[0] = c; | ||||||
|  |         break; | ||||||
|  |     case 0x01: // 0b0001 2, 0, 1],
 | ||||||
|  |         p[2] = a; | ||||||
|  |         p[0] = b; | ||||||
|  |         p[1] = c; | ||||||
|  |         break; | ||||||
|  |     case 0x00: // 0b0000  0, 2, 1],
 | ||||||
|  |         p[0] = a; | ||||||
|  |         p[2] = b; | ||||||
|  |         p[1] = c; | ||||||
|  |         break; | ||||||
|  |     case 0x05: // 0b0101 1, 0, 2],
 | ||||||
|  |         p[1] = a; | ||||||
|  |         p[0] = b; | ||||||
|  |         p[2] = c; | ||||||
|  |         break; | ||||||
|  |     case 0x02: // 0b0010 0, 1, 2],
 | ||||||
|  |     case 0x0A: // 0b1010 0, 1, 2],
 | ||||||
|  |         p[0] = a; | ||||||
|  |         p[1] = b; | ||||||
|  |         p[2] = c; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         FURI_LOG_E(TAG, "Order FAIL"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool subghz_protocol_secplus_v2_mix_order_encode(uint8_t order, uint16_t p[]) { | ||||||
|  |     uint16_t a, b, c; | ||||||
|  | 
 | ||||||
|  |     // selectively reorder buffers
 | ||||||
|  |     switch(order) { | ||||||
|  |     case 0x06: // 0b0110  2, 1, 0],
 | ||||||
|  |     case 0x09: // 0b1001  2, 1, 0],
 | ||||||
|  |         a = p[2]; | ||||||
|  |         b = p[1]; | ||||||
|  |         c = p[0]; | ||||||
|  |         break; | ||||||
|  |     case 0x08: // 0b1000  1, 2, 0],
 | ||||||
|  |     case 0x04: // 0b0100  1, 2, 0],
 | ||||||
|  |         a = p[1]; | ||||||
|  |         b = p[2]; | ||||||
|  |         c = p[0]; | ||||||
|  |         break; | ||||||
|  |     case 0x01: // 0b0001 2, 0, 1],
 | ||||||
|  |         a = p[2]; | ||||||
|  |         b = p[0]; | ||||||
|  |         c = p[1]; | ||||||
|  |         break; | ||||||
|  |     case 0x00: // 0b0000  0, 2, 1],
 | ||||||
|  |         a = p[0]; | ||||||
|  |         b = p[2]; | ||||||
|  |         c = p[1]; | ||||||
|  |         break; | ||||||
|  |     case 0x05: // 0b0101 1, 0, 2],
 | ||||||
|  |         a = p[1]; | ||||||
|  |         b = p[0]; | ||||||
|  |         c = p[2]; | ||||||
|  |         break; | ||||||
|  |     case 0x02: // 0b0010 0, 1, 2],
 | ||||||
|  |     case 0x0A: // 0b1010 0, 1, 2],
 | ||||||
|  |         a = p[0]; | ||||||
|  |         b = p[1]; | ||||||
|  |         c = p[2]; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         FURI_LOG_E(TAG, "Order FAIL"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     p[0] = a; | ||||||
|  |     p[1] = b; | ||||||
|  |     p[2] = c; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * Security+ 2.0 half-message decoding | ||||||
|  |  * @param data data  | ||||||
|  |  * @param roll_array[] return roll_array part | ||||||
|  |  * @param fixed[] return fixed part | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static bool | ||||||
|  |     subghz_protocol_secplus_v2_decode_half(uint64_t data, uint8_t roll_array[], uint32_t* fixed) { | ||||||
|  |     uint8_t order = (data >> 34) & 0x0f; | ||||||
|  |     uint8_t invert = (data >> 30) & 0x0f; | ||||||
|  |     uint16_t p[3] = {0}; | ||||||
|  | 
 | ||||||
|  |     for(int i = 29; i >= 0; i -= 3) { | ||||||
|  |         p[0] = p[0] << 1 | bit_read(data, i); | ||||||
|  |         p[1] = p[1] << 1 | bit_read(data, i - 1); | ||||||
|  |         p[2] = p[2] << 1 | bit_read(data, i - 2); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return false; | ||||||
|  |     if(!subghz_protocol_secplus_v2_mix_order_decode(order, p)) return false; | ||||||
|  | 
 | ||||||
|  |     data = order << 4 | invert; | ||||||
|  |     int k = 0; | ||||||
|  |     for(int i = 6; i >= 0; i -= 2) { | ||||||
|  |         roll_array[k++] = (data >> i) & 0x03; | ||||||
|  |         if(roll_array[k] == 3) { | ||||||
|  |             FURI_LOG_E(TAG, "Roll_Array FAIL"); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for(int i = 8; i >= 0; i -= 2) { | ||||||
|  |         roll_array[k++] = (p[2] >> i) & 0x03; | ||||||
|  |         if(roll_array[k] == 3) { | ||||||
|  |             FURI_LOG_E(TAG, "Roll_Array FAIL"); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fixed[0] = p[0] << 10 | p[1]; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * Analysis of received data | ||||||
|  |  * @param instance Pointer to a SubGhzBlockGeneric* instance | ||||||
|  |  * @param packet_1 first part of the message | ||||||
|  |  */ | ||||||
|  | static void | ||||||
|  |     subghz_protocol_secplus_v2_remote_controller(SubGhzBlockGeneric* instance, uint64_t packet_1) { | ||||||
|  |     uint32_t fixed_1[1]; | ||||||
|  |     uint8_t roll_1[9] = {0}; | ||||||
|  |     uint32_t fixed_2[1]; | ||||||
|  |     uint8_t roll_2[9] = {0}; | ||||||
|  |     uint8_t rolling_digits[18] = {0}; | ||||||
|  | 
 | ||||||
|  |     if(subghz_protocol_secplus_v2_decode_half(packet_1, roll_1, fixed_1) && | ||||||
|  |        subghz_protocol_secplus_v2_decode_half(instance->data, roll_2, fixed_2)) { | ||||||
|  |         rolling_digits[0] = roll_2[8]; | ||||||
|  |         rolling_digits[1] = roll_1[8]; | ||||||
|  | 
 | ||||||
|  |         rolling_digits[2] = roll_2[4]; | ||||||
|  |         rolling_digits[3] = roll_2[5]; | ||||||
|  |         rolling_digits[4] = roll_2[6]; | ||||||
|  |         rolling_digits[5] = roll_2[7]; | ||||||
|  | 
 | ||||||
|  |         rolling_digits[6] = roll_1[4]; | ||||||
|  |         rolling_digits[7] = roll_1[5]; | ||||||
|  |         rolling_digits[8] = roll_1[6]; | ||||||
|  |         rolling_digits[9] = roll_1[7]; | ||||||
|  | 
 | ||||||
|  |         rolling_digits[10] = roll_2[0]; | ||||||
|  |         rolling_digits[11] = roll_2[1]; | ||||||
|  |         rolling_digits[12] = roll_2[2]; | ||||||
|  |         rolling_digits[13] = roll_2[3]; | ||||||
|  | 
 | ||||||
|  |         rolling_digits[14] = roll_1[0]; | ||||||
|  |         rolling_digits[15] = roll_1[1]; | ||||||
|  |         rolling_digits[16] = roll_1[2]; | ||||||
|  |         rolling_digits[17] = roll_1[3]; | ||||||
|  | 
 | ||||||
|  |         uint32_t rolling = 0; | ||||||
|  |         for(int i = 0; i < 18; i++) { | ||||||
|  |             rolling = (rolling * 3) + rolling_digits[i]; | ||||||
|  |         } | ||||||
|  |         // Max value = 2^28 (268435456)
 | ||||||
|  |         if(rolling >= 0x10000000) { | ||||||
|  |             FURI_LOG_E(TAG, "Rolling FAIL"); | ||||||
|  |             instance->cnt = 0; | ||||||
|  |             instance->btn = 0; | ||||||
|  |             instance->serial = 0; | ||||||
|  |         } else { | ||||||
|  |             instance->cnt = subghz_protocol_blocks_reverse_key(rolling, 28); | ||||||
|  |             instance->btn = fixed_1[0] >> 12; | ||||||
|  |             instance->serial = fixed_1[0] << 20 | fixed_2[0]; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         instance->cnt = 0; | ||||||
|  |         instance->btn = 0; | ||||||
|  |         instance->serial = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * Security+ 2.0 half-message encoding | ||||||
|  |  * @param roll_array[] roll_array part | ||||||
|  |  * @param fixed[] fixed part | ||||||
|  |  * @return return data  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static uint64_t subghz_protocol_secplus_v2_encode_half(uint8_t roll_array[], uint32_t fixed) { | ||||||
|  |     uint64_t data = 0; | ||||||
|  |     uint16_t p[3] = {(fixed >> 10) & 0x3FF, fixed & 0x3FF, 0}; | ||||||
|  |     uint8_t order = roll_array[0] << 2 | roll_array[1]; | ||||||
|  |     uint8_t invert = roll_array[2] << 2 | roll_array[3]; | ||||||
|  |     p[2] = (uint16_t)roll_array[4] << 8 | roll_array[5] << 6 | roll_array[6] << 4 | | ||||||
|  |            roll_array[7] << 2 | roll_array[8]; | ||||||
|  | 
 | ||||||
|  |     if(!subghz_protocol_secplus_v2_mix_order_encode(order, p)) return 0; | ||||||
|  |     if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return 0; | ||||||
|  | 
 | ||||||
|  |     for(int i = 0; i < 10; i++) { | ||||||
|  |         data <<= 3; | ||||||
|  |         data |= bit_read(p[0], 9 - i) << 2 | bit_read(p[1], 9 - i) << 1 | bit_read(p[2], 9 - i); | ||||||
|  |     } | ||||||
|  |     data |= ((uint64_t)order) << 34 | ((uint64_t)invert) << 30; | ||||||
|  | 
 | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * Security+ 2.0 message encoding | ||||||
|  |  * @param instance SubGhzProtocolEncoderSecPlus_v2*  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* instance) { | ||||||
|  |     uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20}; | ||||||
|  |     uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF}; | ||||||
|  |     uint8_t rolling_digits[18] = {0}; | ||||||
|  |     uint8_t roll_1[9] = {0}; | ||||||
|  |     uint8_t roll_2[9] = {0}; | ||||||
|  | 
 | ||||||
|  |     instance->generic.cnt++; | ||||||
|  |     //ToDo it is not known what value the counter starts
 | ||||||
|  |     if(instance->generic.cnt > 0xFFFFFFF) instance->generic.cnt = 0xE500000; | ||||||
|  |     uint32_t rolling = subghz_protocol_blocks_reverse_key(instance->generic.cnt, 28); | ||||||
|  | 
 | ||||||
|  |     for(int8_t i = 17; i > -1; i--) { | ||||||
|  |         rolling_digits[i] = rolling % 3; | ||||||
|  |         rolling /= 3; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     roll_2[8] = rolling_digits[0]; | ||||||
|  |     roll_1[8] = rolling_digits[1]; | ||||||
|  | 
 | ||||||
|  |     roll_2[4] = rolling_digits[2]; | ||||||
|  |     roll_2[5] = rolling_digits[3]; | ||||||
|  |     roll_2[6] = rolling_digits[4]; | ||||||
|  |     roll_2[7] = rolling_digits[5]; | ||||||
|  | 
 | ||||||
|  |     roll_1[4] = rolling_digits[6]; | ||||||
|  |     roll_1[5] = rolling_digits[7]; | ||||||
|  |     roll_1[6] = rolling_digits[8]; | ||||||
|  |     roll_1[7] = rolling_digits[9]; | ||||||
|  | 
 | ||||||
|  |     roll_2[0] = rolling_digits[10]; | ||||||
|  |     roll_2[1] = rolling_digits[11]; | ||||||
|  |     roll_2[2] = rolling_digits[12]; | ||||||
|  |     roll_2[3] = rolling_digits[13]; | ||||||
|  | 
 | ||||||
|  |     roll_1[0] = rolling_digits[14]; | ||||||
|  |     roll_1[1] = rolling_digits[15]; | ||||||
|  |     roll_1[2] = rolling_digits[16]; | ||||||
|  |     roll_1[3] = rolling_digits[17]; | ||||||
|  | 
 | ||||||
|  |     instance->secplus_packet_1 = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_1 | | ||||||
|  |                                  subghz_protocol_secplus_v2_encode_half(roll_1, fixed_1[0]); | ||||||
|  |     instance->generic.data = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_2 | | ||||||
|  |                              subghz_protocol_secplus_v2_encode_half(roll_2, fixed_2[0]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static LevelDuration | ||||||
|  |     subghz_protocol_encoder_secplus_v2_add_duration_to_upload(ManchesterEncoderResult result) { | ||||||
|  |     LevelDuration data = {.duration = 0, .level = 0}; | ||||||
|  |     switch(result) { | ||||||
|  |     case ManchesterEncoderResultShortLow: | ||||||
|  |         data.duration = subghz_protocol_secplus_v2_const.te_short; | ||||||
|  |         data.level = false; | ||||||
|  |         break; | ||||||
|  |     case ManchesterEncoderResultLongLow: | ||||||
|  |         data.duration = subghz_protocol_secplus_v2_const.te_long; | ||||||
|  |         data.level = false; | ||||||
|  |         break; | ||||||
|  |     case ManchesterEncoderResultLongHigh: | ||||||
|  |         data.duration = subghz_protocol_secplus_v2_const.te_long; | ||||||
|  |         data.level = true; | ||||||
|  |         break; | ||||||
|  |     case ManchesterEncoderResultShortHigh: | ||||||
|  |         data.duration = subghz_protocol_secplus_v2_const.te_short; | ||||||
|  |         data.level = true; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         furi_crash("SugGhz: ManchesterEncoderResult is incorrect."); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return level_duration_make(data.level, data.duration); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generating an upload from data. | ||||||
|  |  * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance | ||||||
|  |  */ | ||||||
|  | static void | ||||||
|  |     subghz_protocol_encoder_secplus_v2_get_upload(SubGhzProtocolEncoderSecPlus_v2* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     size_t index = 0; | ||||||
|  | 
 | ||||||
|  |     ManchesterEncoderState enc_state; | ||||||
|  |     manchester_encoder_reset(&enc_state); | ||||||
|  |     ManchesterEncoderResult result; | ||||||
|  | 
 | ||||||
|  |     //Send data packet 1
 | ||||||
|  |     for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { | ||||||
|  |         if(!manchester_encoder_advance( | ||||||
|  |                &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result)) { | ||||||
|  |             instance->encoder.upload[index++] = | ||||||
|  |                 subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); | ||||||
|  |             manchester_encoder_advance( | ||||||
|  |                 &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result); | ||||||
|  |         } | ||||||
|  |         instance->encoder.upload[index++] = | ||||||
|  |             subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); | ||||||
|  |     } | ||||||
|  |     instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload( | ||||||
|  |         manchester_encoder_finish(&enc_state)); | ||||||
|  |     if(level_duration_get_level(instance->encoder.upload[index])) { | ||||||
|  |         index++; | ||||||
|  |     } | ||||||
|  |     instance->encoder.upload[index++] = | ||||||
|  |         level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136); | ||||||
|  | 
 | ||||||
|  |     //Send data packet 2
 | ||||||
|  |     manchester_encoder_reset(&enc_state); | ||||||
|  |     for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { | ||||||
|  |         if(!manchester_encoder_advance( | ||||||
|  |                &enc_state, bit_read(instance->generic.data, i - 1), &result)) { | ||||||
|  |             instance->encoder.upload[index++] = | ||||||
|  |                 subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); | ||||||
|  |             manchester_encoder_advance( | ||||||
|  |                 &enc_state, bit_read(instance->generic.data, i - 1), &result); | ||||||
|  |         } | ||||||
|  |         instance->encoder.upload[index++] = | ||||||
|  |             subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); | ||||||
|  |     } | ||||||
|  |     instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload( | ||||||
|  |         manchester_encoder_finish(&enc_state)); | ||||||
|  |     if(level_duration_get_level(instance->encoder.upload[index])) { | ||||||
|  |         index++; | ||||||
|  |     } | ||||||
|  |     instance->encoder.upload[index++] = | ||||||
|  |         level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136); | ||||||
|  | 
 | ||||||
|  |     instance->encoder.size_upload = index; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhzProtocolEncoderSecPlus_v2* instance = context; | ||||||
|  |     bool res = false; | ||||||
|  |     do { | ||||||
|  |         if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { | ||||||
|  |             FURI_LOG_E(TAG, "Deserialize error"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         uint8_t key_data[sizeof(uint64_t)] = {0}; | ||||||
|  |         if(!flipper_format_read_hex( | ||||||
|  |                flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { | ||||||
|  |             FURI_LOG_E(TAG, "Secplus_packet_1"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         for(uint8_t i = 0; i < sizeof(uint64_t); i++) { | ||||||
|  |             instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         subghz_protocol_secplus_v2_remote_controller( | ||||||
|  |             &instance->generic, instance->secplus_packet_1); | ||||||
|  |         subghz_protocol_secplus_v2_encode(instance); | ||||||
|  |         //optional parameter parameter
 | ||||||
|  |         flipper_format_read_uint32( | ||||||
|  |             flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); | ||||||
|  |         subghz_protocol_encoder_secplus_v2_get_upload(instance); | ||||||
|  | 
 | ||||||
|  |         //update data
 | ||||||
|  |         for(size_t i = 0; i < sizeof(uint64_t); i++) { | ||||||
|  |             key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to add Key"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for(size_t i = 0; i < sizeof(uint64_t); i++) { | ||||||
|  |             key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> i * 8) & 0xFF; | ||||||
|  |         } | ||||||
|  |         if(!flipper_format_update_hex( | ||||||
|  |                flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { | ||||||
|  |             FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         instance->encoder.is_runing = true; | ||||||
|  | 
 | ||||||
|  |         res = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void subghz_protocol_encoder_secplus_v2_stop(void* context) { | ||||||
|  |     SubGhzProtocolEncoderSecPlus_v2* instance = context; | ||||||
|  |     instance->encoder.is_runing = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context) { | ||||||
|  |     SubGhzProtocolEncoderSecPlus_v2* instance = context; | ||||||
|  | 
 | ||||||
|  |     if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { | ||||||
|  |         instance->encoder.is_runing = 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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool subghz_protocol_secplus_v2_create_data( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     uint32_t serial, | ||||||
|  |     uint8_t btn, | ||||||
|  |     uint32_t cnt, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     FuriHalSubGhzPreset preset) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SubGhzProtocolEncoderSecPlus_v2* instance = context; | ||||||
|  |     instance->generic.serial = serial; | ||||||
|  |     instance->generic.cnt = cnt; | ||||||
|  |     instance->generic.btn = btn; | ||||||
|  |     instance->generic.data_count_bit = | ||||||
|  |         (uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found; | ||||||
|  |     subghz_protocol_secplus_v2_encode(instance); | ||||||
|  |     bool res = | ||||||
|  |         subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); | ||||||
|  | 
 | ||||||
|  |     uint8_t key_data[sizeof(uint64_t)] = {0}; | ||||||
|  |     for(size_t i = 0; i < sizeof(uint64_t); i++) { | ||||||
|  |         key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> i * 8) & 0xFF; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(res && | ||||||
|  |        !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { | ||||||
|  |         FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); | ||||||
|  |         res = false; | ||||||
|  |     } | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) { | void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) { | ||||||
|     UNUSED(environment); |     UNUSED(environment); | ||||||
|     SubGhzProtocolDecoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v2)); |     SubGhzProtocolDecoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v2)); | ||||||
| @ -213,186 +747,6 @@ void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** 
 |  | ||||||
|  * Security+ 2.0 half-message decoding |  | ||||||
|  * @param data data  |  | ||||||
|  * @param roll_array[] return roll_array part |  | ||||||
|  * @param fixed[] return fixed part |  | ||||||
|  * @return true On success |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| static bool |  | ||||||
|     subghz_protocol_secplus_v2_decode_half(uint64_t data, uint8_t roll_array[], uint32_t* fixed) { |  | ||||||
|     uint8_t order = (data >> 34) & 0x0f; |  | ||||||
|     uint8_t invert = (data >> 30) & 0x0f; |  | ||||||
|     uint16_t p[3] = {0}; |  | ||||||
| 
 |  | ||||||
|     for(int i = 29; i >= 0; i -= 3) { |  | ||||||
|         p[0] = p[0] << 1 | bit_read(data, i); |  | ||||||
|         p[1] = p[1] << 1 | bit_read(data, i - 1); |  | ||||||
|         p[2] = p[2] << 1 | bit_read(data, i - 2); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // selectively invert buffers
 |  | ||||||
|     switch(invert) { |  | ||||||
|     case 0x00: // 0b0000 (True, True, False),
 |  | ||||||
|         p[0] = ~p[0] & 0x03FF; |  | ||||||
|         p[1] = ~p[1] & 0x03FF; |  | ||||||
|         break; |  | ||||||
|     case 0x01: // 0b0001 (False, True, False),
 |  | ||||||
|         p[1] = ~p[1] & 0x03FF; |  | ||||||
|         break; |  | ||||||
|     case 0x02: // 0b0010 (False, False, True),
 |  | ||||||
|         p[2] = ~p[2] & 0x03FF; |  | ||||||
|         break; |  | ||||||
|     case 0x04: // 0b0100 (True, True, True),
 |  | ||||||
|         p[0] = ~p[0] & 0x03FF; |  | ||||||
|         p[1] = ~p[1] & 0x03FF; |  | ||||||
|         p[2] = ~p[2] & 0x03FF; |  | ||||||
|         break; |  | ||||||
|     case 0x05: // 0b0101 (True, False, True),
 |  | ||||||
|     case 0x0a: // 0b1010 (True, False, True),
 |  | ||||||
|         p[0] = ~p[0] & 0x03FF; |  | ||||||
|         p[2] = ~p[2] & 0x03FF; |  | ||||||
|         break; |  | ||||||
|     case 0x06: // 0b0110 (False, True, True),
 |  | ||||||
|         p[1] = ~p[1] & 0x03FF; |  | ||||||
|         p[2] = ~p[2] & 0x03FF; |  | ||||||
|         break; |  | ||||||
|     case 0x08: // 0b1000 (True, False, False),
 |  | ||||||
|         p[0] = ~p[0] & 0x03FF; |  | ||||||
|         break; |  | ||||||
|     case 0x09: // 0b1001 (False, False, False),
 |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         FURI_LOG_E(TAG, "Invert FAIL"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     uint16_t a = p[0], b = p[1], c = p[2]; |  | ||||||
| 
 |  | ||||||
|     // selectively reorder buffers
 |  | ||||||
|     switch(order) { |  | ||||||
|     case 0x06: // 0b0110  2, 1, 0],
 |  | ||||||
|     case 0x09: // 0b1001  2, 1, 0],
 |  | ||||||
|         p[2] = a; |  | ||||||
|         p[1] = b; |  | ||||||
|         p[0] = c; |  | ||||||
|         break; |  | ||||||
|     case 0x08: // 0b1000  1, 2, 0],
 |  | ||||||
|     case 0x04: // 0b0100  1, 2, 0],
 |  | ||||||
|         p[1] = a; |  | ||||||
|         p[2] = b; |  | ||||||
|         p[0] = c; |  | ||||||
|         break; |  | ||||||
|     case 0x01: // 0b0001 2, 0, 1],
 |  | ||||||
|         p[2] = a; |  | ||||||
|         p[0] = b; |  | ||||||
|         p[1] = c; |  | ||||||
|         break; |  | ||||||
|     case 0x00: // 0b0000  0, 2, 1],
 |  | ||||||
|         p[0] = a; |  | ||||||
|         p[2] = b; |  | ||||||
|         p[1] = c; |  | ||||||
|         break; |  | ||||||
|     case 0x05: // 0b0101 1, 0, 2],
 |  | ||||||
|         p[1] = a; |  | ||||||
|         p[0] = b; |  | ||||||
|         p[2] = c; |  | ||||||
|         break; |  | ||||||
|     case 0x02: // 0b0010 0, 1, 2],
 |  | ||||||
|     case 0x0A: // 0b1010 0, 1, 2],
 |  | ||||||
|         p[0] = a; |  | ||||||
|         p[1] = b; |  | ||||||
|         p[2] = c; |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         FURI_LOG_E(TAG, "Order FAIL"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     data = order << 4 | invert; |  | ||||||
|     int k = 0; |  | ||||||
|     for(int i = 6; i >= 0; i -= 2) { |  | ||||||
|         roll_array[k++] = (data >> i) & 0x03; |  | ||||||
|         if(roll_array[k] == 3) { |  | ||||||
|             FURI_LOG_E(TAG, "Roll_Array FAIL"); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     for(int i = 8; i >= 0; i -= 2) { |  | ||||||
|         roll_array[k++] = (p[2] >> i) & 0x03; |  | ||||||
|         if(roll_array[k] == 3) { |  | ||||||
|             FURI_LOG_E(TAG, "Roll_Array FAIL"); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fixed[0] = p[0] << 10 | p[1]; |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** 
 |  | ||||||
|  * Analysis of received data |  | ||||||
|  * @param instance Pointer to a SubGhzBlockGeneric* instance |  | ||||||
|  * @param packet_1 first part of the message |  | ||||||
|  */ |  | ||||||
| static void |  | ||||||
|     subghz_protocol_secplus_v2_remote_controller(SubGhzBlockGeneric* instance, uint64_t packet_1) { |  | ||||||
|     uint32_t fixed_1[1]; |  | ||||||
|     uint8_t roll_1[9] = {0}; |  | ||||||
|     uint32_t fixed_2[1]; |  | ||||||
|     uint8_t roll_2[9] = {0}; |  | ||||||
|     uint8_t rolling_digits[18] = {0}; |  | ||||||
| 
 |  | ||||||
|     if(subghz_protocol_secplus_v2_decode_half(packet_1, roll_1, fixed_1) && |  | ||||||
|        subghz_protocol_secplus_v2_decode_half(instance->data, roll_2, fixed_2)) { |  | ||||||
|         rolling_digits[0] = roll_2[8]; |  | ||||||
|         rolling_digits[1] = roll_1[8]; |  | ||||||
| 
 |  | ||||||
|         rolling_digits[2] = roll_2[4]; |  | ||||||
|         rolling_digits[3] = roll_2[5]; |  | ||||||
|         rolling_digits[4] = roll_2[6]; |  | ||||||
|         rolling_digits[5] = roll_2[7]; |  | ||||||
| 
 |  | ||||||
|         rolling_digits[6] = roll_1[4]; |  | ||||||
|         rolling_digits[7] = roll_1[5]; |  | ||||||
|         rolling_digits[8] = roll_1[6]; |  | ||||||
|         rolling_digits[9] = roll_1[7]; |  | ||||||
| 
 |  | ||||||
|         rolling_digits[10] = roll_2[0]; |  | ||||||
|         rolling_digits[11] = roll_2[1]; |  | ||||||
|         rolling_digits[12] = roll_2[2]; |  | ||||||
|         rolling_digits[13] = roll_2[3]; |  | ||||||
| 
 |  | ||||||
|         rolling_digits[14] = roll_1[0]; |  | ||||||
|         rolling_digits[15] = roll_1[1]; |  | ||||||
|         rolling_digits[16] = roll_1[2]; |  | ||||||
|         rolling_digits[17] = roll_1[3]; |  | ||||||
| 
 |  | ||||||
|         uint32_t rolling = 0; |  | ||||||
|         for(int i = 0; i < 18; i++) { |  | ||||||
|             rolling = (rolling * 3) + rolling_digits[i]; |  | ||||||
|         } |  | ||||||
|         // Max value = 2^28 (268435456)
 |  | ||||||
|         if(rolling >= 0x10000000) { |  | ||||||
|             FURI_LOG_E(TAG, "Rolling FAIL"); |  | ||||||
|             instance->cnt = 0; |  | ||||||
|             instance->btn = 0; |  | ||||||
|             instance->serial = 0; |  | ||||||
|         } else { |  | ||||||
|             instance->cnt = subghz_protocol_blocks_reverse_key(rolling, 28); |  | ||||||
|             instance->btn = fixed_1[0] >> 12; |  | ||||||
|             instance->serial = fixed_1[0] << 20 | fixed_2[0]; |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         instance->cnt = 0; |  | ||||||
|         instance->btn = 0; |  | ||||||
|         instance->serial = 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { | uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SubGhzProtocolDecoderSecPlus_v2* instance = context; |     SubGhzProtocolDecoderSecPlus_v2* instance = context; | ||||||
|  | |||||||
| @ -10,6 +10,61 @@ extern const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder; | |||||||
| extern const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder; | extern const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder; | ||||||
| extern const SubGhzProtocol subghz_protocol_secplus_v2; | extern const SubGhzProtocol subghz_protocol_secplus_v2; | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Allocate SubGhzProtocolEncoderSecPlus_v2. | ||||||
|  |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  |  * @return SubGhzProtocolEncoderSecPlus_v2* pointer to a SubGhzProtocolEncoderSecPlus_v2 instance | ||||||
|  |  */ | ||||||
|  | void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Free SubGhzProtocolEncoderSecPlus_v2. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance | ||||||
|  |  */ | ||||||
|  | void subghz_protocol_encoder_secplus_v2_free(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Deserialize and generating an upload to send. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Forced transmission stop. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance | ||||||
|  |  */ | ||||||
|  | void subghz_protocol_encoder_secplus_v2_stop(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Getting the level and duration of the upload to be loaded into DMA. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance | ||||||
|  |  * @return LevelDuration  | ||||||
|  |  */ | ||||||
|  | LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Key generation from simple data. | ||||||
|  |  * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance | ||||||
|  |  * @param flipper_format Pointer to a FlipperFormat instance | ||||||
|  |  * @param serial Serial number, 32 bit | ||||||
|  |  * @param btn Button number, 8 bit | ||||||
|  |  * @param cnt Container value, 28 bit | ||||||
|  |  * @param manufacture_name Name of manufacturer's key | ||||||
|  |  * @param frequency Transmission frequency, Hz | ||||||
|  |  * @param preset Modulation, FuriHalSubGhzPreset | ||||||
|  |  * @return true On success | ||||||
|  |  */ | ||||||
|  | bool subghz_protocol_secplus_v2_create_data( | ||||||
|  |     void* context, | ||||||
|  |     FlipperFormat* flipper_format, | ||||||
|  |     uint32_t serial, | ||||||
|  |     uint8_t btn, | ||||||
|  |     uint32_t cnt, | ||||||
|  |     uint32_t frequency, | ||||||
|  |     FuriHalSubGhzPreset preset); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Allocate SubGhzProtocolDecoderSecPlus_v2. |  * Allocate SubGhzProtocolDecoderSecPlus_v2. | ||||||
|  * @param environment Pointer to a SubGhzEnvironment instance |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Skorpionm
						Skorpionm