From 8a785993ed9729dac65f77539d3e0e839f02bff7 Mon Sep 17 00:00:00 2001 From: SkorP Date: Sat, 22 Apr 2023 21:03:44 +0400 Subject: [PATCH 01/30] WS: add protocol "Wendox" --- .../protocols/protocol_items.c | 1 + .../protocols/protocol_items.h | 1 + .../weather_station/protocols/wendox.c | 248 ++++++++++++++++++ .../weather_station/protocols/wendox.h | 80 ++++++ 4 files changed, 330 insertions(+) create mode 100644 applications/external/weather_station/protocols/wendox.c create mode 100644 applications/external/weather_station/protocols/wendox.h diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c index 2c9d751c7..098014703 100644 --- a/applications/external/weather_station/protocols/protocol_items.c +++ b/applications/external/weather_station/protocols/protocol_items.c @@ -16,6 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_auriol_th, &ws_protocol_oregon_v1, &ws_protocol_tx_8300, + &ws_protocol_wendox, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h index f9e443abc..2268150f2 100644 --- a/applications/external/weather_station/protocols/protocol_items.h +++ b/applications/external/weather_station/protocols/protocol_items.h @@ -16,5 +16,6 @@ #include "auriol_hg0601a.h" #include "oregon_v1.h" #include "tx_8300.h" +#include "wendox.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/external/weather_station/protocols/wendox.c b/applications/external/weather_station/protocols/wendox.c new file mode 100644 index 000000000..fec1a8bf1 --- /dev/null +++ b/applications/external/weather_station/protocols/wendox.c @@ -0,0 +1,248 @@ +#include "wendox.h" + +#define TAG "WSProtocolWendox" + +static const SubGhzBlockConst ws_protocol_wendox_const = { + .te_short = 1955, + .te_long = 5865, + .te_delta = 300, + .min_count_bit_for_found = 28, +}; + +struct WSProtocolDecoderWendox { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderWendox { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + WendoxDecoderStepReset = 0, + WendoxDecoderStepCheckPreambule, + WendoxDecoderStepSaveDuration, + WendoxDecoderStepCheckDuration, +} WendoxDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_wendox_decoder = { + .alloc = ws_protocol_decoder_wendox_alloc, + .free = ws_protocol_decoder_wendox_free, + + .feed = ws_protocol_decoder_wendox_feed, + .reset = ws_protocol_decoder_wendox_reset, + + .get_hash_data = ws_protocol_decoder_wendox_get_hash_data, + .serialize = ws_protocol_decoder_wendox_serialize, + .deserialize = ws_protocol_decoder_wendox_deserialize, + .get_string = ws_protocol_decoder_wendox_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_wendox_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_wendox = { + .name = WS_PROTOCOL_WENDOX_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_wendox_decoder, + .encoder = &ws_protocol_wendox_encoder, +}; + +void* ws_protocol_decoder_wendox_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderWendox* instance = malloc(sizeof(WSProtocolDecoderWendox)); + instance->base.protocol = &ws_protocol_wendox; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_wendox_free(void* context) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + free(instance); +} + +void ws_protocol_decoder_wendox_reset(void* context) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + instance->decoder.parser_step = WendoxDecoderStepReset; +} + +// static bool ws_protocol_wendox_check(WSProtocolDecoderWendox* instance) { +// if(!instance->decoder.decode_data) return false; +// uint8_t msg[] = { +// instance->decoder.decode_data >> 24, +// instance->decoder.decode_data >> 16, +// instance->decoder.decode_data >> 8}; + +// uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1); +// return (crc == (instance->decoder.decode_data & 0xFF)); +// } + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_wendox_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 4) & 0xFF; + instance->battery_low = WS_NO_BATT; + instance->channel = WS_NO_CHANNEL; + + if(((instance->data >> 22) & 1)) { + instance->temp = (float)(((instance->data >> 13) & 0x1FF) + 12) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 13) & 0x1FF) + 1 - 12) / -10.0f; + } + FURI_LOG_E("TAG", "%llX %f", instance->data, (double)instance->temp); + + instance->btn = WS_NO_BTN; + instance->humidity = WS_NO_HUMIDITY; +} + +void ws_protocol_decoder_wendox_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + + switch(instance->decoder.parser_step) { + case WendoxDecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta)) { + instance->decoder.parser_step = WendoxDecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case WendoxDecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta * 1) && + (DURATION_DIFF(duration, ws_protocol_wendox_const.te_long) < + ws_protocol_wendox_const.te_delta * 2)) { + instance->header_count++; + } else if((instance->header_count > 4) && (instance->header_count < 12)) { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_long) < + ws_protocol_wendox_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + } + break; + + case WendoxDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = WendoxDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + break; + + case WendoxDecoderStepCheckDuration: + if(!level) { + if(duration > ws_protocol_wendox_const.te_short + ws_protocol_wendox_const.te_long) { + if(instance->decoder.decode_count_bit == + ws_protocol_wendox_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_wendox_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + + instance->decoder.parser_step = WendoxDecoderStepReset; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_wendox_const.te_long) < + ws_protocol_wendox_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = WendoxDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_long) < + ws_protocol_wendox_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_wendox_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_wendox_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + ws_protocol_decoder_wendox_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_wendox_const.min_count_bit_for_found); +} + +void ws_protocol_decoder_wendox_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/external/weather_station/protocols/wendox.h b/applications/external/weather_station/protocols/wendox.h new file mode 100644 index 000000000..698ac0bc0 --- /dev/null +++ b/applications/external/weather_station/protocols/wendox.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_WENDOX_NAME "Wendox" + +typedef struct WSProtocolDecoderWendox WSProtocolDecoderWendox; +typedef struct WSProtocolEncoderWendox WSProtocolEncoderWendox; + +extern const SubGhzProtocolDecoder ws_protocol_wendox_decoder; +extern const SubGhzProtocolEncoder ws_protocol_wendox_encoder; +extern const SubGhzProtocol ws_protocol_wendox; + +/** + * Allocate WSProtocolDecoderWendox. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderWendox* pointer to a WSProtocolDecoderWendox instance + */ +void* ws_protocol_decoder_wendox_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderWendox. + * @param context Pointer to a WSProtocolDecoderWendox instance + */ +void ws_protocol_decoder_wendox_free(void* context); + +/** + * Reset decoder WSProtocolDecoderWendox. + * @param context Pointer to a WSProtocolDecoderWendox instance + */ +void ws_protocol_decoder_wendox_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_wendox_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_wendox_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderWendox. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus ws_protocol_decoder_wendox_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderWendox. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + ws_protocol_decoder_wendox_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @param output Resulting text + */ +void ws_protocol_decoder_wendox_get_string(void* context, FuriString* output); From 7aa671689209e434daac403e6d8528f59f9466ac Mon Sep 17 00:00:00 2001 From: SkorP Date: Sat, 22 Apr 2023 21:57:31 +0400 Subject: [PATCH 02/30] WS: add bat status --- applications/external/weather_station/protocols/wendox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/weather_station/protocols/wendox.c b/applications/external/weather_station/protocols/wendox.c index fec1a8bf1..94e09f795 100644 --- a/applications/external/weather_station/protocols/wendox.c +++ b/applications/external/weather_station/protocols/wendox.c @@ -101,7 +101,7 @@ void ws_protocol_decoder_wendox_reset(void* context) { */ static void ws_protocol_wendox_remote_controller(WSBlockGeneric* instance) { instance->id = (instance->data >> 4) & 0xFF; - instance->battery_low = WS_NO_BATT; + instance->battery_low = (instance->data >> 5) & 1; instance->channel = WS_NO_CHANNEL; if(((instance->data >> 22) & 1)) { From 6a9bdeae3e51e8629bd777a7d39f205a3e727f8d Mon Sep 17 00:00:00 2001 From: SkorP Date: Tue, 25 Apr 2023 11:22:35 +0400 Subject: [PATCH 03/30] WS: add CRC, refactoring --- .../protocols/protocol_items.c | 2 +- .../protocols/protocol_items.h | 2 +- .../weather_station/protocols/wendox._w6726.c | 291 ++++++++++++++++++ .../weather_station/protocols/wendox.c | 248 --------------- .../weather_station/protocols/wendox.h | 80 ----- .../weather_station/protocols/wendox_w6726.h | 80 +++++ 6 files changed, 373 insertions(+), 330 deletions(-) create mode 100644 applications/external/weather_station/protocols/wendox._w6726.c delete mode 100644 applications/external/weather_station/protocols/wendox.c delete mode 100644 applications/external/weather_station/protocols/wendox.h create mode 100644 applications/external/weather_station/protocols/wendox_w6726.h diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c index 098014703..cd4bae76d 100644 --- a/applications/external/weather_station/protocols/protocol_items.c +++ b/applications/external/weather_station/protocols/protocol_items.c @@ -16,7 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_auriol_th, &ws_protocol_oregon_v1, &ws_protocol_tx_8300, - &ws_protocol_wendox, + &ws_protocol_wendox_w6726, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h index 2268150f2..0398c11f2 100644 --- a/applications/external/weather_station/protocols/protocol_items.h +++ b/applications/external/weather_station/protocols/protocol_items.h @@ -16,6 +16,6 @@ #include "auriol_hg0601a.h" #include "oregon_v1.h" #include "tx_8300.h" -#include "wendox.h" +#include "wendox_w6726.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/external/weather_station/protocols/wendox._w6726.c b/applications/external/weather_station/protocols/wendox._w6726.c new file mode 100644 index 000000000..3976f6a37 --- /dev/null +++ b/applications/external/weather_station/protocols/wendox._w6726.c @@ -0,0 +1,291 @@ +#include "wendox_w6726.h" + +#define TAG "WSProtocolWendoxW6726" + +/* + * Wendox W6726 + * + * Temperature -50 to +70 + * _ _ _ __ _ + * _| |___| |___| |___ ... | |_| |__...._______________ + * preamble data guard time + * + * 3 reps every 3 minutes + * in the first message 11 bytes of the preamble in the rest by 7 + * + * bit 0: 1955-hi, 5865-lo + * bit 1: 5865-hi, 1955-lo + * + * guard time: 12*1955+(lo last bit) + * data: 29 bit + */ + +static const SubGhzBlockConst ws_protocol_wendox_w6726_const = { + .te_short = 1955, + .te_long = 5865, + .te_delta = 300, + .min_count_bit_for_found = 29, +}; + +struct WSProtocolDecoderWendoxW6726 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderWendoxW6726 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + WendoxW6726DecoderStepReset = 0, + WendoxW6726DecoderStepCheckPreambule, + WendoxW6726DecoderStepSaveDuration, + WendoxW6726DecoderStepCheckDuration, +} WendoxW6726DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder = { + .alloc = ws_protocol_decoder_wendox_w6726_alloc, + .free = ws_protocol_decoder_wendox_w6726_free, + + .feed = ws_protocol_decoder_wendox_w6726_feed, + .reset = ws_protocol_decoder_wendox_w6726_reset, + + .get_hash_data = ws_protocol_decoder_wendox_w6726_get_hash_data, + .serialize = ws_protocol_decoder_wendox_w6726_serialize, + .deserialize = ws_protocol_decoder_wendox_w6726_deserialize, + .get_string = ws_protocol_decoder_wendox_w6726_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_wendox_w6726 = { + .name = WS_PROTOCOL_WENDOX_W6726_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_wendox_w6726_decoder, + .encoder = &ws_protocol_wendox_w6726_encoder, +}; + +void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderWendoxW6726* instance = malloc(sizeof(WSProtocolDecoderWendoxW6726)); + instance->base.protocol = &ws_protocol_wendox_w6726; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_wendox_w6726_free(void* context) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + free(instance); +} + +void ws_protocol_decoder_wendox_w6726_reset(void* context) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + instance->decoder.parser_step = WendoxW6726DecoderStepReset; +} + +static bool ws_protocol_wendox_w6726_check(WSProtocolDecoderWendoxW6726* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + instance->decoder.decode_data >> 28, + instance->decoder.decode_data >> 20, + instance->decoder.decode_data >> 12, + instance->decoder.decode_data >> 4}; + + uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD); + return (crc == (instance->decoder.decode_data & 0x0F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_wendox_w6726_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 24) & 0xFF; + instance->battery_low = (instance->data >> 6) & 1; + instance->channel = WS_NO_CHANNEL; + + if(((instance->data >> 23) & 1)) { + instance->temp = (float)(((instance->data >> 14) & 0x1FF) + 12) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 14) & 0x1FF) + 1 - 12) / -10.0f; + } + + if(instance->temp < -50.0f) { + instance->temp = -50.0f; + } else if(instance->temp > 70.0f) { + instance->temp = 70.0f; + } + + instance->btn = WS_NO_BTN; + instance->humidity = WS_NO_HUMIDITY; +} + +void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + + switch(instance->decoder.parser_step) { + case WendoxW6726DecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta)) { + instance->decoder.parser_step = WendoxW6726DecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case WendoxW6726DecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta * 1) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2)) { + instance->header_count++; + } else if((instance->header_count > 4) && (instance->header_count < 12)) { + if((DURATION_DIFF( + instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + } + break; + + case WendoxW6726DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = WendoxW6726DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + break; + + case WendoxW6726DecoderStepCheckDuration: + if(!level) { + if(duration > + ws_protocol_wendox_w6726_const.te_short + ws_protocol_wendox_w6726_const.te_long) { + if(DURATION_DIFF( + instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else if( + DURATION_DIFF( + instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + if((instance->decoder.decode_count_bit == + ws_protocol_wendox_w6726_const.min_count_bit_for_found) && + ws_protocol_wendox_w6726_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_wendox_w6726_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_wendox_w6726_const.min_count_bit_for_found); +} + +void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/external/weather_station/protocols/wendox.c b/applications/external/weather_station/protocols/wendox.c deleted file mode 100644 index 94e09f795..000000000 --- a/applications/external/weather_station/protocols/wendox.c +++ /dev/null @@ -1,248 +0,0 @@ -#include "wendox.h" - -#define TAG "WSProtocolWendox" - -static const SubGhzBlockConst ws_protocol_wendox_const = { - .te_short = 1955, - .te_long = 5865, - .te_delta = 300, - .min_count_bit_for_found = 28, -}; - -struct WSProtocolDecoderWendox { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - - uint16_t header_count; -}; - -struct WSProtocolEncoderWendox { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - WendoxDecoderStepReset = 0, - WendoxDecoderStepCheckPreambule, - WendoxDecoderStepSaveDuration, - WendoxDecoderStepCheckDuration, -} WendoxDecoderStep; - -const SubGhzProtocolDecoder ws_protocol_wendox_decoder = { - .alloc = ws_protocol_decoder_wendox_alloc, - .free = ws_protocol_decoder_wendox_free, - - .feed = ws_protocol_decoder_wendox_feed, - .reset = ws_protocol_decoder_wendox_reset, - - .get_hash_data = ws_protocol_decoder_wendox_get_hash_data, - .serialize = ws_protocol_decoder_wendox_serialize, - .deserialize = ws_protocol_decoder_wendox_deserialize, - .get_string = ws_protocol_decoder_wendox_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_wendox_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_wendox = { - .name = WS_PROTOCOL_WENDOX_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_wendox_decoder, - .encoder = &ws_protocol_wendox_encoder, -}; - -void* ws_protocol_decoder_wendox_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderWendox* instance = malloc(sizeof(WSProtocolDecoderWendox)); - instance->base.protocol = &ws_protocol_wendox; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_wendox_free(void* context) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - free(instance); -} - -void ws_protocol_decoder_wendox_reset(void* context) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - instance->decoder.parser_step = WendoxDecoderStepReset; -} - -// static bool ws_protocol_wendox_check(WSProtocolDecoderWendox* instance) { -// if(!instance->decoder.decode_data) return false; -// uint8_t msg[] = { -// instance->decoder.decode_data >> 24, -// instance->decoder.decode_data >> 16, -// instance->decoder.decode_data >> 8}; - -// uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1); -// return (crc == (instance->decoder.decode_data & 0xFF)); -// } - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_wendox_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 4) & 0xFF; - instance->battery_low = (instance->data >> 5) & 1; - instance->channel = WS_NO_CHANNEL; - - if(((instance->data >> 22) & 1)) { - instance->temp = (float)(((instance->data >> 13) & 0x1FF) + 12) / 10.0f; - } else { - instance->temp = (float)((~(instance->data >> 13) & 0x1FF) + 1 - 12) / -10.0f; - } - FURI_LOG_E("TAG", "%llX %f", instance->data, (double)instance->temp); - - instance->btn = WS_NO_BTN; - instance->humidity = WS_NO_HUMIDITY; -} - -void ws_protocol_decoder_wendox_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - - switch(instance->decoder.parser_step) { - case WendoxDecoderStepReset: - if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta)) { - instance->decoder.parser_step = WendoxDecoderStepCheckPreambule; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - - case WendoxDecoderStepCheckPreambule: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta * 1) && - (DURATION_DIFF(duration, ws_protocol_wendox_const.te_long) < - ws_protocol_wendox_const.te_delta * 2)) { - instance->header_count++; - } else if((instance->header_count > 4) && (instance->header_count < 12)) { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_long) < - ws_protocol_wendox_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta)) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = WendoxDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - } - break; - - case WendoxDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = WendoxDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - break; - - case WendoxDecoderStepCheckDuration: - if(!level) { - if(duration > ws_protocol_wendox_const.te_short + ws_protocol_wendox_const.te_long) { - if(instance->decoder.decode_count_bit == - ws_protocol_wendox_const.min_count_bit_for_found) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_wendox_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - - instance->decoder.parser_step = WendoxDecoderStepReset; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_wendox_const.te_long) < - ws_protocol_wendox_const.te_delta * 3)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = WendoxDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_long) < - ws_protocol_wendox_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = WendoxDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_wendox_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_wendox_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_wendox_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_wendox_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_wendox_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/wendox.h b/applications/external/weather_station/protocols/wendox.h deleted file mode 100644 index 698ac0bc0..000000000 --- a/applications/external/weather_station/protocols/wendox.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_WENDOX_NAME "Wendox" - -typedef struct WSProtocolDecoderWendox WSProtocolDecoderWendox; -typedef struct WSProtocolEncoderWendox WSProtocolEncoderWendox; - -extern const SubGhzProtocolDecoder ws_protocol_wendox_decoder; -extern const SubGhzProtocolEncoder ws_protocol_wendox_encoder; -extern const SubGhzProtocol ws_protocol_wendox; - -/** - * Allocate WSProtocolDecoderWendox. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderWendox* pointer to a WSProtocolDecoderWendox instance - */ -void* ws_protocol_decoder_wendox_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderWendox. - * @param context Pointer to a WSProtocolDecoderWendox instance - */ -void ws_protocol_decoder_wendox_free(void* context); - -/** - * Reset decoder WSProtocolDecoderWendox. - * @param context Pointer to a WSProtocolDecoderWendox instance - */ -void ws_protocol_decoder_wendox_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_wendox_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_wendox_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderWendox. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_wendox_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderWendox. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_wendox_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @param output Resulting text - */ -void ws_protocol_decoder_wendox_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/wendox_w6726.h b/applications/external/weather_station/protocols/wendox_w6726.h new file mode 100644 index 000000000..236777a1c --- /dev/null +++ b/applications/external/weather_station/protocols/wendox_w6726.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_WENDOX_W6726_NAME "Wendox W6726" + +typedef struct WSProtocolDecoderWendoxW6726 WSProtocolDecoderWendoxW6726; +typedef struct WSProtocolEncoderWendoxW6726 WSProtocolEncoderWendoxW6726; + +extern const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder; +extern const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder; +extern const SubGhzProtocol ws_protocol_wendox_w6726; + +/** + * Allocate WSProtocolDecoderWendoxW6726. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderWendoxW6726* pointer to a WSProtocolDecoderWendoxW6726 instance + */ +void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + */ +void ws_protocol_decoder_wendox_w6726_free(void* context); + +/** + * Reset decoder WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + */ +void ws_protocol_decoder_wendox_w6726_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param output Resulting text + */ +void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output); From 9caedb422dd945e074471c6a8acc95f60f1fab60 Mon Sep 17 00:00:00 2001 From: SkorP Date: Tue, 25 Apr 2023 11:36:27 +0400 Subject: [PATCH 04/30] WS: description added --- .../external/weather_station/protocols/wendox._w6726.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/applications/external/weather_station/protocols/wendox._w6726.c b/applications/external/weather_station/protocols/wendox._w6726.c index 3976f6a37..265c77f70 100644 --- a/applications/external/weather_station/protocols/wendox._w6726.c +++ b/applications/external/weather_station/protocols/wendox._w6726.c @@ -15,9 +15,17 @@ * * bit 0: 1955-hi, 5865-lo * bit 1: 5865-hi, 1955-lo - * * guard time: 12*1955+(lo last bit) * data: 29 bit + * + * IIIII | ZTTTTTTTTT | uuuuuuuBuu | CCCC + * + * I: identification; + * Z: temperature sign; + * T: temperature sign dependent +12 Ѱ; + * B: battery low; flag to indicate low battery voltage; + * C: CRC4 (polynomial = 0x9, start_data = 0xD); + * u: unknown; */ static const SubGhzBlockConst ws_protocol_wendox_w6726_const = { From 6b09dfca766b9337d08628b47744ee4c4e98e17d Mon Sep 17 00:00:00 2001 From: SkorP Date: Tue, 25 Apr 2023 11:40:50 +0400 Subject: [PATCH 05/30] WS: fix name file --- .../weather_station/protocols/{wendox._w6726.c => wendox_w6726.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename applications/external/weather_station/protocols/{wendox._w6726.c => wendox_w6726.c} (100%) diff --git a/applications/external/weather_station/protocols/wendox._w6726.c b/applications/external/weather_station/protocols/wendox_w6726.c similarity index 100% rename from applications/external/weather_station/protocols/wendox._w6726.c rename to applications/external/weather_station/protocols/wendox_w6726.c From d70ba2b74064e24586adea56231b569ff66d71df Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 25 Apr 2023 20:33:13 +0400 Subject: [PATCH 06/30] [FL-3286] Don't reboot on crash in debug builds (#2613) * furi: never reboot on furi_crash in debug builds * furi: crash info: added registers * furi: check and assert optimization, split registers and stack info dump * furi: macro uppercase Co-authored-by: SG --- furi/core/check.c | 31 ++++++++++++++++++++++++++++++- furi/core/check.h | 24 ++++++++++++++---------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/furi/core/check.c b/furi/core/check.c index 64f9f72f1..f5390639d 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -14,7 +14,7 @@ #include PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL; -PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; +PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[13] = {0}; /** Load r12 value to __furi_check_message and store registers to __furi_check_registers */ #define GET_MESSAGE_AND_STORE_REGISTERS() \ @@ -22,6 +22,7 @@ PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; "str r12, [r11] \n" \ "ldr r12, =__furi_check_registers \n" \ "stm r12, {r0-r11} \n" \ + "str lr, [r12, #48] \n" \ : \ : \ : "memory"); @@ -62,6 +63,25 @@ static void __furi_put_uint32_as_text(uint32_t data) { furi_hal_console_puts(tmp_str); } +static void __furi_put_uint32_as_hex(uint32_t data) { + char tmp_str[] = "0xFFFFFFFF"; + itoa(data, tmp_str, 16); + furi_hal_console_puts(tmp_str); +} + +static void __furi_print_register_info() { + // Print registers + for(uint8_t i = 0; i < 12; i++) { + furi_hal_console_puts("\r\n\tr"); + __furi_put_uint32_as_text(i); + furi_hal_console_puts(" : "); + __furi_put_uint32_as_hex(__furi_check_registers[i]); + } + + furi_hal_console_puts("\r\n\tlr : "); + __furi_put_uint32_as_hex(__furi_check_registers[12]); +} + static void __furi_print_stack_info() { furi_hal_console_puts("\r\n\tstack watermark: "); __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); @@ -101,32 +121,41 @@ FURI_NORETURN void __furi_crash() { if(__furi_check_message == NULL) { __furi_check_message = "Fatal Error"; + } else if(__furi_check_message == (void*)__FURI_ASSERT_MESSAGE_FLAG) { + __furi_check_message = "furi_assert failed"; + } else if(__furi_check_message == (void*)__FURI_CHECK_MESSAGE_FLAG) { + __furi_check_message = "furi_check failed"; } furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); __furi_print_name(isr); furi_hal_console_puts(__furi_check_message); + __furi_print_register_info(); if(!isr) { __furi_print_stack_info(); } __furi_print_heap_info(); +#ifndef FURI_DEBUG // Check if debug enabled by DAP // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; if(debug) { +#endif furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\033[0m\r\n"); furi_hal_debug_enable(); RESTORE_REGISTERS_AND_HALT_MCU(true); +#ifndef FURI_DEBUG } else { furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); furi_hal_console_puts("\r\nRebooting system.\r\n"); furi_hal_console_puts("\033[0m\r\n"); furi_hal_power_reset(); } +#endif __builtin_unreachable(); } diff --git a/furi/core/check.h b/furi/core/check.h index 192c5260e..a507fc1ea 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -21,6 +21,10 @@ extern "C" { #define FURI_NORETURN noreturn #endif +// Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls. +#define __FURI_ASSERT_MESSAGE_FLAG (0x01) +#define __FURI_CHECK_MESSAGE_FLAG (0x02) + /** Crash system */ FURI_NORETURN void __furi_crash(); @@ -44,20 +48,20 @@ FURI_NORETURN void __furi_halt(); } while(0) /** Check condition and crash if check failed */ -#define furi_check(__e) \ - do { \ - if(!(__e)) { \ - furi_crash("furi_check failed\r\n"); \ - } \ +#define furi_check(__e) \ + do { \ + if(!(__e)) { \ + furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ + } \ } while(0) /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define furi_assert(__e) \ - do { \ - if(!(__e)) { \ - furi_crash("furi_assert failed\r\n"); \ - } \ +#define furi_assert(__e) \ + do { \ + if(!(__e)) { \ + furi_crash(__FURI_CHECK_MESSAGE_FLAG); \ + } \ } while(0) #else #define furi_assert(__e) \ From 35d876b816a5359e140f91409f3c25cccbda8717 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:56:09 +0100 Subject: [PATCH 07/30] Updated ac.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/ac.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index bf047c5e7..49a4acd20 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 14th Apr, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: raw From 3bb5763b1b7315e3b64aa967eee2096a817cb151 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:56:26 +0100 Subject: [PATCH 08/30] Updated audio.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/audio.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 7e93cb3b8..f6183ed86 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 14th Apr, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: parsed From b49314deb01e044cc02412e34d8565cdd80adb81 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:56:44 +0100 Subject: [PATCH 09/30] Updated audio.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/fans.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 134133e17..6bef11b34 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 14th Apr, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: raw From f6820531748646dce19e7cc628f2f1a5bbbc185b Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:57:02 +0100 Subject: [PATCH 10/30] Updated projectors.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/projectors.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index c039f2389..00e331bd4 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 14th Apr, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # # ON name: POWER From a5f86b3025591d6db32c908325aa2fd846049bd1 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:57:18 +0100 Subject: [PATCH 11/30] Updated tv.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/tv.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 4d9c38f98..c55ad267d 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 07th Mar, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: parsed From 0ec8fc4c55178e9ad7daa014a9268dcfbb8da573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 26 Apr 2023 05:11:42 +0900 Subject: [PATCH 12/30] FuriHal: use proper divider for core2 when transition to sleep, remove extra stop mode transition checks, cleanup code. Furi: proper assert and check messages. (#2615) --- firmware/targets/f7/ble_glue/ble_app.c | 2 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 8 ++----- firmware/targets/f7/furi_hal/furi_hal_clock.c | 10 ++++++++- firmware/targets/f7/furi_hal/furi_hal_power.c | 21 ++----------------- furi/core/check.h | 20 +++++++++--------- 5 files changed, 24 insertions(+), 37 deletions(-) diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index a325830cf..37d8f7cd0 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -88,7 +88,7 @@ bool ble_app_init() { .min_tx_power = 0, .max_tx_power = 0, .rx_model_config = 1, - /* New stack (13.3->16.0)*/ + /* New stack (13.3->15.0) */ .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 0857fe4ee..b08c9ea27 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -84,9 +84,7 @@ void furi_hal_bt_init() { } // Explicitly tell that we are in charge of CLK48 domain - if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { - furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); - } + furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); // Start Core2 ble_glue_init(); @@ -129,9 +127,7 @@ bool furi_hal_bt_start_radio_stack() { furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever); // Explicitly tell that we are in charge of CLK48 domain - if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { - furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); - } + furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); do { // Wait until C2 is started or timeout diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index d85524ce4..a4df4877e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -213,7 +213,11 @@ void furi_hal_clock_switch_to_hsi() { while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; - LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); + LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + + LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0) + ; } void furi_hal_clock_switch_to_pll() { @@ -228,7 +232,11 @@ void furi_hal_clock_switch_to_pll() { while(!LL_RCC_PLLSAI1_IsReady()) ; + LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); + LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) + ; LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 3e4e3f48b..7d9334c2c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -29,10 +29,6 @@ #define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3) #endif -#ifndef FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO -#define FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO (&gpio_ext_pb3) -#endif - #ifndef FURI_HAL_POWER_STOP_MODE #define FURI_HAL_POWER_STOP_MODE (LL_PWR_MODE_STOP2) #endif @@ -92,10 +88,8 @@ void furi_hal_power_init() { #ifdef FURI_HAL_POWER_DEBUG furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull); furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, GpioModeOutputPushPull); furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, 0); #endif LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); @@ -158,9 +152,7 @@ bool furi_hal_power_sleep_available() { static inline bool furi_hal_power_deep_sleep_available() { return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) && - !furi_hal_debug_is_gdb_session_active() && !LL_PWR_IsActiveFlag_CRPE() && - !LL_PWR_IsActiveFlag_CRP() && !LL_PWR_IsActiveFlag_BLEA() && - !LL_PWR_IsActiveFlag_BLEWU(); + !furi_hal_debug_is_gdb_session_active(); } static inline void furi_hal_power_light_sleep() { @@ -211,16 +203,7 @@ static inline void furi_hal_power_deep_sleep() { __force_stores(); #endif - bool should_abort_sleep = LL_PWR_IsActiveFlag_CRPE() || LL_PWR_IsActiveFlag_CRP() || - LL_PWR_IsActiveFlag_BLEA() || LL_PWR_IsActiveFlag_BLEWU(); - - if(should_abort_sleep) { -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, 1); -#endif - } else { - __WFI(); - } + __WFI(); LL_LPM_EnableSleep(); diff --git a/furi/core/check.h b/furi/core/check.h index a507fc1ea..ea83f2219 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -48,21 +48,21 @@ FURI_NORETURN void __furi_halt(); } while(0) /** Check condition and crash if check failed */ -#define furi_check(__e) \ - do { \ - if(!(__e)) { \ - furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ - } \ - } while(0) - -/** Only in debug build: Assert condition and crash if assert failed */ -#ifdef FURI_DEBUG -#define furi_assert(__e) \ +#define furi_check(__e) \ do { \ if(!(__e)) { \ furi_crash(__FURI_CHECK_MESSAGE_FLAG); \ } \ } while(0) + +/** Only in debug build: Assert condition and crash if assert failed */ +#ifdef FURI_DEBUG +#define furi_assert(__e) \ + do { \ + if(!(__e)) { \ + furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ + } \ + } while(0) #else #define furi_assert(__e) \ do { \ From 451ec9cba00ba8d43a7810092c8fe539522e8f06 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:50:37 +0300 Subject: [PATCH 13/30] Update TOTP https://github.com/akopachov/flipper-zero_authenticator --- applications/external/totp/application.fam | 3 - applications/external/totp/cli/cli.c | 2 +- applications/external/totp/cli/cli_helpers.c | 42 +- applications/external/totp/cli/cli_helpers.h | 67 +- .../external/totp/cli/commands/add/add.c | 167 ++-- .../totp/cli/commands/automation/automation.c | 20 +- .../totp/cli/commands/delete/delete.c | 53 +- .../totp/cli/commands/details/details.c | 56 +- .../external/totp/cli/commands/list/list.c | 30 +- .../external/totp/cli/commands/move/move.c | 59 +- .../cli/commands/notification/notification.c | 21 +- .../external/totp/cli/commands/pin/pin.c | 66 +- .../external/totp/cli/commands/reset/reset.c | 10 +- .../external/totp/cli/commands/reset/reset.h | 5 +- .../totp/cli/commands/timezone/timezone.c | 13 +- .../totp/cli/commands/update/update.c | 244 +++--- .../totp/cli/common_command_arguments.c | 8 +- .../totp/cli/common_command_arguments.h | 6 + .../external/totp/lib/base64/base64.c | 1 + .../totp/lib/linked_list/linked_list.c | 136 --- .../totp/lib/linked_list/linked_list.h | 98 --- .../external/totp/lib/roll_value/roll_value.c | 2 +- .../external/totp/lib/roll_value/roll_value.h | 5 +- .../external/totp/services/config/config.c | 805 ++++++------------ .../external/totp/services/config/config.h | 127 +-- .../services/config/config_file_context.h | 3 + .../external/totp/services/config/constants.h | 5 +- .../config/migrations/common_migration.c | 43 +- .../config/migrations/common_migration.h | 8 +- .../services/config/token_info_iterator.c | 551 ++++++++++++ .../services/config/token_info_iterator.h | 121 +++ .../external/totp/services/crypto/crypto.c | 14 +- .../external/totp/services/crypto/crypto.h | 25 +- applications/external/totp/totp_app.c | 43 +- applications/external/totp/types/common.c | 3 + applications/external/totp/types/common.h | 2 +- applications/external/totp/types/nullable.h | 17 - .../external/totp/types/plugin_state.h | 17 +- applications/external/totp/types/token_info.c | 45 +- applications/external/totp/types/token_info.h | 42 +- .../external/totp/ui/scene_director.c | 20 +- .../external/totp/ui/scene_director.h | 5 +- .../add_new_token/totp_scene_add_new_token.c | 99 +-- .../add_new_token/totp_scene_add_new_token.h | 8 +- .../scenes/app_settings/totp_app_settings.c | 32 +- .../scenes/app_settings/totp_app_settings.h | 8 +- .../authenticate/totp_scene_authenticate.c | 13 +- .../totp_scene_generate_token.c | 146 ++-- .../totp_scene_generate_token.h | 8 +- .../external/totp/ui/scenes/standby/standby.c | 12 + .../external/totp/ui/scenes/standby/standby.h | 5 + .../scenes/token_menu/totp_scene_token_menu.c | 75 +- .../scenes/token_menu/totp_scene_token_menu.h | 8 +- .../external/totp/ui/totp_scenes_enum.h | 7 +- .../totp/workers/bt_type_code/bt_type_code.c | 30 + .../totp/workers/bt_type_code/bt_type_code.h | 79 +- .../generate_totp_code/generate_totp_code.c | 18 +- .../generate_totp_code/generate_totp_code.h | 65 +- .../external/totp/workers/type_code_common.c | 8 +- .../external/totp/workers/type_code_common.h | 8 + .../workers/usb_type_code/usb_type_code.c | 13 + .../workers/usb_type_code/usb_type_code.h | 52 +- 62 files changed, 2025 insertions(+), 1679 deletions(-) delete mode 100644 applications/external/totp/lib/linked_list/linked_list.c delete mode 100644 applications/external/totp/lib/linked_list/linked_list.h create mode 100644 applications/external/totp/services/config/config_file_context.h create mode 100644 applications/external/totp/services/config/token_info_iterator.c create mode 100644 applications/external/totp/services/config/token_info_iterator.h create mode 100644 applications/external/totp/types/common.c delete mode 100644 applications/external/totp/types/nullable.h create mode 100644 applications/external/totp/ui/scenes/standby/standby.c create mode 100644 applications/external/totp/ui/scenes/standby/standby.h diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index 2c7445734..4f71c2ce8 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -27,9 +27,6 @@ App( Lib( name="base64", ), - Lib( - name="linked_list" - ), Lib( name="timezone_utils", ), diff --git a/applications/external/totp/cli/cli.c b/applications/external/totp/cli/cli.c index 30876c7e4..c860b5a36 100644 --- a/applications/external/totp/cli/cli.c +++ b/applications/external/totp/cli/cli.c @@ -63,7 +63,7 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) { totp_cli_command_automation_handle(plugin_state, args, cli); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) { - totp_cli_command_reset_handle(cli, cli_context->event_queue); + totp_cli_command_reset_handle(plugin_state, cli, cli_context->event_queue); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_UPDATE) == 0) { totp_cli_command_update_handle(plugin_state, args, cli); } else if( diff --git a/applications/external/totp/cli/cli_helpers.c b/applications/external/totp/cli/cli_helpers.c index 36b98cf65..226917237 100644 --- a/applications/external/totp/cli/cli_helpers.c +++ b/applications/external/totp/cli/cli_helpers.c @@ -3,6 +3,11 @@ #include #include "../types/plugin_event.h" +const char* TOTP_CLI_COLOR_ERROR = "91m"; +const char* TOTP_CLI_COLOR_WARNING = "93m"; +const char* TOTP_CLI_COLOR_SUCCESS = "92m"; +const char* TOTP_CLI_COLOR_INFO = "96m"; + bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) { if(plugin_state->current_scene == TotpSceneAuthentication) { TOTP_CLI_PRINTF("Pleases enter PIN on your flipper device\r\n"); @@ -13,10 +18,11 @@ bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) { furi_delay_ms(100); } - TOTP_CLI_DELETE_LAST_LINE(); + totp_cli_delete_last_line(); if(plugin_state->current_scene == TotpSceneAuthentication || //-V560 plugin_state->current_scene == TotpSceneNone) { //-V560 + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); return false; } } @@ -54,7 +60,7 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) { } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { size_t out_str_size = furi_string_size(out_str); if(out_str_size > 0) { - TOTP_CLI_DELETE_LAST_CHAR(); + totp_cli_delete_last_char(); furi_string_left(out_str, out_str_size - 1); } } else if(c == CliSymbolAsciiCR) { @@ -83,3 +89,35 @@ void furi_string_secure_free(FuriString* str) { furi_string_free(str); } + +void totp_cli_print_invalid_arguments() { + TOTP_CLI_PRINTF_ERROR( + "Invalid command arguments. use \"help\" command to get list of available commands"); +} + +void totp_cli_print_error_updating_config_file() { + TOTP_CLI_PRINTF_ERROR("An error has occurred during updating config file\r\n"); +} + +void totp_cli_print_error_loading_token_info() { + TOTP_CLI_PRINTF_ERROR("An error has occurred during loading token information\r\n"); +} + +void totp_cli_print_processing() { + TOTP_CLI_PRINTF("Processing, please wait...\r\n"); +} + +void totp_cli_delete_last_char() { + TOTP_CLI_PRINTF("\b \b"); + fflush(stdout); +} + +void totp_cli_delete_current_line() { + TOTP_CLI_PRINTF("\33[2K\r"); + fflush(stdout); +} + +void totp_cli_delete_last_line() { + TOTP_CLI_PRINTF("\033[A\33[2K\r"); + fflush(stdout); +} diff --git a/applications/external/totp/cli/cli_helpers.h b/applications/external/totp/cli/cli_helpers.h index dd5a282d4..0f8b24364 100644 --- a/applications/external/totp/cli/cli_helpers.h +++ b/applications/external/totp/cli/cli_helpers.h @@ -14,6 +14,11 @@ #define DOCOPT_OPTIONS "[options]" #define DOCOPT_DEFAULT(val) "[default: " val "]" +extern const char* TOTP_CLI_COLOR_ERROR; +extern const char* TOTP_CLI_COLOR_WARNING; +extern const char* TOTP_CLI_COLOR_SUCCESS; +extern const char* TOTP_CLI_COLOR_INFO; + #define TOTP_CLI_PRINTF(format, ...) printf(format, ##__VA_ARGS__) #define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \ @@ -22,11 +27,6 @@ printf("\e[0m"); \ fflush(stdout) -#define TOTP_CLI_COLOR_ERROR "91m" -#define TOTP_CLI_COLOR_WARNING "93m" -#define TOTP_CLI_COLOR_SUCCESS "92m" -#define TOTP_CLI_COLOR_INFO "96m" - #define TOTP_CLI_PRINTF_ERROR(format, ...) \ TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_ERROR, format, ##__VA_ARGS__) #define TOTP_CLI_PRINTF_WARNING(format, ...) \ @@ -36,24 +36,12 @@ #define TOTP_CLI_PRINTF_INFO(format, ...) \ TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_INFO, format, ##__VA_ARGS__) -#define TOTP_CLI_DELETE_LAST_LINE() \ - TOTP_CLI_PRINTF("\033[A\33[2K\r"); \ - fflush(stdout) +#define TOTP_CLI_LOCK_UI(plugin_state) \ + Scene __previous_scene = plugin_state->current_scene; \ + totp_scene_director_activate_scene(plugin_state, TotpSceneStandby) -#define TOTP_CLI_DELETE_CURRENT_LINE() \ - TOTP_CLI_PRINTF("\33[2K\r"); \ - fflush(stdout) - -#define TOTP_CLI_DELETE_LAST_CHAR() \ - TOTP_CLI_PRINTF("\b \b"); \ - fflush(stdout) - -#define TOTP_CLI_PRINT_INVALID_ARGUMENTS() \ - TOTP_CLI_PRINTF_ERROR( \ - "Invalid command arguments. use \"help\" command to get list of available commands") - -#define TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE() \ - TOTP_CLI_PRINTF_ERROR("An error has occurred during updating config file\r\n") +#define TOTP_CLI_UNLOCK_UI(plugin_state) \ + totp_scene_director_activate_scene(plugin_state, __previous_scene) /** * @brief Checks whether user is authenticated and entered correct PIN. @@ -92,3 +80,38 @@ bool args_read_uint8_and_trim(FuriString* args, uint8_t* value); * @param str instance to free */ void furi_string_secure_free(FuriString* str); + +/** + * @brief Deletes last printed line in console + */ +void totp_cli_delete_last_line(); + +/** + * @brief Deletes current printed line in console + */ +void totp_cli_delete_current_line(); + +/** + * @brief Deletes last printed char in console + */ +void totp_cli_delete_last_char(); + +/** + * @brief Prints error message about invalid command arguments + */ +void totp_cli_print_invalid_arguments(); + +/** + * @brief Prints error message about config file update error + */ +void totp_cli_print_error_updating_config_file(); + +/** + * @brief Prints error message about config file loading error + */ +void totp_cli_print_error_loading_token_info(); + +/** + * @brief Prints message to let user knwo that command is processing now + */ +void totp_cli_print_processing(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c index 3549e785b..b2cd01352 100644 --- a/applications/external/totp/cli/commands/add/add.c +++ b/applications/external/totp/cli/commands/add/add.c @@ -1,7 +1,6 @@ #include "add.h" #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" @@ -9,6 +8,77 @@ #include "../../../ui/scene_director.h" #include "../../common_command_arguments.h" +struct TotpAddContext { + FuriString* args; + Cli* cli; + uint8_t* iv; +}; + +enum TotpIteratorUpdateTokenResultsEx { + TotpIteratorUpdateTokenResultInvalidSecret = 1, + TotpIteratorUpdateTokenResultCancelled = 2, + TotpIteratorUpdateTokenResultInvalidArguments = 3 +}; + +static TotpIteratorUpdateTokenResult + add_token_handler(TokenInfo* token_info, const void* context) { + const struct TotpAddContext* context_t = context; + + // Reading token name + if(!args_read_probably_quoted_string_and_trim(context_t->args, token_info->name)) { + return TotpIteratorUpdateTokenResultInvalidArguments; + } + + FuriString* temp_str = furi_string_alloc(); + + // Read optional arguments + bool mask_user_input = true; + PlainTokenSecretEncoding token_secret_encoding = PlainTokenSecretEncodingBase32; + while(args_read_string_and_trim(context_t->args, temp_str)) { + bool parsed = false; + if(!totp_cli_try_read_algo(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_digits(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_duration(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && + !totp_cli_try_read_automation_features(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_plain_token_secret_encoding( + temp_str, context_t->args, &parsed, &token_secret_encoding)) { + totp_cli_printf_unknown_argument(temp_str); + } + + if(!parsed) { + furi_string_free(temp_str); + return TotpIteratorUpdateTokenResultInvalidArguments; + } + } + + // Reading token secret + furi_string_reset(temp_str); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + if(!totp_cli_read_line(context_t->cli, temp_str, mask_user_input)) { + totp_cli_delete_last_line(); + furi_string_secure_free(temp_str); + return TotpIteratorUpdateTokenResultCancelled; + } + + totp_cli_delete_last_line(); + + bool secret_set = token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + token_secret_encoding, + context_t->iv); + + furi_string_secure_free(temp_str); + + if(!secret_set) { + return TotpIteratorUpdateTokenResultInvalidSecret; + } + + return TotpIteratorUpdateTokenResultSuccess; +} + void totp_cli_command_add_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT ", " TOTP_CLI_COMMAND_ADD_ALT2 " Add new token\r\n"); @@ -75,90 +145,33 @@ void totp_cli_command_add_docopt_options() { } void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { - FuriString* temp_str = furi_string_alloc(); - TokenInfo* token_info = token_info_alloc(); - - // Reading token name - if(!args_read_probably_quoted_string_and_trim(args, temp_str)) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - furi_string_free(temp_str); - token_info_free(token_info); + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; } - size_t temp_cstr_len = furi_string_size(temp_str); - token_info->name = malloc(temp_cstr_len + 1); - furi_check(token_info->name != NULL); - strlcpy(token_info->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); - // Read optional arguments - bool mask_user_input = true; - PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; - while(args_read_string_and_trim(args, temp_str)) { - bool parsed = false; - if(!totp_cli_try_read_algo(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_digits(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && - !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_plain_token_secret_encoding( - temp_str, args, &parsed, &token_secret_encoding)) { - totp_cli_printf_unknown_argument(temp_str); - } + TOTP_CLI_LOCK_UI(plugin_state); - if(!parsed) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - furi_string_free(temp_str); - token_info_free(token_info); - return; - } - } + struct TotpAddContext add_context = {.args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + TotpIteratorUpdateTokenResult add_result = + totp_token_info_iterator_add_new_token(iterator_context, &add_token_handler, &add_context); - // Reading token secret - furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); - if(!totp_cli_read_line(cli, temp_str, mask_user_input) || - !totp_cli_ensure_authenticated(plugin_state, cli)) { - TOTP_CLI_DELETE_LAST_LINE(); + if(add_result == TotpIteratorUpdateTokenResultSuccess) { + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully added\r\n", + furi_string_get_cstr( + totp_token_info_iterator_get_current_token(iterator_context)->name)); + } else if(add_result == TotpIteratorUpdateTokenResultCancelled) { TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); - furi_string_secure_free(temp_str); - token_info_free(token_info); - return; - } - - TOTP_CLI_DELETE_LAST_LINE(); - - bool load_generate_token_scene = false; - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - load_generate_token_scene = true; - } - - bool secret_set = token_info_set_secret( - token_info, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - token_secret_encoding, - plugin_state->iv); - - furi_string_secure_free(temp_str); - - if(secret_set) { - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check); - plugin_state->tokens_count++; - - if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully added\r\n", token_info->name); - } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - } - } else { - token_info_free(token_info); + } else if(add_result == TotpIteratorUpdateTokenResultInvalidArguments) { + totp_cli_print_invalid_arguments(); + } else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) { TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); + } else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) { + totp_cli_print_error_updating_config_file(); } - if(load_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/automation/automation.c b/applications/external/totp/cli/commands/automation/automation.c index 1af2e5dd7..c9f6ac34b 100644 --- a/applications/external/totp/cli/commands/automation/automation.c +++ b/applications/external/totp/cli/commands/automation/automation.c @@ -31,7 +31,7 @@ void totp_cli_command_automation_docopt_arguments() { "\r\n"); } -static void totp_cli_command_automation_print_method(AutomationMethod method, char* color) { +static void totp_cli_command_automation_print_method(AutomationMethod method, const char* color) { #ifdef TOTP_BADBT_TYPE_ENABLED bool has_previous_method = false; #endif @@ -88,26 +88,20 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a do { if(!args_valid) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); break; } if(new_method_provided) { - Scene previous_scene = TotpSceneNone; - if(plugin_state->current_scene == TotpSceneGenerateToken || - plugin_state->current_scene == TotpSceneAppSettings) { - previous_scene = plugin_state->current_scene; - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - } + TOTP_CLI_LOCK_UI(plugin_state); plugin_state->automation_method = new_method; - if(totp_config_file_update_automation_method(new_method) == - TotpConfigFileUpdateSuccess) { + if(totp_config_file_update_automation_method(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Automation method is set to "); totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); cli_nl(); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_print_error_updating_config_file(); } #ifdef TOTP_BADBT_TYPE_ENABLED @@ -118,9 +112,7 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a } #endif - if(previous_scene != TotpSceneNone) { - totp_scene_director_activate_scene(plugin_state, previous_scene, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_INFO("Current automation method is "); totp_cli_command_automation_print_method( diff --git a/applications/external/totp/cli/commands/delete/delete.c b/applications/external/totp/cli/commands/delete/delete.c index a45525e4b..9a084b23f 100644 --- a/applications/external/totp/cli/commands/delete/delete.c +++ b/applications/external/totp/cli/commands/delete/delete.c @@ -3,7 +3,6 @@ #include #include #include -#include #include "../../../services/config/config.h" #include "../../cli_helpers.h" #include "../../../ui/scene_director.h" @@ -37,10 +36,13 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, return; } + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + int token_number; if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || - token_number > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) { + totp_cli_print_invalid_arguments(); return; } @@ -51,23 +53,27 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, confirm_needed = false; } else { totp_cli_printf_unknown_argument(temp_str); - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); furi_string_free(temp_str); return; } } furi_string_free(temp_str); - ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1); + TOTP_CLI_LOCK_UI(plugin_state); - TokenInfo* token_info = list_node->data; + size_t original_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + totp_token_info_iterator_go_to(iterator_context, token_number - 1); + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); + const char* token_info_name = furi_string_get_cstr(token_info->name); bool confirmed = !confirm_needed; if(confirm_needed) { TOTP_CLI_PRINTF_WARNING("WARNING!\r\n"); TOTP_CLI_PRINTF_WARNING( "TOKEN \"%s\" WILL BE PERMANENTLY DELETED WITHOUT ABILITY TO RECOVER IT.\r\n", - token_info->name); + token_info_name); TOTP_CLI_PRINTF_WARNING("Confirm? [y/n]\r\n"); fflush(stdout); char user_pick; @@ -80,32 +86,21 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, } if(confirmed) { - if(!totp_cli_ensure_authenticated(plugin_state, cli)) { - return; - } - - bool activate_generate_token_scene = false; - if(plugin_state->current_scene != TotpSceneAuthentication) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - activate_generate_token_scene = true; - } - - plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node); - plugin_state->tokens_count--; - - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + totp_cli_print_processing(); + if(totp_token_info_iterator_remove_current_token_info(iterator_context)) { + totp_cli_delete_last_line(); TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully deleted\r\n", token_info->name); + "Token \"%s\" has been successfully deleted\r\n", token_info_name); + totp_token_info_iterator_go_to(iterator_context, 0); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - } - - token_info_free(token_info); - - if(activate_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + totp_cli_delete_last_line(); + totp_cli_print_error_updating_config_file(); + totp_token_info_iterator_go_to(iterator_context, original_token_index); } } else { TOTP_CLI_PRINTF_INFO("User has not confirmed\r\n"); + totp_token_info_iterator_go_to(iterator_context, original_token_index); } + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/details/details.c b/applications/external/totp/cli/commands/details/details.c index 1b9289454..1677121b1 100644 --- a/applications/external/totp/cli/commands/details/details.c +++ b/applications/external/totp/cli/commands/details/details.c @@ -1,9 +1,10 @@ #include "details.h" #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/constants.h" +#include "../../../services/config/config.h" +#include "../../../ui/scene_director.h" #include "../../cli_helpers.h" #include "../../common_command_arguments.h" @@ -17,21 +18,21 @@ } while(false) static void print_automation_features(const TokenInfo* token_info) { - if(token_info->automation_features == TOKEN_AUTOMATION_FEATURE_NONE) { + if(token_info->automation_features == TokenAutomationFeatureNone) { TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Automation features", "None"); return; } bool header_printed = false; - if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { + if(token_info->automation_features & TokenAutomationFeatureEnterAtTheEnd) { TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type key at the end", header_printed); } - if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END) { + if(token_info->automation_features & TokenAutomationFeatureTabAtTheEnd) { TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type key at the end", header_printed); } - if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { + if(token_info->automation_features & TokenAutomationFeatureTypeSlower) { TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type slower", header_printed); } } @@ -53,26 +54,39 @@ void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args } int token_number; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || - token_number > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) { + totp_cli_print_invalid_arguments(); return; } - ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1); + TOTP_CLI_LOCK_UI(plugin_state); - TokenInfo* token_info = list_node->data; + size_t original_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + if(totp_token_info_iterator_go_to(iterator_context, token_number - 1)) { + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); - TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); - TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number); - TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Name", token_info->name); - TOTP_CLI_PRINTF( - "| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info)); - TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits); - TOTP_CLI_PRINTF( - "| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " "); - print_automation_features(token_info); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number); + TOTP_CLI_PRINTF( + "| %-20s | %-28.28s |\r\n", "Name", furi_string_get_cstr(token_info->name)); + TOTP_CLI_PRINTF( + "| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info)); + TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits); + TOTP_CLI_PRINTF( + "| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " "); + print_automation_features(token_info); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + } else { + totp_cli_print_error_loading_token_info(); + } + + totp_token_info_iterator_go_to(iterator_context, original_token_index); + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/list/list.c b/applications/external/totp/cli/commands/list/list.c index 951c102a0..bf5a8da52 100644 --- a/applications/external/totp/cli/commands/list/list.c +++ b/applications/external/totp/cli/commands/list/list.c @@ -1,8 +1,9 @@ #include "list.h" #include -#include #include "../../../types/token_info.h" #include "../../../services/config/constants.h" +#include "../../../services/config/config.h" +#include "../../../ui/scene_director.h" #include "../../cli_helpers.h" void totp_cli_command_list_docopt_commands() { @@ -20,25 +21,36 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) { return; } - if(plugin_state->tokens_list == NULL) { + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t total_count = totp_token_info_iterator_get_total_count(iterator_context); + if(total_count <= 0) { TOTP_CLI_PRINTF("There are no tokens"); return; } + TOTP_CLI_LOCK_UI(plugin_state); + + size_t original_index = totp_token_info_iterator_get_current_token_index(iterator_context); + TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); TOTP_CLI_PRINTF("| %-3s | %-25s | %-6s | %-s | %-s |\r\n", "#", "Name", "Algo", "Ln", "Dur"); TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); - uint16_t index = 1; - TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { - TokenInfo* token_info = (TokenInfo*)node->data; + for(size_t i = 0; i < total_count; i++) { + totp_token_info_iterator_go_to(iterator_context, i); + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); TOTP_CLI_PRINTF( "| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n", - index, - token_info->name, + i + 1, + furi_string_get_cstr(token_info->name), token_info_get_algo_as_cstr(token_info), token_info->digits, token_info->duration); - index++; - }); + } + TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); + + totp_token_info_iterator_go_to(iterator_context, original_index); + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/move/move.c b/applications/external/totp/cli/commands/move/move.c index 5c4bdfcd6..f26cda46e 100644 --- a/applications/external/totp/cli/commands/move/move.c +++ b/applications/external/totp/cli/commands/move/move.c @@ -2,7 +2,6 @@ #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../cli_helpers.h" @@ -33,42 +32,52 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C return; } - int token_index; - if(!args_read_int_and_trim(args, &token_index) || token_index < 1 || - token_index > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + int token_number; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t total_count = totp_token_info_iterator_get_total_count(iterator_context); + if(!args_read_int_and_trim(args, &token_number) || token_number < 1 || + (size_t)token_number > total_count) { + totp_cli_print_invalid_arguments(); return; } - int new_token_index = 0; + int new_token_number = 0; - if(!args_read_int_and_trim(args, &new_token_index) || new_token_index < 1 || - new_token_index > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + if(!args_read_int_and_trim(args, &new_token_number) || new_token_number < 1 || + (size_t)new_token_number > total_count) { + totp_cli_print_invalid_arguments(); return; } - bool activate_generate_token_scene = false; - if(plugin_state->current_scene != TotpSceneAuthentication) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - activate_generate_token_scene = true; + if(token_number == new_token_number) { + TOTP_CLI_PRINTF_ERROR("New token number matches current token number\r\n"); + return; } - TokenInfo* token_info = NULL; - plugin_state->tokens_list = - list_remove_at(plugin_state->tokens_list, token_index - 1, (void**)&token_info); - furi_check(token_info != NULL); - plugin_state->tokens_list = - list_insert_at(plugin_state->tokens_list, new_token_index - 1, token_info); + TOTP_CLI_LOCK_UI(plugin_state); - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + size_t token_index = token_number - 1; + size_t new_token_index = new_token_number - 1; + + size_t original_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + + totp_cli_print_processing(); + + if(totp_token_info_iterator_go_to(iterator_context, token_index) && + totp_token_info_iterator_move_current_token_info(iterator_context, new_token_index)) { + totp_cli_delete_last_line(); TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully updated\r\n", token_info->name); + "Token \"%s\" has been successfully updated\r\n", + furi_string_get_cstr( + totp_token_info_iterator_get_current_token(iterator_context)->name)); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_delete_last_line(); + totp_cli_print_error_updating_config_file(); } - if(activate_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_token_info_iterator_go_to(iterator_context, original_token_index); + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/notification/notification.c b/applications/external/totp/cli/commands/notification/notification.c index b81b7371a..f91b1b982 100644 --- a/applications/external/totp/cli/commands/notification/notification.c +++ b/applications/external/totp/cli/commands/notification/notification.c @@ -28,7 +28,8 @@ void totp_cli_command_notification_docopt_arguments() { ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\r\n"); } -static void totp_cli_command_notification_print_method(NotificationMethod method, char* color) { +static void + totp_cli_command_notification_print_method(NotificationMethod method, const char* color) { bool has_previous_method = false; if(method & NotificationMethodSound) { TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "\""); @@ -73,31 +74,23 @@ void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString* do { if(!args_valid) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); break; } if(new_method_provided) { - Scene previous_scene = TotpSceneNone; - if(plugin_state->current_scene == TotpSceneGenerateToken || - plugin_state->current_scene == TotpSceneAppSettings) { - previous_scene = plugin_state->current_scene; - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - } + TOTP_CLI_LOCK_UI(plugin_state); plugin_state->notification_method = new_method; - if(totp_config_file_update_notification_method(new_method) == - TotpConfigFileUpdateSuccess) { + if(totp_config_file_update_notification_method(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Notification method is set to "); totp_cli_command_notification_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); cli_nl(); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_print_error_updating_config_file(); } - if(previous_scene != TotpSceneNone) { - totp_scene_director_activate_scene(plugin_state, previous_scene, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_INFO("Current notification method is "); totp_cli_command_notification_print_method( diff --git a/applications/external/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c index 9b9038ae7..62531b96a 100644 --- a/applications/external/totp/cli/commands/pin/pin.c +++ b/applications/external/totp/cli/commands/pin/pin.c @@ -2,7 +2,6 @@ #include #include -#include #include "../../../types/token_info.h" #include "../../../types/user_pin_codes.h" #include "../../../services/config/config.h" @@ -65,14 +64,14 @@ static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) { } } } else if(c == CliSymbolAsciiETX) { - TOTP_CLI_DELETE_CURRENT_LINE(); + totp_cli_delete_current_line(); TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); return false; } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { if(*pin_length > 0) { *pin_length = *pin_length - 1; pin[*pin_length] = 0; - TOTP_CLI_DELETE_LAST_CHAR(); + totp_cli_delete_last_char(); } } else if(c == CliSymbolAsciiCR) { cli_nl(); @@ -80,7 +79,7 @@ static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) { } } - TOTP_CLI_DELETE_LAST_LINE(); + totp_cli_delete_last_line(); return true; } @@ -97,22 +96,22 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) == 0) { do_remove = true; } else { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); } } else { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); } if((do_change || do_remove) && totp_cli_ensure_authenticated(plugin_state, cli)) { - bool load_generate_token_scene = false; + TOTP_CLI_LOCK_UI(plugin_state); do { uint8_t old_iv[TOTP_IV_SIZE]; memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); uint8_t new_pin[TOTP_IV_SIZE]; + memset(&new_pin[0], 0, TOTP_IV_SIZE); uint8_t new_pin_length = 0; if(do_change) { - if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length) || - !totp_cli_ensure_authenticated(plugin_state, cli)) { + if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length)) { memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); break; } @@ -121,7 +120,7 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl memset(&new_pin[0], 0, TOTP_IV_SIZE); } - char* backup_path = totp_config_file_backup(); + char* backup_path = totp_config_file_backup(plugin_state); if(backup_path != NULL) { TOTP_CLI_PRINTF_WARNING("Backup conf file %s has been created\r\n", backup_path); TOTP_CLI_PRINTF_WARNING( @@ -134,61 +133,28 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl break; } - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - load_generate_token_scene = true; - } + TOTP_CLI_PRINTF("Encrypting...\r\n"); - TOTP_CLI_PRINTF("Encrypting, please wait...\r\n"); - - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE); - if(plugin_state->crypto_verify_data != NULL) { - free(plugin_state->crypto_verify_data); - plugin_state->crypto_verify_data = NULL; - } - - if(!totp_crypto_seed_iv( - plugin_state, new_pin_length > 0 ? &new_pin[0] : NULL, new_pin_length)) { - memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - break; - } + bool update_result = + totp_config_file_update_encryption(plugin_state, new_pin, new_pin_length); memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); - TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { - TokenInfo* token_info = node->data; - size_t plain_token_length; - uint8_t* plain_token = totp_crypto_decrypt( - token_info->token, token_info->token_length, &old_iv[0], &plain_token_length); - free(token_info->token); - token_info->token = totp_crypto_encrypt( - plain_token, - plain_token_length, - &plugin_state->iv[0], - &token_info->token_length); - memset_s(plain_token, plain_token_length, 0, plain_token_length); - free(plain_token); - }); + totp_cli_delete_last_line(); - TOTP_CLI_DELETE_LAST_LINE(); - - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + if(update_result) { if(do_change) { TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully changed\r\n"); } else if(do_remove) { TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully removed\r\n"); } } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_print_error_updating_config_file(); } } while(false); - if(load_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } furi_string_free(temp_str); diff --git a/applications/external/totp/cli/commands/reset/reset.c b/applications/external/totp/cli/commands/reset/reset.c index cd2d1bf46..96b2cc56a 100644 --- a/applications/external/totp/cli/commands/reset/reset.c +++ b/applications/external/totp/cli/commands/reset/reset.c @@ -3,6 +3,7 @@ #include #include #include "../../cli_helpers.h" +#include "../../../ui/scene_director.h" #include "../../../services/config/config.h" #define TOTP_CLI_RESET_CONFIRMATION_KEYWORD "YES" @@ -16,7 +17,11 @@ void totp_cli_command_reset_docopt_usage() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_RESET "\r\n"); } -void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue) { +void totp_cli_command_reset_handle( + PluginState* plugin_state, + Cli* cli, + FuriMessageQueue* event_queue) { + TOTP_CLI_LOCK_UI(plugin_state); TOTP_CLI_PRINTF_WARNING( "As a result of reset all the settings and tokens will be permanently lost.\r\n"); TOTP_CLI_PRINTF_WARNING("Do you really want to reset application?\r\n"); @@ -27,11 +32,12 @@ void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue) { furi_string_cmpi_str(temp_str, TOTP_CLI_RESET_CONFIRMATION_KEYWORD) == 0; furi_string_free(temp_str); if(is_confirmed) { - totp_config_file_reset(); + totp_config_file_reset(plugin_state); TOTP_CLI_PRINTF_SUCCESS("Application has been successfully reset to default.\r\n"); TOTP_CLI_PRINTF_SUCCESS("Now application will be closed to apply all the changes.\r\n"); totp_cli_force_close_app(event_queue); } else { TOTP_CLI_PRINTF_INFO("Action was not confirmed by user\r\n"); + TOTP_CLI_UNLOCK_UI(plugin_state); } } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/reset/reset.h b/applications/external/totp/cli/commands/reset/reset.h index 7c879e13e..3a4675587 100644 --- a/applications/external/totp/cli/commands/reset/reset.h +++ b/applications/external/totp/cli/commands/reset/reset.h @@ -5,6 +5,9 @@ #define TOTP_CLI_COMMAND_RESET "reset" -void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue); +void totp_cli_command_reset_handle( + PluginState* plugin_state, + Cli* cli, + FuriMessageQueue* event_queue); void totp_cli_command_reset_docopt_commands(); void totp_cli_command_reset_docopt_usage(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/timezone/timezone.c b/applications/external/totp/cli/commands/timezone/timezone.c index 61e4fa065..0f6bc5a76 100644 --- a/applications/external/totp/cli/commands/timezone/timezone.c +++ b/applications/external/totp/cli/commands/timezone/timezone.c @@ -33,19 +33,14 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg char* strtof_endptr; float tz = strtof(furi_string_get_cstr(temp_str), &strtof_endptr); if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) { + TOTP_CLI_LOCK_UI(plugin_state); plugin_state->timezone_offset = tz; - if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) { + if(totp_config_file_update_timezone_offset(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", (double)tz); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - } - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } else if(plugin_state->current_scene == TotpSceneAppSettings) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); + totp_cli_print_error_updating_config_file(); } + TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n"); } diff --git a/applications/external/totp/cli/commands/update/update.c b/applications/external/totp/cli/commands/update/update.c index bba7cad35..49beb7b6d 100644 --- a/applications/external/totp/cli/commands/update/update.c +++ b/applications/external/totp/cli/commands/update/update.c @@ -1,7 +1,6 @@ #include "update.h" #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" @@ -11,6 +10,103 @@ #define TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX "-s" +struct TotpUpdateContext { + FuriString* args; + Cli* cli; + uint8_t* iv; +}; + +enum TotpIteratorUpdateTokenResultsEx { + TotpIteratorUpdateTokenResultInvalidSecret = 1, + TotpIteratorUpdateTokenResultCancelled = 2, + TotpIteratorUpdateTokenResultInvalidArguments = 3 +}; + +static bool totp_cli_try_read_name( + TokenInfo* token_info, + const FuriString* arg, + FuriString* args, + bool* parsed) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_NAME_PREFIX) == 0) { + if(!args_read_probably_quoted_string_and_trim(args, token_info->name) || + furi_string_empty(token_info->name)) { + totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_NAME_PREFIX); + } else { + *parsed = true; + } + + return true; + } + + return false; +} + +static bool totp_cli_try_read_change_secret_flag(const FuriString* arg, bool* parsed, bool* flag) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) == 0) { + *flag = true; + *parsed = true; + return true; + } + + return false; +} + +static TotpIteratorUpdateTokenResult + update_token_handler(TokenInfo* token_info, const void* context) { + const struct TotpUpdateContext* context_t = context; + + // Read optional arguments + FuriString* temp_str = furi_string_alloc(); + bool mask_user_input = true; + bool update_token_secret = false; + PlainTokenSecretEncoding token_secret_encoding = PlainTokenSecretEncodingBase32; + while(args_read_string_and_trim(context_t->args, temp_str)) { + bool parsed = false; + if(!totp_cli_try_read_name(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_algo(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_digits(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_duration(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && + !totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) && + !totp_cli_try_read_automation_features(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_plain_token_secret_encoding( + temp_str, context_t->args, &parsed, &token_secret_encoding)) { + totp_cli_printf_unknown_argument(temp_str); + } + + if(!parsed) { + furi_string_free(temp_str); + return TotpIteratorUpdateTokenResultInvalidArguments; + } + } + + if(update_token_secret) { + // Reading token secret + furi_string_reset(temp_str); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + bool token_secret_read = totp_cli_read_line(context_t->cli, temp_str, mask_user_input); + totp_cli_delete_last_line(); + if(!token_secret_read) { + furi_string_secure_free(temp_str); + return TotpIteratorUpdateTokenResultCancelled; + } + + if(!token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + token_secret_encoding, + context_t->iv)) { + furi_string_secure_free(temp_str); + return TotpIteratorUpdateTokenResultInvalidSecret; + } + } + + furi_string_secure_free(temp_str); + + return TotpIteratorUpdateTokenResultSuccess; +} + void totp_cli_command_update_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_UPDATE " Update existing token\r\n"); } @@ -34,136 +130,46 @@ void totp_cli_command_update_docopt_options() { TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) " Update token secret\r\n"); } -static bool - totp_cli_try_read_name(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed) { - if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_NAME_PREFIX) == 0) { - if(!args_read_probably_quoted_string_and_trim(args, arg) || furi_string_empty(arg)) { - totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_NAME_PREFIX); - } else { - if(token_info->name != NULL) { - free(token_info->name); - } - - size_t temp_cstr_len = furi_string_size(arg); - token_info->name = malloc(temp_cstr_len + 1); - furi_check(token_info->name != NULL); - strlcpy(token_info->name, furi_string_get_cstr(arg), temp_cstr_len + 1); - *parsed = true; - } - - return true; - } - - return false; -} - -static bool totp_cli_try_read_change_secret_flag(const FuriString* arg, bool* parsed, bool* flag) { - if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) == 0) { - *flag = true; - *parsed = true; - return true; - } - - return false; -} - void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { - FuriString* temp_str = furi_string_alloc(); - if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; } + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + int token_number; if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || - token_number > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) { + totp_cli_print_invalid_arguments(); return; } - ListNode* list_item = list_element_at(plugin_state->tokens_list, token_number - 1); - TokenInfo* existing_token_info = list_item->data; - TokenInfo* token_info = token_info_clone(existing_token_info); + TOTP_CLI_LOCK_UI(plugin_state); - // Read optional arguments - bool mask_user_input = true; - bool update_token_secret = false; - PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; - while(args_read_string_and_trim(args, temp_str)) { - bool parsed = false; - if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_algo(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_digits(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && - !totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) && - !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_plain_token_secret_encoding( - temp_str, args, &parsed, &token_secret_encoding)) { - totp_cli_printf_unknown_argument(temp_str); - } + size_t previous_index = totp_token_info_iterator_get_current_token_index(iterator_context); + totp_token_info_iterator_go_to(iterator_context, token_number - 1); - if(!parsed) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - furi_string_free(temp_str); - token_info_free(token_info); - return; - } + struct TotpUpdateContext update_context = { + .args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + TotpIteratorUpdateTokenResult update_result = totp_token_info_iterator_update_current_token( + iterator_context, &update_token_handler, &update_context); + + if(update_result == TotpIteratorUpdateTokenResultSuccess) { + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully updated\r\n", + furi_string_get_cstr( + totp_token_info_iterator_get_current_token(iterator_context)->name)); + } else if(update_result == TotpIteratorUpdateTokenResultInvalidArguments) { + totp_cli_print_invalid_arguments(); + } else if(update_result == TotpIteratorUpdateTokenResultCancelled) { + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); + } else if(update_result == TotpIteratorUpdateTokenResultInvalidSecret) { + TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); + } else if(update_result == TotpIteratorUpdateTokenResultFileUpdateFailed) { + totp_cli_print_error_updating_config_file(); } - bool token_secret_read = false; - if(update_token_secret) { - // Reading token secret - furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); - token_secret_read = totp_cli_read_line(cli, temp_str, mask_user_input); - TOTP_CLI_DELETE_LAST_LINE(); - if(!token_secret_read) { - TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); - } - } - - bool load_generate_token_scene = false; - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - load_generate_token_scene = true; - } - - bool token_secret_set = false; - if(update_token_secret && token_secret_read) { - if(token_info->token != NULL) { - free(token_info->token); - } - token_secret_set = token_info_set_secret( - token_info, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - token_secret_encoding, - plugin_state->iv); - if(!token_secret_set) { - TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); - } - } - - furi_string_secure_free(temp_str); - - if(!update_token_secret || (token_secret_read && token_secret_set)) { - list_item->data = token_info; - - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully updated\r\n", token_info->name); - token_info_free(existing_token_info); - } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - list_item->data = existing_token_info; - token_info_free(token_info); - } - } else { - token_info_free(token_info); - } - - if(load_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_token_info_iterator_go_to(iterator_context, previous_index); + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/common_command_arguments.c b/applications/external/totp/cli/common_command_arguments.c index 9ed9f0126..c3129a157 100644 --- a/applications/external/totp/cli/common_command_arguments.c +++ b/applications/external/totp/cli/common_command_arguments.c @@ -1,11 +1,11 @@ #include "common_command_arguments.h" #include -inline void totp_cli_printf_missed_argument_value(char* arg) { +void totp_cli_printf_missed_argument_value(char* arg) { TOTP_CLI_PRINTF_ERROR("Missed or incorrect value for argument \"%s\"\r\n", arg); } -inline void totp_cli_printf_unknown_argument(const FuriString* arg) { +void totp_cli_printf_unknown_argument(const FuriString* arg) { TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(arg)); } @@ -121,10 +121,10 @@ bool totp_cli_try_read_plain_token_secret_encoding( totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX); } else { if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE32_NAME) == 0) { - *secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; + *secret_encoding = PlainTokenSecretEncodingBase32; *parsed = true; } else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) { - *secret_encoding = PLAIN_TOKEN_ENCODING_BASE64; + *secret_encoding = PlainTokenSecretEncodingBase64; *parsed = true; } else { TOTP_CLI_PRINTF_ERROR( diff --git a/applications/external/totp/cli/common_command_arguments.h b/applications/external/totp/cli/common_command_arguments.h index be01c216d..be1c0bdfe 100644 --- a/applications/external/totp/cli/common_command_arguments.h +++ b/applications/external/totp/cli/common_command_arguments.h @@ -19,23 +19,29 @@ #define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding" void totp_cli_printf_unknown_argument(const FuriString* arg); + void totp_cli_printf_missed_argument_value(char* arg); + bool totp_cli_try_read_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_digits( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_duration( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_automation_features( TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag); bool totp_cli_try_read_plain_token_secret_encoding( diff --git a/applications/external/totp/lib/base64/base64.c b/applications/external/totp/lib/base64/base64.c index dd0a12b5e..2cb0d2ffc 100644 --- a/applications/external/totp/lib/base64/base64.c +++ b/applications/external/totp/lib/base64/base64.c @@ -1,6 +1,7 @@ /* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005, Jouni Malinen + * Modified and optimized for Flipepr Zero device purposes by Alex Kopachov (@akopachov) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/applications/external/totp/lib/linked_list/linked_list.c b/applications/external/totp/lib/linked_list/linked_list.c deleted file mode 100644 index 23d10c548..000000000 --- a/applications/external/totp/lib/linked_list/linked_list.c +++ /dev/null @@ -1,136 +0,0 @@ -#include "linked_list.h" - -ListNode* list_init_head(void* data) { - ListNode* new = malloc(sizeof(ListNode)); - if(new == NULL) return NULL; - new->data = data; - new->next = NULL; - return new; -} - -ListNode* list_add(ListNode* head, void* data) { - ListNode* new = malloc(sizeof(ListNode)); - if(new == NULL) return NULL; - new->data = data; - new->next = NULL; - - if(head == NULL) - head = new; - else { - ListNode* it; - - for(it = head; it->next != NULL; it = it->next) - ; - - it->next = new; - } - - return head; -} - -ListNode* list_find(ListNode* head, const void* data) { - ListNode* it = NULL; - - for(it = head; it != NULL; it = it->next) - if(it->data == data) break; - - return it; -} - -ListNode* list_element_at(ListNode* head, uint16_t index) { - ListNode* it; - uint16_t i; - for(it = head, i = 0; it != NULL && i < index; it = it->next, i++) - ; - return it; -} - -ListNode* list_remove(ListNode* head, ListNode* ep) { - if(head == NULL) { - return NULL; - } - - if(head == ep) { - ListNode* new_head = head->next; - free(head); - return new_head; - } - - ListNode* it; - - for(it = head; it->next != ep; it = it->next) - ; - - it->next = ep->next; - free(ep); - - return head; -} - -ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data) { - if(head == NULL) { - return NULL; - } - - ListNode* it; - ListNode* prev = NULL; - - uint16_t i; - - for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) - ; - - if(it == NULL) return head; - - ListNode* new_head = head; - if(prev == NULL) { - new_head = it->next; - } else { - prev->next = it->next; - } - - if(removed_node_data != NULL) { - *removed_node_data = it->data; - } - - free(it); - - return new_head; -} - -ListNode* list_insert_at(ListNode* head, uint16_t index, void* data) { - if(index == 0 || head == NULL) { - ListNode* new_head = list_init_head(data); - if(new_head != NULL) { - new_head->next = head; - } - return new_head; - } - - ListNode* it; - ListNode* prev = NULL; - - uint16_t i; - - for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) - ; - - ListNode* new = malloc(sizeof(ListNode)); - if(new == NULL) return NULL; - new->data = data; - new->next = it; - prev->next = new; - - return head; -} - -void list_free(ListNode* head) { - ListNode* it = head; - ListNode* tmp; - - while(it != NULL) { - tmp = it; - it = it->next; - free(tmp); - } -} diff --git a/applications/external/totp/lib/linked_list/linked_list.h b/applications/external/totp/lib/linked_list/linked_list.h deleted file mode 100644 index 3c938e59a..000000000 --- a/applications/external/totp/lib/linked_list/linked_list.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include -#include - -/** - * @brief Single linked list node - */ -typedef struct ListNode { - /** - * @brief Pointer to the data assigned to the current list node - */ - void* data; - - /** - * @brief Pointer to the next list node - */ - struct ListNode* next; -} ListNode; - -/** - * @brief Initializes a new list node head - * @param data data to be assigned to the head list node - * @return Head list node - */ -ListNode* list_init_head(void* data); - -/** - * @brief Adds new list node to the end of the list - * @param head head list node - * @param data data to be assigned to the newly added list node - * @return Head list node - */ -ListNode* list_add( - ListNode* head, - void* data); /* adds element with specified data to the end of the list and returns new head node. */ - -/** - * @brief Searches list node with the given assigned \p data in the list - * @param head head list node - * @param data data to be searched - * @return List node containing \p data if there is such a node in the list; \c NULL otherwise - */ -ListNode* list_find(ListNode* head, const void* data); - -/** - * @brief Searches list node with the given \p index in the list - * @param head head list node - * @param index desired list node index - * @return List node with the given \p index in the list if there is such a list node; \c NULL otherwise - */ -ListNode* list_element_at(ListNode* head, uint16_t index); - -/** - * @brief Removes list node from the list - * @param head head list node - * @param ep list node to be removed - * @return Head list node - */ -ListNode* list_remove(ListNode* head, ListNode* ep); - -/** - * @brief Removes list node with the given \p index in the list from the list - * @param head head list node - * @param index index of the node to be removed - * @param[out] removed_node_data data which was assigned to the removed list node - * @return Head list node - */ -ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data); - -/** - * @brief Inserts new list node at the given index - * @param head head list node - * @param index index in the list where the new list node should be inserted - * @param data data to be assgned to the new list node - * @return Head list node - */ -ListNode* list_insert_at(ListNode* head, uint16_t index, void* data); - -/** - * @brief Disposes all the list nodes in the list - * @param head head list node - */ -void list_free(ListNode* head); - -#define TOTP_LIST_INIT_OR_ADD(head, item, assert) \ - if(head == NULL) { \ - head = list_init_head(item); \ - assert(head != NULL); \ - } else { \ - assert(list_add(head, item) != NULL); \ - } - -#define TOTP_LIST_FOREACH(head, node, action) \ - ListNode* node = head; \ - while(node != NULL) { \ - action node = node->next; \ - } diff --git a/applications/external/totp/lib/roll_value/roll_value.c b/applications/external/totp/lib/roll_value/roll_value.c index b8f30e078..326c7846a 100644 --- a/applications/external/totp/lib/roll_value/roll_value.c +++ b/applications/external/totp/lib/roll_value/roll_value.c @@ -25,4 +25,4 @@ TOTP_ROLL_VALUE_FN(int8_t, int8_t) TOTP_ROLL_VALUE_FN(uint8_t, int8_t) -TOTP_ROLL_VALUE_FN(uint16_t, int16_t); \ No newline at end of file +TOTP_ROLL_VALUE_FN(size_t, int16_t); \ No newline at end of file diff --git a/applications/external/totp/lib/roll_value/roll_value.h b/applications/external/totp/lib/roll_value/roll_value.h index 3c270be9a..0c1894fd6 100644 --- a/applications/external/totp/lib/roll_value/roll_value.h +++ b/applications/external/totp/lib/roll_value/roll_value.h @@ -1,6 +1,7 @@ #pragma once #include +#include typedef uint8_t TotpRollValueOverflowBehavior; @@ -47,7 +48,7 @@ TOTP_ROLL_VALUE_FN_HEADER(int8_t, int8_t); TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t); /** - * @brief Rolls \c uint16_t \p value using \p min and \p max as an value constraints with \p step step. + * @brief Rolls \c size_t \p value using \p min and \p max as an value constraints with \p step step. * When value reaches constraint value \p overflow_behavior defines what to do next. * @param[in,out] value value to roll * @param step step to be used to change value @@ -55,4 +56,4 @@ TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t); * @param max maximum possible value * @param overflow_behavior defines what to do when value reaches constraint value */ -TOTP_ROLL_VALUE_FN_HEADER(uint16_t, int16_t); \ No newline at end of file +TOTP_ROLL_VALUE_FN_HEADER(size_t, int16_t); \ No newline at end of file diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index 0453338d3..f97bc6b26 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -1,19 +1,41 @@ #include "config.h" #include #include -#include +#include +#include +#include +#include +#include #include "../../types/common.h" #include "../../types/token_info.h" #include "../../features_config.h" +#include "../crypto/crypto.h" #include "migrations/common_migration.h" -#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" -#define CONFIG_FILE_BACKUP_BASE_PATH CONFIG_FILE_PATH ".backup" +#define CONFIG_FILE_BACKUP_DIR CONFIG_FILE_DIRECTORY_PATH "/backups" +#define CONFIG_FILE_BACKUP_BASE_PATH CONFIG_FILE_BACKUP_DIR "/totp.conf" #define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp" #define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig" #define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf" +struct ConfigFileContext { + /** + * @brief Config file reference + */ + FlipperFormat* config_file; + + /** + * @brief Storage reference + */ + Storage* storage; + + /** + * @brief Token list iterator context + */ + TokenInfoIteratorContext* token_info_iterator_context; +}; + /** * @brief Opens storage record * @return Storage record @@ -45,16 +67,31 @@ static void totp_close_config_file(FlipperFormat* file) { * @return backup path if backup successfully taken; \c NULL otherwise */ static char* totp_config_file_backup_i(Storage* storage) { - uint8_t backup_path_size = sizeof(CONFIG_FILE_BACKUP_BASE_PATH) + 5; + if(!storage_dir_exists(storage, CONFIG_FILE_BACKUP_DIR) && + !storage_simply_mkdir(storage, CONFIG_FILE_BACKUP_DIR)) { + return NULL; + } + + FuriHalRtcDateTime current_datetime; + furi_hal_rtc_get_datetime(¤t_datetime); + + uint8_t backup_path_size = sizeof(CONFIG_FILE_BACKUP_BASE_PATH) + 14; char* backup_path = malloc(backup_path_size); furi_check(backup_path != NULL); memcpy(backup_path, CONFIG_FILE_BACKUP_BASE_PATH, sizeof(CONFIG_FILE_BACKUP_BASE_PATH)); uint16_t i = 1; bool backup_file_exists; - while((backup_file_exists = storage_common_exists(storage, backup_path)) && i <= 9999) { - snprintf(backup_path, backup_path_size, CONFIG_FILE_BACKUP_BASE_PATH ".%" PRIu16, i); + do { + snprintf( + backup_path, + backup_path_size, + CONFIG_FILE_BACKUP_BASE_PATH ".%4" PRIu16 "%02" PRIu8 "%02" PRIu8 "-%" PRIu16, + current_datetime.year, + current_datetime.month, + current_datetime.day, + i); i++; - } + } while((backup_file_exists = storage_common_exists(storage, backup_path)) && i <= 9999); if(backup_file_exists || storage_common_copy(storage, CONFIG_FILE_PATH, backup_path) != FSE_OK) { @@ -73,7 +110,7 @@ static char* totp_config_file_backup_i(Storage* storage) { * @param[out] file opened config file * @return Config file open result */ -static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperFormat** file) { +static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK) { @@ -81,7 +118,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF if(!flipper_format_file_open_existing(fff_data_file, CONFIG_FILE_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error opening existing file %s", CONFIG_FILE_PATH); totp_close_config_file(fff_data_file); - return TotpConfigFileOpenError; + return false; } } else if(storage_common_stat(storage, CONFIG_FILE_PATH_PREVIOUS, NULL) == FSE_OK) { FURI_LOG_D(LOGGING_TAG, "Old config file %s found", CONFIG_FILE_PATH_PREVIOUS); @@ -93,13 +130,13 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); totp_close_config_file(fff_data_file); - return TotpConfigFileOpenError; + return false; } } if(storage_common_rename(storage, CONFIG_FILE_PATH_PREVIOUS, CONFIG_FILE_PATH) != FSE_OK) { FURI_LOG_E(LOGGING_TAG, "Error moving config to %s", CONFIG_FILE_PATH); totp_close_config_file(fff_data_file); - return TotpConfigFileOpenError; + return false; } FURI_LOG_I(LOGGING_TAG, "Applied config file path migration"); return totp_open_config_file(storage, file); @@ -112,421 +149,155 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF CONFIG_FILE_DIRECTORY_PATH); if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); - return TotpConfigFileOpenError; + return false; } } if(!flipper_format_file_open_new(fff_data_file, CONFIG_FILE_PATH)) { totp_close_config_file(fff_data_file); FURI_LOG_E(LOGGING_TAG, "Error creating new file %s", CONFIG_FILE_PATH); - return TotpConfigFileOpenError; + return false; } flipper_format_write_header_cstr( fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION); - float tmp_tz = 0; - flipper_format_write_comment_cstr(fff_data_file, " "); + flipper_format_write_comment_cstr( fff_data_file, - "Timezone offset in hours. Important note: do not put '+' sign for positive values"); + "Config file format specification can be found here: https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md"); + + float tmp_tz = 0; flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1); uint32_t tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr( - fff_data_file, - "How to notify user when new token is generated or badusb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)"); flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); tmp_uint32 = AutomationMethodBadUsb; - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr( - fff_data_file, - "Automation method (0 - None, 1 - BadUSB, 2 - BadBT, 3 - BadUSB and BadBT)"); flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1); - FuriString* temp_str = furi_string_alloc(); - - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE BEGIN ==="); - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr( - fff_data_file, "# Token name which will be visible in the UI."); - furi_string_printf(temp_str, "%s: Sample token name", TOTP_CONFIG_KEY_TOKEN_NAME); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# Plain token secret without spaces, dashes and etc, just pure alpha-numeric characters. Important note: plain token will be encrypted and replaced by TOTP app"); - furi_string_printf(temp_str, "%s: plaintokensecret", TOTP_CONFIG_KEY_TOKEN_SECRET); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - furi_string_printf( - temp_str, - " # Token hashing algorithm to use during code generation. Supported options are %s, %s, %s, and %s. If you are not use which one to use - use %s", - TOTP_TOKEN_ALGO_SHA1_NAME, - TOTP_TOKEN_ALGO_SHA256_NAME, - TOTP_TOKEN_ALGO_SHA512_NAME, - TOTP_TOKEN_ALGO_STEAM_NAME, - TOTP_TOKEN_ALGO_SHA1_NAME); - flipper_format_write_comment(fff_data_file, temp_str); - furi_string_printf( - temp_str, "%s: %s", TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# How many digits there should be in generated code. Available options are 5, 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6"); - furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# Token lifetime duration in seconds. Should be between 15 and 255. Majority websites requires 30, however some rare websites may require custom lifetime. If you are not sure which one to use - use 30"); - furi_string_printf(temp_str, "%s: 30", TOTP_CONFIG_KEY_TOKEN_DURATION); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# Token input automation features (0 - None, 1 - press \"Enter\" key at the end of automation)"); - furi_string_printf(temp_str, "%s: 0", TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE END ==="); - flipper_format_write_comment_cstr(fff_data_file, " "); - - furi_string_free(temp_str); if(!flipper_format_rewind(fff_data_file)) { totp_close_config_file(fff_data_file); FURI_LOG_E(LOGGING_TAG, "Rewind error"); - return TotpConfigFileOpenError; + return false; } } *file = fff_data_file; - return TotpConfigFileOpenSuccess; + return true; } -static TotpConfigFileUpdateResult - totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) { - TotpConfigFileUpdateResult update_result; +char* totp_config_file_backup(const PluginState* plugin_state) { + if(plugin_state->config_file_context == NULL) return NULL; + + totp_close_config_file(plugin_state->config_file_context->config_file); + + char* result = totp_config_file_backup_i(plugin_state->config_file_context->storage); + + totp_open_config_file( + plugin_state->config_file_context->storage, + &plugin_state->config_file_context->config_file); + + totp_token_info_iterator_attach_to_config_file( + plugin_state->config_file_context->token_info_iterator_context, + plugin_state->config_file_context->config_file); + + return result; +} + +bool totp_config_file_update_timezone_offset(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; + do { - if(!flipper_format_seek_to_end(file)) { - update_result = TotpConfigFileUpdateError; + if(!flipper_format_insert_or_update_float( + file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { break; } - if(!flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) { - update_result = TotpConfigFileUpdateError; - break; - } - - bool token_is_valid = token_info->token != NULL && token_info->token_length > 0; - if(!token_is_valid && - !flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!")) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_write_hex( - file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length)) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!token_is_valid && !flipper_format_write_comment_cstr(file, "!!! WARNING END !!!")) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_write_string_cstr( - file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info))) { - update_result = TotpConfigFileUpdateError; - break; - } - - uint32_t tmp_uint32 = token_info->digits; - if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - tmp_uint32 = token_info->duration; - if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DURATION, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - tmp_uint32 = token_info->automation_features; - if(!flipper_format_write_uint32( - file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; + update_result = true; } while(false); return update_result; } -char* totp_config_file_backup() { - Storage* storage = totp_open_storage(); - char* result = totp_config_file_backup_i(storage); - totp_close_storage(); - return result; -} - -TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - if(totp_config_file_save_new_token_i(file, token_info) != - TotpConfigFileUpdateSuccess) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - if(!flipper_format_insert_or_update_float( - file, TOTP_CONFIG_KEY_TIMEZONE, &new_timezone_offset, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult - totp_config_file_update_notification_method(NotificationMethod new_notification_method) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - uint32_t tmp_uint32 = new_notification_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult - totp_config_file_update_automation_method(AutomationMethod new_automation_method) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - uint32_t tmp_uint32 = new_automation_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - if(!flipper_format_insert_or_update_float( - file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - uint32_t tmp_uint32 = plugin_state->notification_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - tmp_uint32 = plugin_state->automation_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state) { - Storage* storage = totp_open_storage(); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - TotpConfigFileUpdateResult result = TotpConfigFileUpdateSuccess; +bool totp_config_file_update_notification_method(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; do { - if(!flipper_format_file_open_always(fff_data_file, CONFIG_FILE_TEMP_PATH)) { - result = TotpConfigFileUpdateError; + uint32_t tmp_uint32 = plugin_state->notification_method; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { break; } - if(!flipper_format_write_header_cstr( - fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION)) { - result = TotpConfigFileUpdateError; + update_result = true; + } while(false); + + return update_result; +} + +bool totp_config_file_update_automation_method(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; + + do { + uint32_t tmp_uint32 = plugin_state->automation_method; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { break; } - if(!flipper_format_write_hex( - fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) { - result = TotpConfigFileUpdateError; - break; - } + update_result = true; + } while(false); - if(!flipper_format_write_hex( - fff_data_file, - TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length)) { - result = TotpConfigFileUpdateError; - break; - } + return update_result; +} - if(!flipper_format_write_float( - fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { - result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_write_bool( - fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { - result = TotpConfigFileUpdateError; +bool totp_config_file_update_user_settings(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; + do { + if(!flipper_format_insert_or_update_float( + file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { break; } uint32_t tmp_uint32 = plugin_state->notification_method; - if(!flipper_format_write_uint32( - fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { - result = TotpConfigFileUpdateError; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { break; } tmp_uint32 = plugin_state->automation_method; - if(!flipper_format_write_uint32( - fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { - result = TotpConfigFileUpdateError; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { break; } - bool tokens_written = true; - TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { - const TokenInfo* token_info = node->data; - tokens_written = tokens_written && - totp_config_file_save_new_token_i(fff_data_file, token_info) == - TotpConfigFileUpdateSuccess; - }); - - if(!tokens_written) { - result = TotpConfigFileUpdateError; - break; - } + update_result = true; } while(false); - totp_close_config_file(fff_data_file); - - if(result == TotpConfigFileUpdateSuccess) { - if(storage_file_exists(storage, CONFIG_FILE_ORIG_PATH)) { - storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH); - } - - if(storage_common_rename(storage, CONFIG_FILE_PATH, CONFIG_FILE_ORIG_PATH) != FSE_OK) { - result = TotpConfigFileUpdateError; - } else if(storage_common_rename(storage, CONFIG_FILE_TEMP_PATH, CONFIG_FILE_PATH) != FSE_OK) { - result = TotpConfigFileUpdateError; - } else if(!storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH)) { - result = TotpConfigFileUpdateError; - } - } - - totp_close_storage(); - return result; + return update_result; } -TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state) { +bool totp_config_file_load(PluginState* const plugin_state) { Storage* storage = totp_open_storage(); FlipperFormat* fff_data_file; - - TotpConfigFileOpenResult result; - if((result = totp_open_config_file(storage, &fff_data_file)) != TotpConfigFileOpenSuccess) { + if(!totp_open_config_file(storage, &fff_data_file)) { totp_close_storage(); - return result; + return false; } + flipper_format_rewind(fff_data_file); + + bool result = false; + plugin_state->timezone_offset = 0; FuriString* temp_str = furi_string_alloc(); @@ -535,7 +306,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st uint32_t file_version; if(!flipper_format_read_header(fff_data_file, temp_str, &file_version)) { FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header"); - result = TotpConfigFileOpenError; break; } @@ -551,8 +321,7 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st char* backup_path = totp_config_file_backup_i(storage); if(backup_path != NULL) { - if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) { - result = TotpConfigFileOpenError; + if(totp_open_config_file(storage, &fff_data_file) != true) { break; } @@ -560,7 +329,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st if(!flipper_format_file_open_existing(fff_backup_data_file, backup_path)) { flipper_format_file_close(fff_backup_data_file); flipper_format_free(fff_backup_data_file); - result = TotpConfigFileOpenError; break; } @@ -575,7 +343,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st LOGGING_TAG, "An error occurred during migration to version %" PRId16, CONFIG_FILE_ACTUAL_VERSION); - result = TotpConfigFileOpenError; break; } @@ -588,7 +355,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st LOGGING_TAG, "An error occurred during taking backup of %s before migration", CONFIG_FILE_PATH); - result = TotpConfigFileOpenError; break; } } @@ -599,7 +365,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } if(!flipper_format_rewind(fff_data_file)) { - result = TotpConfigFileOpenError; break; } @@ -626,7 +391,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } if(!flipper_format_rewind(fff_data_file)) { - result = TotpConfigFileOpenError; break; } @@ -637,7 +401,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } if(!flipper_format_rewind(fff_data_file)) { - result = TotpConfigFileOpenError; break; } @@ -664,186 +427,176 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } plugin_state->automation_method = tmp_uint32; + + plugin_state->config_file_context = malloc(sizeof(ConfigFileContext)); + furi_check(plugin_state->config_file_context != NULL); + plugin_state->config_file_context->storage = storage; + plugin_state->config_file_context->config_file = fff_data_file; + plugin_state->config_file_context->token_info_iterator_context = + totp_token_info_iterator_alloc( + storage, plugin_state->config_file_context->config_file, plugin_state->iv); + result = true; } while(false); furi_string_free(temp_str); - totp_close_config_file(fff_data_file); - totp_close_storage(); return result; } -TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) { - Storage* storage = totp_open_storage(); - FlipperFormat* fff_data_file; - if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) { - totp_close_storage(); - return TokenLoadingResultError; - } - - FuriString* temp_str = furi_string_alloc(); - uint32_t temp_data32; - - if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { - FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header"); - totp_close_storage(); - furi_string_free(temp_str); - return TokenLoadingResultError; - } - - TokenLoadingResult result = TokenLoadingResultSuccess; - uint16_t index = 0; - bool has_any_plain_secret = false; - - while(true) { - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { +bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state) { + FlipperFormat* config_file = plugin_state->config_file_context->config_file; + flipper_format_rewind(config_file); + bool update_result = false; + do { + if(!flipper_format_insert_or_update_hex( + config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) { break; } - TokenInfo* tokenInfo = token_info_alloc(); - - size_t temp_cstr_len = furi_string_size(temp_str); - tokenInfo->name = malloc(temp_cstr_len + 1); - furi_check(tokenInfo->name != NULL); - strlcpy(tokenInfo->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); - - uint32_t secret_bytes_count; - if(!flipper_format_get_value_count( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { - secret_bytes_count = 0; + if(!flipper_format_insert_or_update_hex( + config_file, + TOTP_CONFIG_KEY_CRYPTO_VERIFY, + plugin_state->crypto_verify_data, + plugin_state->crypto_verify_data_length)) { + break; } - if(secret_bytes_count == 1) { // Plain secret key - if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { - if(token_info_set_secret( - tokenInfo, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - PLAIN_TOKEN_ENCODING_BASE32, - &plugin_state->iv[0])) { - FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name); - } else { - tokenInfo->token = NULL; - tokenInfo->token_length = 0; - FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has invalid secret", tokenInfo->name); - result = TokenLoadingResultWarning; - } - } else { - tokenInfo->token = NULL; - tokenInfo->token_length = 0; - result = TokenLoadingResultWarning; - } - - has_any_plain_secret = true; - } else { // encrypted - tokenInfo->token_length = secret_bytes_count; - if(secret_bytes_count > 0) { - tokenInfo->token = malloc(tokenInfo->token_length); - furi_check(tokenInfo->token != NULL); - if(!flipper_format_read_hex( - fff_data_file, - TOTP_CONFIG_KEY_TOKEN_SECRET, - tokenInfo->token, - tokenInfo->token_length)) { - free(tokenInfo->token); - tokenInfo->token = NULL; - tokenInfo->token_length = 0; - result = TokenLoadingResultWarning; - } - } else { - tokenInfo->token = NULL; - result = TokenLoadingResultWarning; - } + if(!flipper_format_insert_or_update_bool( + config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { + break; } - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str) || - !token_info_set_algo_from_str(tokenInfo, temp_str)) { - tokenInfo->algo = SHA1; - } + update_result = true; + } while(false); - if(!flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) || - !token_info_set_digits_from_int(tokenInfo, temp_data32)) { - tokenInfo->digits = TOTP_6_DIGITS; - } + return update_result; +} - if(!flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &temp_data32, 1) || - !token_info_set_duration_from_int(tokenInfo, temp_data32)) { - tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; - } - - if(flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) { - tokenInfo->automation_features = temp_data32; - } else { - tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; - } - - FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name); - - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check); - - index++; - } - - plugin_state->tokens_count = index; - plugin_state->token_list_loaded = true; - - FURI_LOG_D(LOGGING_TAG, "Found %" PRIu16 " tokens", index); - - furi_string_free(temp_str); - totp_close_config_file(fff_data_file); +void totp_config_file_close(PluginState* const plugin_state) { + if(plugin_state->config_file_context == NULL) return; + totp_token_info_iterator_free(plugin_state->config_file_context->token_info_iterator_context); + totp_close_config_file(plugin_state->config_file_context->config_file); + free(plugin_state->config_file_context); + plugin_state->config_file_context = NULL; totp_close_storage(); +} - if(has_any_plain_secret) { - totp_full_save_config_file(plugin_state); +void totp_config_file_reset(PluginState* const plugin_state) { + totp_config_file_close(plugin_state); + Storage* storage = totp_open_storage(); + storage_simply_remove(storage, CONFIG_FILE_PATH); + totp_close_storage(); +} + +bool totp_config_file_update_encryption( + PluginState* plugin_state, + const uint8_t* new_pin, + uint8_t new_pin_length) { + FlipperFormat* config_file = plugin_state->config_file_context->config_file; + Stream* stream = flipper_format_get_raw_stream(config_file); + size_t original_offset = stream_tell(stream); + if(!stream_rewind(stream)) { + return false; } + uint8_t old_iv[TOTP_IV_SIZE]; + memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); + + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE); + if(plugin_state->crypto_verify_data != NULL) { + free(plugin_state->crypto_verify_data); + plugin_state->crypto_verify_data = NULL; + } + + CryptoSeedIVResult seed_result = + totp_crypto_seed_iv(plugin_state, new_pin_length > 0 ? new_pin : NULL, new_pin_length); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { + return false; + } + + char buffer[sizeof(TOTP_CONFIG_KEY_TOKEN_SECRET) + 1]; + bool result = true; + + while(true) { + if(!stream_seek_to_char(stream, '\n', StreamDirectionForward)) { + break; + } + + size_t buffer_read_size; + if((buffer_read_size = stream_read(stream, (uint8_t*)&buffer[0], sizeof(buffer))) == 0) { + break; + } + + if(!stream_seek(stream, -(int32_t)buffer_read_size, StreamOffsetFromCurrent)) { + result = false; + break; + } + + if(strncmp(buffer, "\n" TOTP_CONFIG_KEY_TOKEN_SECRET ":", sizeof(buffer)) == 0) { + uint32_t secret_bytes_count; + if(!flipper_format_get_value_count( + config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { + secret_bytes_count = 0; + } + + if(secret_bytes_count > 1) { + size_t secret_token_start = stream_tell(stream) + 1; + uint8_t* encrypted_token = malloc(secret_bytes_count); + furi_check(encrypted_token != NULL); + + if(!flipper_format_read_hex( + config_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + encrypted_token, + secret_bytes_count)) { + result = false; + free(encrypted_token); + break; + } + + size_t plain_token_length; + uint8_t* plain_token = totp_crypto_decrypt( + encrypted_token, secret_bytes_count, &old_iv[0], &plain_token_length); + + free(encrypted_token); + size_t encrypted_token_length; + encrypted_token = totp_crypto_encrypt( + plain_token, plain_token_length, &plugin_state->iv[0], &encrypted_token_length); + + memset_s(plain_token, plain_token_length, 0, plain_token_length); + free(plain_token); + + if(!stream_seek(stream, secret_token_start, StreamOffsetFromStart)) { + result = false; + free(encrypted_token); + break; + } + + if(!flipper_format_write_hex( + config_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + encrypted_token, + encrypted_token_length)) { + free(encrypted_token); + result = false; + break; + } + + free(encrypted_token); + } + } + } + + stream_seek(stream, original_offset, StreamOffsetFromStart); + return result; } -TotpConfigFileUpdateResult - totp_config_file_update_crypto_signatures(const PluginState* plugin_state) { - Storage* storage = totp_open_storage(); - FlipperFormat* config_file; - TotpConfigFileUpdateResult update_result; - if(totp_open_config_file(storage, &config_file) == TotpConfigFileOpenSuccess) { - do { - if(!flipper_format_insert_or_update_hex( - config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_insert_or_update_hex( - config_file, - TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length)) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_insert_or_update_bool( - config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(config_file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -void totp_config_file_reset() { - Storage* storage = totp_open_storage(); - storage_simply_remove(storage, CONFIG_FILE_PATH); - totp_close_storage(); +TokenInfoIteratorContext* totp_config_get_token_iterator_context(const PluginState* plugin_state) { + return plugin_state->config_file_context->token_info_iterator_context; } \ No newline at end of file diff --git a/applications/external/totp/services/config/config.h b/applications/external/totp/services/config/config.h index 5bd169525..d2fe957c6 100644 --- a/applications/external/totp/services/config/config.h +++ b/applications/external/totp/services/config/config.h @@ -1,137 +1,90 @@ #pragma once -#include #include "../../types/plugin_state.h" #include "../../types/token_info.h" +#include "config_file_context.h" #include "constants.h" +#include "token_info_iterator.h" -typedef uint8_t TokenLoadingResult; typedef uint8_t TotpConfigFileOpenResult; typedef uint8_t TotpConfigFileUpdateResult; -/** - * @brief Token loading results - */ -enum TokenLoadingResults { - /** - * @brief All the tokens loaded successfully - */ - TokenLoadingResultSuccess, - - /** - * @brief All the tokens loaded, but there are some warnings - */ - TokenLoadingResultWarning, - - /** - * @brief Tokens not loaded because of error(s) - */ - TokenLoadingResultError -}; - -/** - * @brief Config file opening result - */ -enum TotpConfigFileOpenResults { - /** - * @brief Config file opened successfully - */ - TotpConfigFileOpenSuccess = 0, - - /** - * @brief An error has occurred during opening config file - */ - TotpConfigFileOpenError = 1 -}; - -/** - * @brief Config file updating result - */ -enum TotpConfigFileUpdateResults { - /** - * @brief Config file updated successfully - */ - TotpConfigFileUpdateSuccess, - - /** - * @brief An error has occurred during updating config file - */ - TotpConfigFileUpdateError -}; - /** * @brief Tries to take a config file backup + * @param plugin_state application state * @return backup path if backup successfully taken; \c NULL otherwise */ -char* totp_config_file_backup(); - -/** - * @brief Saves all the settings and tokens to an application config file - * @param plugin_state application state - * @return Config file update result - */ -TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state); +char* totp_config_file_backup(const PluginState* plugin_state); /** * @brief Loads basic information from an application config file into application state without loading all the tokens * @param plugin_state application state * @return Config file open result */ -TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state); - -/** - * @brief Loads tokens from an application config file into application state - * @param plugin_state application state - * @return Results of the loading - */ -TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state); - -/** - * @brief Add new token to the end of the application config file - * @param token_info token information to be saved - * @return Config file update result - */ -TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info); +bool totp_config_file_load(PluginState* const plugin_state); /** * @brief Updates timezone offset in an application config file - * @param new_timezone_offset new timezone offset to be set + * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset); +bool totp_config_file_update_timezone_offset(const PluginState* plugin_state); /** * @brief Updates notification method in an application config file - * @param new_notification_method new notification method to be set + * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult - totp_config_file_update_notification_method(NotificationMethod new_notification_method); +bool totp_config_file_update_notification_method(const PluginState* plugin_state); /** * @brief Updates automation method in an application config file - * @param new_automation_method new automation method to be set + * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult - totp_config_file_update_automation_method(AutomationMethod new_automation_method); +bool totp_config_file_update_automation_method(const PluginState* plugin_state); /** * @brief Updates application user settings * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state); +bool totp_config_file_update_user_settings(const PluginState* plugin_state); /** * @brief Updates crypto signatures information * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult - totp_config_file_update_crypto_signatures(const PluginState* plugin_state); +bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state); /** * @brief Reset all the settings to default + * @param plugin_state application state */ -void totp_config_file_reset(); \ No newline at end of file +void totp_config_file_reset(PluginState* const plugin_state); + +/** + * @brief Closes config file and releases all the resources + * @param plugin_state application state + */ +void totp_config_file_close(PluginState* const plugin_state); + +/** + * @brief Updates config file encryption by re-encrypting it using new user's PIN and new randomly generated IV + * @param plugin_state application state + * @param new_pin new user's PIN + * @param new_pin_length new user's PIN length + * @return \c true if config file encryption successfully updated; \c false otherwise + */ +bool totp_config_file_update_encryption( + PluginState* plugin_state, + const uint8_t* new_pin, + uint8_t new_pin_length); + +/** + * @brief Gets token info iterator context + * @param plugin_state application state + * @return token info iterator context + */ +TokenInfoIteratorContext* totp_config_get_token_iterator_context(const PluginState* plugin_state); \ No newline at end of file diff --git a/applications/external/totp/services/config/config_file_context.h b/applications/external/totp/services/config/config_file_context.h new file mode 100644 index 000000000..98badbcbb --- /dev/null +++ b/applications/external/totp/services/config/config_file_context.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct ConfigFileContext ConfigFileContext; \ No newline at end of file diff --git a/applications/external/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h index 7137e2374..3a33c80b3 100644 --- a/applications/external/totp/services/config/constants.h +++ b/applications/external/totp/services/config/constants.h @@ -1,7 +1,10 @@ #pragma once +#include + +#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION (4) +#define CONFIG_FILE_ACTUAL_VERSION (5) #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" diff --git a/applications/external/totp/services/config/migrations/common_migration.c b/applications/external/totp/services/config/migrations/common_migration.c index 073eaab12..07026fb1f 100644 --- a/applications/external/totp/services/config/migrations/common_migration.c +++ b/applications/external/totp/services/config/migrations/common_migration.c @@ -1,6 +1,7 @@ #include "common_migration.h" #include "../constants.h" #include "../../../types/token_info.h" +#include bool totp_config_migrate_to_latest( FlipperFormat* fff_data_file, @@ -57,18 +58,12 @@ bool totp_config_migrate_to_latest( flipper_format_rewind(fff_backup_data_file); - FuriString* comment_str = furi_string_alloc(); - while(true) { if(!flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { break; } - furi_string_printf( - comment_str, "=== BEGIN \"%s\" ===", furi_string_get_cstr(temp_str)); - flipper_format_write_comment(fff_data_file, comment_str); - furi_string_printf(comment_str, "=== END \"%s\" ===", furi_string_get_cstr(temp_str)); flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str); flipper_format_read_string( @@ -78,15 +73,32 @@ bool totp_config_migrate_to_latest( if(current_version > 1) { flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); + + if(current_version < 5) { + uint32_t algo_as_uint32t = SHA1; + if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_SHA256_NAME) == 0) { + algo_as_uint32t = SHA256; + } else if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_SHA512_NAME) == 0) { + algo_as_uint32t = SHA512; + } else if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_STEAM_NAME) == 0) { + algo_as_uint32t = STEAM; + } + + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &algo_as_uint32t, 1); + } else { + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); + } flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); } else { - flipper_format_write_string_cstr( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME); - const uint32_t default_digits = TOTP_6_DIGITS; + const uint32_t default_algo = SHA1; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &default_algo, 1); + const uint32_t default_digits = TotpSixDigitsCount; flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1); } @@ -108,18 +120,21 @@ bool totp_config_migrate_to_latest( flipper_format_write_string( fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, temp_str); } else { - const uint32_t default_automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + const uint32_t default_automation_features = TokenAutomationFeatureNone; flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &default_automation_features, 1); } - - flipper_format_write_comment(fff_data_file, comment_str); } - furi_string_free(comment_str); + Stream* stream = flipper_format_get_raw_stream(fff_data_file); + size_t current_pos = stream_tell(stream); + size_t total_size = stream_size(stream); + if(current_pos < total_size) { + stream_delete(stream, total_size - current_pos); + } result = true; } while(false); diff --git a/applications/external/totp/services/config/migrations/common_migration.h b/applications/external/totp/services/config/migrations/common_migration.h index 71defc384..326277f14 100644 --- a/applications/external/totp/services/config/migrations/common_migration.h +++ b/applications/external/totp/services/config/migrations/common_migration.h @@ -2,6 +2,12 @@ #include +/** + * @brief Migrates config file to the latest version + * @param fff_data_file original config file to be migrated + * @param fff_backup_data_file backup copy of original config file + * @return \c true if operation succeeded; \c false otherwise + */ bool totp_config_migrate_to_latest( FlipperFormat* fff_data_file, - FlipperFormat* fff_backup_data_file); \ No newline at end of file + FlipperFormat* fff_backup_data_file); diff --git a/applications/external/totp/services/config/token_info_iterator.c b/applications/external/totp/services/config/token_info_iterator.c new file mode 100644 index 000000000..9b7dd5550 --- /dev/null +++ b/applications/external/totp/services/config/token_info_iterator.c @@ -0,0 +1,551 @@ +#include "token_info_iterator.h" + +#include +#include +#include +#include "../../types/common.h" + +#define CONFIG_FILE_PART_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf.part" +#define STREAM_COPY_BUFFER_SIZE 128 + +struct TokenInfoIteratorContext { + size_t total_count; + size_t current_index; + size_t last_seek_offset; + size_t last_seek_index; + TokenInfo* current_token; + FlipperFormat* config_file; + uint8_t* iv; + Storage* storage; +}; + +static bool + flipper_format_seek_to_siblinig_token_start(Stream* stream, StreamDirection direction) { + char buffer[sizeof(TOTP_CONFIG_KEY_TOKEN_NAME) + 1]; + bool found = false; + while(!found) { + if(!stream_seek_to_char(stream, '\n', direction)) { + break; + } + + size_t buffer_read_size; + if((buffer_read_size = stream_read(stream, (uint8_t*)&buffer[0], sizeof(buffer))) == 0) { + break; + } + + if(!stream_seek(stream, -(int32_t)buffer_read_size, StreamOffsetFromCurrent)) { + break; + } + + if(strncmp(buffer, "\n" TOTP_CONFIG_KEY_TOKEN_NAME ":", sizeof(buffer)) == 0) { + found = true; + } + } + + return found; +} + +static bool seek_to_token(size_t token_index, TokenInfoIteratorContext* context) { + furi_check(context != NULL && context->config_file != NULL); + if(token_index >= context->total_count) { + return false; + } + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + long token_index_diff = (long)token_index - (long)context->last_seek_index; + size_t token_index_diff_weight = (size_t)labs(token_index_diff); + StreamDirection direction = token_index_diff >= 0 ? StreamDirectionForward : + StreamDirectionBackward; + if(token_index_diff_weight > token_index || context->last_seek_offset == 0) { + context->last_seek_offset = 0; + context->last_seek_index = 0; + token_index_diff = token_index + 1; + direction = StreamDirectionForward; + } else if(token_index_diff_weight > (context->total_count - token_index - 1)) { + context->last_seek_offset = stream_size(stream); + context->last_seek_index = context->total_count - 1; + token_index_diff = -(long)(context->total_count - token_index); + direction = StreamDirectionBackward; + } + + stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart); + + if(token_index_diff != 0) { + long i = 0; + long i_inc = token_index_diff >= 0 ? 1 : -1; + do { + if(!flipper_format_seek_to_siblinig_token_start(stream, direction)) { + break; + } + + i += i_inc; + } while((i_inc > 0 && i < token_index_diff) || (i_inc < 0 && i > token_index_diff)); + + if((i_inc > 0 && i < token_index_diff) || (i_inc < 0 && i > token_index_diff)) { + context->last_seek_offset = 0; + FURI_LOG_D(LOGGING_TAG, "Was not able to move"); + return false; + } + + context->last_seek_offset = stream_tell(stream); + context->last_seek_index = token_index; + } else { + if(!stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart)) { + return false; + } + } + + return true; +} + +static bool stream_insert_stream(Stream* dst, Stream* src) { + uint8_t buffer[STREAM_COPY_BUFFER_SIZE]; + size_t buffer_read_size; + while((buffer_read_size = stream_read(src, buffer, sizeof(buffer))) != 0) { + if(!stream_insert(dst, buffer, buffer_read_size)) { + return false; + } + } + + return true; +} + +static bool ensure_stream_ends_with_lf(Stream* stream) { + uint8_t last_char; + size_t original_pos = stream_tell(stream); + if(!stream_seek(stream, -1, StreamOffsetFromEnd) || stream_read(stream, &last_char, 1) < 1) { + return false; + } + + const uint8_t lf = '\n'; + if(last_char != lf && !stream_write(stream, &lf, 1)) { + return false; + } + + if(!stream_seek(stream, original_pos, StreamOffsetFromStart)) { + return false; + } + + return true; +} + +static bool + totp_token_info_iterator_save_current_token_info_changes(TokenInfoIteratorContext* context) { + bool is_new_token = context->current_index >= context->total_count; + Stream* stream = flipper_format_get_raw_stream(context->config_file); + if(is_new_token) { + if(!ensure_stream_ends_with_lf(stream) || + !flipper_format_seek_to_end(context->config_file)) { + return false; + } + } else { + if(!seek_to_token(context->current_index, context)) { + return false; + } + } + + size_t offset_start = stream_tell(stream); + + size_t offset_end; + if(is_new_token) { + offset_end = offset_start; + } else if(context->current_index + 1 >= context->total_count) { + offset_end = stream_size(stream); + } else if(seek_to_token(context->current_index + 1, context)) { + offset_end = stream_tell(stream); + } else { + return false; + } + + FlipperFormat* temp_ff = flipper_format_file_alloc(context->storage); + if(!flipper_format_file_open_always(temp_ff, CONFIG_FILE_PART_FILE_PATH)) { + flipper_format_free(temp_ff); + return false; + } + + TokenInfo* token_info = context->current_token; + bool result = false; + + do { + if(!flipper_format_write_string(temp_ff, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) { + break; + } + + if(!flipper_format_write_hex( + temp_ff, + TOTP_CONFIG_KEY_TOKEN_SECRET, + token_info->token, + token_info->token_length)) { + break; + } + + uint32_t tmp_uint32 = token_info->algo; + if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_ALGO, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = token_info->digits; + if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = token_info->duration; + if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_DURATION, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = token_info->automation_features; + if(!flipper_format_write_uint32( + temp_ff, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) { + break; + } + + Stream* temp_stream = flipper_format_get_raw_stream(temp_ff); + + if(!stream_rewind(temp_stream)) { + break; + } + + if(!stream_seek(stream, offset_start, StreamOffsetFromStart)) { + break; + } + + if(offset_end != offset_start && !stream_delete(stream, offset_end - offset_start)) { + break; + } + + if(!is_new_token && !stream_write_char(stream, '\n')) { + break; + } + + if(!stream_insert_stream(stream, temp_stream)) { + break; + } + + if(is_new_token) { + context->total_count++; + } + + result = true; + } while(false); + + flipper_format_free(temp_ff); + storage_common_remove(context->storage, CONFIG_FILE_PART_FILE_PATH); + + stream_seek(stream, offset_start, StreamOffsetFromStart); + context->last_seek_offset = offset_start; + context->last_seek_index = context->current_index; + + return result; +} + +TokenInfoIteratorContext* + totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv) { + Stream* stream = flipper_format_get_raw_stream(config_file); + stream_rewind(stream); + size_t tokens_count = 0; + while(true) { + if(!flipper_format_seek_to_siblinig_token_start(stream, StreamDirectionForward)) { + break; + } + + tokens_count++; + } + + TokenInfoIteratorContext* context = malloc(sizeof(TokenInfoIteratorContext)); + furi_check(context != NULL); + + context->total_count = tokens_count; + context->current_token = token_info_alloc(); + context->config_file = config_file; + context->iv = iv; + context->storage = storage; + return context; +} + +void totp_token_info_iterator_free(TokenInfoIteratorContext* context) { + if(context == NULL) return; + token_info_free(context->current_token); + free(context); +} + +bool totp_token_info_iterator_remove_current_token_info(TokenInfoIteratorContext* context) { + if(!seek_to_token(context->current_index, context)) { + return false; + } + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + size_t begin_offset = stream_tell(stream); + size_t end_offset; + if(!ensure_stream_ends_with_lf(stream)) { + return false; + } + + if(context->current_index >= context->total_count - 1) { + end_offset = stream_size(stream) - 1; + } else if(seek_to_token(context->current_index + 1, context)) { + end_offset = stream_tell(stream); + } else { + return false; + } + + if(!stream_seek(stream, begin_offset, StreamOffsetFromStart) || + !stream_delete(stream, end_offset - begin_offset)) { + return false; + } + + context->total_count--; + if(context->current_index >= context->total_count) { + context->current_index = context->total_count - 1; + } + + return true; +} + +bool totp_token_info_iterator_move_current_token_info( + TokenInfoIteratorContext* context, + size_t new_index) { + if(context->current_index == new_index) return true; + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + + if(!ensure_stream_ends_with_lf(stream)) { + return false; + } + + if(!seek_to_token(context->current_index, context)) { + return false; + } + + size_t begin_offset = stream_tell(stream); + size_t end_offset; + if(context->current_index >= context->total_count - 1) { + end_offset = stream_size(stream) - 1; + } else if(seek_to_token(context->current_index + 1, context)) { + end_offset = stream_tell(stream); + } else { + return false; + } + + Stream* temp_stream = file_stream_alloc(context->storage); + if(!file_stream_open( + temp_stream, CONFIG_FILE_PART_FILE_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) { + stream_free(temp_stream); + return false; + } + + size_t moving_size = end_offset - begin_offset; + + bool result = false; + do { + if(!stream_seek(stream, begin_offset, StreamOffsetFromStart)) { + break; + } + + if(stream_copy(stream, temp_stream, moving_size) < moving_size) { + break; + } + + if(!stream_rewind(temp_stream)) { + break; + } + + if(!stream_seek(stream, begin_offset, StreamOffsetFromStart)) { + break; + } + + if(!stream_delete(stream, moving_size)) { + break; + } + + context->last_seek_offset = 0; + context->last_seek_index = 0; + if(new_index >= context->total_count - 1) { + if(!stream_seek(stream, stream_size(stream) - 1, StreamOffsetFromStart)) { + break; + } + } else if(!seek_to_token(new_index, context)) { + break; + } + + result = stream_insert_stream(stream, temp_stream); + } while(false); + + stream_free(temp_stream); + storage_common_remove(context->storage, CONFIG_FILE_PART_FILE_PATH); + + context->last_seek_offset = 0; + context->last_seek_index = 0; + + return result; +} + +TotpIteratorUpdateTokenResult totp_token_info_iterator_update_current_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context) { + TotpIteratorUpdateTokenResult result = update(context->current_token, update_context); + if(result == TotpIteratorUpdateTokenResultSuccess) { + if(!totp_token_info_iterator_save_current_token_info_changes(context)) { + result = TotpIteratorUpdateTokenResultFileUpdateFailed; + } + + return result; + } + + totp_token_info_iterator_go_to(context, context->current_index); + return result; +} + +TotpIteratorUpdateTokenResult totp_token_info_iterator_add_new_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context) { + size_t previous_index = context->current_index; + context->current_index = context->total_count; + token_info_set_defaults(context->current_token); + TotpIteratorUpdateTokenResult result = update(context->current_token, update_context); + if(result == TotpIteratorUpdateTokenResultSuccess && + !totp_token_info_iterator_save_current_token_info_changes(context)) { + result = TotpIteratorUpdateTokenResultFileUpdateFailed; + } + + if(result != TotpIteratorUpdateTokenResultSuccess) { + totp_token_info_iterator_go_to(context, previous_index); + } + + return result; +} + +bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t token_index) { + furi_check(context != NULL); + context->current_index = token_index; + if(!seek_to_token(context->current_index, context)) { + return false; + } + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + size_t original_offset = stream_tell(stream); + + if(!flipper_format_read_string( + context->config_file, TOTP_CONFIG_KEY_TOKEN_NAME, context->current_token->name)) { + stream_seek(stream, original_offset, StreamOffsetFromStart); + return false; + } + + uint32_t secret_bytes_count; + if(!flipper_format_get_value_count( + context->config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { + secret_bytes_count = 0; + } + TokenInfo* tokenInfo = context->current_token; + bool token_update_needed = false; + if(tokenInfo->token != NULL) { + free(tokenInfo->token); + tokenInfo->token_length = 0; + } + + if(secret_bytes_count == 1) { // Plain secret key + FuriString* temp_str = furi_string_alloc(); + + if(flipper_format_read_string( + context->config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { + if(token_info_set_secret( + tokenInfo, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + PlainTokenSecretEncodingBase32, + context->iv)) { + FURI_LOG_W( + LOGGING_TAG, + "Token \"%s\" has plain secret", + furi_string_get_cstr(tokenInfo->name)); + token_update_needed = true; + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + FURI_LOG_W( + LOGGING_TAG, + "Token \"%s\" has invalid secret", + furi_string_get_cstr(tokenInfo->name)); + } + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + } + + furi_string_free(temp_str); + } else { // encrypted + tokenInfo->token_length = secret_bytes_count; + if(secret_bytes_count > 0) { + tokenInfo->token = malloc(tokenInfo->token_length); + furi_check(tokenInfo->token != NULL); + if(!flipper_format_read_hex( + context->config_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + tokenInfo->token, + tokenInfo->token_length)) { + free(tokenInfo->token); + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + } + } else { + tokenInfo->token = NULL; + } + } + + uint32_t temp_data32; + if(flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &temp_data32, 1) && + temp_data32 <= STEAM) { + tokenInfo->algo = (TokenHashAlgo)temp_data32; + } else { + tokenInfo->algo = SHA1; + } + + if(!flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) || + !token_info_set_digits_from_int(tokenInfo, temp_data32)) { + tokenInfo->digits = TotpSixDigitsCount; + } + + if(!flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &temp_data32, 1) || + !token_info_set_duration_from_int(tokenInfo, temp_data32)) { + tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; + } + + if(flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) { + tokenInfo->automation_features = temp_data32; + } else { + tokenInfo->automation_features = TokenAutomationFeatureNone; + } + + stream_seek(stream, original_offset, StreamOffsetFromStart); + + if(token_update_needed && !totp_token_info_iterator_save_current_token_info_changes(context)) { + return false; + } + + return true; +} + +const TokenInfo* + totp_token_info_iterator_get_current_token(const TokenInfoIteratorContext* context) { + return context->current_token; +} + +size_t totp_token_info_iterator_get_current_token_index(const TokenInfoIteratorContext* context) { + return context->current_index; +} + +size_t totp_token_info_iterator_get_total_count(const TokenInfoIteratorContext* context) { + return context->total_count; +} + +void totp_token_info_iterator_attach_to_config_file( + TokenInfoIteratorContext* context, + FlipperFormat* config_file) { + context->config_file = config_file; +} \ No newline at end of file diff --git a/applications/external/totp/services/config/token_info_iterator.h b/applications/external/totp/services/config/token_info_iterator.h new file mode 100644 index 000000000..7e9a65853 --- /dev/null +++ b/applications/external/totp/services/config/token_info_iterator.h @@ -0,0 +1,121 @@ +#pragma once + +#include "../../types/token_info.h" +#include +#include "constants.h" + +typedef int TotpIteratorUpdateTokenResult; + +typedef TotpIteratorUpdateTokenResult ( + *TOTP_ITERATOR_UPDATE_TOKEN_ACTION)(TokenInfo* const token_info, const void* context); + +typedef struct TokenInfoIteratorContext TokenInfoIteratorContext; + +enum TotpIteratorUpdateTokenResults { + + /** + * @brief Token successfully updated + */ + TotpIteratorUpdateTokenResultSuccess = 0, + + /** + * @brief An error ocurred during updating config file + */ + TotpIteratorUpdateTokenResultFileUpdateFailed = -1 +}; + +/** + * @brief Initializes a new token info iterator + * @param storage storage reference + * @param config_file config file to use + * @param iv initialization vector (IV) to be used for encryption\decryption + * @return Token info iterator context + */ +TokenInfoIteratorContext* + totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv); + +/** + * @brief Navigates iterator to the token with given index + * @param context token info iterator context + * @param token_index token index to navigate to + * @return \c true if navigation succeeded; \c false otherwise + */ +bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t token_index); + +/** + * @brief Moves current token to a given new index + * @param context token info iterator context + * @param new_index new token index to move current token to + * @return \c true if operation succeeded; \c false otherwise + */ +bool totp_token_info_iterator_move_current_token_info( + TokenInfoIteratorContext* context, + size_t new_index); + +/** + * @brief Updates current token info using given update action + * @param context token info iterator context + * @param update action which is responsible to make all the necessary updates to token info + * @param update_context update action context + * @return \c true if operation succeeded; \c false otherwise + */ +TotpIteratorUpdateTokenResult totp_token_info_iterator_update_current_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context); + +/** + * @brief Adds new token info to the end of the list using given update action + * @param context token info iterator context + * @param update action which is responsible to make all the necessary updates to token info + * @param update_context update action context + * @return \c true if operation succeeded; \c false otherwise + */ +TotpIteratorUpdateTokenResult totp_token_info_iterator_add_new_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context); + +/** + * @brief Remvoves current token info + * @param context token info iterator context + * @return \c true if operation succeeded; \c false otherwise + */ +bool totp_token_info_iterator_remove_current_token_info(TokenInfoIteratorContext* context); + +/** + * @brief Disposes token info iterator and releases all the resources + * @param context token info iterator context + */ +void totp_token_info_iterator_free(TokenInfoIteratorContext* context); + +/** + * @brief Gets current token info + * @param context token info iterator context + * @return current token info + */ +const TokenInfo* + totp_token_info_iterator_get_current_token(const TokenInfoIteratorContext* context); + +/** + * @brief Gets current token info index + * @param context token info iterator context + * @return current token info index + */ +size_t totp_token_info_iterator_get_current_token_index(const TokenInfoIteratorContext* context); + +/** + * @brief Gets total amount of token infos found + * @param context token info iterator context + * @return amount of token infos found + */ +size_t totp_token_info_iterator_get_total_count(const TokenInfoIteratorContext* context); + +/** + * @brief Attaches token info iterator to another config file + * @param context token info iterator context + * @param config_file config file reference to attach token info iterator to + */ +void totp_token_info_iterator_attach_to_config_file( + TokenInfoIteratorContext* context, + FlipperFormat* config_file); diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 00f5ab0a8..03d9c9d51 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -2,7 +2,6 @@ #include #include #include -#include "../config/config.h" #include "../../types/common.h" #include "memset_s.h" @@ -62,9 +61,11 @@ uint8_t* totp_crypto_decrypt( return decrypted_data; } -bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { +CryptoSeedIVResult + totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { + CryptoSeedIVResult result; if(plugin_state->crypto_verify_data == NULL) { - FURI_LOG_D(LOGGING_TAG, "Generating new IV"); + FURI_LOG_I(LOGGING_TAG, "Generating new IV"); furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE); } @@ -95,9 +96,9 @@ bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t } } - bool result = true; + result = CryptoSeedIVResultFlagSuccess; if(plugin_state->crypto_verify_data == NULL) { - FURI_LOG_D(LOGGING_TAG, "Generating crypto verify data"); + FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data"); plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); furi_check(plugin_state->crypto_verify_data != NULL); plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; @@ -110,8 +111,7 @@ bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t plugin_state->pin_set = pin != NULL && pin_length > 0; - result = totp_config_file_update_crypto_signatures(plugin_state) == - TotpConfigFileUpdateSuccess; + result |= CryptoSeedIVResultFlagNewCryptoVerifyData; } return result; diff --git a/applications/external/totp/services/crypto/crypto.h b/applications/external/totp/services/crypto/crypto.h index 3442b9a6e..ab27191a8 100644 --- a/applications/external/totp/services/crypto/crypto.h +++ b/applications/external/totp/services/crypto/crypto.h @@ -2,6 +2,26 @@ #include "../../types/plugin_state.h" +typedef uint8_t CryptoSeedIVResult; + +enum CryptoSeedIVResults { + + /** + * @brief IV seeding operation failed + */ + CryptoSeedIVResultFailed = 0b00, + + /** + * @brief IV seeding operation succeeded + */ + CryptoSeedIVResultFlagSuccess = 0b01, + + /** + * @brief As a part of IV seeding operation new crypto verify data has been generated + */ + CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10 +}; + /** * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) * @param plain_data plain data to be encrypted @@ -35,9 +55,10 @@ uint8_t* totp_crypto_decrypt( * @param plugin_state application state * @param pin user's PIN * @param pin_length user's PIN length - * @return \c true on success; \c false otherwise + * @return Results of seeding IV */ -bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); +CryptoSeedIVResult + totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); /** * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 0b70167a2..f620a3b48 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -51,23 +51,39 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) { dialog_message_show(plugin_state->dialogs_app, message); dialog_message_free(message); if(dialog_result == DialogMessageButtonRight) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } else { - if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { totp_dialogs_config_loading_error(plugin_state); return false; } - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } } else if(plugin_state->pin_set) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } else { - if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { totp_dialogs_config_loading_error(plugin_state); return false; } + if(totp_crypto_verify_key(plugin_state)) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } else { FURI_LOG_E( LOGGING_TAG, @@ -96,7 +112,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS); memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - if(totp_config_file_load_base(plugin_state) != TotpConfigFileOpenSuccess) { + if(!totp_config_file_load(plugin_state)) { totp_dialogs_config_loading_error(plugin_state); return false; } @@ -119,15 +135,7 @@ static void totp_plugin_state_free(PluginState* plugin_state) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); - ListNode* node = plugin_state->tokens_list; - ListNode* tmp; - while(node != NULL) { - tmp = node->next; - TokenInfo* tokenInfo = node->data; - token_info_free(tokenInfo); - free(node); - node = tmp; - } + totp_config_file_close(plugin_state); if(plugin_state->crypto_verify_data != NULL) { free(plugin_state->crypto_verify_data); @@ -193,8 +201,9 @@ int32_t totp_app() { } } else if( plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication && + plugin_state->current_scene != TotpSceneStandby && furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } view_port_update(view_port); diff --git a/applications/external/totp/types/common.c b/applications/external/totp/types/common.c new file mode 100644 index 000000000..ec5eb3ebd --- /dev/null +++ b/applications/external/totp/types/common.c @@ -0,0 +1,3 @@ +#include "common.h" + +const char* LOGGING_TAG = "TOTP APP"; \ No newline at end of file diff --git a/applications/external/totp/types/common.h b/applications/external/totp/types/common.h index 2c6d6b293..737adb82d 100644 --- a/applications/external/totp/types/common.h +++ b/applications/external/totp/types/common.h @@ -1,3 +1,3 @@ #pragma once -#define LOGGING_TAG "TOTP APP" +extern const char* LOGGING_TAG; diff --git a/applications/external/totp/types/nullable.h b/applications/external/totp/types/nullable.h deleted file mode 100644 index 4f9b7bc01..000000000 --- a/applications/external/totp/types/nullable.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -#define TOTP_NULLABLE_STRUCT(value_type) \ - typedef struct TotpNullable_##value_type { \ - bool is_null; \ - value_type value; \ - } TotpNullable_##value_type - -#define TOTP_NULLABLE_NULL(s) s.is_null = true -#define TOTP_NULLABLE_VALUE(s, v) \ - s.is_null = false; \ - s.value = v - -TOTP_NULLABLE_STRUCT(uint16_t); diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index cacf68426..c20594f37 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -4,8 +4,8 @@ #include #include #include "../features_config.h" -#include #include "../ui/totp_scenes_enum.h" +#include "../services/config/config_file_context.h" #include "notification_method.h" #include "automation_method.h" #ifdef TOTP_BADBT_TYPE_ENABLED @@ -48,20 +48,7 @@ typedef struct { */ float timezone_offset; - /** - * @brief Token list head node - */ - ListNode* tokens_list; - - /** - * @brief Whether token list is loaded or not - */ - bool token_list_loaded; - - /** - * @brief Tokens list length - */ - uint16_t tokens_count; + ConfigFileContext* config_file_context; /** * @brief Encrypted well-known string data diff --git a/applications/external/totp/types/token_info.c b/applications/external/totp/types/token_info.c index 5b85de719..2f108033b 100644 --- a/applications/external/totp/types/token_info.c +++ b/applications/external/totp/types/token_info.c @@ -8,17 +8,15 @@ TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); furi_check(tokenInfo != NULL); - tokenInfo->algo = SHA1; - tokenInfo->digits = TOTP_6_DIGITS; - tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; - tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + tokenInfo->name = furi_string_alloc(); + token_info_set_defaults(tokenInfo); return tokenInfo; } void token_info_free(TokenInfo* token_info) { if(token_info == NULL) return; - free(token_info->name); free(token_info->token); + furi_string_free(token_info->name); free(token_info); } @@ -32,13 +30,13 @@ bool token_info_set_secret( uint8_t* plain_secret; size_t plain_secret_length; size_t plain_secret_size; - if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE32) { + if(plain_token_secret_encoding == PlainTokenSecretEncodingBase32) { plain_secret_size = token_secret_length; plain_secret = malloc(plain_secret_size); furi_check(plain_secret != NULL); plain_secret_length = base32_decode((const uint8_t*)plain_token_secret, plain_secret, plain_secret_size); - } else if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE64) { + } else if(plain_token_secret_encoding == PlainTokenSecretEncodingBase64) { plain_secret_length = 0; plain_secret = base64_decode( (const uint8_t*)plain_token_secret, @@ -52,6 +50,10 @@ bool token_info_set_secret( bool result; if(plain_secret_length > 0) { + if(token_info->token != NULL) { + free(token_info->token); + } + token_info->token = totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); result = true; @@ -67,13 +69,13 @@ bool token_info_set_secret( bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) { switch(digits) { case 5: - token_info->digits = TOTP_5_DIGITS; + token_info->digits = TotpFiveDigitsCount; return true; case 6: - token_info->digits = TOTP_6_DIGITS; + token_info->digits = TotpSixDigitsCount; return true; case 8: - token_info->digits = TOTP_8_DIGITS; + token_info->digits = TotpEightDigitsCount; return true; default: break; @@ -134,22 +136,22 @@ char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const FuriString* str) { if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME) == 0) { - token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END; + token_info->automation_features |= TokenAutomationFeatureEnterAtTheEnd; return true; } if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME) == 0) { - token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END; + token_info->automation_features |= TokenAutomationFeatureTabAtTheEnd; return true; } if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME) == 0) { - token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER; + token_info->automation_features |= TokenAutomationFeatureTypeSlower; return true; } if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME) == 0) { - token_info->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + token_info->automation_features = TokenAutomationFeatureNone; return true; } @@ -164,10 +166,17 @@ TokenInfo* token_info_clone(const TokenInfo* src) { furi_check(clone->token != NULL); memcpy(clone->token, src->token, src->token_length); - int name_length = strnlen(src->name, TOTP_TOKEN_MAX_LENGTH); - clone->name = malloc(name_length + 1); - furi_check(clone->name != NULL); - strlcpy(clone->name, src->name, name_length + 1); + clone->name = furi_string_alloc(); + furi_string_set(clone->name, src->name); return clone; +} + +void token_info_set_defaults(TokenInfo* token_info) { + furi_check(token_info != NULL); + token_info->algo = SHA1; + token_info->digits = TotpSixDigitsCount; + token_info->duration = TOTP_TOKEN_DURATION_DEFAULT; + token_info->automation_features = TokenAutomationFeatureNone; + furi_string_reset(token_info->name); } \ No newline at end of file diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index 21968553f..138ad32b1 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -20,6 +20,8 @@ #define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab" #define TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME "slower" +#define TOTP_TOKEN_DIGITS_MAX_COUNT (8) + typedef uint8_t TokenHashAlgo; typedef uint8_t TokenDigitsCount; typedef uint8_t TokenAutomationFeature; @@ -32,22 +34,22 @@ enum TokenHashAlgos { /** * @brief SHA1 hashing algorithm */ - SHA1, + SHA1 = 0, /** * @brief SHA256 hashing algorithm */ - SHA256, + SHA256 = 1, /** * @brief SHA512 hashing algorithm */ - SHA512, + SHA512 = 2, /** * @brief Algorithm used by Steam (Valve) */ - STEAM + STEAM = 3 }; /** @@ -55,19 +57,19 @@ enum TokenHashAlgos { */ enum TokenDigitsCounts { /** - * @brief 6 digits + * @brief 5 digits */ - TOTP_5_DIGITS = 5, + TotpFiveDigitsCount = 5, /** * @brief 6 digits */ - TOTP_6_DIGITS = 6, + TotpSixDigitsCount = 6, /** * @brief 8 digits */ - TOTP_8_DIGITS = 8 + TotpEightDigitsCount = 8 }; /** @@ -77,22 +79,22 @@ enum TokenAutomationFeatures { /** * @brief No features enabled */ - TOKEN_AUTOMATION_FEATURE_NONE = 0b000, + TokenAutomationFeatureNone = 0b000, /** * @brief Press "Enter" key at the end as a part of token input automation */ - TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END = 0b001, + TokenAutomationFeatureEnterAtTheEnd = 0b001, /** * @brief Press "Tab" key at the end as a part of token input automation */ - TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END = 0b010, + TokenAutomationFeatureTabAtTheEnd = 0b010, /** * @brief Press keys slower and wait longer between keystrokes */ - TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100 + TokenAutomationFeatureTypeSlower = 0b100 }; /** @@ -103,16 +105,14 @@ enum PlainTokenSecretEncodings { /** * @brief Base32 encoding */ - PLAIN_TOKEN_ENCODING_BASE32 = 0, + PlainTokenSecretEncodingBase32 = 0, /** * @brief Base64 encoding */ - PLAIN_TOKEN_ENCODING_BASE64 = 1 + PlainTokenSecretEncodingBase64 = 1 }; -#define TOTP_TOKEN_DIGITS_MAX_COUNT (8) - /** * @brief TOTP token information */ @@ -130,7 +130,7 @@ typedef struct { /** * @brief User-friendly token name */ - char* name; + FuriString* name; /** * @brief Hashing algorithm @@ -225,4 +225,10 @@ bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const Fur * @param src instance to clone * @return cloned instance */ -TokenInfo* token_info_clone(const TokenInfo* src); \ No newline at end of file +TokenInfo* token_info_clone(const TokenInfo* src); + +/** + * @brief Sets default values to all the properties of \c token_info + * @param token_info instance to set defaults to + */ +void token_info_set_defaults(TokenInfo* token_info); diff --git a/applications/external/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c index c6f709006..657762a94 100644 --- a/applications/external/totp/ui/scene_director.c +++ b/applications/external/totp/ui/scene_director.c @@ -5,29 +5,28 @@ #include "scenes/add_new_token/totp_scene_add_new_token.h" #include "scenes/token_menu/totp_scene_token_menu.h" #include "scenes/app_settings/totp_app_settings.h" +#include "scenes/standby/standby.h" -void totp_scene_director_activate_scene( - PluginState* const plugin_state, - Scene scene, - const void* context) { +void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene) { totp_scene_director_deactivate_active_scene(plugin_state); switch(scene) { case TotpSceneGenerateToken: - totp_scene_generate_token_activate(plugin_state, context); + totp_scene_generate_token_activate(plugin_state); break; case TotpSceneAuthentication: totp_scene_authenticate_activate(plugin_state); break; case TotpSceneAddNewToken: - totp_scene_add_new_token_activate(plugin_state, context); + totp_scene_add_new_token_activate(plugin_state); break; case TotpSceneTokenMenu: - totp_scene_token_menu_activate(plugin_state, context); + totp_scene_token_menu_activate(plugin_state); break; case TotpSceneAppSettings: - totp_scene_app_settings_activate(plugin_state, context); + totp_scene_app_settings_activate(plugin_state); break; case TotpSceneNone: + case TotpSceneStandby: break; default: break; @@ -56,6 +55,7 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state totp_scene_app_settings_deactivate(plugin_state); break; case TotpSceneNone: + case TotpSceneStandby: break; default: break; @@ -81,6 +81,9 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ break; case TotpSceneNone: break; + case TotpSceneStandby: + totp_scene_standby_render(canvas); + break; default: break; } @@ -105,6 +108,7 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con processing = totp_scene_app_settings_handle_event(event, plugin_state); break; case TotpSceneNone: + case TotpSceneStandby: break; default: break; diff --git a/applications/external/totp/ui/scene_director.h b/applications/external/totp/ui/scene_director.h index 71709978f..e45223997 100644 --- a/applications/external/totp/ui/scene_director.h +++ b/applications/external/totp/ui/scene_director.h @@ -11,10 +11,7 @@ * @param scene scene to be activated * @param context scene context to be passed to the scene activation method */ -void totp_scene_director_activate_scene( - PluginState* const plugin_state, - Scene scene, - const void* context); +void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene); /** * @brief Deactivate current scene diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c index 3f8e4fd93..d525e3399 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -4,17 +4,18 @@ #include "../../scene_director.h" #include "totp_input_text.h" #include "../../../types/token_info.h" -#include #include "../../../services/config/config.h" #include "../../ui_controls.h" #include "../../common_dialogs.h" #include -#include "../../../types/nullable.h" #include "../generate_token/totp_scene_generate_token.h" char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"}; char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"}; -TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_5_DIGITS, TOTP_6_DIGITS, TOTP_8_DIGITS}; +TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = { + TotpFiveDigitsCount, + TotpSixDigitsCount, + TotpEightDigitsCount}; typedef enum { TokenNameTextBox, @@ -36,7 +37,6 @@ typedef struct { InputTextSceneContext* token_secret_input_context; InputTextSceneState* input_state; uint32_t input_started_at; - TotpNullable_uint16_t current_token_index; int16_t screen_y_offset; TokenHashAlgo algo; uint8_t digits_count_index; @@ -44,6 +44,13 @@ typedef struct { FuriString* duration_text; } SceneState; +struct TotpAddContext { + SceneState* scene_state; + uint8_t* iv; +}; + +enum TotpIteratorUpdateTokenResultsEx { TotpIteratorUpdateTokenResultInvalidSecret = 1 }; + static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) { SceneState* scene_state = result->callback_data; free(scene_state->token_name); @@ -66,9 +73,29 @@ static void update_duration_text(SceneState* scene_state) { furi_string_printf(scene_state->duration_text, "%d sec.", scene_state->duration); } -void totp_scene_add_new_token_activate( - PluginState* plugin_state, - const TokenAddEditSceneContext* context) { +static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, const void* context) { + const struct TotpAddContext* context_t = context; + if(!token_info_set_secret( + tokenInfo, + context_t->scene_state->token_secret, + context_t->scene_state->token_secret_length, + PlainTokenSecretEncodingBase32, + context_t->iv)) { + return TotpIteratorUpdateTokenResultInvalidSecret; + } + + furi_string_set_strn( + tokenInfo->name, + context_t->scene_state->token_name, + context_t->scene_state->token_name_length + 1); + tokenInfo->algo = context_t->scene_state->algo; + tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[context_t->scene_state->digits_count_index]; + tokenInfo->duration = context_t->scene_state->duration; + + return TotpIteratorUpdateTokenResultSuccess; +} + +void totp_scene_add_new_token_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); plugin_state->current_scene_state = scene_state; @@ -97,12 +124,6 @@ void totp_scene_add_new_token_activate( scene_state->duration = TOTP_TOKEN_DURATION_DEFAULT; scene_state->duration_text = furi_string_alloc(); update_duration_text(scene_state); - - if(context == NULL) { - TOTP_NULLABLE_NULL(scene_state->current_token_index); - } else { - TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); - } } void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state) { @@ -260,38 +281,16 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState case TokenDurationSelect: break; case ConfirmButton: { - TokenInfo* tokenInfo = token_info_alloc(); - bool token_secret_set = token_info_set_secret( - tokenInfo, - scene_state->token_secret, - scene_state->token_secret_length, - PLAIN_TOKEN_ENCODING_BASE32, - &plugin_state->iv[0]); + struct TotpAddContext add_context = { + .iv = plugin_state->iv, .scene_state = scene_state}; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token( + iterator_context, &add_token_handler, &add_context); - if(token_secret_set) { - tokenInfo->name = malloc(scene_state->token_name_length + 1); - furi_check(tokenInfo->name != NULL); - strlcpy( - tokenInfo->name, scene_state->token_name, scene_state->token_name_length + 1); - tokenInfo->algo = scene_state->algo; - tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[scene_state->digits_count_index]; - tokenInfo->duration = scene_state->duration; - - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check); - plugin_state->tokens_count++; - - if(totp_config_file_save_new_token(tokenInfo) != TotpConfigFileUpdateSuccess) { - token_info_free(tokenInfo); - totp_dialogs_config_updating_error(plugin_state); - return false; - } - - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = plugin_state->tokens_count - 1}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - token_info_free(tokenInfo); + if(add_result == TotpIteratorUpdateTokenResultSuccess) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); + } else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) { DialogMessage* message = dialog_message_alloc(); dialog_message_set_buttons(message, "Back", NULL, NULL); dialog_message_set_text( @@ -305,7 +304,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState dialog_message_free(message); scene_state->selected_control = TokenSecretTextBox; update_screen_y_offset(scene_state); + } else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) { + totp_dialogs_config_updating_error(plugin_state); } + break; } default: @@ -313,14 +315,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState } break; case InputKeyBack: - if(!scene_state->current_token_index.is_null) { - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); break; default: break; diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h index e05a95dbd..dd6b32994 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} TokenAddEditSceneContext; - -void totp_scene_add_new_token_activate( - PluginState* plugin_state, - const TokenAddEditSceneContext* context); +void totp_scene_add_new_token_activate(PluginState* plugin_state); void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state); void totp_scene_add_new_token_deactivate(PluginState* plugin_state); diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c index d2cf629d2..6dcf0dbc9 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -9,7 +9,6 @@ #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" #include -#include "../../../types/nullable.h" #include "../../../features_config.h" #ifdef TOTP_BADBT_TYPE_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" @@ -40,21 +39,13 @@ typedef struct { bool badbt_enabled; #endif uint8_t y_offset; - TotpNullable_uint16_t current_token_index; Control selected_control; } SceneState; -void totp_scene_app_settings_activate( - PluginState* plugin_state, - const AppSettingsSceneContext* context) { +void totp_scene_app_settings_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); plugin_state->current_scene_state = scene_state; - if(context != NULL) { - TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); - } else { - TOTP_NULLABLE_NULL(scene_state->current_token_index); - } float off_int; float off_dec = modff(plugin_state->timezone_offset, &off_int); @@ -281,8 +272,7 @@ bool totp_scene_app_settings_handle_event( AutomationMethodNone; #endif - if(totp_config_file_update_user_settings(plugin_state) != - TotpConfigFileUpdateSuccess) { + if(!totp_config_file_update_user_settings(plugin_state)) { totp_dialogs_config_updating_error(plugin_state); return false; } @@ -294,25 +284,11 @@ bool totp_scene_app_settings_handle_event( } #endif - if(!scene_state->current_token_index.is_null) { - TokenMenuSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneTokenMenu, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu); } break; case InputKeyBack: { - if(!scene_state->current_token_index.is_null) { - TokenMenuSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneTokenMenu, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu); break; } default: diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h index a0e408b00..e54aab87b 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} AppSettingsSceneContext; - -void totp_scene_app_settings_activate( - PluginState* plugin_state, - const AppSettingsSceneContext* context); +void totp_scene_app_settings_activate(PluginState* plugin_state); void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state); bool totp_scene_app_settings_handle_event( const PluginEvent* const event, diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c index c0a0b5744..86e1e8e2b 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -114,12 +114,18 @@ bool totp_scene_authenticate_handle_event( scene_state->code_length++; } break; - case InputKeyOk: - totp_crypto_seed_iv(plugin_state, &scene_state->code_input[0], scene_state->code_length); + case InputKeyOk: { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv( + plugin_state, &scene_state->code_input[0], scene_state->code_length); + + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + totp_config_file_update_crypto_signatures(plugin_state); + } if(totp_crypto_verify_key(plugin_state)) { FURI_LOG_D(LOGGING_TAG, "PIN is valid"); - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } else { FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); @@ -140,6 +146,7 @@ bool totp_scene_authenticate_handle_event( dialog_message_free(message); } break; + } case InputKeyBack: if(scene_state->code_length > 0) { scene_state->code_input[scene_state->code_length - 1] = 0; diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index 92a45eb4a..b111242dc 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -31,9 +31,7 @@ typedef struct { } UiPrecalculatedDimensions; typedef struct { - uint16_t current_token_index; char last_code[TOTP_TOKEN_DIGITS_MAX_COUNT + 1]; - TokenInfo* current_token; TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context; NotificationMessage const** notification_sequence_new_token; NotificationMessage const** notification_sequence_automation; @@ -128,19 +126,21 @@ static const NotificationSequence* return (NotificationSequence*)scene_state->notification_sequence_automation; } -static void update_totp_params(PluginState* const plugin_state) { +static void update_totp_params(PluginState* const plugin_state, size_t token_index) { SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - - if(scene_state->current_token_index < plugin_state->tokens_count) { - scene_state->current_token = - list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_go_to(iterator_context, token_index)) { totp_generate_code_worker_notify( scene_state->generate_code_worker_context, TotpGenerateCodeWorkerEventForceUpdate); } } -static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_state) { - uint8_t code_length = scene_state->current_token->digits; +static void draw_totp_code(Canvas* const canvas, const PluginState* const plugin_state) { + const SceneState* scene_state = plugin_state->current_scene_state; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + uint8_t code_length = totp_token_info_iterator_get_current_token(iterator_context)->digits; uint8_t offset_x = scene_state->ui_precalculated_dimensions.code_offset_x; uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; uint8_t offset_x_inc = scene_state->ui_precalculated_dimensions.code_offset_x_inc; @@ -163,10 +163,18 @@ static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_s static void on_new_token_code_generated(bool time_left, void* context) { const PluginState* plugin_state = context; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { + return; + } + SceneState* scene_state = plugin_state->current_scene_state; + const TokenInfo* current_token = totp_token_info_iterator_get_current_token(iterator_context); + uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; scene_state->ui_precalculated_dimensions.code_total_length = - scene_state->current_token->digits * (char_width + modeNine_15ptFontInfo.spacePixels); + current_token->digits * (char_width + modeNine_15ptFontInfo.spacePixels); scene_state->ui_precalculated_dimensions.code_offset_x = (SCREEN_WIDTH - scene_state->ui_precalculated_dimensions.code_total_length) >> 1; scene_state->ui_precalculated_dimensions.code_offset_x_inc = @@ -192,43 +200,9 @@ static void on_code_lifetime_updated_generated(float code_lifetime_percent, void PROGRESS_BAR_MARGIN; } -void totp_scene_generate_token_activate( - PluginState* plugin_state, - const GenerateTokenSceneContext* context) { - if(!plugin_state->token_list_loaded) { - TokenLoadingResult token_load_result = totp_config_file_load_tokens(plugin_state); - if(token_load_result != TokenLoadingResultSuccess) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, NULL, "Okay", NULL); - if(token_load_result == TokenLoadingResultWarning) { - dialog_message_set_text( - message, - "Unable to load some tokens\nPlease review conf file", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - } else if(token_load_result == TokenLoadingResultError) { - dialog_message_set_text( - message, - "Unable to load tokens\nPlease review conf file", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - } - - dialog_message_show(plugin_state->dialogs_app, message); - dialog_message_free(message); - } - } +void totp_scene_generate_token_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); - if(context == NULL || context->current_token_index > plugin_state->tokens_count) { - scene_state->current_token_index = 0; - } else { - scene_state->current_token_index = context->current_token_index; - } plugin_state->current_scene_state = scene_state; FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset); @@ -254,10 +228,11 @@ void totp_scene_generate_token_activate( scene_state->last_code_update_sync); } #endif - + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); scene_state->generate_code_worker_context = totp_generate_code_worker_start( scene_state->last_code, - &scene_state->current_token, + totp_token_info_iterator_get_current_token(iterator_context), scene_state->last_code_update_sync, plugin_state->timezone_offset, plugin_state->iv); @@ -270,11 +245,14 @@ void totp_scene_generate_token_activate( &on_code_lifetime_updated_generated, scene_state); - update_totp_params(plugin_state); + update_totp_params( + plugin_state, totp_token_info_iterator_get_current_token_index(iterator_context)); } void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) { - if(plugin_state->tokens_count == 0) { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { canvas_draw_str_aligned( canvas, SCREEN_WIDTH_CENTER, @@ -295,7 +273,9 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; canvas_set_font(canvas, FontPrimary); - uint16_t token_name_width = canvas_string_width(canvas, scene_state->current_token->name); + const char* token_name_cstr = + furi_string_get_cstr(totp_token_info_iterator_get_current_token(iterator_context)->name); + uint16_t token_name_width = canvas_string_width(canvas, token_name_cstr); if(SCREEN_WIDTH - token_name_width > 18) { canvas_draw_str_aligned( canvas, @@ -303,22 +283,17 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ SCREEN_HEIGHT_CENTER - 20, AlignCenter, AlignCenter, - scene_state->current_token->name); + token_name_cstr); } else { canvas_draw_str_aligned( - canvas, - 9, - SCREEN_HEIGHT_CENTER - 20, - AlignLeft, - AlignCenter, - scene_state->current_token->name); + canvas, 9, SCREEN_HEIGHT_CENTER - 20, AlignLeft, AlignCenter, token_name_cstr); canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 0, SCREEN_HEIGHT_CENTER - 24, 9, 9); canvas_draw_box(canvas, SCREEN_WIDTH - 10, SCREEN_HEIGHT_CENTER - 24, 9, 9); canvas_set_color(canvas, ColorBlack); } - draw_totp_code(canvas, scene_state); + draw_totp_code(canvas, plugin_state); canvas_draw_box( canvas, @@ -326,11 +301,10 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ SCREEN_HEIGHT - PROGRESS_BAR_MARGIN - PROGRESS_BAR_HEIGHT, scene_state->ui_precalculated_dimensions.progress_bar_width, PROGRESS_BAR_HEIGHT); - - if(plugin_state->tokens_count > 1) { + if(totp_token_info_iterator_get_total_count(iterator_context) > 1) { canvas_draw_icon(canvas, 0, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_left_8x9); canvas_draw_icon( - canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); + canvas, SCREEN_WIDTH - 8, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); } #ifdef TOTP_AUTOMATION_ICONS_ENABLED @@ -351,7 +325,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ #ifdef TOTP_BADBT_TYPE_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt && plugin_state->bt_type_code_worker_context != NULL && - plugin_state->bt_type_code_worker_context->is_advertising) { + totp_bt_type_code_worker_is_advertising(plugin_state->bt_type_code_worker_context)) { canvas_draw_icon( canvas, SCREEN_WIDTH_CENTER + @@ -379,10 +353,12 @@ bool totp_scene_generate_token_handle_event( if(event->input.key == InputKeyDown && plugin_state->automation_method & AutomationMethodBadUsb) { scene_state = (SceneState*)plugin_state->current_scene_state; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_usb_type_code_worker_notify( scene_state->usb_type_code_worker_context, TotpUsbTypeCodeWorkerEventType, - scene_state->current_token->automation_features); + totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( plugin_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); @@ -393,10 +369,12 @@ bool totp_scene_generate_token_handle_event( event->input.key == InputKeyUp && plugin_state->automation_method & AutomationMethodBadBt) { scene_state = (SceneState*)plugin_state->current_scene_state; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_bt_type_code_worker_notify( plugin_state->bt_type_code_worker_context, TotpBtTypeCodeWorkerEventType, - scene_state->current_token->automation_features); + totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( plugin_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); @@ -409,37 +387,43 @@ bool totp_scene_generate_token_handle_event( return true; } - scene_state = (SceneState*)plugin_state->current_scene_state; switch(event->input.key) { case InputKeyUp: break; case InputKeyDown: break; - case InputKeyRight: - totp_roll_value_uint16_t( - &scene_state->current_token_index, + case InputKeyRight: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t current_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + totp_roll_value_size_t( + ¤t_token_index, 1, 0, - plugin_state->tokens_count - 1, + totp_token_info_iterator_get_total_count(iterator_context) - 1, RollOverflowBehaviorRoll); - update_totp_params(plugin_state); + + update_totp_params(plugin_state, current_token_index); break; - case InputKeyLeft: - totp_roll_value_uint16_t( - &scene_state->current_token_index, + } + case InputKeyLeft: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t current_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + totp_roll_value_size_t( + ¤t_token_index, -1, 0, - plugin_state->tokens_count - 1, + totp_token_info_iterator_get_total_count(iterator_context) - 1, RollOverflowBehaviorRoll); - update_totp_params(plugin_state); + + update_totp_params(plugin_state, current_token_index); break; + } case InputKeyOk: - if(plugin_state->tokens_count == 0) { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } else { - TokenMenuSceneContext ctx = {.current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu); break; case InputKeyBack: break; diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h index e183f53d2..3f7bc0408 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} GenerateTokenSceneContext; - -void totp_scene_generate_token_activate( - PluginState* plugin_state, - const GenerateTokenSceneContext* context); +void totp_scene_generate_token_activate(PluginState* plugin_state); void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_generate_token_handle_event( const PluginEvent* const event, diff --git a/applications/external/totp/ui/scenes/standby/standby.c b/applications/external/totp/ui/scenes/standby/standby.c new file mode 100644 index 000000000..5cd6bae6a --- /dev/null +++ b/applications/external/totp/ui/scenes/standby/standby.c @@ -0,0 +1,12 @@ +#include "standby.h" +#include +#include "../../constants.h" + +void totp_scene_standby_render(Canvas* const canvas) { + canvas_draw_icon(canvas, SCREEN_WIDTH - 56, SCREEN_HEIGHT - 48, &I_DolphinCommon_56x48); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignTop, "CLI command"); + + canvas_draw_str_aligned(canvas, 5, 24, AlignLeft, AlignTop, "is running now"); +} \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/standby/standby.h b/applications/external/totp/ui/scenes/standby/standby.h new file mode 100644 index 000000000..78e2b0915 --- /dev/null +++ b/applications/external/totp/ui/scenes/standby/standby.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void totp_scene_standby_render(Canvas* const canvas); \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c index 7b00f0a1b..a8c8de28a 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c @@ -6,12 +6,10 @@ #include "../../constants.h" #include "../../scene_director.h" #include "../../../services/config/config.h" -#include #include "../../../types/token_info.h" #include "../generate_token/totp_scene_generate_token.h" #include "../add_new_token/totp_scene_add_new_token.h" #include "../app_settings/totp_app_settings.h" -#include "../../../types/nullable.h" #include #define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3) @@ -21,25 +19,19 @@ typedef enum { AddNewToken, DeleteToken, AppSettings } Control; typedef struct { Control selected_control; - TotpNullable_uint16_t current_token_index; } SceneState; -void totp_scene_token_menu_activate( - PluginState* plugin_state, - const TokenMenuSceneContext* context) { +void totp_scene_token_menu_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); plugin_state->current_scene_state = scene_state; - if(context != NULL) { - TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); - } else { - TOTP_NULLABLE_NULL(scene_state->current_token_index); - } } void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) { const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(scene_state->current_token_index.is_null) { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { ui_control_button_render( canvas, SCREEN_WIDTH_CENTER - 36, @@ -95,22 +87,28 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt } switch(event->input.key) { - case InputKeyUp: + case InputKeyUp: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_roll_value_uint8_t( &scene_state->selected_control, -1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); if(scene_state->selected_control == DeleteToken && - scene_state->current_token_index.is_null) { + totp_token_info_iterator_get_total_count(iterator_context) == 0) { scene_state->selected_control--; } break; - case InputKeyDown: + } + case InputKeyDown: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_roll_value_uint8_t( &scene_state->selected_control, 1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); if(scene_state->selected_control == DeleteToken && - scene_state->current_token_index.is_null) { + totp_token_info_iterator_get_total_count(iterator_context) == 0) { scene_state->selected_control++; } break; + } case InputKeyRight: break; case InputKeyLeft: @@ -118,14 +116,7 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt case InputKeyOk: switch(scene_state->selected_control) { case AddNewToken: { - if(scene_state->current_token_index.is_null) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken, NULL); - } else { - TokenAddEditSceneContext add_new_token_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneAddNewToken, &add_new_token_scene_context); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken); break; } case DeleteToken: { @@ -142,34 +133,21 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs_app, message); dialog_message_free(message); + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); if(dialog_result == DialogMessageButtonRight && - !scene_state->current_token_index.is_null) { - TokenInfo* tokenInfo = NULL; - plugin_state->tokens_list = list_remove_at( - plugin_state->tokens_list, - scene_state->current_token_index.value, - (void**)&tokenInfo); - plugin_state->tokens_count--; - furi_check(tokenInfo != NULL); - token_info_free(tokenInfo); - - if(totp_full_save_config_file(plugin_state) != TotpConfigFileUpdateSuccess) { + totp_token_info_iterator_get_total_count(iterator_context) > 0) { + if(!totp_token_info_iterator_remove_current_token_info(iterator_context)) { totp_dialogs_config_updating_error(plugin_state); return false; } - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } break; } case AppSettings: { - if(!scene_state->current_token_index.is_null) { - AppSettingsSceneContext app_settings_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneAppSettings, &app_settings_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings); break; } default: @@ -177,14 +155,7 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt } break; case InputKeyBack: { - if(!scene_state->current_token_index.is_null) { - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); break; } default: diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h index f9d4b4cbf..a715c9748 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} TokenMenuSceneContext; - -void totp_scene_token_menu_activate( - PluginState* plugin_state, - const TokenMenuSceneContext* context); +void totp_scene_token_menu_activate(PluginState* plugin_state); void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state); void totp_scene_token_menu_deactivate(PluginState* plugin_state); diff --git a/applications/external/totp/ui/totp_scenes_enum.h b/applications/external/totp/ui/totp_scenes_enum.h index 0c73af772..4624eddd9 100644 --- a/applications/external/totp/ui/totp_scenes_enum.h +++ b/applications/external/totp/ui/totp_scenes_enum.h @@ -34,5 +34,10 @@ enum Scenes { /** * @brief Scene where user can change application settings */ - TotpSceneAppSettings + TotpSceneAppSettings, + + /** + * @brief Scene which informs user that CLI command is running + */ + TotpSceneStandby }; diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c index 5a1f56298..bf55dba6a 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.c +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c @@ -2,13 +2,39 @@ #include #include #include +#include +#include +#include +#include +#include #include #include "../../types/common.h" #include "../../types/token_info.h" #include "../type_code_common.h" +#include "../../features_config.h" + +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME +#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH +#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE +#endif #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys") +struct TotpBtTypeCodeWorkerContext { + char* code_buffer; + uint8_t code_buffer_size; + uint8_t flags; + FuriThread* thread; + FuriMutex* code_buffer_sync; + Bt* bt; + bool is_advertising; + bool is_connected; +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME + char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; + uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; +#endif +}; + static inline bool totp_type_code_worker_stop_requested() { return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop; } @@ -197,4 +223,8 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) { context->bt = NULL; free(context); +} + +bool totp_bt_type_code_worker_is_advertising(const TotpBtTypeCodeWorkerContext* context) { + return context->is_advertising; } \ No newline at end of file diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h index 6c7e502c6..85016592e 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.h +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -1,50 +1,79 @@ #pragma once -#include -#include +#include +#include #include -#include -#include -#include -#include "../../features_config.h" - -#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME -#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH -#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE -#endif typedef uint8_t TotpBtTypeCodeWorkerEvent; -typedef struct { - char* code_buffer; - uint8_t code_buffer_size; - uint8_t flags; - FuriThread* thread; - FuriMutex* code_buffer_sync; - Bt* bt; - bool is_advertising; - bool is_connected; -#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME - char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; - uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; -#endif -} TotpBtTypeCodeWorkerContext; +typedef struct TotpBtTypeCodeWorkerContext TotpBtTypeCodeWorkerContext; +/** + * @brief Bluetooth token input automation worker events + */ enum TotpBtTypeCodeWorkerEvents { + + /** + * @brief Reserved, should not be used anywhere + */ TotpBtTypeCodeWorkerEventReserved = 0b00, + + /** + * @brief Stop worker + */ TotpBtTypeCodeWorkerEventStop = 0b01, + + /** + * @brief Trigger token input automation + */ TotpBtTypeCodeWorkerEventType = 0b10 }; +/** + * @brief Initializes bluetooth token input automation worker + * @return worker context + */ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init(); + +/** + * @brief Disposes bluetooth token input automation worker and releases all the allocated resources + * @param context worker context + */ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context); + +/** + * @brief Starts bluetooth token input automation worker + * @param context worker context + * @param code_buffer code buffer to be used to automate + * @param code_buffer_size code buffer size + * @param code_buffer_sync code buffer synchronization primitive + */ void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, char* code_buffer, uint8_t code_buffer_size, FuriMutex* code_buffer_sync); + +/** + * @brief Stops bluetooth token input automation worker + * @param context worker context + */ void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context); + +/** + * @brief Notifies bluetooth token input automation worker with a given event + * @param context worker context + * @param event event to notify worker with + * @param flags event flags + */ void totp_bt_type_code_worker_notify( TotpBtTypeCodeWorkerContext* context, TotpBtTypeCodeWorkerEvent event, uint8_t flags); + +/** + * @brief Gets information whether Bluetooth is advertising now or not + * @param context worker context + * @return \c true if Bluetooth is advertising now; \c false otherwise + */ +bool totp_bt_type_code_worker_is_advertising(const TotpBtTypeCodeWorkerContext* context); diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c index 4919cf942..7e9356c45 100644 --- a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c @@ -1,4 +1,5 @@ #include "generate_totp_code.h" +#include #include "../../services/crypto/crypto.h" #include "../../services/totp/totp.h" #include "../../services/convert/convert.h" @@ -7,6 +8,19 @@ #define ONE_SEC_MS (1000) +struct TotpGenerateCodeWorkerContext { + char* code_buffer; + FuriThread* thread; + FuriMutex* code_buffer_sync; + const TokenInfo* token_info; + float timezone_offset; + uint8_t* iv; + TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler; + void* on_new_code_generated_handler_context; + TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler; + void* on_code_lifetime_changed_handler_context; +}; + static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY"; static void @@ -93,7 +107,7 @@ static int32_t totp_generate_worker_callback(void* context) { if(flags & TotpGenerateCodeWorkerEventStop) break; - const TokenInfo* token_info = *(t_context->token_info); + const TokenInfo* token_info = t_context->token_info; if(token_info == NULL) { continue; } @@ -127,7 +141,7 @@ static int32_t totp_generate_worker_callback(void* context) { TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( char* code_buffer, - TokenInfo** token_info, + const TokenInfo* token_info, FuriMutex* code_buffer_sync, float timezone_offset, uint8_t* iv) { diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h index c7a93dc95..f351ffa68 100644 --- a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include "../../types/token_info.h" @@ -10,39 +9,77 @@ typedef uint8_t TotpGenerateCodeWorkerEvent; typedef void (*TOTP_NEW_CODE_GENERATED_HANDLER)(bool time_left, void* context); typedef void (*TOTP_CODE_LIFETIME_CHANGED_HANDLER)(float code_lifetime_percent, void* context); -typedef struct { - char* code_buffer; - FuriThread* thread; - FuriMutex* code_buffer_sync; - TokenInfo** token_info; - float timezone_offset; - uint8_t* iv; - TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler; - void* on_new_code_generated_handler_context; - TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler; - void* on_code_lifetime_changed_handler_context; -} TotpGenerateCodeWorkerContext; +typedef struct TotpGenerateCodeWorkerContext TotpGenerateCodeWorkerContext; +/** + * @brief Generate token worker events + */ enum TotGenerateCodeWorkerEvents { + + /** + * @brief Reserved, should not be used anywhere + */ TotpGenerateCodeWorkerEventReserved = 0b00, + + /** + * @brief Stop worker + */ TotpGenerateCodeWorkerEventStop = 0b01, + + /** + * @brief Trigger token input automation + */ TotpGenerateCodeWorkerEventForceUpdate = 0b10 }; +/** + * @brief Starts generate code worker + * @param code_buffer code buffer to generate code to + * @param token_info token info to be used to generate code + * @param code_buffer_sync code buffer synchronization primitive + * @param timezone_offset timezone offset to be used to generate code + * @param iv initialization vector (IV) to be used to decrypt token secret + * @return worker context + */ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( char* code_buffer, - TokenInfo** token_info, + const TokenInfo* token_info, FuriMutex* code_buffer_sync, float timezone_offset, uint8_t* iv); + +/** + * @brief Stops generate code worker + * @param context worker context + */ void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context); + +/** + * @brief Notifies generate code worker with a given event + * @param context worker context + * @param event event to notify worker with + */ void totp_generate_code_worker_notify( TotpGenerateCodeWorkerContext* context, TotpGenerateCodeWorkerEvent event); + +/** + * @brief Sets new handler for "on new code generated" event + * @param context worker context + * @param on_new_code_generated_handler handler + * @param on_new_code_generated_handler_context handler context + */ void totp_generate_code_worker_set_code_generated_handler( TotpGenerateCodeWorkerContext* context, TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler, void* on_new_code_generated_handler_context); + +/** + * @brief Sets new handler for "on code lifetime changed" event + * @param context worker context + * @param on_code_lifetime_changed_handler handler + * @param on_code_lifetime_changed_handler_context handler context + */ void totp_generate_code_worker_set_lifetime_changed_handler( TotpGenerateCodeWorkerContext* context, TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler, diff --git a/applications/external/totp/workers/type_code_common.c b/applications/external/totp/workers/type_code_common.c index fa5e7290f..696df3b1f 100644 --- a/applications/external/totp/workers/type_code_common.c +++ b/applications/external/totp/workers/type_code_common.c @@ -14,7 +14,7 @@ static const uint8_t hid_number_keys[] = { HID_KEYBOARD_Z}; static uint32_t get_keystroke_delay(TokenAutomationFeature features) { - if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { + if(features & TokenAutomationFeatureTypeSlower) { return 100; } @@ -22,7 +22,7 @@ static uint32_t get_keystroke_delay(TokenAutomationFeature features) { } static uint32_t get_keypress_delay(TokenAutomationFeature features) { - if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { + if(features & TokenAutomationFeatureTypeSlower) { return 60; } @@ -64,13 +64,13 @@ void totp_type_code_worker_execute_automation( i++; } - if(features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { + if(features & TokenAutomationFeatureEnterAtTheEnd) { furi_delay_ms(get_keystroke_delay(features)); totp_type_code_worker_press_key( HID_KEYBOARD_RETURN, key_press_fn, key_release_fn, features); } - if(features & TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END) { + if(features & TokenAutomationFeatureTabAtTheEnd) { furi_delay_ms(get_keystroke_delay(features)); totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features); } diff --git a/applications/external/totp/workers/type_code_common.h b/applications/external/totp/workers/type_code_common.h index 1516928cf..db357329a 100644 --- a/applications/external/totp/workers/type_code_common.h +++ b/applications/external/totp/workers/type_code_common.h @@ -4,6 +4,14 @@ typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); +/** + * @brief Executes token input automation using given key press\release handlers + * @param key_press_fn key press handler + * @param key_release_fn key release handler + * @param code_buffer code buffer to be typed + * @param code_buffer_size code buffer size + * @param features automation features + */ void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.c b/applications/external/totp/workers/usb_type_code/usb_type_code.c index 10034907d..a391bdf82 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.c +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.c @@ -1,9 +1,22 @@ #include "usb_type_code.h" +#include #include +#include +#include +#include #include "../../services/convert/convert.h" #include "../../types/token_info.h" #include "../type_code_common.h" +struct TotpUsbTypeCodeWorkerContext { + char* code_buffer; + uint8_t code_buffer_size; + uint8_t flags; + FuriThread* thread; + FuriMutex* code_buffer_sync; + FuriHalUsbInterface* usb_mode_prev; +}; + static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) { if(context->usb_mode_prev != NULL) { furi_hal_usb_set_config(context->usb_mode_prev, NULL); diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.h b/applications/external/totp/workers/usb_type_code/usb_type_code.h index 21213f4d4..0a700e7fe 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.h +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.h @@ -1,35 +1,59 @@ #pragma once -#include -#include +#include +#include #include -#include -#include -#include typedef uint8_t TotpUsbTypeCodeWorkerEvent; -typedef struct { - char* code_buffer; - uint8_t code_buffer_size; - uint8_t flags; - FuriThread* thread; - FuriMutex* code_buffer_sync; - FuriHalUsbInterface* usb_mode_prev; -} TotpUsbTypeCodeWorkerContext; +typedef struct TotpUsbTypeCodeWorkerContext TotpUsbTypeCodeWorkerContext; +/** + * @brief USB token input automation worker events + */ enum TotpUsbTypeCodeWorkerEvents { + + /** + * @brief Reserved, should not be used anywhere + */ TotpUsbTypeCodeWorkerEventReserved = 0b00, + + /** + * @brief Stop worker + */ TotpUsbTypeCodeWorkerEventStop = 0b01, + + /** + * @brief Trigger token input automation + */ TotpUsbTypeCodeWorkerEventType = 0b10 }; +/** + * @brief Starts USB token input automation worker + * @param code_buffer code buffer to be used to automate + * @param code_buffer_size code buffer size + * @param code_buffer_sync code buffer synchronization primitive + * @return worker context + */ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( char* code_buffer, uint8_t code_buffer_size, FuriMutex* code_buffer_sync); + +/** + * @brief Stops USB token input automation worker + * @param context worker context + */ void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context); + +/** + * @brief Notifies USB token input automation worker with a given event + * @param context worker context + * @param event event to notify worker with + * @param flags event flags + */ void totp_usb_type_code_worker_notify( TotpUsbTypeCodeWorkerContext* context, TotpUsbTypeCodeWorkerEvent event, - uint8_t flags); \ No newline at end of file + uint8_t flags); From 10c4cd0f419993cd6d8551fb4c8c89d42448fbee Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 00:13:28 +0300 Subject: [PATCH 14/30] Update WifI marauder app https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/11 --- .../assets/DolphinCommon_56x48.png | Bin 0 -> 1416 bytes .../file/sequential_file.c | 46 + .../file/sequential_file.h | 15 + .../scenes/wifi_marauder_scene_config.h | 9 + .../wifi_marauder_scene_console_output.c | 106 +- ...ifi_marauder_scene_script_confirm_delete.c | 83 + .../scenes/wifi_marauder_scene_script_edit.c | 125 + .../wifi_marauder_scene_script_edit_list.c | 188 ++ .../wifi_marauder_scene_script_options.c | 111 + .../wifi_marauder_scene_script_select.c | 90 + .../wifi_marauder_scene_script_settings.c | 87 + .../wifi_marauder_scene_script_stage_add.c | 297 ++ .../wifi_marauder_scene_script_stage_edit.c | 203 ++ .../scenes/wifi_marauder_scene_start.c | 26 +- .../scenes/wifi_marauder_scene_user_input.c | 155 + .../wifi_marauder_companion/script/cJSON.c | 2743 +++++++++++++++++ .../wifi_marauder_companion/script/cJSON.h | 321 ++ .../menu/wifi_marauder_script_stage_menu.c | 32 + .../menu/wifi_marauder_script_stage_menu.h | 42 + ...wifi_marauder_script_stage_menu_beaconap.c | 27 + ...fi_marauder_script_stage_menu_beaconlist.c | 59 + .../wifi_marauder_script_stage_menu_config.h | 14 + .../wifi_marauder_script_stage_menu_deauth.c | 27 + .../wifi_marauder_script_stage_menu_delay.c | 27 + .../wifi_marauder_script_stage_menu_exec.c | 30 + .../wifi_marauder_script_stage_menu_probe.c | 27 + .../wifi_marauder_script_stage_menu_scan.c | 93 + .../wifi_marauder_script_stage_menu_select.c | 95 + ...i_marauder_script_stage_menu_sniffbeacon.c | 27 + ...i_marauder_script_stage_menu_sniffdeauth.c | 27 + ...wifi_marauder_script_stage_menu_sniffesp.c | 27 + ...fi_marauder_script_stage_menu_sniffpmkid.c | 91 + ...wifi_marauder_script_stage_menu_sniffpwn.c | 27 + ...wifi_marauder_script_stage_menu_sniffraw.c | 27 + .../script/wifi_marauder_script.c | 947 ++++++ .../script/wifi_marauder_script.h | 257 ++ .../script/wifi_marauder_script_executor.c | 307 ++ .../script/wifi_marauder_script_executor.h | 6 + .../script/wifi_marauder_script_worker.c | 74 + .../script/wifi_marauder_script_worker.h | 43 + .../wifi_marauder_app.c | 13 + .../wifi_marauder_app_i.h | 41 +- .../wifi_marauder_custom_event.h | 3 +- 43 files changed, 6966 insertions(+), 29 deletions(-) create mode 100644 applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png create mode 100644 applications/external/wifi_marauder_companion/file/sequential_file.c create mode 100644 applications/external/wifi_marauder_companion/file/sequential_file.h create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c create mode 100644 applications/external/wifi_marauder_companion/script/cJSON.c create mode 100644 applications/external/wifi_marauder_companion/script/cJSON.h create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script.c create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script.h create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h diff --git a/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png b/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png new file mode 100644 index 0000000000000000000000000000000000000000..089aaed83507431993a76ca25d32fdd9664c1c84 GIT binary patch literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj literal 0 HcmV?d00001 diff --git a/applications/external/wifi_marauder_companion/file/sequential_file.c b/applications/external/wifi_marauder_companion/file/sequential_file.c new file mode 100644 index 000000000..d780deb12 --- /dev/null +++ b/applications/external/wifi_marauder_companion/file/sequential_file.c @@ -0,0 +1,46 @@ +#include "sequential_file.h" + +char* sequential_file_resolve_path( + Storage* storage, + const char* dir, + const char* prefix, + const char* extension) { + if(storage == NULL || dir == NULL || prefix == NULL || extension == NULL) { + return NULL; + } + + char file_path[256]; + int file_index = 0; + + do { + if(snprintf( + file_path, sizeof(file_path), "%s/%s_%d.%s", dir, prefix, file_index, extension) < + 0) { + return NULL; + } + file_index++; + } while(storage_file_exists(storage, file_path)); + + return strdup(file_path); +} + +bool sequential_file_open( + Storage* storage, + File* file, + const char* dir, + const char* prefix, + const char* extension) { + if(storage == NULL || file == NULL || dir == NULL || prefix == NULL || extension == NULL) { + return false; + } + + char* file_path = sequential_file_resolve_path(storage, dir, prefix, extension); + if(file_path == NULL) { + return false; + } + + bool success = storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS); + free(file_path); + + return success; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/file/sequential_file.h b/applications/external/wifi_marauder_companion/file/sequential_file.h new file mode 100644 index 000000000..4fd4794c2 --- /dev/null +++ b/applications/external/wifi_marauder_companion/file/sequential_file.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +char* sequential_file_resolve_path( + Storage* storage, + const char* dir, + const char* prefix, + const char* extension); +bool sequential_file_open( + Storage* storage, + File* file, + const char* dir, + const char* prefix, + const char* extension); \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h index 715897d17..ae976c6bf 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h @@ -3,3 +3,12 @@ ADD_SCENE(wifi_marauder, console_output, ConsoleOutput) ADD_SCENE(wifi_marauder, text_input, TextInput) ADD_SCENE(wifi_marauder, settings_init, SettingsInit) ADD_SCENE(wifi_marauder, log_viewer, LogViewer) +ADD_SCENE(wifi_marauder, user_input, UserInput) +ADD_SCENE(wifi_marauder, script_select, ScriptSelect) +ADD_SCENE(wifi_marauder, script_options, ScriptOptions) +ADD_SCENE(wifi_marauder, script_edit, ScriptEdit) +ADD_SCENE(wifi_marauder, script_settings, ScriptSettings) +ADD_SCENE(wifi_marauder, script_confirm_delete, ScriptConfirmDelete) +ADD_SCENE(wifi_marauder, script_stage_edit, ScriptStageEdit) +ADD_SCENE(wifi_marauder, script_stage_add, ScriptStageAdd) +ADD_SCENE(wifi_marauder, script_stage_edit_list, ScriptStageEditList) diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c index 0729500eb..05d94fe80 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c @@ -1,5 +1,34 @@ #include "../wifi_marauder_app_i.h" +char* _wifi_marauder_get_prefix_from_cmd(const char* command) { + int end = strcspn(command, " "); + char* prefix = (char*)malloc(sizeof(char) * (end + 1)); + strncpy(prefix, command, end); + prefix[end] = '\0'; + return prefix; +} + +bool _wifi_marauder_is_save_pcaps_enabled(WifiMarauderApp* app) { + if(!app->ok_to_save_pcaps) { + return false; + } + // If it is a script that contains a sniff function + if(app->script != NULL) { + return wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffRaw) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffBeacon) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffDeauth) || + wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffEsp) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffPmkid) || + wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffPwn); + } + // If it is a sniff function + return app->is_command && app->selected_tx_string && + strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0; +} + void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) { furi_assert(context); WifiMarauderApp* app = context; @@ -34,23 +63,29 @@ void wifi_marauder_console_output_handle_rx_packets_cb(uint8_t* buf, size_t len, void wifi_marauder_scene_console_output_on_enter(void* context) { WifiMarauderApp* app = context; + // Reset text box and set font TextBox* text_box = app->text_box; - text_box_reset(app->text_box); + text_box_reset(text_box); text_box_set_font(text_box, TextBoxFontText); + + // Set focus on start or end if(app->focus_console_start) { text_box_set_focus(text_box, TextBoxFocusStart); } else { text_box_set_focus(text_box, TextBoxFocusEnd); } + + // Set command-related messages if(app->is_command) { furi_string_reset(app->text_box_store); app->text_box_store_strlen = 0; + // Help message if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { const char* help_msg = "Marauder companion " WIFI_MARAUDER_APP_VERSION "\n"; furi_string_cat_str(app->text_box_store, help_msg); app->text_box_store_strlen += strlen(help_msg); } - + // Stopscan message if(app->show_stopscan_tip) { const char* help_msg = "Press BACK to send stopscan\n"; furi_string_cat_str(app->text_box_store, help_msg); @@ -58,13 +93,14 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { } } - // Set starting text - for "View Log from end", this will just be what was already in the text box store + // Set starting text text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); + // Set scene state and switch view scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneConsoleOutput, 0); view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); - // Register callback to receive data + // Register callbacks to receive data wifi_marauder_uart_set_handle_rx_data_cb( app->uart, wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread @@ -73,25 +109,53 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread // Get ready to send command - if(app->is_command && app->selected_tx_string) { + if((app->is_command && app->selected_tx_string) || app->script) { + const char* prefix = + strlen(app->selected_tx_string) > 0 ? + _wifi_marauder_get_prefix_from_cmd(app->selected_tx_string) : // Function name + app->script->name; // Script name + // Create files *before* sending command // (it takes time to iterate through the directory) if(app->ok_to_save_logs) { - app->is_writing_log = true; - wifi_marauder_create_log_file(app); + strcpy( + app->log_file_path, + sequential_file_resolve_path( + app->storage, MARAUDER_APP_FOLDER_LOGS, prefix, "log")); + if(app->log_file_path != NULL) { + if(storage_file_open( + app->log_file, app->log_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + app->is_writing_log = true; + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot open log file"); + } + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot resolve log path"); + } } - // If it is a sniff function, open the pcap file for recording - if(app->ok_to_save_pcaps && - strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0) { - app->is_writing_pcap = true; - wifi_marauder_create_pcap_file(app); + // If it is a sniff function or script, open the pcap file for recording + if(_wifi_marauder_is_save_pcaps_enabled(app)) { + if(sequential_file_open( + app->storage, app->capture_file, MARAUDER_APP_FOLDER_PCAPS, prefix, "pcap")) { + app->is_writing_pcap = true; + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot open pcap file"); + } } // Send command with newline '\n' - wifi_marauder_uart_tx( - (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + if(app->selected_tx_string) { + wifi_marauder_uart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + } + + // Run the script if the file with the script has been opened + if(app->script != NULL) { + app->script_worker = wifi_marauder_script_worker_alloc(); + wifi_marauder_script_worker_start(app->script_worker, app->script); + } } } @@ -113,14 +177,18 @@ bool wifi_marauder_scene_console_output_on_event(void* context, SceneManagerEven void wifi_marauder_scene_console_output_on_exit(void* context) { WifiMarauderApp* app = context; + // Automatically stop the scan when exiting view + if(app->is_command) { + wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); + furi_delay_ms(50); + } + // Unregister rx callback wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL); wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL); - // Automatically stop the scan when exiting view - if(app->is_command) { - wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); - } + wifi_marauder_script_worker_free(app->script_worker); + app->script_worker = NULL; app->is_writing_pcap = false; if(app->capture_file && storage_file_is_open(app->capture_file)) { diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c new file mode 100644 index 000000000..8e436fe2b --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c @@ -0,0 +1,83 @@ +#include "../wifi_marauder_app_i.h" + +void wifi_marauder_scene_script_confirm_delete_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + WifiMarauderApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void wifi_marauder_scene_script_confirm_delete_on_enter(void* context) { + WifiMarauderApp* app = context; + + widget_add_button_element( + app->widget, + GuiButtonTypeLeft, + "No", + wifi_marauder_scene_script_confirm_delete_widget_callback, + app); + widget_add_button_element( + app->widget, + GuiButtonTypeRight, + "Yes", + wifi_marauder_scene_script_confirm_delete_widget_callback, + app); + + widget_add_string_element( + app->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Are you sure?"); + widget_add_text_box_element( + app->widget, + 0, + 12, + 128, + 38, + AlignCenter, + AlignCenter, + "The script will be\npermanently deleted", + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewWidget); +} + +bool wifi_marauder_scene_script_confirm_delete_on_event(void* context, SceneManagerEvent event) { + WifiMarauderApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + // get which button press: "Yes" or "No" + if(event.event == GuiButtonTypeRight) { + // Yes + if(app->script != NULL) { + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + app->script->name); + storage_simply_remove(app->storage, script_path); + wifi_marauder_script_free(app->script); + app->script = NULL; + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text(message, "Deleted!", 88, 32, AlignCenter, AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6); + dialog_message_set_buttons(message, NULL, "Ok", NULL); + dialog_message_show(app->dialogs, message); + dialog_message_free(message); + } + } + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + + return consumed; +} + +void wifi_marauder_scene_script_confirm_delete_on_exit(void* context) { + WifiMarauderApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c new file mode 100644 index 000000000..697daba4f --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c @@ -0,0 +1,125 @@ +#include "../wifi_marauder_app_i.h" + +static void wifi_marauder_scene_script_edit_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + WifiMarauderScriptStage* current_stage = app->script->first_stage; + uint32_t stage_index = 0; + + while(current_stage != NULL && stage_index < index) { + current_stage = current_stage->next_stage; + stage_index++; + } + app->script_edit_selected_stage = current_stage; + + if(app->script_edit_selected_stage != NULL) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEdit); + } +} + +static void wifi_marauder_scene_script_edit_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageAdd); +} + +void wifi_marauder_scene_script_edit_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + WifiMarauderScript* script = app->script; + submenu_set_header(submenu, script->name); + + WifiMarauderScriptStage* current_stage = script->first_stage; + int stage_index = 0; + while(current_stage != NULL) { + switch(current_stage->type) { + case WifiMarauderScriptStageTypeScan: + submenu_add_item( + submenu, "Scan", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSelect: + submenu_add_item( + submenu, "Select", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeDeauth: + submenu_add_item( + submenu, "Deauth", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeProbe: + submenu_add_item( + submenu, "Probe", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffRaw: + submenu_add_item( + submenu, "Sniff raw", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + submenu_add_item( + submenu, + "Sniff beacon", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + submenu_add_item( + submenu, + "Sniff deauth", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeSniffEsp: + submenu_add_item( + submenu, "Sniff esp", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + submenu_add_item( + submenu, "Sniff PMKID", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffPwn: + submenu_add_item( + submenu, "Sniff pwn", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeBeaconList: + submenu_add_item( + submenu, "Beacon list", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeBeaconAp: + submenu_add_item( + submenu, "Beacon AP", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeExec: + submenu_add_item( + submenu, + "Custom command", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeDelay: + submenu_add_item( + submenu, "Delay", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + } + current_stage = current_stage->next_stage; + stage_index++; + } + + submenu_add_item( + submenu, "[+] ADD STAGE", stage_index++, wifi_marauder_scene_script_edit_add_callback, app); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_edit_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_edit_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c new file mode 100644 index 000000000..7a3284caf --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c @@ -0,0 +1,188 @@ +#include "../wifi_marauder_app_i.h" + +static void + wifi_marauder_scene_script_stage_edit_list_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + // Creates new item + WifiMarauderScriptStageListItem* new_item = + (WifiMarauderScriptStageListItem*)malloc(sizeof(WifiMarauderScriptStageListItem)); + new_item->value = malloc(64); + new_item->next_item = NULL; + + if(app->script_stage_edit_first_item == NULL) { + app->script_stage_edit_first_item = new_item; + } else { + WifiMarauderScriptStageListItem* last_item = app->script_stage_edit_first_item; + while(last_item->next_item != NULL) { + last_item = last_item->next_item; + } + last_item->next_item = new_item; + } + + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList, index); + app->user_input_type = WifiMarauderUserInputTypeString; + app->user_input_string_reference = &new_item->value; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); +} + +static void wifi_marauder_scene_script_stage_edit_list_deallocate_items(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + app->script_stage_edit_first_item = NULL; +} + +static void wifi_marauder_scene_script_stage_edit_list_save_strings(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + int array_size = 0; + + // Calculates the required array size + while(current_item != NULL) { + array_size++; + current_item = current_item->next_item; + } + + // Reallocate the array of strings if necessary + if(*app->script_stage_edit_string_count_reference < array_size) { + *app->script_stage_edit_strings_reference = + realloc(*app->script_stage_edit_strings_reference, array_size * sizeof(char*)); + } + + // Fills the array of strings + current_item = app->script_stage_edit_first_item; + int i = 0; + while(current_item != NULL) { + char* current_str = malloc(strlen(current_item->value) + 1); + strncpy(current_str, current_item->value, strlen(current_item->value) + 1); + (*app->script_stage_edit_strings_reference)[i] = current_str; + current_item = current_item->next_item; + i++; + } + + *app->script_stage_edit_string_count_reference = array_size; +} + +static void wifi_marauder_scene_script_stage_edit_list_save_numbers(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + int array_size = 0; + + // Calculates the required array size + while(current_item != NULL) { + array_size++; + current_item = current_item->next_item; + } + + // Reallocate the array of integers if necessary + if(*app->script_stage_edit_number_count_reference < array_size) { + *app->script_stage_edit_numbers_reference = + realloc(*app->script_stage_edit_numbers_reference, array_size * sizeof(int)); + } + + // Fills the array of integers + current_item = app->script_stage_edit_first_item; + int i = 0; + while(current_item != NULL) { + (*app->script_stage_edit_numbers_reference)[i] = atoi(current_item->value); + current_item = current_item->next_item; + i++; + } + + *app->script_stage_edit_number_count_reference = array_size; +} + +static void + wifi_marauder_scene_script_stage_edit_list_save_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + if(app->script_stage_edit_strings_reference != NULL && + app->script_stage_edit_string_count_reference != NULL) { + wifi_marauder_scene_script_stage_edit_list_save_strings(app); + } + + if(app->script_stage_edit_numbers_reference != NULL && + app->script_stage_edit_number_count_reference != NULL) { + wifi_marauder_scene_script_stage_edit_list_save_numbers(app); + } + + wifi_marauder_scene_script_stage_edit_list_deallocate_items(app); + scene_manager_previous_scene(app->scene_manager); +} + +static void + wifi_marauder_scene_script_stage_edit_list_clear_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + wifi_marauder_scene_script_stage_edit_list_deallocate_items(app); + + submenu_reset(app->submenu); + submenu_add_item( + app->submenu, + "[+] ADD ITEM", + 99, + wifi_marauder_scene_script_stage_edit_list_add_callback, + app); + submenu_add_item( + app->submenu, + "[*] SAVE ITEMS", + 99, + wifi_marauder_scene_script_stage_edit_list_save_callback, + app); + submenu_add_item( + app->submenu, + "[-] CLEAR LIST", + 99, + wifi_marauder_scene_script_stage_edit_list_clear_callback, + app); +} + +void wifi_marauder_scene_script_stage_edit_list_on_enter(void* context) { + WifiMarauderApp* app = context; + int item_index = 0; + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + + while(current_item != NULL) { + submenu_add_item(app->submenu, current_item->value, item_index++, NULL, app); + current_item = current_item->next_item; + } + submenu_add_item( + app->submenu, + "[+] ADD ITEM", + 99, + wifi_marauder_scene_script_stage_edit_list_add_callback, + app); + submenu_add_item( + app->submenu, + "[*] SAVE ITEMS", + 99, + wifi_marauder_scene_script_stage_edit_list_save_callback, + app); + submenu_add_item( + app->submenu, + "[-] CLEAR LIST", + 99, + wifi_marauder_scene_script_stage_edit_list_clear_callback, + app); + + submenu_set_selected_item( + app->submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_stage_edit_list_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_edit_list_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c new file mode 100644 index 000000000..35b374f61 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c @@ -0,0 +1,111 @@ +#include "../wifi_marauder_app_i.h" + +enum SubmenuIndex { + SubmenuIndexRun, + SubmenuIndexSettings, + SubmenuIndexEditStages, + SubmenuIndexSave, + SubmenuIndexDelete +}; + +void wifi_marauder_scene_script_options_save_script(WifiMarauderApp* app) { + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + app->script->name); + wifi_marauder_script_save_json(app->storage, script_path, app->script); + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text(message, "Saved!", 88, 32, AlignCenter, AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6); + dialog_message_set_buttons(message, NULL, "Ok", NULL); + dialog_message_show(app->dialogs, message); + dialog_message_free(message); +} + +static void wifi_marauder_scene_script_options_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + switch(index) { + case SubmenuIndexRun: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexSettings: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSettings); + break; + case SubmenuIndexEditStages: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptEdit); + break; + case SubmenuIndexSave: + wifi_marauder_scene_script_options_save_script(app); + break; + case SubmenuIndexDelete: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptConfirmDelete); + break; + } +} + +void wifi_marauder_scene_script_options_on_enter(void* context) { + WifiMarauderApp* app = context; + + // If returning after confirming script deletion + if(app->script == NULL) { + scene_manager_previous_scene(app->scene_manager); + return; + } + + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, app->script->name); + submenu_add_item( + submenu, "[>] RUN", SubmenuIndexRun, wifi_marauder_scene_script_options_callback, app); + submenu_add_item( + submenu, + "[S] SETTINGS", + SubmenuIndexSettings, + wifi_marauder_scene_script_options_callback, + app); + submenu_add_item( + submenu, + "[+] EDIT STAGES", + SubmenuIndexEditStages, + wifi_marauder_scene_script_options_callback, + app); + submenu_add_item( + submenu, "[*] SAVE", SubmenuIndexSave, wifi_marauder_scene_script_options_callback, app); + submenu_add_item( + submenu, + "[X] DELETE", + SubmenuIndexDelete, + wifi_marauder_scene_script_options_callback, + app); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_options_on_event(void* context, SceneManagerEvent event) { + WifiMarauderApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + wifi_marauder_script_free(app->script); + app->script = NULL; + } + + return consumed; +} + +void wifi_marauder_scene_script_options_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c new file mode 100644 index 000000000..bc0746858 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c @@ -0,0 +1,90 @@ +#include "../wifi_marauder_app_i.h" + +static void wifi_marauder_scene_script_select_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + furi_string_get_cstr(app->script_list[index])); + + app->script = wifi_marauder_script_parse_json(app->storage, script_path); + if(app->script) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptOptions); + } +} + +static void wifi_marauder_scene_script_select_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index); + + app->user_input_type = WifiMarauderUserInputTypeFileName; + app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER_SCRIPTS); + app->user_input_file_extension = strdup("json"); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); +} + +void wifi_marauder_scene_script_select_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + + File* dir_scripts = storage_file_alloc(app->storage); + if(storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS)) { + FileInfo file_info; + char file_path[255]; + app->script_list_count = 0; + // Goes through the files in the folder counting the ones that end with the json extension + while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + app->script_list_count++; + } + if(app->script_list_count > 0) { + submenu_set_header(submenu, "Select a script:"); + app->script_list = malloc(app->script_list_count * sizeof(FuriString*)); + storage_dir_close(dir_scripts); + storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS); + // Read the files again from the beginning, adding the scripts in the list + int script_index = 0; + while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + app->script_list[script_index] = furi_string_alloc(); + path_extract_filename_no_ext(file_path, app->script_list[script_index]); + submenu_add_item( + submenu, + furi_string_get_cstr(app->script_list[script_index]), + script_index, + wifi_marauder_scene_script_select_callback, + app); + script_index++; + } + } else { + submenu_set_header(submenu, "No script found"); + } + submenu_add_item( + submenu, "[+] ADD SCRIPT", 99, wifi_marauder_scene_script_select_add_callback, app); + storage_dir_close(dir_scripts); + } + storage_file_free(dir_scripts); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_select_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); + + for(int i = 0; i < app->script_list_count; i++) { + furi_string_free(app->script_list[i]); + } + free(app->script_list); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c new file mode 100644 index 000000000..b4903af05 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c @@ -0,0 +1,87 @@ +#include "../wifi_marauder_app_i.h" + +enum ScriptSettingsOption { + ScriptSettingsOptionRepeat, + ScriptSettingsOptionSavePcap, + ScriptSettingsOptionEnableLed +}; + +const char* option_values[3] = {"No", "Yes", "Default"}; + +static void wifi_marauder_scene_script_settings_enter_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + // Accept script repeat value + if(index == ScriptSettingsOptionRepeat) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings, index); + app->user_input_type = WifiMarauderUserInputTypeNumber; + app->user_input_number_reference = &app->script->repeat; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } +} + +static void wifi_marauder_scene_script_settings_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + uint8_t current_option = variable_item_list_get_selected_item_index(app->var_item_list); + uint8_t option_value_index = variable_item_get_current_value_index(item); + + switch(current_option) { + case ScriptSettingsOptionSavePcap: + variable_item_set_current_value_text(item, option_values[option_value_index]); + app->script->save_pcap = option_value_index; + break; + case ScriptSettingsOptionEnableLed: + variable_item_set_current_value_text(item, option_values[option_value_index]); + app->script->enable_led = option_value_index; + break; + } +} + +void wifi_marauder_scene_script_settings_on_enter(void* context) { + WifiMarauderApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + variable_item_list_set_enter_callback( + app->var_item_list, wifi_marauder_scene_script_settings_enter_callback, app); + + // Script repeat option + VariableItem* repeat_item = variable_item_list_add(app->var_item_list, "Repeat", 1, NULL, app); + char repeat_str[32]; + snprintf(repeat_str, sizeof(repeat_str), "%d", app->script->repeat); + variable_item_set_current_value_text(repeat_item, repeat_str); + + // Save PCAP option + VariableItem* save_pcap_item = variable_item_list_add( + app->var_item_list, + "Save PCAP", + 3, + wifi_marauder_scene_script_settings_change_callback, + app); + variable_item_set_current_value_index(save_pcap_item, app->script->save_pcap); + variable_item_set_current_value_text(save_pcap_item, option_values[app->script->save_pcap]); + + // Enable board LED option + VariableItem* enable_led_item = variable_item_list_add( + app->var_item_list, + "Enable LED", + 3, + wifi_marauder_scene_script_settings_change_callback, + app); + variable_item_set_current_value_index(enable_led_item, app->script->enable_led); + variable_item_set_current_value_text(enable_led_item, option_values[app->script->enable_led]); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList); +} + +bool wifi_marauder_scene_script_settings_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_settings_on_exit(void* context) { + WifiMarauderApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c new file mode 100644 index 000000000..33f1a2f03 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c @@ -0,0 +1,297 @@ +#include "../wifi_marauder_app_i.h" + +// Scan +static void wifi_marauder_scene_script_stage_add_scan_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageScan* stage = + (WifiMarauderScriptStageScan*)malloc(sizeof(WifiMarauderScriptStageScan)); + stage->type = WifiMarauderScriptScanTypeAp; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeScan, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Select +static void wifi_marauder_scene_script_stage_add_select_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSelect* stage = + (WifiMarauderScriptStageSelect*)malloc(sizeof(WifiMarauderScriptStageSelect)); + stage->type = WifiMarauderScriptSelectTypeAp; + stage->filter = strdup("all"); + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSelect, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Deauth +static void wifi_marauder_scene_script_stage_add_deauth_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageDeauth* stage = + (WifiMarauderScriptStageDeauth*)malloc(sizeof(WifiMarauderScriptStageDeauth)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDeauth, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Probe +static void wifi_marauder_scene_script_stage_add_probe_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageProbe* stage = + (WifiMarauderScriptStageProbe*)malloc(sizeof(WifiMarauderScriptStageProbe)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeProbe, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff RAW +static void wifi_marauder_scene_script_stage_add_sniffraw_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffRaw* stage = + (WifiMarauderScriptStageSniffRaw*)malloc(sizeof(WifiMarauderScriptStageSniffRaw)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffRaw, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Beacon +static void + wifi_marauder_scene_script_stage_add_sniffbeacon_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffBeacon* stage = + (WifiMarauderScriptStageSniffBeacon*)malloc(sizeof(WifiMarauderScriptStageSniffBeacon)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffBeacon, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Deauth +static void + wifi_marauder_scene_script_stage_add_sniffdeauth_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffDeauth* stage = + (WifiMarauderScriptStageSniffDeauth*)malloc(sizeof(WifiMarauderScriptStageSniffDeauth)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffDeauth, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Esp +static void wifi_marauder_scene_script_stage_add_sniffesp_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffEsp* stage = + (WifiMarauderScriptStageSniffEsp*)malloc(sizeof(WifiMarauderScriptStageSniffEsp)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffEsp, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff PMKID +static void + wifi_marauder_scene_script_stage_add_sniffpmkid_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffPmkid* stage = + (WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid)); + stage->channel = 0; + stage->force_deauth = WifiMarauderScriptBooleanTrue; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPmkid, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Pwn +static void wifi_marauder_scene_script_stage_add_sniffpwn_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffPwn* stage = + (WifiMarauderScriptStageSniffPwn*)malloc(sizeof(WifiMarauderScriptStageSniffPwn)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPwn, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Beacon list +static void + wifi_marauder_scene_script_stage_add_beaconlist_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageBeaconList* stage = + (WifiMarauderScriptStageBeaconList*)malloc(sizeof(WifiMarauderScriptStageBeaconList)); + stage->ssids = NULL; + stage->ssid_count = 0; + stage->random_ssids = 0; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconList, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Beacon AP +static void wifi_marauder_scene_script_stage_add_beaconap_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageBeaconAp* stage = + (WifiMarauderScriptStageBeaconAp*)malloc(sizeof(WifiMarauderScriptStageBeaconAp)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconAp, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Exec +static void wifi_marauder_scene_script_stage_add_exec_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageExec* stage = + (WifiMarauderScriptStageExec*)malloc(sizeof(WifiMarauderScriptStageExec)); + stage->command = NULL; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeExec, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Delay +static void wifi_marauder_scene_script_stage_add_delay_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageDelay* stage = + (WifiMarauderScriptStageDelay*)malloc(sizeof(WifiMarauderScriptStageDelay)); + stage->timeout = 0; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDelay, stage); + scene_manager_previous_scene(app->scene_manager); +} + +void wifi_marauder_scene_script_stage_add_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + submenu_set_header(submenu, "Add stage"); + + int menu_index = 0; + submenu_add_item( + submenu, "[+] Scan", menu_index++, wifi_marauder_scene_script_stage_add_scan_callback, app); + submenu_add_item( + submenu, + "[+] Select", + menu_index++, + wifi_marauder_scene_script_stage_add_select_callback, + app); + submenu_add_item( + submenu, + "[+] Deauth", + menu_index++, + wifi_marauder_scene_script_stage_add_deauth_callback, + app); + submenu_add_item( + submenu, + "[+] Probe", + menu_index++, + wifi_marauder_scene_script_stage_add_probe_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff RAW", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffraw_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Beacon", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffbeacon_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Deauth", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffdeauth_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Esp", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffesp_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff PMKID", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffpmkid_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Pwnagotchi", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffpwn_callback, + app); + submenu_add_item( + submenu, + "[+] Beacon List", + menu_index++, + wifi_marauder_scene_script_stage_add_beaconlist_callback, + app); + submenu_add_item( + submenu, + "[+] Beacon AP", + menu_index++, + wifi_marauder_scene_script_stage_add_beaconap_callback, + app); + submenu_add_item( + submenu, + "[+] Custom command", + menu_index++, + wifi_marauder_scene_script_stage_add_exec_callback, + app); + submenu_add_item( + submenu, + "[+] Delay", + menu_index++, + wifi_marauder_scene_script_stage_add_delay_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_stage_add_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_add_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c new file mode 100644 index 000000000..b8581e3e7 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c @@ -0,0 +1,203 @@ +#include "../wifi_marauder_app_i.h" + +void wifi_marauder_scene_script_stage_edit_create_list_strings( + WifiMarauderApp* app, + char** strings, + int string_count) { + // Deallocates the existing list + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + + // Create a new list with numbers + WifiMarauderScriptStageListItem* first_item = NULL; + WifiMarauderScriptStageListItem* previous_item = NULL; + for(int i = 0; i < string_count; i++) { + WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem)); + item->value = strdup(strings[i]); + item->next_item = NULL; + + if(previous_item == NULL) { + first_item = item; + } else { + previous_item->next_item = item; + } + previous_item = item; + } + + app->script_stage_edit_first_item = first_item; +} + +void wifi_marauder_scene_script_stage_edit_create_list_numbers( + WifiMarauderApp* app, + int* numbers, + int number_count) { + // Deallocates the existing list + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + + // Create a new list with numbers + WifiMarauderScriptStageListItem* first_item = NULL; + WifiMarauderScriptStageListItem* previous_item = NULL; + for(int i = 0; i < number_count; i++) { + char number_str[32]; + snprintf(number_str, sizeof(number_str), "%d", numbers[i]); + + WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem)); + item->value = strdup(number_str); + item->next_item = NULL; + + if(previous_item == NULL) { + first_item = item; + } else { + previous_item->next_item = item; + } + previous_item = item; + } + + app->script_stage_edit_first_item = first_item; +} + +static void + wifi_marauder_scene_script_stage_edit_list_enter_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + const WifiMarauderScriptMenuItem* menu_item = &app->script_stage_menu->items[index]; + + // Fixed delete item + if(index == app->script_stage_menu->num_items) { + uint32_t deleted_stage_index = + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit); + if(deleted_stage_index > 0) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneScriptEdit, deleted_stage_index - 1); + } + WifiMarauderScriptStage* previous_stage = NULL; + WifiMarauderScriptStage* current_stage = app->script->first_stage; + uint32_t current_stage_index = 0; + + while(current_stage != NULL && current_stage_index < deleted_stage_index) { + previous_stage = current_stage; + current_stage = current_stage->next_stage; + current_stage_index++; + } + + // Delete the stage + if(current_stage != NULL) { + if(previous_stage != NULL) { + if(current_stage->next_stage != NULL) { + previous_stage->next_stage = current_stage->next_stage; + } else { + previous_stage->next_stage = NULL; + app->script->last_stage = previous_stage; + } + } else { + if(current_stage->next_stage != NULL) { + app->script->first_stage = current_stage->next_stage; + } else { + app->script->first_stage = NULL; + app->script->last_stage = NULL; + } + } + } + app->script_edit_selected_stage = NULL; + + scene_manager_previous_scene(app->scene_manager); + return; + } + + if(menu_item->select_callback == NULL) { + return; + } + if(menu_item->type == WifiMarauderScriptMenuItemTypeNumber) { + // Accepts user number input, assigning the value to the reference passed as a parameter + menu_item->select_callback(app); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + app->user_input_type = WifiMarauderUserInputTypeNumber; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeString) { + // Accepts user string input, assigning the value to the reference passed as a parameter + menu_item->select_callback(app); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + app->user_input_type = WifiMarauderUserInputTypeString; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeListString) { + // Accepts the strings that compose the list + menu_item->select_callback(app); + wifi_marauder_scene_script_stage_edit_create_list_strings( + app, + *app->script_stage_edit_strings_reference, + *app->script_stage_edit_string_count_reference); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeListNumber) { + // Accepts the numbers that compose the list + menu_item->select_callback(app); + wifi_marauder_scene_script_stage_edit_create_list_numbers( + app, + *app->script_stage_edit_numbers_reference, + *app->script_stage_edit_number_count_reference); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList); + } +} + +void wifi_marauder_scene_script_stage_edit_on_enter(void* context) { + WifiMarauderApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + + variable_item_list_set_enter_callback( + app->var_item_list, wifi_marauder_scene_script_stage_edit_list_enter_callback, app); + app->script_stage_menu = + wifi_marauder_script_stage_menu_create(app->script_edit_selected_stage->type); + + if(app->script_stage_menu->items != NULL) { + for(uint32_t i = 0; i < app->script_stage_menu->num_items; i++) { + WifiMarauderScriptMenuItem* stage_item = &app->script_stage_menu->items[i]; + + // Changes the list item to handle it in callbacks + VariableItem* list_item = variable_item_list_add( + app->var_item_list, + stage_item->name, + stage_item->num_options, + stage_item->change_callback, + app); + + variable_item_list_set_selected_item(app->var_item_list, i); + if(stage_item->setup_callback != NULL) { + stage_item->setup_callback(list_item); + } + if(stage_item->change_callback != NULL) { + stage_item->change_callback(list_item); + } + } + } + + variable_item_list_add(app->var_item_list, "[-] DELETE STAGE", 0, NULL, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList); +} + +bool wifi_marauder_scene_script_stage_edit_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_edit_on_exit(void* context) { + WifiMarauderApp* app = context; + wifi_marauder_script_stage_menu_free(app->script_stage_menu); + app->script_stage_menu = NULL; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c index 2b2ee3a8a..c77543fd0 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c @@ -127,6 +127,7 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { {"Update", {"ota", "sd"}, 2, {"update -w", "update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, + {"Scripts", {""}, 1, {""}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Save to flipper sdcard", // keep as last entry or change logic in callback below {""}, 1, @@ -143,13 +144,6 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin furi_assert(index < NUM_MENU_ITEMS); const WifiMarauderItem* item = &items[index]; - if(index == NUM_MENU_ITEMS - 1) { - // "Save to flipper sdcard" special case - start SettingsInit widget - view_dispatcher_send_custom_event( - app->view_dispatcher, WifiMarauderEventStartSettingsInit); - return; - } - const int selected_option_index = app->selected_option_index[index]; furi_assert(selected_option_index < item->num_options_menu); app->selected_tx_string = item->actual_commands[selected_option_index]; @@ -167,6 +161,20 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin return; } + // Select automation script + if(index == NUM_MENU_ITEMS - 2) { + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartScriptSelect); + return; + } + + if(index == NUM_MENU_ITEMS - 1) { + // "Save to flipper sdcard" special case - start SettingsInit widget + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartSettingsInit); + return; + } + bool needs_keyboard = (item->needs_keyboard == TOGGLE_ARGS) ? (selected_option_index != 0) : item->needs_keyboard; if(needs_keyboard) { @@ -242,6 +250,10 @@ bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state( app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); scene_manager_next_scene(app->scene_manager, WifiMarauderSceneLogViewer); + } else if(event.event == WifiMarauderEventStartScriptSelect) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSelect); } consumed = true; } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c new file mode 100644 index 000000000..3d5697caf --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c @@ -0,0 +1,155 @@ +#include "../wifi_marauder_app_i.h" + +bool wifi_marauder_scene_user_input_validator_number_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + for(int i = 0; text[i] != '\0'; i++) { + if(text[i] < '0' || text[i] > '9') { + furi_string_printf(error, "This is not\na valid\nnumber!"); + return false; + } + } + return true; +} + +bool wifi_marauder_scene_user_input_validator_file_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + if(strlen(text) == 0) { + furi_string_printf(error, "File name\ncannot be\nblank!"); + return false; + } + return true; +} + +void wifi_marauder_scene_user_input_ok_callback(void* context) { + WifiMarauderApp* app = context; + + File* file = NULL; + char* file_path = NULL; + + switch(app->user_input_type) { + // Writes the string value of the reference + case WifiMarauderUserInputTypeString: + if(app->user_input_string_reference != NULL) { + strncpy( + *app->user_input_string_reference, + app->text_input_store, + strlen(app->text_input_store) + 1); + app->user_input_string_reference = NULL; + } + break; + // Writes the numerical value of the reference + case WifiMarauderUserInputTypeNumber: + if(app->user_input_number_reference != NULL) { + *app->user_input_number_reference = atoi(app->text_input_store); + app->user_input_number_reference = NULL; + } + break; + // Creates a file with the name entered by the user, if it does not exist + case WifiMarauderUserInputTypeFileName: + file = storage_file_alloc(app->storage); + // Use application directory if not specified + if(app->user_input_file_dir == NULL) { + app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER); + } + if(app->user_input_file_extension != NULL) { + size_t file_path_len = strlen(app->user_input_file_dir) + + strlen(app->text_input_store) + + strlen(app->user_input_file_extension) + 3; + file_path = (char*)malloc(file_path_len); + snprintf( + file_path, + file_path_len, + "%s/%s.%s", + app->user_input_file_dir, + app->text_input_store, + app->user_input_file_extension); + } else { + size_t file_path_len = + strlen(app->user_input_file_dir) + strlen(app->text_input_store) + 2; + file_path = (char*)malloc(file_path_len); + snprintf( + file_path, file_path_len, "%s/%s", app->user_input_file_dir, app->text_input_store); + } + if(storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_NEW)) { + storage_file_close(file); + } + // Free memory + free(app->user_input_file_dir); + app->user_input_file_dir = NULL; + free(app->user_input_file_extension); + app->user_input_file_extension = NULL; + free(file_path); + storage_file_free(file); + break; + default: + break; + } + + scene_manager_previous_scene(app->scene_manager); +} + +void wifi_marauder_scene_user_input_on_enter(void* context) { + WifiMarauderApp* app = context; + + switch(app->user_input_type) { + // Loads the string value of the reference + case WifiMarauderUserInputTypeString: + wifi_text_input_set_header_text(app->text_input, "Enter value:"); + wifi_text_input_set_validator(app->text_input, NULL, app); + if(app->user_input_string_reference != NULL) { + strncpy( + app->text_input_store, + *app->user_input_string_reference, + strlen(*app->user_input_string_reference) + 1); + } + break; + // Loads the numerical value of the reference + case WifiMarauderUserInputTypeNumber: + wifi_text_input_set_header_text(app->text_input, "Enter a valid number:"); + wifi_text_input_set_validator( + app->text_input, wifi_marauder_scene_user_input_validator_number_callback, app); + if(app->user_input_number_reference != NULL) { + char number_str[32]; + snprintf(number_str, sizeof(number_str), "%d", *app->user_input_number_reference); + strncpy(app->text_input_store, number_str, strlen(number_str) + 1); + } + break; + // File name + case WifiMarauderUserInputTypeFileName: + wifi_text_input_set_header_text(app->text_input, "Enter file name:"); + wifi_text_input_set_validator( + app->text_input, wifi_marauder_scene_user_input_validator_file_callback, app); + break; + default: + scene_manager_previous_scene(app->scene_manager); + return; + } + + wifi_text_input_set_result_callback( + app->text_input, + wifi_marauder_scene_user_input_ok_callback, + app, + app->text_input_store, + WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE, + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewTextInput); +} + +bool wifi_marauder_scene_user_input_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_user_input_on_exit(void* context) { + WifiMarauderApp* app = context; + memset(app->text_input_store, 0, sizeof(app->text_input_store)); + wifi_text_input_reset(app->text_input); +} diff --git a/applications/external/wifi_marauder_companion/script/cJSON.c b/applications/external/wifi_marauder_companion/script/cJSON.c new file mode 100644 index 000000000..06341fe38 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/cJSON.c @@ -0,0 +1,2743 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning(push) +/* disable warning about single line comments in system headers */ +#pragma warning(disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0 / 0.0 +#endif +#endif + +typedef struct { + const unsigned char* json; + size_t position; +} error; +static error global_error = {NULL, 0}; + +CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void) { + return (const char*)(global_error.json + global_error.position); +} + +CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item) { + if(!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item) { + if(!cJSON_IsNumber(item)) { + return (double)NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if(CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) +#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) { + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char* string1, const unsigned char* string2) { + if((string1 == NULL) || (string2 == NULL)) { + return 1; + } + + if(string1 == string2) { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { + if(*string1 == '\0') { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks { + void*(CJSON_CDECL* allocate)(size_t size); + void(CJSON_CDECL* deallocate)(void* pointer); + void*(CJSON_CDECL* reallocate)(void* pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void* CJSON_CDECL internal_malloc(size_t size) { + return malloc(size); +} +static void CJSON_CDECL internal_free(void* pointer) { + free(pointer); +} +static void* CJSON_CDECL internal_realloc(void* pointer, size_t size) { + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc}; + +static unsigned char* + cJSON_strdup(const unsigned char* string, const internal_hooks* const hooks) { + size_t length = 0; + unsigned char* copy = NULL; + + if(string == NULL) { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if(copy == NULL) { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { + if(hooks == NULL) { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if(hooks->malloc_fn != NULL) { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if(hooks->free_fn != NULL) { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON* cJSON_New_Item(const internal_hooks* const hooks) { + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if(node) { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON* item) { + cJSON* next = NULL; + while(item != NULL) { + next = item->next; + if(!(item->type & cJSON_IsReference) && (item->child != NULL)) { + cJSON_Delete(item->child); + } + if(!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { + global_hooks.deallocate(item->valuestring); + } + if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) { +#ifdef ENABLE_LOCALES + struct lconv* lconv = localeconv(); + return (unsigned char)lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct { + const unsigned char* content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) \ + ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) \ + ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Converts an array of characters to double. Alternative implementation of strtod() */ +double string_to_double(const char* str, char** endptr) { + double result = 0.0; + int sign = 1; + const char* p = str; + + while(isspace((unsigned char)*p)) p++; + + if(*p == '-') { + sign = -1; + p++; + } else if(*p == '+') { + p++; + } + + while(isdigit((unsigned char)*p)) { + result = result * (double)(10) + ((double)(*p - '0')); + p++; + } + + if(*p == '.') { + double fraction = 0.1; + p++; + + while(isdigit((unsigned char)p[0])) { + fraction *= 0.1L; + result += (p++[0] - '0') * fraction; + } + } + + if(*p == 'e' || *p == 'E') { + int exponent = 0; + int exp_sign = 1; + p++; + + if(*p == '-') { + exp_sign = -1; + p++; + } else if(*p == '+') { + p++; + } + + while(isdigit((unsigned char)*p)) { + exponent = exponent * 10 + (*p - '0'); + p++; + } + + exponent *= exp_sign; + result *= pow(10, exponent); + } + + *endptr = (char*)p; + + return sign * result; +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON* const item, parse_buffer* const input_buffer) { + double number = 0; + unsigned char* after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for string_to_double) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for(i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { + switch(buffer_at_offset(input_buffer)[i]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = string_to_double((const char*)number_c_string, (char**)&after_end); + if(number_c_string == after_end) { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if(number >= INT_MAX) { + item->valueint = INT_MAX; + } else if(number <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number) { + if(number >= INT_MAX) { + object->valueint = INT_MAX; + } else if(number <= (double)INT_MIN) { + object->valueint = INT_MIN; + } else { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring) { + char* copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if(!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { + return NULL; + } + if(strlen(valuestring) <= strlen(object->valuestring)) { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*)cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if(copy == NULL) { + return NULL; + } + if(object->valuestring != NULL) { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct { + unsigned char* buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer* const p, size_t needed) { + unsigned char* newbuffer = NULL; + size_t newsize = 0; + + if((p == NULL) || (p->buffer == NULL)) { + return NULL; + } + + if((p->length > 0) && (p->offset >= p->length)) { + /* make sure that offset is valid */ + return NULL; + } + + if(needed > INT_MAX) { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if(needed <= p->length) { + return p->buffer + p->offset; + } + + if(p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if(needed > (INT_MAX / 2)) { + /* overflow of int, use INT_MAX if possible */ + if(needed <= INT_MAX) { + newsize = INT_MAX; + } else { + return NULL; + } + } else { + newsize = needed * 2; + } + + if(p->hooks.reallocate != NULL) { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if(newbuffer == NULL) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } else { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if(!newbuffer) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer* const buffer) { + const unsigned char* buffer_pointer = NULL; + if((buffer == NULL) || (buffer->buffer == NULL)) { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) { + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if(output_buffer == NULL) { + return false; + } + + /* This checks for NaN and Infinity */ + if(isnan(d) || isinf(d)) { + length = snprintf((char*)number_buffer, sizeof(number_buffer), "null"); + } else { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.15g", d); + + /* Check whether the original double can be recovered */ + if((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { + /* If not, print with 17 decimal places of precision */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if(output_pointer == NULL) { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for(i = 0; i < ((size_t)length); i++) { + if(number_buffer[i] == decimal_point) { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char* const input) { + unsigned int h = 0; + size_t i = 0; + + for(i = 0; i < 4; i++) { + /* parse digit */ + if((input[i] >= '0') && (input[i] <= '9')) { + h += (unsigned int)input[i] - '0'; + } else if((input[i] >= 'A') && (input[i] <= 'F')) { + h += (unsigned int)10 + input[i] - 'A'; + } else if((input[i] >= 'a') && (input[i] <= 'f')) { + h += (unsigned int)10 + input[i] - 'a'; + } else /* invalid */ + { + return 0; + } + + if(i < 3) { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8( + const unsigned char* const input_pointer, + const unsigned char* const input_end, + unsigned char** output_pointer) { + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char* first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if((input_end - first_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if(((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { + goto fail; + } + + /* UTF16 surrogate pair */ + if((first_code >= 0xD800) && (first_code <= 0xDBFF)) { + const unsigned char* second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if((input_end - second_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + if((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if((second_code < 0xDC00) || (second_code > 0xDFFF)) { + /* invalid second half of the surrogate pair */ + goto fail; + } + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } else { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if(codepoint < 0x80) { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } else if(codepoint < 0x800) { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } else if(codepoint < 0x10000) { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } else if(codepoint <= 0x10FFFF) { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } else { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for(utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if(utf8_length > 1) { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } else { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON* const item, parse_buffer* const input_buffer) { + const unsigned char* input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char* input_end = buffer_at_offset(input_buffer) + 1; + unsigned char* output_pointer = NULL; + unsigned char* output = NULL; + + /* not a string */ + if(buffer_at_offset(input_buffer)[0] != '\"') { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while(((size_t)(input_end - input_buffer->content) < input_buffer->length) && + (*input_end != '\"')) { + /* is escape sequence */ + if(input_end[0] == '\\') { + if((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if(((size_t)(input_end - input_buffer->content) >= input_buffer->length) || + (*input_end != '\"')) { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if(output == NULL) { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while(input_pointer < input_end) { + if(*input_pointer != '\\') { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else { + unsigned char sequence_length = 2; + if((input_end - input_pointer) < 1) { + goto fail; + } + + switch(input_pointer[1]) { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if(sequence_length == 0) { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t)(input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if(output != NULL) { + input_buffer->hooks.deallocate(output); + } + + if(input_pointer != NULL) { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool + print_string_ptr(const unsigned char* const input, printbuffer* const output_buffer) { + const unsigned char* input_pointer = NULL; + unsigned char* output = NULL; + unsigned char* output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if(output_buffer == NULL) { + return false; + } + + /* empty string */ + if(input == NULL) { + output = ensure(output_buffer, sizeof("\"\"")); + if(output == NULL) { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for(input_pointer = input; *input_pointer; input_pointer++) { + switch(*input_pointer) { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if(*input_pointer < 32) { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if(output == NULL) { + return false; + } + + /* no characters have to be escaped */ + if(escape_characters == 0) { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for(input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { + if((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { + /* normal character, copy */ + *output_pointer = *input_pointer; + } else { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch(*input_pointer) { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + snprintf((char*)output_pointer, 6, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON* const item, printbuffer* const p) { + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer); +static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer); +static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer* buffer_skip_whitespace(parse_buffer* const buffer) { + if((buffer == NULL) || (buffer->content == NULL)) { + return NULL; + } + + if(cannot_access_at_index(buffer, 0)) { + return buffer; + } + + while(can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { + buffer->offset++; + } + + if(buffer->offset == buffer->length) { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer* skip_utf8_bom(parse_buffer* const buffer) { + if((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { + return NULL; + } + + if(can_access_at_index(buffer, 4) && + (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithOpts( + const char* value, + const char** return_parse_end, + cJSON_bool require_null_terminated) { + size_t buffer_length; + + if(NULL == value) { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts( + value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithLengthOpts( + const char* value, + size_t buffer_length, + const char** return_parse_end, + cJSON_bool require_null_terminated) { + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; + cJSON* item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if(value == NULL || 0 == buffer_length) { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if(item == NULL) /* memory fail */ + { + goto fail; + } + + if(!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if(require_null_terminated) { + buffer_skip_whitespace(&buffer); + if((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { + goto fail; + } + } + if(return_parse_end) { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if(item != NULL) { + cJSON_Delete(item); + } + + if(value != NULL) { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if(buffer.offset < buffer.length) { + local_error.position = buffer.offset; + } else if(buffer.length > 0) { + local_error.position = buffer.length - 1; + } + + if(return_parse_end != NULL) { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value) { + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length) { + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char* + print(const cJSON* const item, cJSON_bool format, const internal_hooks* const hooks) { + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char* printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*)hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if(buffer->buffer == NULL) { + goto fail; + } + + /* print the value */ + if(!print_value(item, buffer)) { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if(hooks->reallocate != NULL) { + printed = (unsigned char*)hooks->reallocate(buffer->buffer, buffer->offset + 1); + if(printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*)hooks->allocate(buffer->offset + 1); + if(printed == NULL) { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if(buffer->buffer != NULL) { + hooks->deallocate(buffer->buffer); + } + + if(printed != NULL) { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item) { + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item) { + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if(prebuffer < 0) { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if(!p.buffer) { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if(!print_value(item, &p)) { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if((length < 0) || (buffer == NULL)) { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer) { + if((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if(can_read(input_buffer, 4) && + (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if(can_read(input_buffer, 5) && + (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if(can_read(input_buffer, 4) && + (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { + return parse_string(item, input_buffer); + } + /* number */ + if(can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || + ((buffer_at_offset(input_buffer)[0] >= '0') && + (buffer_at_offset(input_buffer)[0] <= '9')))) { + return parse_number(item, input_buffer); + } + /* array */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { + return parse_array(item, input_buffer); + } + /* object */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output = NULL; + + if((item == NULL) || (output_buffer == NULL)) { + return false; + } + + switch((item->type) & 0xFF) { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if(output == NULL) { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if(output == NULL) { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if(output == NULL) { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: { + size_t raw_length = 0; + if(item->valuestring == NULL) { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if(output == NULL) { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer) { + cJSON* head = NULL; /* head of the linked list */ + cJSON* current_item = NULL; + + if(input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if(buffer_at_offset(input_buffer)[0] != '[') { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if(cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks)); + if(new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if(head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if(cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if(head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if(head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + size_t length = 0; + cJSON* current_element = item->child; + + if(output_buffer == NULL) { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if(output_pointer == NULL) { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while(current_element != NULL) { + if(!print_value(current_element, output_buffer)) { + return false; + } + update_offset(output_buffer); + if(current_element->next) { + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer) { + cJSON* head = NULL; /* linked list head */ + cJSON* current_item = NULL; + + if(input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if(cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks)); + if(new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if(head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_string(current_item, input_buffer)) { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if(head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if(head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + size_t length = 0; + cJSON* current_item = item->child; + + if(output_buffer == NULL) { + return false; + } + + /* Compose the output: */ + length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if(output_buffer->format) { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while(current_item) { + if(output_buffer->format) { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if(output_pointer == NULL) { + return false; + } + for(i = 0; i < output_buffer->depth; i++) { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if(!print_string_ptr((unsigned char*)current_item->string, output_buffer)) { + return false; + } + update_offset(output_buffer); + + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ':'; + if(output_buffer->format) { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if(!print_value(current_item, output_buffer)) { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + if(current_item->next) { + *output_pointer++ = ','; + } + + if(output_buffer->format) { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if(output_pointer == NULL) { + return false; + } + if(output_buffer->format) { + size_t i; + for(i = 0; i < (output_buffer->depth - 1); i++) { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array) { + cJSON* child = NULL; + size_t size = 0; + + if(array == NULL) { + return 0; + } + + child = array->child; + + while(child != NULL) { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON* array, size_t index) { + cJSON* current_child = NULL; + + if(array == NULL) { + return NULL; + } + + current_child = array->child; + while((current_child != NULL) && (index > 0)) { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index) { + if(index < 0) { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON* get_object_item( + const cJSON* const object, + const char* const name, + const cJSON_bool case_sensitive) { + cJSON* current_element = NULL; + + if((object == NULL) || (name == NULL)) { + return NULL; + } + + current_element = object->child; + if(case_sensitive) { + while((current_element != NULL) && (current_element->string != NULL) && + (strcmp(name, current_element->string) != 0)) { + current_element = current_element->next; + } + } else { + while((current_element != NULL) && + (case_insensitive_strcmp( + (const unsigned char*)name, (const unsigned char*)(current_element->string)) != + 0)) { + current_element = current_element->next; + } + } + + if((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string) { + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON*) +cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string) { + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string) { + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON* prev, cJSON* item) { + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON* create_reference(const cJSON* item, const internal_hooks* const hooks) { + cJSON* reference = NULL; + if(item == NULL) { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if(reference == NULL) { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON* array, cJSON* item) { + cJSON* child = NULL; + + if((item == NULL) || (array == NULL) || (array == item)) { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if(child == NULL) { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } else { + /* append to the end */ + if(child->prev) { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item) { + return add_item_to_array(array, item); +} + +#if defined(__clang__) || \ + (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) { + return (void*)string; +} +#if defined(__clang__) || \ + (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic pop +#endif + +static cJSON_bool add_item_to_object( + cJSON* const object, + const char* const string, + cJSON* const item, + const internal_hooks* const hooks, + const cJSON_bool constant_key) { + char* new_key = NULL; + int new_type = cJSON_Invalid; + + if((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { + return false; + } + + if(constant_key) { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } else { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if(new_key == NULL) { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item) { + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item) { + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item) { + if(array == NULL) { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item) { + if((object == NULL) || (string == NULL)) { + return false; + } + + return add_item_to_object( + object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name) { + cJSON* null = cJSON_CreateNull(); + if(add_item_to_object(object, name, null, &global_hooks, false)) { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name) { + cJSON* true_item = cJSON_CreateTrue(); + if(add_item_to_object(object, name, true_item, &global_hooks, false)) { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name) { + cJSON* false_item = cJSON_CreateFalse(); + if(add_item_to_object(object, name, false_item, &global_hooks, false)) { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean) { + cJSON* bool_item = cJSON_CreateBool(boolean); + if(add_item_to_object(object, name, bool_item, &global_hooks, false)) { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number) { + cJSON* number_item = cJSON_CreateNumber(number); + if(add_item_to_object(object, name, number_item, &global_hooks, false)) { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string) { + cJSON* string_item = cJSON_CreateString(string); + if(add_item_to_object(object, name, string_item, &global_hooks, false)) { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw) { + cJSON* raw_item = cJSON_CreateRaw(raw); + if(add_item_to_object(object, name, raw_item, &global_hooks, false)) { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name) { + cJSON* object_item = cJSON_CreateObject(); + if(add_item_to_object(object, name, object_item, &global_hooks, false)) { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name) { + cJSON* array = cJSON_CreateArray(); + if(add_item_to_object(object, name, array, &global_hooks, false)) { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item) { + if((parent == NULL) || (item == NULL)) { + return NULL; + } + + if(item != parent->child) { + /* not the first element */ + item->prev->next = item->next; + } + if(item->next != NULL) { + /* not the last element */ + item->next->prev = item->prev; + } + + if(item == parent->child) { + /* first element */ + parent->child = item->next; + } else if(item->next == NULL) { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which) { + if(which < 0) { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which) { + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string) { + cJSON* to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string) { + cJSON* to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string) { + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string) { + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON* array, int which, cJSON* newitem) { + cJSON* after_inserted = NULL; + + if(which < 0) { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if(after_inserted == NULL) { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if(after_inserted == array->child) { + array->child = newitem; + } else { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement) { + if((parent == NULL) || (replacement == NULL) || (item == NULL)) { + return false; + } + + if(replacement == item) { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if(replacement->next != NULL) { + replacement->next->prev = replacement; + } + if(parent->child == item) { + if(parent->child->prev == parent->child) { + replacement->prev = replacement; + } + parent->child = replacement; + } else { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if(replacement->prev != NULL) { + replacement->prev->next = replacement; + } + if(replacement->next == NULL) { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem) { + if(which < 0) { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object( + cJSON* object, + const char* string, + cJSON* replacement, + cJSON_bool case_sensitive) { + if((replacement == NULL) || (string == NULL)) { + return false; + } + + /* replace the name in the replacement */ + if(!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer( + object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem) { + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem) { + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if(num >= INT_MAX) { + item->valueint = INT_MAX; + } else if(num <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (strings == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateString(strings[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse) { + cJSON* newitem = NULL; + cJSON* child = NULL; + cJSON* next = NULL; + cJSON* newchild = NULL; + + /* Bail on bad ptr */ + if(!item) { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if(!newitem) { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if(item->valuestring) { + newitem->valuestring = + (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if(!newitem->valuestring) { + goto fail; + } + } + if(item->string) { + newitem->string = (item->type & cJSON_StringIsConst) ? + item->string : + (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if(!newitem->string) { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if(!recurse) { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while(child != NULL) { + newchild = cJSON_Duplicate( + child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if(!newchild) { + goto fail; + } + if(next != NULL) { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } else { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if(newitem && newitem->child) { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if(newitem != NULL) { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char** input) { + *input += static_strlen("//"); + + for(; (*input)[0] != '\0'; ++(*input)) { + if((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char** input) { + *input += static_strlen("/*"); + + for(; (*input)[0] != '\0'; ++(*input)) { + if(((*input)[0] == '*') && ((*input)[1] == '/')) { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char** input, char** output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + for(; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if(((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char* json) { + char* into = json; + + if(json == NULL) { + return; + } + + while(json[0] != '\0') { + switch(json[0]) { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if(json[1] == '/') { + skip_oneline_comment(&json); + } else if(json[1] == '*') { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive) { + if((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { + return false; + } + + /* check if type is valid */ + switch(a->type & 0xFF) { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if(a == b) { + return true; + } + + switch(a->type & 0xFF) { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if(compare_double(a->valuedouble, b->valuedouble)) { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if((a->valuestring == NULL) || (b->valuestring == NULL)) { + return false; + } + if(strcmp(a->valuestring, b->valuestring) == 0) { + return true; + } + + return false; + + case cJSON_Array: { + cJSON* a_element = a->child; + cJSON* b_element = b->child; + + for(; (a_element != NULL) && (b_element != NULL);) { + if(!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if(a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: { + cJSON* a_element = NULL; + cJSON* b_element = NULL; + cJSON_ArrayForEach(a_element, a) { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if(b_element == NULL) { + return false; + } + + if(!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) { + a_element = get_object_item(a, b_element->string, case_sensitive); + if(a_element == NULL) { + return false; + } + + if(!cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void*) cJSON_malloc(size_t size) { + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void* object) { + global_hooks.deallocate(object); +} diff --git a/applications/external/wifi_marauder_companion/script/cJSON.h b/applications/external/wifi_marauder_companion/script/cJSON.h new file mode 100644 index 000000000..14ec83d9d --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/cJSON.h @@ -0,0 +1,321 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__WINDOWS__) && \ + (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && \ + !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if(defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && \ + defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 15 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON { + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON* next; + struct cJSON* prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON* child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char* valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char* string; +} cJSON; + +typedef struct cJSON_Hooks { + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void*(CJSON_CDECL* malloc_fn)(size_t sz); + void(CJSON_CDECL* free_fn)(void* ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value); +CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithOpts( + const char* value, + const char** return_parse_end, + cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithLengthOpts( + const char* value, + size_t buffer_length, + const char** return_parse_end, + cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON* item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string); +CJSON_PUBLIC(cJSON*) +cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string); +/* raw json */ +CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw); +CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child); +CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item); +CJSON_PUBLIC(cJSON_bool) +cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_InsertItemInArray( + cJSON* array, + int which, + cJSON* newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem); +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem); +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char* json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) +cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) +cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number); +CJSON_PUBLIC(cJSON*) +cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string); +CJSON_PUBLIC(cJSON*) +cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) \ + ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number); +#define cJSON_SetNumberValue(object, number) \ + ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) \ + for(element = (array != NULL) ? (array)->child : NULL; element != NULL; \ + element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void*) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void* object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c new file mode 100644 index 000000000..6fe853eb6 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c @@ -0,0 +1,32 @@ +#include "wifi_marauder_script_stage_menu.h" + +WifiMarauderScriptStageMenu* + wifi_marauder_script_stage_menu_create(WifiMarauderScriptStageType stage_type) { + WifiMarauderScriptStageMenu* script_stage_menu = malloc(sizeof(WifiMarauderScriptStageMenu)); + + switch(stage_type) { +#define ADD_STAGE(name, id) \ + case WifiMarauderScriptStageType##id: \ + wifi_marauder_script_stage_menu_##name##_load(script_stage_menu); \ + break; + +#include "wifi_marauder_script_stage_menu_config.h" +#undef ADD_STAGE + } + return script_stage_menu; +} + +void wifi_marauder_script_stage_menu_free(WifiMarauderScriptStageMenu* stage_menu) { + if(stage_menu == NULL) { + return; + } + for(uint32_t i = 0; i < stage_menu->num_items; i++) { + WifiMarauderScriptMenuItem* item = &(stage_menu->items[i]); + for(int j = 0; j < item->num_options; j++) { + free(item->options[j]); + } + free(item->name); + } + free(stage_menu->items); + free(stage_menu); +} diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h new file mode 100644 index 000000000..f5186526c --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include "../wifi_marauder_script.h" + +#define ITEM_EDIT_MAX_OPTIONS (12) + +typedef void (*VariableItemSetupCallback)(VariableItem* item); +typedef void (*VariableItemSelectCallback)(void* context); + +typedef enum WifiMarauderScriptMenuItemType { + WifiMarauderScriptMenuItemTypeString, + WifiMarauderScriptMenuItemTypeNumber, + WifiMarauderScriptMenuItemTypeOptionsString, + WifiMarauderScriptMenuItemTypeOptionsNumber, + WifiMarauderScriptMenuItemTypeListString, + WifiMarauderScriptMenuItemTypeListNumber +} WifiMarauderScriptMenuItemType; + +typedef struct WifiMarauderScriptMenuItem { + char* name; + WifiMarauderScriptMenuItemType type; + int num_options; + char* options[ITEM_EDIT_MAX_OPTIONS]; + VariableItemSetupCallback setup_callback; + VariableItemChangeCallback change_callback; + VariableItemSelectCallback select_callback; +} WifiMarauderScriptMenuItem; + +typedef struct WifiMarauderScriptStageMenu { + WifiMarauderScriptMenuItem* items; + uint32_t num_items; +} WifiMarauderScriptStageMenu; + +#define ADD_STAGE(name, id) \ + void wifi_marauder_script_stage_menu_##name##_load(WifiMarauderScriptStageMenu*); +#include "wifi_marauder_script_stage_menu_config.h" +#undef ADD_STAGE + +WifiMarauderScriptStageMenu* + wifi_marauder_script_stage_menu_create(WifiMarauderScriptStageType stage_type); +void wifi_marauder_script_stage_menu_free(WifiMarauderScriptStageMenu* list); diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c new file mode 100644 index 000000000..35a74ee3d --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_beaconap_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconAp* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_beaconap_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconAp* stage_beaconap = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconap->timeout; +} + +void wifi_marauder_script_stage_menu_beaconap_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = "Timeout", + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconap_stage_timeout_setup_callback, + .select_callback = wifi_marauder_beaconap_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c new file mode 100644 index 000000000..6f320db3e --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c @@ -0,0 +1,59 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_beaconlist_stage_ssids_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->script_stage_edit_strings_reference = &stage_beaconlist->ssids; + app->script_stage_edit_string_count_reference = &stage_beaconlist->ssid_count; +} + +void wifi_marauder_beaconlist_stage_random_ssids_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage; + char random_ssids_str[32]; + snprintf(random_ssids_str, sizeof(random_ssids_str), "%d", stage->random_ssids); + variable_item_set_current_value_text(item, random_ssids_str); +} + +void wifi_marauder_beaconlist_stage_random_ssids_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconlist->random_ssids; +} + +void wifi_marauder_beaconlist_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_beaconlist_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconlist->timeout; +} + +void wifi_marauder_script_stage_menu_beaconlist_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("SSIDs"), + .type = WifiMarauderScriptMenuItemTypeListString, + .num_options = 1, + .select_callback = wifi_marauder_beaconlist_stage_ssids_select_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Generate random"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconlist_stage_random_ssids_setup_callback, + .select_callback = wifi_marauder_beaconlist_stage_random_ssids_select_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconlist_stage_timeout_setup_callback, + .select_callback = wifi_marauder_beaconlist_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h new file mode 100644 index 000000000..1fd2a314b --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h @@ -0,0 +1,14 @@ +ADD_STAGE(scan, Scan) +ADD_STAGE(select, Select) +ADD_STAGE(deauth, Deauth) +ADD_STAGE(probe, Probe) +ADD_STAGE(sniffraw, SniffRaw) +ADD_STAGE(sniffbeacon, SniffBeacon) +ADD_STAGE(sniffdeauth, SniffDeauth) +ADD_STAGE(sniffesp, SniffEsp) +ADD_STAGE(sniffpmkid, SniffPmkid) +ADD_STAGE(sniffpwn, SniffPwn) +ADD_STAGE(beaconlist, BeaconList) +ADD_STAGE(beaconap, BeaconAp) +ADD_STAGE(exec, Exec) +ADD_STAGE(delay, Delay) \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c new file mode 100644 index 000000000..b15b6f461 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_deauth_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageDeauth* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_deauth_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageDeauth* stage_deauth = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_deauth->timeout; +} + +void wifi_marauder_script_stage_menu_deauth_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_deauth_stage_timeout_setup_callback, + .select_callback = wifi_marauder_deauth_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c new file mode 100644 index 000000000..ffd74f720 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_delay_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageDelay* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_delay_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageDelay* stage_delay = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_delay->timeout; +} + +void wifi_marauder_script_stage_menu_delay_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_delay_stage_timeout_setup_callback, + .select_callback = wifi_marauder_delay_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c new file mode 100644 index 000000000..62afdc2f3 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c @@ -0,0 +1,30 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_exec_stage_filter_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageExec* stage = app->script_edit_selected_stage->stage; + if(stage->command != NULL) { + variable_item_set_current_value_text(item, stage->command); + } +} + +void wifi_marauder_exec_stage_filter_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageExec* stage_select = app->script_edit_selected_stage->stage; + if(stage_select->command == NULL) { + stage_select->command = malloc(128); + } + app->user_input_string_reference = &stage_select->command; +} + +void wifi_marauder_script_stage_menu_exec_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Command"), + .type = WifiMarauderScriptMenuItemTypeString, + .num_options = 1, + .setup_callback = wifi_marauder_exec_stage_filter_setup_callback, + .select_callback = wifi_marauder_exec_stage_filter_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c new file mode 100644 index 000000000..53fa26f47 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_probe_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageProbe* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_probe_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageProbe* stage_probe = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_probe->timeout; +} + +void wifi_marauder_script_stage_menu_probe_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_probe_stage_timeout_setup_callback, + .select_callback = wifi_marauder_probe_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c new file mode 100644 index 000000000..3aab740bb --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c @@ -0,0 +1,93 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_scan_stage_type_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->type); +} + +void wifi_marauder_scan_stage_type_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + stage->type = option_index; +} + +void wifi_marauder_scan_stage_channel_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + if(stage->channel >= 0 && stage->channel < 12) { + variable_item_set_current_value_index(item, stage->channel); + } else { + variable_item_set_current_value_index(item, 0); + } +} + +void wifi_marauder_scan_stage_channel_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + stage->channel = option_index; +} + +void wifi_marauder_scan_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_scan_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageScan* stage_scan = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_scan->timeout; +} + +void wifi_marauder_script_stage_menu_scan_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Type"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"ap", "station"}, + .setup_callback = wifi_marauder_scan_stage_type_setup_callback, + .change_callback = wifi_marauder_scan_stage_type_change_callback, + }; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Channel"), + .type = WifiMarauderScriptMenuItemTypeOptionsNumber, + .num_options = 12, + .options = {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + .setup_callback = wifi_marauder_scan_stage_channel_setup_callback, + .change_callback = wifi_marauder_scan_stage_channel_change_callback, + }; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_scan_stage_timeout_setup_callback, + .select_callback = wifi_marauder_scan_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c new file mode 100644 index 000000000..a6121db95 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c @@ -0,0 +1,95 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_select_stage_type_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->type); +} + +void wifi_marauder_select_stage_type_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + stage->type = option_index; +} + +void wifi_marauder_select_stage_filter_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + + if(stage->filter != NULL) { + variable_item_set_current_value_index(item, 0); + variable_item_set_current_value_text(item, stage->filter); + } else { + variable_item_set_current_value_index(item, 1); + } +} + +void wifi_marauder_select_stage_filter_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + + // Clears the filter if you change the option. Flipper input box does not accept blank text + if(variable_item_get_current_value_index(item) == 1) { + stage->filter = NULL; + variable_item_set_current_value_index(item, 0); + variable_item_set_values_count(item, 1); + } + + if(stage->filter != NULL) { + variable_item_set_current_value_text(item, stage->filter); + } else { + variable_item_set_current_value_text(item, ""); + } +} + +void wifi_marauder_select_stage_filter_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage; + if(stage_select->filter == NULL) { + stage_select->filter = malloc(128); + } + app->user_input_string_reference = &stage_select->filter; +} + +void wifi_marauder_select_stage_indexes_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage; + app->script_stage_edit_numbers_reference = &stage_select->indexes; + app->script_stage_edit_number_count_reference = &stage_select->index_count; +} + +void wifi_marauder_script_stage_menu_select_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Type"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"ap", "station"}, + .setup_callback = wifi_marauder_select_stage_type_setup_callback, + .change_callback = wifi_marauder_select_stage_type_change_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Filter"), + .type = WifiMarauderScriptMenuItemTypeString, + .num_options = 2, + .setup_callback = wifi_marauder_select_stage_filter_setup_callback, + .change_callback = wifi_marauder_select_stage_filter_change_callback, + .select_callback = wifi_marauder_select_stage_filter_select_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Indexes"), + .type = WifiMarauderScriptMenuItemTypeListNumber, + .num_options = 1, + .select_callback = wifi_marauder_select_stage_indexes_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c new file mode 100644 index 000000000..11e7b3297 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffbeacon_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffBeacon* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffbeacon_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffBeacon* stage_sniffbeacon = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffbeacon->timeout; +} + +void wifi_marauder_script_stage_menu_sniffbeacon_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffbeacon_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffbeacon_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c new file mode 100644 index 000000000..935a55936 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffdeauth_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffDeauth* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffdeauth_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffDeauth* stage_sniffdeauth = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffdeauth->timeout; +} + +void wifi_marauder_script_stage_menu_sniffdeauth_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffdeauth_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffdeauth_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c new file mode 100644 index 000000000..e90d6b06c --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffesp_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffEsp* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffesp_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffEsp* stage_sniffesp = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffesp->timeout; +} + +void wifi_marauder_script_stage_menu_sniffesp_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffesp_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffesp_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c new file mode 100644 index 000000000..d4f1f8f36 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c @@ -0,0 +1,91 @@ +#include "../../wifi_marauder_app_i.h" + +static void wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->force_deauth); +} + +static void wifi_marauder_sniffpmkid_stage_force_deauth_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->force_deauth = option_index; +} + +static void wifi_marauder_sniffpmkid_stage_channel_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + if(stage->channel >= 0 && stage->channel < 12) { + variable_item_set_current_value_index(item, stage->channel); + } else { + variable_item_set_current_value_index(item, 0); + } +} + +static void wifi_marauder_sniffpmkid_stage_channel_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->channel = option_index; +} + +static void wifi_marauder_sniffpmkid_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +static void wifi_marauder_sniffpmkid_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffPmkid* stage_sniffpmkid = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffpmkid->timeout; +} + +void wifi_marauder_script_stage_menu_sniffpmkid_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Force deauth"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"no", "yes"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_force_deauth_change_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Channel"), + .type = WifiMarauderScriptMenuItemTypeOptionsNumber, + .num_options = 12, + .options = {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_channel_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_channel_change_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffpmkid_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffpmkid_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c new file mode 100644 index 000000000..d0859cd8b --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffpwn_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPwn* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffpwn_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffPwn* stage_sniffpwn = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffpwn->timeout; +} + +void wifi_marauder_script_stage_menu_sniffpwn_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffpwn_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffpwn_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c new file mode 100644 index 000000000..39641f1ee --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffraw_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffRaw* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffraw_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffRaw* stage_sniffraw = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffraw->timeout; +} + +void wifi_marauder_script_stage_menu_sniffraw_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffraw_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffraw_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c new file mode 100644 index 000000000..64dfacef5 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c @@ -0,0 +1,947 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script.h" + +WifiMarauderScript* wifi_marauder_script_alloc() { + WifiMarauderScript* script = (WifiMarauderScript*)malloc(sizeof(WifiMarauderScript)); + if(script == NULL) { + return NULL; + } + script->name = NULL; + script->description = NULL; + script->first_stage = NULL; + script->last_stage = NULL; + script->enable_led = WifiMarauderScriptBooleanUndefined; + script->save_pcap = WifiMarauderScriptBooleanUndefined; + script->repeat = 1; + return script; +} + +WifiMarauderScript* wifi_marauder_script_create(const char* script_name) { + WifiMarauderScript* script = wifi_marauder_script_alloc(); + script->name = strdup(script_name); + return script; +} + +void _wifi_marauder_script_load_meta(WifiMarauderScript* script, cJSON* meta_section) { + if(meta_section != NULL) { + // Script description + cJSON* description = cJSON_GetObjectItem(meta_section, "description"); + if(description != NULL) { + script->description = strdup(description->valuestring); + } + // Enable LED + cJSON* enable_led_json = cJSON_GetObjectItem(meta_section, "enableLed"); + if(cJSON_IsBool(enable_led_json)) { + script->enable_led = enable_led_json->valueint; + } + // Save PCAP + cJSON* save_pcap_json = cJSON_GetObjectItem(meta_section, "savePcap"); + if(cJSON_IsBool(save_pcap_json)) { + script->save_pcap = save_pcap_json->valueint; + } + // Times the script will be repeated + cJSON* repeat = cJSON_GetObjectItem(meta_section, "repeat"); + if(repeat != NULL) { + script->repeat = repeat->valueint; + } + } + if(script->description == NULL) { + script->description = strdup("My script"); + } +} + +WifiMarauderScriptStageScan* _wifi_marauder_script_get_stage_scan(cJSON* stages) { + cJSON* stage_scan = cJSON_GetObjectItem(stages, "scan"); + if(stage_scan == NULL) { + return NULL; + } + cJSON* type = cJSON_GetObjectItem(stage_scan, "type"); + if(type == NULL) { + return NULL; + } + WifiMarauderScriptScanType scan_type; + if(strcmp(type->valuestring, "ap") == 0) { + scan_type = WifiMarauderScriptScanTypeAp; + } else if(strcmp(type->valuestring, "station") == 0) { + scan_type = WifiMarauderScriptScanTypeStation; + } else { + return NULL; + } + cJSON* channel = cJSON_GetObjectItem(stage_scan, "channel"); + int scan_channel = channel != NULL ? (int)cJSON_GetNumberValue(channel) : 0; + cJSON* timeout = cJSON_GetObjectItem(stage_scan, "timeout"); + int scan_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN; + + WifiMarauderScriptStageScan* scan_stage = + (WifiMarauderScriptStageScan*)malloc(sizeof(WifiMarauderScriptStageScan)); + scan_stage->type = scan_type; + scan_stage->channel = scan_channel; + scan_stage->timeout = scan_timeout; + + return scan_stage; +} + +WifiMarauderScriptStageSelect* _wifi_marauder_script_get_stage_select(cJSON* stages) { + cJSON* select_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "select"); + if(select_stage_json == NULL) { + return NULL; + } + + cJSON* type_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "type"); + cJSON* filter_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "filter"); + cJSON* indexes_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "indexes"); + cJSON* allow_repeat_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "allow_repeat"); + + if(!cJSON_IsString(type_json)) { + return NULL; + } + WifiMarauderScriptSelectType select_type; + if(strcmp(type_json->valuestring, "ap") == 0) { + select_type = WifiMarauderScriptSelectTypeAp; + } else if(strcmp(type_json->valuestring, "station") == 0) { + select_type = WifiMarauderScriptSelectTypeStation; + } else if(strcmp(type_json->valuestring, "ssid") == 0) { + select_type = WifiMarauderScriptSelectTypeSsid; + } else { + return NULL; + } + char* filter_str = cJSON_IsString(filter_json) ? strdup(filter_json->valuestring) : NULL; + + WifiMarauderScriptStageSelect* stage_select = + (WifiMarauderScriptStageSelect*)malloc(sizeof(WifiMarauderScriptStageSelect)); + stage_select->type = select_type; + stage_select->allow_repeat = cJSON_IsBool(allow_repeat_json) ? allow_repeat_json->valueint : + true; + stage_select->filter = filter_str; + + if(cJSON_IsArray(indexes_json)) { + int indexes_size = cJSON_GetArraySize(indexes_json); + int* indexes = (int*)malloc(indexes_size * sizeof(int)); + for(int i = 0; i < indexes_size; i++) { + cJSON* index_item = cJSON_GetArrayItem(indexes_json, i); + if(cJSON_IsNumber(index_item)) { + indexes[i] = index_item->valueint; + } + } + stage_select->indexes = indexes; + stage_select->index_count = indexes_size; + } else { + stage_select->indexes = NULL; + stage_select->index_count = 0; + } + + return stage_select; +} + +WifiMarauderScriptStageDeauth* _wifi_marauder_script_get_stage_deauth(cJSON* stages) { + cJSON* deauth_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "deauth"); + if(deauth_stage_json == NULL) { + return NULL; + } + + cJSON* timeout = cJSON_GetObjectItem(deauth_stage_json, "timeout"); + int deauth_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH; + + WifiMarauderScriptStageDeauth* deauth_stage = + (WifiMarauderScriptStageDeauth*)malloc(sizeof(WifiMarauderScriptStageDeauth)); + deauth_stage->timeout = deauth_timeout; + + return deauth_stage; +} + +WifiMarauderScriptStageProbe* _wifi_marauder_script_get_stage_probe(cJSON* stages) { + cJSON* probe_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "probe"); + if(probe_stage_json == NULL) { + return NULL; + } + + cJSON* timeout = cJSON_GetObjectItem(probe_stage_json, "timeout"); + int probe_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE; + + WifiMarauderScriptStageProbe* probe_stage = + (WifiMarauderScriptStageProbe*)malloc(sizeof(WifiMarauderScriptStageProbe)); + probe_stage->timeout = probe_timeout; + + return probe_stage; +} + +WifiMarauderScriptStageSniffRaw* _wifi_marauder_script_get_stage_sniff_raw(cJSON* stages) { + cJSON* sniffraw_stage_json = cJSON_GetObjectItem(stages, "sniffraw"); + if(sniffraw_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffraw_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffRaw* sniff_raw_stage = + (WifiMarauderScriptStageSniffRaw*)malloc(sizeof(WifiMarauderScriptStageSniffRaw)); + sniff_raw_stage->timeout = timeout; + + return sniff_raw_stage; +} + +WifiMarauderScriptStageSniffBeacon* _wifi_marauder_script_get_stage_sniff_beacon(cJSON* stages) { + cJSON* sniffbeacon_stage_json = cJSON_GetObjectItem(stages, "sniffbeacon"); + if(sniffbeacon_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffbeacon_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffBeacon* sniff_beacon_stage = + (WifiMarauderScriptStageSniffBeacon*)malloc(sizeof(WifiMarauderScriptStageSniffBeacon)); + sniff_beacon_stage->timeout = timeout; + + return sniff_beacon_stage; +} + +WifiMarauderScriptStageSniffDeauth* _wifi_marauder_script_get_stage_sniff_deauth(cJSON* stages) { + cJSON* sniffdeauth_stage_json = cJSON_GetObjectItem(stages, "sniffdeauth"); + if(sniffdeauth_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffdeauth_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffDeauth* sniff_deauth_stage = + (WifiMarauderScriptStageSniffDeauth*)malloc(sizeof(WifiMarauderScriptStageSniffDeauth)); + sniff_deauth_stage->timeout = timeout; + + return sniff_deauth_stage; +} + +WifiMarauderScriptStageSniffEsp* _wifi_marauder_script_get_stage_sniff_esp(cJSON* stages) { + cJSON* sniffesp_stage_json = cJSON_GetObjectItem(stages, "sniffesp"); + if(sniffesp_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffesp_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffEsp* sniff_esp_stage = + (WifiMarauderScriptStageSniffEsp*)malloc(sizeof(WifiMarauderScriptStageSniffEsp)); + sniff_esp_stage->timeout = timeout; + + return sniff_esp_stage; +} + +WifiMarauderScriptStageSniffPmkid* _wifi_marauder_script_get_stage_sniff_pmkid(cJSON* stages) { + cJSON* sniffpmkid_stage_json = cJSON_GetObjectItem(stages, "sniffpmkid"); + if(sniffpmkid_stage_json == NULL) { + return NULL; + } + + cJSON* channel_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "channel"); + int channel = channel_json != NULL ? (int)cJSON_GetNumberValue(channel_json) : 0; + cJSON* timeout_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + cJSON* force_deauth_json = + cJSON_GetObjectItemCaseSensitive(sniffpmkid_stage_json, "forceDeauth"); + bool force_deauth = cJSON_IsBool(force_deauth_json) ? force_deauth_json->valueint : true; + + WifiMarauderScriptStageSniffPmkid* sniff_pmkid_stage = + (WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid)); + sniff_pmkid_stage->channel = channel; + sniff_pmkid_stage->timeout = timeout; + sniff_pmkid_stage->force_deauth = force_deauth; + + return sniff_pmkid_stage; +} + +WifiMarauderScriptStageSniffPwn* _wifi_marauder_script_get_stage_sniff_pwn(cJSON* stages) { + cJSON* sniffpwn_stage_json = cJSON_GetObjectItem(stages, "sniffpwn"); + if(sniffpwn_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffpwn_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffPwn* sniff_pwn_stage = + (WifiMarauderScriptStageSniffPwn*)malloc(sizeof(WifiMarauderScriptStageSniffPwn)); + sniff_pwn_stage->timeout = timeout; + + return sniff_pwn_stage; +} + +WifiMarauderScriptStageBeaconList* _wifi_marauder_script_get_stage_beacon_list(cJSON* stages) { + cJSON* stage_beaconlist = cJSON_GetObjectItem(stages, "beaconList"); + if(stage_beaconlist == NULL) { + return NULL; + } + WifiMarauderScriptStageBeaconList* beaconlist_stage = + (WifiMarauderScriptStageBeaconList*)malloc(sizeof(WifiMarauderScriptStageBeaconList)); + if(beaconlist_stage == NULL) { + return NULL; + } + cJSON* ssids = cJSON_GetObjectItem(stage_beaconlist, "ssids"); + if(ssids == NULL) { + return NULL; + } + // SSID count + int ssid_count = cJSON_GetArraySize(ssids); + if(ssid_count == 0) { + return NULL; + } + beaconlist_stage->ssid_count = ssid_count; + // SSIDs + beaconlist_stage->ssids = (char**)malloc(sizeof(char*) * ssid_count); + if(beaconlist_stage->ssids == NULL) { + return NULL; + } + for(int i = 0; i < ssid_count; i++) { + cJSON* ssid = cJSON_GetArrayItem(ssids, i); + if(ssid == NULL) { + continue; + } + char* ssid_string = cJSON_GetStringValue(ssid); + if(ssid_string == NULL) { + continue; + } + beaconlist_stage->ssids[i] = (char*)malloc(sizeof(char) * (strlen(ssid_string) + 1)); + strcpy(beaconlist_stage->ssids[i], ssid_string); + } + // Timeout + cJSON* timeout = cJSON_GetObjectItem(stage_beaconlist, "timeout"); + beaconlist_stage->timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + // Random SSIDs + cJSON* random_ssids = cJSON_GetObjectItem(stage_beaconlist, "generate"); + beaconlist_stage->random_ssids = + random_ssids != NULL ? (int)cJSON_GetNumberValue(random_ssids) : 0; + + return beaconlist_stage; +} + +WifiMarauderScriptStageBeaconAp* _wifi_marauder_script_get_stage_beacon_ap(cJSON* stages) { + cJSON* beaconap_stage_json = cJSON_GetObjectItem(stages, "beaconAp"); + if(beaconap_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(beaconap_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + WifiMarauderScriptStageBeaconAp* beacon_ap_stage = + (WifiMarauderScriptStageBeaconAp*)malloc(sizeof(WifiMarauderScriptStageBeaconAp)); + beacon_ap_stage->timeout = timeout; + + return beacon_ap_stage; +} + +WifiMarauderScriptStageExec* _wifi_marauder_script_get_stage_exec(cJSON* stages) { + cJSON* exec_stage_json = cJSON_GetObjectItem(stages, "exec"); + if(exec_stage_json == NULL) { + return NULL; + } + + cJSON* command_json = cJSON_GetObjectItemCaseSensitive(exec_stage_json, "command"); + char* command_str = cJSON_IsString(command_json) ? strdup(command_json->valuestring) : NULL; + + WifiMarauderScriptStageExec* exec_stage = + (WifiMarauderScriptStageExec*)malloc(sizeof(WifiMarauderScriptStageExec)); + exec_stage->command = command_str; + + return exec_stage; +} + +WifiMarauderScriptStageDelay* _wifi_marauder_script_get_stage_delay(cJSON* stages) { + cJSON* delay_stage_json = cJSON_GetObjectItem(stages, "delay"); + if(delay_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(delay_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : 0; + + WifiMarauderScriptStageDelay* delay_stage = + (WifiMarauderScriptStageDelay*)malloc(sizeof(WifiMarauderScriptStageDelay)); + delay_stage->timeout = timeout; + + return delay_stage; +} + +WifiMarauderScriptStage* + _wifi_marauder_script_create_stage(WifiMarauderScriptStageType type, void* stage_data) { + WifiMarauderScriptStage* stage = + (WifiMarauderScriptStage*)malloc(sizeof(WifiMarauderScriptStage)); + stage->type = type; + stage->stage = stage_data; + stage->next_stage = NULL; + return stage; +} + +void wifi_marauder_script_add_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type, + void* stage_data) { + if(script == NULL || stage_data == NULL) { + return; + } + WifiMarauderScriptStage* stage = _wifi_marauder_script_create_stage(stage_type, stage_data); + if(script->last_stage != NULL) { + script->last_stage->next_stage = stage; + } else { + script->first_stage = stage; + } + script->last_stage = stage; +} + +void _wifi_marauder_script_load_stages(WifiMarauderScript* script, cJSON* stages) { + // Scan stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeScan, _wifi_marauder_script_get_stage_scan(stages)); + // Select stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeSelect, _wifi_marauder_script_get_stage_select(stages)); + // Deauth stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeDeauth, _wifi_marauder_script_get_stage_deauth(stages)); + // Probe stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeProbe, _wifi_marauder_script_get_stage_probe(stages)); + // Sniff raw stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffRaw, + _wifi_marauder_script_get_stage_sniff_raw(stages)); + // Sniff beacon stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffBeacon, + _wifi_marauder_script_get_stage_sniff_beacon(stages)); + // Sniff deauth stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffDeauth, + _wifi_marauder_script_get_stage_sniff_deauth(stages)); + // Sniff esp stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffEsp, + _wifi_marauder_script_get_stage_sniff_esp(stages)); + // Sniff PMKID stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffPmkid, + _wifi_marauder_script_get_stage_sniff_pmkid(stages)); + // Sniff pwn stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffPwn, + _wifi_marauder_script_get_stage_sniff_pwn(stages)); + // Beacon List stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeBeaconList, + _wifi_marauder_script_get_stage_beacon_list(stages)); + // Beacon Ap stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeBeaconAp, + _wifi_marauder_script_get_stage_beacon_ap(stages)); + // Exec stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeExec, _wifi_marauder_script_get_stage_exec(stages)); + // Delay stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeDelay, _wifi_marauder_script_get_stage_delay(stages)); +} + +WifiMarauderScript* wifi_marauder_script_parse_raw(const char* json_raw) { + WifiMarauderScript* script = wifi_marauder_script_alloc(); + if(script == NULL) { + return NULL; + } + cJSON* json = cJSON_Parse(json_raw); + if(json == NULL) { + return NULL; + } + cJSON* meta = cJSON_GetObjectItem(json, "meta"); + _wifi_marauder_script_load_meta(script, meta); + + cJSON* stages = cJSON_GetObjectItem(json, "stages"); + if(cJSON_IsArray(stages)) { + cJSON* stage_item = NULL; + cJSON_ArrayForEach(stage_item, stages) { + _wifi_marauder_script_load_stages(script, stage_item); + } + } else { + _wifi_marauder_script_load_stages(script, stages); + } + + return script; +} + +WifiMarauderScript* wifi_marauder_script_parse_json(Storage* storage, const char* file_path) { + WifiMarauderScript* script = NULL; + File* script_file = storage_file_alloc(storage); + FuriString* script_name = furi_string_alloc(); + path_extract_filename_no_ext(file_path, script_name); + + if(storage_file_open(script_file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + uint32_t file_size = storage_file_size(script_file); + char* json_buffer = (char*)malloc(file_size + 1); + uint16_t bytes_read = storage_file_read(script_file, json_buffer, file_size); + json_buffer[bytes_read] = '\0'; + + script = wifi_marauder_script_parse_raw(json_buffer); + } + if(script == NULL) { + script = wifi_marauder_script_create(furi_string_get_cstr(script_name)); + } + script->name = strdup(furi_string_get_cstr(script_name)); + + furi_string_free(script_name); + storage_file_close(script_file); + storage_file_free(script_file); + return script; +} + +cJSON* _wifi_marauder_script_create_json_meta(WifiMarauderScript* script) { + cJSON* meta_json = cJSON_CreateObject(); + if(script->description != NULL) { + cJSON_AddStringToObject(meta_json, "description", script->description); + } else { + cJSON_AddStringToObject(meta_json, "description", "My Script"); + } + if(script->enable_led != WifiMarauderScriptBooleanUndefined) { + cJSON_AddBoolToObject( + meta_json, "enableLed", (script->enable_led == WifiMarauderScriptBooleanTrue)); + } + if(script->save_pcap != WifiMarauderScriptBooleanUndefined) { + cJSON_AddBoolToObject( + meta_json, "savePcap", (script->save_pcap == WifiMarauderScriptBooleanTrue)); + } + cJSON_AddNumberToObject(meta_json, "repeat", script->repeat); + return meta_json; +} + +cJSON* _wifi_marauder_script_create_json_scan(WifiMarauderScriptStageScan* scan_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "scan", cJSON_CreateObject()); + cJSON* scan_json = cJSON_GetObjectItem(stage_json, "scan"); + // Scan type + cJSON_AddStringToObject( + scan_json, "type", scan_stage->type == WifiMarauderScriptScanTypeAp ? "ap" : "station"); + // Channel + if(scan_stage->channel > 0) { + cJSON_AddNumberToObject(scan_json, "channel", scan_stage->channel); + } + // Timeout + if(scan_stage->timeout > 0) { + cJSON_AddNumberToObject(scan_json, "timeout", scan_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_select(WifiMarauderScriptStageSelect* select_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "select", cJSON_CreateObject()); + cJSON* select_json = cJSON_GetObjectItem(stage_json, "select"); + // Select type + cJSON_AddStringToObject( + select_json, + "type", + select_stage->type == WifiMarauderScriptSelectTypeAp ? "ap" : + select_stage->type == WifiMarauderScriptSelectTypeStation ? "station" : + "ssid"); + if(select_stage->filter != NULL) { + cJSON_AddStringToObject(select_json, "filter", select_stage->filter); + } + // Indexes + if(select_stage->indexes != NULL && select_stage->index_count > 0) { + cJSON* indexes_json = cJSON_CreateArray(); + for(int i = 0; i < select_stage->index_count; i++) { + cJSON_AddItemToArray(indexes_json, cJSON_CreateNumber(select_stage->indexes[i])); + } + cJSON_AddItemToObject(select_json, "indexes", indexes_json); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_deauth(WifiMarauderScriptStageDeauth* deauth_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "deauth", cJSON_CreateObject()); + cJSON* deauth_json = cJSON_GetObjectItem(stage_json, "deauth"); + // Timeout + if(deauth_stage->timeout > 0) { + cJSON_AddNumberToObject(deauth_json, "timeout", deauth_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_probe(WifiMarauderScriptStageProbe* probe_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "probe", cJSON_CreateObject()); + cJSON* probe_json = cJSON_GetObjectItem(stage_json, "probe"); + // Timeout + if(probe_stage->timeout > 0) { + cJSON_AddNumberToObject(probe_json, "timeout", probe_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffraw(WifiMarauderScriptStageSniffRaw* sniffraw_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffRaw", cJSON_CreateObject()); + cJSON* sniffraw_json = cJSON_GetObjectItem(stage_json, "sniffRaw"); + // Timeout + if(sniffraw_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffraw_json, "timeout", sniffraw_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffbeacon( + WifiMarauderScriptStageSniffBeacon* sniffbeacon_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffBeacon", cJSON_CreateObject()); + cJSON* sniffbeacon_json = cJSON_GetObjectItem(stage_json, "sniffBeacon"); + // Timeout + if(sniffbeacon_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffbeacon_json, "timeout", sniffbeacon_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffdeauth( + WifiMarauderScriptStageSniffDeauth* sniffdeauth_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffDeauth", cJSON_CreateObject()); + cJSON* sniffdeauth_json = cJSON_GetObjectItem(stage_json, "sniffDeauth"); + // Timeout + if(sniffdeauth_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffdeauth_json, "timeout", sniffdeauth_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffesp(WifiMarauderScriptStageSniffEsp* sniffesp_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffEsp", cJSON_CreateObject()); + cJSON* sniffesp_json = cJSON_GetObjectItem(stage_json, "sniffEsp"); + // Timeout + if(sniffesp_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffesp_json, "timeout", sniffesp_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffpmkid( + WifiMarauderScriptStageSniffPmkid* sniffpmkid_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffPmkid", cJSON_CreateObject()); + cJSON* sniffpmkid_json = cJSON_GetObjectItem(stage_json, "sniffPmkid"); + // Force deauth + cJSON_AddBoolToObject(sniffpmkid_json, "forceDeauth", sniffpmkid_stage->force_deauth); + // Channel + if(sniffpmkid_stage->channel > 0) { + cJSON_AddNumberToObject(sniffpmkid_json, "channel", sniffpmkid_stage->channel); + } + // Timeout + if(sniffpmkid_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffpmkid_json, "timeout", sniffpmkid_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffpwn(WifiMarauderScriptStageSniffPwn* sniffpwn_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffPwn", cJSON_CreateObject()); + cJSON* sniffpwn_json = cJSON_GetObjectItem(stage_json, "sniffPwn"); + // Timeout + if(sniffpwn_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffpwn_json, "timeout", sniffpwn_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_beaconlist( + WifiMarauderScriptStageBeaconList* beaconlist_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "beaconList", cJSON_CreateObject()); + cJSON* beaconlist_json = cJSON_GetObjectItem(stage_json, "beaconList"); + // SSIDs + if(beaconlist_stage->ssids != NULL) { + cJSON* ssids_json = cJSON_CreateStringArray( + (const char**)beaconlist_stage->ssids, beaconlist_stage->ssid_count); + cJSON_AddItemToObject(beaconlist_json, "ssids", ssids_json); + } + // Random SSIDs + if(beaconlist_stage->random_ssids > 0) { + cJSON_AddNumberToObject(beaconlist_json, "generate", beaconlist_stage->random_ssids); + } + // Timeout + if(beaconlist_stage->timeout > 0) { + cJSON_AddNumberToObject(beaconlist_json, "timeout", beaconlist_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_beaconap(WifiMarauderScriptStageBeaconAp* beaconap_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "beaconAp", cJSON_CreateObject()); + cJSON* beaconap_json = cJSON_GetObjectItem(stage_json, "beaconAp"); + // Timeout + if(beaconap_stage->timeout > 0) { + cJSON_AddNumberToObject(beaconap_json, "timeout", beaconap_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_exec(WifiMarauderScriptStageExec* exec_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "exec", cJSON_CreateObject()); + cJSON* exec_json = cJSON_GetObjectItem(stage_json, "exec"); + // Command + cJSON_AddStringToObject( + exec_json, "command", exec_stage->command != NULL ? exec_stage->command : ""); + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_delay(WifiMarauderScriptStageDelay* delay_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "delay", cJSON_CreateObject()); + cJSON* delay_json = cJSON_GetObjectItem(stage_json, "delay"); + // Timeout + if(delay_stage->timeout > 0) { + cJSON_AddNumberToObject(delay_json, "timeout", delay_stage->timeout); + } + return stage_json; +} + +void wifi_marauder_script_save_json( + Storage* storage, + const char* file_path, + WifiMarauderScript* script) { + File* script_file = storage_file_alloc(storage); + + if(storage_file_open(script_file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + cJSON* root_json = cJSON_CreateObject(); + + // Meta info + cJSON* meta_json = _wifi_marauder_script_create_json_meta(script); + cJSON_AddItemToObject(root_json, "meta", meta_json); + + // Create array for stages + cJSON* stages_array = cJSON_CreateArray(); + cJSON_AddItemToObject(root_json, "stages", stages_array); + + // Iterate over each stage and create the corresponding JSON object + WifiMarauderScriptStage* stage = script->first_stage; + while(stage != NULL) { + cJSON* stage_json = NULL; + + switch(stage->type) { + case WifiMarauderScriptStageTypeScan: { + WifiMarauderScriptStageScan* scan_stage = + (WifiMarauderScriptStageScan*)stage->stage; + stage_json = _wifi_marauder_script_create_json_scan(scan_stage); + break; + } + case WifiMarauderScriptStageTypeSelect: { + WifiMarauderScriptStageSelect* select_stage = + (WifiMarauderScriptStageSelect*)stage->stage; + stage_json = _wifi_marauder_script_create_json_select(select_stage); + break; + } + case WifiMarauderScriptStageTypeDeauth: { + WifiMarauderScriptStageDeauth* deauth_stage = + (WifiMarauderScriptStageDeauth*)stage->stage; + stage_json = _wifi_marauder_script_create_json_deauth(deauth_stage); + break; + } + case WifiMarauderScriptStageTypeProbe: { + WifiMarauderScriptStageProbe* probe_stage = + (WifiMarauderScriptStageProbe*)stage->stage; + stage_json = _wifi_marauder_script_create_json_probe(probe_stage); + break; + } + case WifiMarauderScriptStageTypeSniffRaw: { + WifiMarauderScriptStageSniffRaw* sniffraw_stage = + (WifiMarauderScriptStageSniffRaw*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffraw(sniffraw_stage); + break; + } + case WifiMarauderScriptStageTypeSniffBeacon: { + WifiMarauderScriptStageSniffBeacon* sniffbeacon_stage = + (WifiMarauderScriptStageSniffBeacon*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffbeacon(sniffbeacon_stage); + break; + } + case WifiMarauderScriptStageTypeSniffDeauth: { + WifiMarauderScriptStageSniffDeauth* sniffdeauth_stage = + (WifiMarauderScriptStageSniffDeauth*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffdeauth(sniffdeauth_stage); + break; + } + case WifiMarauderScriptStageTypeSniffEsp: { + WifiMarauderScriptStageSniffEsp* sniffesp_stage = + (WifiMarauderScriptStageSniffEsp*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffesp(sniffesp_stage); + break; + } + case WifiMarauderScriptStageTypeSniffPmkid: { + WifiMarauderScriptStageSniffPmkid* sniffpmkid_stage = + (WifiMarauderScriptStageSniffPmkid*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffpmkid(sniffpmkid_stage); + break; + } + case WifiMarauderScriptStageTypeSniffPwn: { + WifiMarauderScriptStageSniffPwn* sniffpwn_stage = + (WifiMarauderScriptStageSniffPwn*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffpwn(sniffpwn_stage); + break; + } + case WifiMarauderScriptStageTypeBeaconList: { + WifiMarauderScriptStageBeaconList* beaconlist_stage = + (WifiMarauderScriptStageBeaconList*)stage->stage; + stage_json = _wifi_marauder_script_create_json_beaconlist(beaconlist_stage); + break; + } + case WifiMarauderScriptStageTypeBeaconAp: { + WifiMarauderScriptStageBeaconAp* beaconap_stage = + (WifiMarauderScriptStageBeaconAp*)stage->stage; + stage_json = _wifi_marauder_script_create_json_beaconap(beaconap_stage); + break; + } + case WifiMarauderScriptStageTypeExec: { + WifiMarauderScriptStageExec* exec_stage = + (WifiMarauderScriptStageExec*)stage->stage; + stage_json = _wifi_marauder_script_create_json_exec(exec_stage); + break; + } + case WifiMarauderScriptStageTypeDelay: { + WifiMarauderScriptStageDelay* delay_stage = + (WifiMarauderScriptStageDelay*)stage->stage; + stage_json = _wifi_marauder_script_create_json_delay(delay_stage); + break; + } + } + + // Add the stage JSON object to the "stages" array + if(stage_json != NULL) { + cJSON_AddItemToArray(stages_array, stage_json); + } + + stage = stage->next_stage; + } + + // Write JSON to file + char* json_str = cJSON_Print(root_json); + storage_file_write(script_file, json_str, strlen(json_str)); + + //free(json_str); + storage_file_close(script_file); + } + storage_file_free(script_file); +} + +bool wifi_marauder_script_has_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type) { + if(script == NULL) { + return false; + } + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL) { + if(current_stage->type == stage_type) { + return true; + } + current_stage = current_stage->next_stage; + } + return false; +} + +void wifi_marauder_script_free(WifiMarauderScript* script) { + if(script == NULL) { + return; + } + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL) { + WifiMarauderScriptStage* next_stage = current_stage->next_stage; + switch(current_stage->type) { + case WifiMarauderScriptStageTypeScan: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSelect: + if(((WifiMarauderScriptStageSelect*)current_stage->stage)->filter != NULL) { + free(((WifiMarauderScriptStageSelect*)current_stage->stage)->filter); + } + if(((WifiMarauderScriptStageSelect*)current_stage->stage)->indexes != NULL) { + free(((WifiMarauderScriptStageSelect*)current_stage->stage)->indexes); + } + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeDeauth: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeProbe: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffRaw: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffEsp: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffPwn: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeBeaconList: + for(int i = 0; + i < ((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssid_count; + i++) { + free(((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssids[i]); + } + free(((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssids); + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeBeaconAp: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeExec: + if(((WifiMarauderScriptStageExec*)current_stage->stage)->command != NULL) { + free(((WifiMarauderScriptStageExec*)current_stage->stage)->command); + } + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeDelay: + free(current_stage->stage); + break; + } + free(current_stage); + current_stage = next_stage; + } + free(script->name); + free(script->description); + free(script); +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h new file mode 100644 index 000000000..e11ee267f --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h @@ -0,0 +1,257 @@ +/* + * ---------------------------------------------------------------------------------------------------- + * STEPS TO ADD A NEW STAGE: + * + * wifi_marauder_script.h + * - Complement WifiMarauderScriptStageType enum with new stage + * - Create struct WifiMarauderScriptStage???? for the new stage + * + * wifi_marauder_script.c + * - Change _wifi_marauder_script_load_stages() to load new stage + * - Change wifi_marauder_script_save_json() to support the new stage + * - Add case to free memory in wifi_marauder_script_free() + * + * wifi_marauder_script_executor.c + * - Create function "void _wifi_marauder_script_execute_????(WifiMarauderScriptStage????* stage)" + * - Add case in wifi_marauder_script_execute_stage() + * + * wifi_marauder_scene_script_edit.c + * - Add case in wifi_marauder_scene_script_edit_on_enter() + * + * wifi_marauder_scene_script_stage_add.c + * - Create stage creation function and add in wifi_marauder_scene_script_stage_add_on_enter() + * + * wifi_marauder_script_stage_menu_config.h + * - Add the new stage and implement its functions in a new file + * + * ---------------------------------------------------------------------------------------------------- + * SCRIPT SYNTAX (In order of execution): + * { + * "meta": { + * "description": "My script", + * "repeat": times the script will repeat (default 1), + * "enableLed": true (default) | false, + * "savePcap": true (default) | false + * }, + * "stages": { + * "scan": { + * "type": "ap" | "station", + * "timeout": seconds, + * "channel": 1-11 + * }, + * "select": { + * "type": "ap" | "station" | "ssid", + * "filter": "all" | "contains -f '{SSID fragment}' or equals '{SSID}' or ...", + * "indexes": [0, 1, 2, 3...], + * }, + * "deauth": { + * "timeout": seconds + * }, + * "probe": { + * "timeout": seconds + * }, + * "sniffRaw": { + * "timeout": seconds + * }, + * "sniffBeacon": { + * "timeout": seconds + * }, + * "sniffDeauth": { + * "timeout": seconds + * }, + * "sniffEsp": { + * "timeout": seconds + * }, + * "sniffPmkid": { + * "forceDeauth": true (default) | false, + * "channel": 1-11, + * "timeout": seconds + * }, + * "sniffPwn": { + * "timeout": seconds + * }, + * "beaconList": { + * "ssids": [ + * "SSID 1", + * "SSID 2", + * "SSID 3" + * ], + * "generate": number of random SSIDs that will be generated, + * "timeout": seconds + * } + * "beaconAp": { + * "timeout": seconds + * } + * "exec": { + * "command": Command (eg: "clearlist -a") + * } + * "delay": { + * "timeout": seconds + * } + * } + * } + * + * Note: It is possible to inform "stages" as an array, allowing ordering and repetition of stages of the same type: + * "stages": [ + * { + * "beaconList": { "ssids": ["SSID 1", "SSID 2"] } + * }, + * { + * "beaconList": { "generate": 4 } + * }, + * ] + * ---------------------------------------------------------------------------------------------------- + */ + +#pragma once + +#include +#include "cJSON.h" + +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN 15 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH 30 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE 60 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF 60 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON 60 + +typedef enum { + WifiMarauderScriptBooleanFalse = 0, + WifiMarauderScriptBooleanTrue = 1, + WifiMarauderScriptBooleanUndefined = 2 +} WifiMarauderScriptBoolean; + +typedef enum { + WifiMarauderScriptStageTypeScan, + WifiMarauderScriptStageTypeSelect, + WifiMarauderScriptStageTypeDeauth, + WifiMarauderScriptStageTypeProbe, + WifiMarauderScriptStageTypeSniffRaw, + WifiMarauderScriptStageTypeSniffBeacon, + WifiMarauderScriptStageTypeSniffDeauth, + WifiMarauderScriptStageTypeSniffEsp, + WifiMarauderScriptStageTypeSniffPmkid, + WifiMarauderScriptStageTypeSniffPwn, + WifiMarauderScriptStageTypeBeaconList, + WifiMarauderScriptStageTypeBeaconAp, + WifiMarauderScriptStageTypeExec, + WifiMarauderScriptStageTypeDelay, +} WifiMarauderScriptStageType; + +typedef enum { + WifiMarauderScriptScanTypeAp = 0, + WifiMarauderScriptScanTypeStation = 1 +} WifiMarauderScriptScanType; + +typedef enum { + WifiMarauderScriptSelectTypeAp, + WifiMarauderScriptSelectTypeStation, + WifiMarauderScriptSelectTypeSsid +} WifiMarauderScriptSelectType; + +// Stages +typedef struct WifiMarauderScriptStage { + WifiMarauderScriptStageType type; + void* stage; + struct WifiMarauderScriptStage* next_stage; +} WifiMarauderScriptStage; + +typedef struct WifiMarauderScriptStageScan { + WifiMarauderScriptScanType type; + int channel; + int timeout; +} WifiMarauderScriptStageScan; + +typedef struct WifiMarauderScriptStageSelect { + WifiMarauderScriptSelectType type; + char* filter; + int* indexes; + int index_count; + // TODO: Implement a feature to not select the same items in the next iteration of the script + bool allow_repeat; +} WifiMarauderScriptStageSelect; + +typedef struct WifiMarauderScriptStageDeauth { + int timeout; +} WifiMarauderScriptStageDeauth; + +typedef struct WifiMarauderScriptStageProbe { + int timeout; +} WifiMarauderScriptStageProbe; + +typedef struct WifiMarauderScriptStageSniffRaw { + int timeout; +} WifiMarauderScriptStageSniffRaw; + +typedef struct WifiMarauderScriptStageSniffBeacon { + int timeout; +} WifiMarauderScriptStageSniffBeacon; + +typedef struct WifiMarauderScriptStageSniffDeauth { + int timeout; +} WifiMarauderScriptStageSniffDeauth; + +typedef struct WifiMarauderScriptStageSniffEsp { + int timeout; +} WifiMarauderScriptStageSniffEsp; + +typedef struct WifiMarauderScriptStageSniffPmkid { + bool force_deauth; + int channel; + int timeout; +} WifiMarauderScriptStageSniffPmkid; + +typedef struct WifiMarauderScriptStageSniffPwn { + int timeout; +} WifiMarauderScriptStageSniffPwn; + +typedef struct WifiMarauderScriptStageBeaconList { + char** ssids; + int ssid_count; + int random_ssids; + int timeout; +} WifiMarauderScriptStageBeaconList; + +typedef struct WifiMarauderScriptStageBeaconAp { + int timeout; +} WifiMarauderScriptStageBeaconAp; + +typedef struct WifiMarauderScriptStageExec { + char* command; +} WifiMarauderScriptStageExec; + +typedef struct WifiMarauderScriptStageDelay { + int timeout; +} WifiMarauderScriptStageDelay; + +// Script +typedef struct WifiMarauderScript { + char* name; + char* description; + WifiMarauderScriptStage* first_stage; + WifiMarauderScriptStage* last_stage; + WifiMarauderScriptBoolean enable_led; + WifiMarauderScriptBoolean save_pcap; + int repeat; +} WifiMarauderScript; + +typedef struct WifiMarauderScriptStageListItem { + char* value; + struct WifiMarauderScriptStageListItem* next_item; +} WifiMarauderScriptStageListItem; + +WifiMarauderScript* wifi_marauder_script_alloc(); +WifiMarauderScript* wifi_marauder_script_create(const char* script_name); +WifiMarauderScript* wifi_marauder_script_parse_raw(const char* script_raw); +WifiMarauderScript* wifi_marauder_script_parse_json(Storage* storage, const char* file_path); +void wifi_marauder_script_save_json( + Storage* storage, + const char* file_path, + WifiMarauderScript* script); +void wifi_marauder_script_add_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type, + void* stage_data); +bool wifi_marauder_script_has_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type); +void wifi_marauder_script_free(WifiMarauderScript* script); diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c new file mode 100644 index 000000000..d7799c300 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c @@ -0,0 +1,307 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script_executor.h" + +void _wifi_marauder_script_delay(WifiMarauderScriptWorker* worker, uint32_t delay_secs) { + for(uint32_t i = 0; i < delay_secs && worker->is_running; i++) furi_delay_ms(1000); +} + +void _send_stop() { + const char stop_command[] = "stopscan\n"; + wifi_marauder_uart_tx((uint8_t*)(stop_command), strlen(stop_command)); +} + +void _send_line_break() { + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); +} + +void _send_channel_select(int channel) { + char command[30]; + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + snprintf(command, sizeof(command), "channel -s %d\n", channel); + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); +} + +void _wifi_marauder_script_execute_scan( + WifiMarauderScriptStageScan* stage, + WifiMarauderScriptWorker* worker) { + char command[15]; + // Set channel + if(stage->channel > 0) { + _send_channel_select(stage->channel); + } + // Start scan + if(stage->type == WifiMarauderScriptScanTypeAp) { + snprintf(command, sizeof(command), "scanap\n"); + } else { + snprintf(command, sizeof(command), "scansta\n"); + } + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_select(WifiMarauderScriptStageSelect* stage) { + const char* select_type = NULL; + switch(stage->type) { + case WifiMarauderScriptSelectTypeAp: + select_type = "-a"; + break; + case WifiMarauderScriptSelectTypeStation: + select_type = "-c"; + break; + case WifiMarauderScriptSelectTypeSsid: + select_type = "-s"; + break; + default: + return; // invalid stage + } + + char command[256]; + size_t command_length = 0; + + if(stage->indexes != NULL && stage->index_count > 0) { + command_length = snprintf(command, sizeof(command), "select %s ", select_type); + + for(int i = 0; i < stage->index_count; i++) { + int index = stage->indexes[i]; + command_length += snprintf( + command + command_length, sizeof(command) - command_length, "%d, ", index); + } + + // Remove the trailing comma and space + command_length -= 2; + command[command_length] = '\n'; + command_length++; + } else if(stage->filter == NULL || strcmp(stage->filter, "all") == 0) { + command_length = snprintf(command, sizeof(command), "select %s all\n", select_type); + } else { + command_length = snprintf( + command, sizeof(command), "select %s -f \"%s\"\n", select_type, stage->filter); + } + + wifi_marauder_uart_tx((uint8_t*)command, command_length); +} + +void _wifi_marauder_script_execute_deauth( + WifiMarauderScriptStageDeauth* stage, + WifiMarauderScriptWorker* worker) { + const char attack_command[] = "attack -t deauth\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_probe( + WifiMarauderScriptStageProbe* stage, + WifiMarauderScriptWorker* worker) { + const char attack_command[] = "attack -t probe\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_raw( + WifiMarauderScriptStageSniffRaw* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffraw\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_beacon( + WifiMarauderScriptStageSniffBeacon* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffbeacon\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_deauth( + WifiMarauderScriptStageSniffDeauth* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffdeauth\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_esp( + WifiMarauderScriptStageSniffEsp* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffesp\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_pmkid( + WifiMarauderScriptStageSniffPmkid* stage, + WifiMarauderScriptWorker* worker) { + char attack_command[50] = "sniffpmkid"; + int len = strlen(attack_command); + + if(stage->channel > 0) { + len += + snprintf(attack_command + len, sizeof(attack_command) - len, " -c %d", stage->channel); + } + + if(stage->force_deauth) { + len += snprintf(attack_command + len, sizeof(attack_command) - len, " -d"); + } + + len += snprintf(attack_command + len, sizeof(attack_command) - len, "\n"); + + wifi_marauder_uart_tx((uint8_t*)attack_command, len); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_pwn( + WifiMarauderScriptStageSniffPwn* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffpwn\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_beacon_list( + WifiMarauderScriptStageBeaconList* stage, + WifiMarauderScriptWorker* worker) { + const char clearlist_command[] = "clearlist -s\n"; + wifi_marauder_uart_tx((uint8_t*)(clearlist_command), strlen(clearlist_command)); + + char command[100]; + char* ssid; + + for(int i = 0; i < stage->ssid_count; i++) { + ssid = stage->ssids[i]; + snprintf(command, sizeof(command), "ssid -a -n \"%s\"", ssid); + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + _send_line_break(); + } + if(stage->random_ssids > 0) { + char add_random_command[50]; + snprintf( + add_random_command, + sizeof(add_random_command), + "ssid -a -r -g %d\n", + stage->random_ssids); + wifi_marauder_uart_tx((uint8_t*)add_random_command, strlen(add_random_command)); + } + const char attack_command[] = "attack -t beacon -l\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_beacon_ap( + WifiMarauderScriptStageBeaconAp* stage, + WifiMarauderScriptWorker* worker) { + const char command[] = "attack -t beacon -a\n"; + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_exec(WifiMarauderScriptStageExec* stage) { + if(stage->command != NULL) { + wifi_marauder_uart_tx((uint8_t*)stage->command, strlen(stage->command)); + } +} + +void _wifi_marauder_script_execute_delay( + WifiMarauderScriptStageDelay* stage, + WifiMarauderScriptWorker* worker) { + _wifi_marauder_script_delay(worker, stage->timeout); +} + +void wifi_marauder_script_execute_start(void* context) { + furi_assert(context); + WifiMarauderScriptWorker* worker = context; + WifiMarauderScript* script = worker->script; + char command[100]; + + // Enables or disables the LED according to script settings + if(script->enable_led != WifiMarauderScriptBooleanUndefined) { + snprintf( + command, + sizeof(command), + "settings -s EnableLED %s", + script->enable_led ? "enable" : "disable"); + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _send_line_break(); + } + + // Enables or disables PCAP saving according to script settings + if(script->save_pcap != WifiMarauderScriptBooleanUndefined) { + snprintf( + command, + sizeof(command), + "settings -s SavePCAP %s", + script->save_pcap ? "enable" : "disable"); + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _send_line_break(); + } +} + +void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* context) { + furi_assert(context); + WifiMarauderScriptWorker* worker = context; + void* stage_data = stage->stage; + + switch(stage->type) { + case WifiMarauderScriptStageTypeScan: + _wifi_marauder_script_execute_scan((WifiMarauderScriptStageScan*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSelect: + _wifi_marauder_script_execute_select((WifiMarauderScriptStageSelect*)stage_data); + break; + case WifiMarauderScriptStageTypeDeauth: + _wifi_marauder_script_execute_deauth((WifiMarauderScriptStageDeauth*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeProbe: + _wifi_marauder_script_execute_probe((WifiMarauderScriptStageProbe*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffRaw: + _wifi_marauder_script_execute_sniff_raw( + (WifiMarauderScriptStageSniffRaw*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + _wifi_marauder_script_execute_sniff_beacon( + (WifiMarauderScriptStageSniffBeacon*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + _wifi_marauder_script_execute_sniff_deauth( + (WifiMarauderScriptStageSniffDeauth*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffEsp: + _wifi_marauder_script_execute_sniff_esp( + (WifiMarauderScriptStageSniffEsp*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + _wifi_marauder_script_execute_sniff_pmkid( + (WifiMarauderScriptStageSniffPmkid*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffPwn: + _wifi_marauder_script_execute_sniff_pwn( + (WifiMarauderScriptStageSniffPwn*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeBeaconList: + _wifi_marauder_script_execute_beacon_list( + (WifiMarauderScriptStageBeaconList*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeBeaconAp: + _wifi_marauder_script_execute_beacon_ap( + (WifiMarauderScriptStageBeaconAp*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeExec: + _wifi_marauder_script_execute_exec((WifiMarauderScriptStageExec*)stage_data); + break; + case WifiMarauderScriptStageTypeDelay: + _wifi_marauder_script_execute_delay((WifiMarauderScriptStageDelay*)stage_data, worker); + break; + } +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h new file mode 100644 index 000000000..654712849 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h @@ -0,0 +1,6 @@ +#pragma once + +#include "wifi_marauder_script.h" + +void wifi_marauder_script_execute_start(void* context); +void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* context); diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c new file mode 100644 index 000000000..45c5b56ba --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c @@ -0,0 +1,74 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script_worker.h" + +WifiMarauderScriptWorker* wifi_marauder_script_worker_alloc() { + WifiMarauderScriptWorker* worker = malloc(sizeof(WifiMarauderScriptWorker)); + if(worker == NULL) { + return NULL; + } + worker->callback_start = NULL; + worker->callback_stage = NULL; + worker->worker_thread = NULL; + worker->is_running = false; + return worker; +} + +int32_t _wifi_marauder_script_worker_task(void* worker) { + WifiMarauderScriptWorker* script_worker = worker; + WifiMarauderScript* script = script_worker->script; + if(script == NULL) { + return WifiMarauderScriptWorkerStatusInvalidScript; + } + + // Setup + script_worker->callback_start(script_worker->context); + if(!script_worker->is_running) { + return WifiMarauderScriptWorkerStatusForceExit; + } + + // Stages + for(int i = 0; i < script->repeat; i++) { + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL && script_worker->is_running) { + script_worker->callback_stage(current_stage, script_worker->context); + current_stage = current_stage->next_stage; + } + if(!script_worker->is_running) { + return WifiMarauderScriptWorkerStatusForceExit; + } + } + + script_worker->is_running = false; + return WifiMarauderScriptWorkerStatusSuccess; +} + +bool wifi_marauder_script_worker_start( + WifiMarauderScriptWorker* instance, + WifiMarauderScript* script) { + if(!instance || !script) { + return false; + } + instance->callback_start = wifi_marauder_script_execute_start; + instance->callback_stage = wifi_marauder_script_execute_stage; + instance->script = script; + instance->context = instance; + instance->is_running = true; + instance->worker_thread = furi_thread_alloc_ex( + "WifiMarauderScriptWorker", 1024, _wifi_marauder_script_worker_task, instance); + if(!instance->worker_thread) { + return false; + } + furi_thread_start(instance->worker_thread); + return true; +} + +void wifi_marauder_script_worker_free(WifiMarauderScriptWorker* worker) { + if(worker != NULL) { + if(worker->worker_thread != NULL) { + worker->is_running = false; + furi_thread_join(worker->worker_thread); + furi_thread_free(worker->worker_thread); + } + free(worker); + } +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h new file mode 100644 index 000000000..76ff070d2 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h @@ -0,0 +1,43 @@ +#pragma once + +#include "wifi_marauder_script.h" + +typedef enum { + WifiMarauderScriptWorkerStatusSuccess = 0, + WifiMarauderScriptWorkerStatusInvalidScript = 1, + WifiMarauderScriptWorkerStatusForceExit = 2 +} WifiMarauderScriptWorkerStatus; + +typedef struct WifiMarauderScriptWorker { + WifiMarauderScript* script; + FuriThread* worker_thread; + void (*callback_start)(void*); + void (*callback_stage)(WifiMarauderScriptStage*, void*); + void* context; + bool is_running; +} WifiMarauderScriptWorker; + +/** + * @brief Allocates a new instance of WifiMarauderScriptWorker. + * + * @return A pointer to the allocated instance or NULL if allocation fails. + */ +WifiMarauderScriptWorker* wifi_marauder_script_worker_alloc(); + +/** + * @brief Starts the execution of the worker and sets the callback function to be called after each stage is executed. + * + * @param instance A pointer to the instance of WifiMarauderScriptWorker to start. + * @param script Script to be executed + * @return True if the worker was successfully started, false otherwise. + */ +bool wifi_marauder_script_worker_start( + WifiMarauderScriptWorker* instance, + WifiMarauderScript* script); + +/** + * @brief Frees the memory used by the instance of WifiMarauderScriptWorker. + * + * @param script A pointer to the instance of WifiMarauderScriptWorker to free. + */ +void wifi_marauder_script_worker_free(WifiMarauderScriptWorker* script); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index 9958b09eb..97b1d9715 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -81,6 +81,11 @@ WifiMarauderApp* wifi_marauder_app_alloc() { (!storage_file_exists(app->storage, SAVE_PCAP_SETTING_FILEPATH) || !storage_file_exists(app->storage, SAVE_LOGS_SETTING_FILEPATH)); + // Submenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WifiMarauderAppViewSubmenu, submenu_get_view(app->submenu)); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneStart); return app; @@ -100,6 +105,10 @@ void wifi_marauder_make_app_folder(WifiMarauderApp* app) { if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_LOGS)) { dialog_message_show_storage_error(app->dialogs, "Cannot create\npcaps folder"); } + + if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_SCRIPTS)) { + dialog_message_show_storage_error(app->dialogs, "Cannot create\nscripts folder"); + } } void wifi_marauder_load_settings(WifiMarauderApp* app) { @@ -134,10 +143,14 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewTextInput); view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewWidget); + view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); + widget_free(app->widget); text_box_free(app->text_box); furi_string_free(app->text_box_store); wifi_text_input_free(app->text_input); + submenu_free(app->submenu); + variable_item_list_free(app->var_item_list); storage_file_free(app->capture_file); storage_file_free(app->log_file); storage_file_free(app->save_pcap_setting_file); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h index 1bc8f78fe..2a16522bb 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -6,20 +6,27 @@ #include "scenes/wifi_marauder_scene.h" #include "wifi_marauder_custom_event.h" #include "wifi_marauder_uart.h" -#include "wifi_marauder_pcap.h" +#include "file/sequential_file.h" +#include "script/wifi_marauder_script.h" +#include "script/wifi_marauder_script_worker.h" +#include "script/wifi_marauder_script_executor.h" +#include "script/menu/wifi_marauder_script_stage_menu.h" #include #include #include #include +#include #include #include #include "wifi_marauder_text_input.h" +#include #include +#include #include -#define NUM_MENU_ITEMS (17) +#define NUM_MENU_ITEMS (18) #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096) #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) @@ -30,9 +37,17 @@ #define MARAUDER_APP_FOLDER_LOGS MARAUDER_APP_FOLDER "/logs" #define MARAUDER_APP_FOLDER_USER_PCAPS MARAUDER_APP_FOLDER_USER "/pcaps" #define MARAUDER_APP_FOLDER_USER_LOGS MARAUDER_APP_FOLDER_USER "/logs" +#define MARAUDER_APP_FOLDER_SCRIPTS MARAUDER_APP_FOLDER "/scripts" +#define MARAUDER_APP_SCRIPT_PATH(file_name) MARAUDER_APP_FOLDER_SCRIPTS "/" file_name ".json" #define SAVE_PCAP_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_pcaps_here.setting" #define SAVE_LOGS_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_logs_here.setting" +typedef enum WifiMarauderUserInputType { + WifiMarauderUserInputTypeString, + WifiMarauderUserInputTypeNumber, + WifiMarauderUserInputTypeFileName +} WifiMarauderUserInputType; + struct WifiMarauderApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -58,6 +73,7 @@ struct WifiMarauderApp { VariableItemList* var_item_list; Widget* widget; + Submenu* submenu; int open_log_file_page; int open_log_file_num_pages; @@ -73,6 +89,26 @@ struct WifiMarauderApp { bool is_writing_pcap; bool is_writing_log; + // User input + WifiMarauderUserInputType user_input_type; + char** user_input_string_reference; + int* user_input_number_reference; + char* user_input_file_dir; + char* user_input_file_extension; + + // Automation script + WifiMarauderScript* script; + WifiMarauderScriptWorker* script_worker; + FuriString** script_list; + int script_list_count; + WifiMarauderScriptStage* script_edit_selected_stage; + WifiMarauderScriptStageMenu* script_stage_menu; + WifiMarauderScriptStageListItem* script_stage_edit_first_item; + char*** script_stage_edit_strings_reference; + int* script_stage_edit_string_count_reference; + int** script_stage_edit_numbers_reference; + int* script_stage_edit_number_count_reference; + // For input source and destination MAC in targeted deauth attack int special_case_input_step; char special_case_input_src_addr[20]; @@ -105,4 +141,5 @@ typedef enum { WifiMarauderAppViewConsoleOutput, WifiMarauderAppViewTextInput, WifiMarauderAppViewWidget, + WifiMarauderAppViewSubmenu, } WifiMarauderAppView; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h index 79f96b107..5acdfa38e 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h @@ -7,5 +7,6 @@ typedef enum { WifiMarauderEventSaveSourceMac, WifiMarauderEventSaveDestinationMac, WifiMarauderEventStartSettingsInit, - WifiMarauderEventStartLogViewer + WifiMarauderEventStartLogViewer, + WifiMarauderEventStartScriptSelect } WifiMarauderCustomEvent; From f48a2713bc171af2ad30187f391937e712d11c56 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 00:18:12 +0300 Subject: [PATCH 15/30] Add log files icon in wifi marauder --- .../wifi_marauder_companion/assets/Text_10x10.png | Bin 0 -> 158 bytes .../scenes/wifi_marauder_scene_log_viewer.c | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 applications/external/wifi_marauder_companion/assets/Text_10x10.png diff --git a/applications/external/wifi_marauder_companion/assets/Text_10x10.png b/applications/external/wifi_marauder_companion/assets/Text_10x10.png new file mode 100644 index 0000000000000000000000000000000000000000..8e8a6183dd50535729dc9c9b4f220a12dd4c600f GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxB}__|Nk$&IsYz@rRC}3 z7*a7OIiZ2U&CSi=;0cBn1vTatM&Z;3u7g(^G9`qQn09G2aWeNXaKC0S=Q~tg57Z@F z;u=vBoS#-wo>-L1;E+?AmspUPnOCA;ke9BToS%}K{MA`f4ycg9)78&qol`;+00Iau A9smFU literal 0 HcmV?d00001 diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c index f4e84ccc8..6edb4a49d 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c @@ -148,8 +148,10 @@ bool wifi_marauder_scene_log_viewer_on_event(void* context, SceneManagerEvent ev // Browse FuriString* predefined_filepath = furi_string_alloc_set_str(MARAUDER_APP_FOLDER_LOGS); FuriString* selected_filepath = furi_string_alloc(); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".log", &I_Text_10x10); if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, NULL)) { + app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { strncpy( app->log_file_path, furi_string_get_cstr(selected_filepath), From 0a32cd252820d6a32ff7ffebb45bf918e507150a Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 27 Apr 2023 00:27:57 +0300 Subject: [PATCH 16/30] I was outplayed by the C programming language --- lib/nfc/nfc_worker.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 28a1f6827..5a0145bf4 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -661,7 +661,8 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyA, !deactivated, cuid)) { mf_classic_set_key_found(data, i, MfClassicKeyA, key); - FURI_LOG_D(TAG, "Key A found"); + FURI_LOG_D( + TAG, "Key A found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); uint64_t found_key; @@ -683,8 +684,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key A is marked as found and matches the searching key, invalidate it + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + if(mf_classic_is_key_found(data, i, MfClassicKeyA) && - data->block[i].value[0] == key) { + found_key == current_key) { mf_classic_set_key_not_found(data, i, MfClassicKeyA); is_key_a_found = false; FURI_LOG_D(TAG, "Key %dA not found in attack", i); @@ -694,7 +701,8 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { - FURI_LOG_D(TAG, "Key B found"); + FURI_LOG_D( + TAG, "Key B found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); mf_classic_set_key_found(data, i, MfClassicKeyB, key); nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); @@ -702,8 +710,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key B is marked as found and matches the searching key, invalidate it + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value + 10, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + if(mf_classic_is_key_found(data, i, MfClassicKeyB) && - data->block[i].value[10] == key) { + found_key == current_key) { mf_classic_set_key_not_found(data, i, MfClassicKeyB); is_key_b_found = false; FURI_LOG_D(TAG, "Key %dB not found in attack", i); From 102dd690b7ec6b679bfd56d75bfe682af6c81e00 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 27 Apr 2023 00:28:21 +0300 Subject: [PATCH 17/30] Fix emulating empty keys as 0s --- lib/nfc/protocols/mifare_classic.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index d2d7467dc..204e3a5eb 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -892,11 +892,25 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) { - key = nfc_util_bytes2num(sector_trailer->key_a, 6); - access_key = MfClassicKeyA; + if(mf_classic_is_key_found( + &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyA)) { + key = nfc_util_bytes2num(sector_trailer->key_a, 6); + access_key = MfClassicKeyA; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } else { - key = nfc_util_bytes2num(sector_trailer->key_b, 6); - access_key = MfClassicKeyB; + if(mf_classic_is_key_found( + &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyB)) { + key = nfc_util_bytes2num(sector_trailer->key_b, 6); + access_key = MfClassicKeyB; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; From 6119d6e1022adb03f2985fdf4c87c64026f93062 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 02:02:27 +0300 Subject: [PATCH 18/30] OFW PR 2616: Picopass: remove spaces in CSN by bettse --- .../external/picopass/scenes/picopass_scene_read_card_success.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c index 198b21d98..cc18ac066 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c @@ -34,7 +34,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(csn_str, "%02X ", csn[i]); + furi_string_cat_printf(csn_str, "%02X", csn[i]); } bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); From 243edf7e13ed92f1d703d1ccd345622d6be5abed Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 02:40:33 +0300 Subject: [PATCH 19/30] Confirm when removing t5577 password add extra scene --- .../lfrfid/scenes/lfrfid_scene_clear_t5577.c | 9 ++-- .../scenes/lfrfid_scene_clear_t5577_confirm.c | 46 +++++++++++++++++++ .../main/lfrfid/scenes/lfrfid_scene_config.h | 1 + .../scenes/lfrfid_scene_extra_actions.c | 2 +- 4 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index c57373a3a..3b9b41909 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -83,10 +83,11 @@ bool lfrfid_scene_clear_t5577_on_event(void* context, SceneManagerEvent event) { LfRfid* app = context; bool consumed = false; - const uint32_t prev_scene = LfRfidSceneExtraActions; - - if(event.type == SceneManagerEventTypeCustom && event.event == LfRfidEventPopupClosed) { - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, prev_scene); + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom && event.event == LfRfidEventPopupClosed) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, LfRfidSceneExtraActions); consumed = true; } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c new file mode 100644 index 000000000..ca0d43176 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c @@ -0,0 +1,46 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_clear_t5577_confirm_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "Start", lfrfid_widget_callback, app); + widget_add_string_multiline_element( + widget, 64, 22, AlignCenter, AlignBottom, FontPrimary, "Apply tag to\nFlipper's back"); + widget_add_string_multiline_element( + widget, + 64, + 45, + AlignCenter, + AlignBottom, + FontSecondary, + "And don't move it\nwhile process is running"); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); +} + +bool lfrfid_scene_clear_t5577_confirm_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneExtraActions); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(scene_manager, LfRfidSceneClearT5577); + } + } + + return consumed; +} + +void lfrfid_scene_clear_t5577_confirm_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_config.h b/applications/main/lfrfid/scenes/lfrfid_scene_config.h index 54b840844..7789e133e 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_config.h +++ b/applications/main/lfrfid/scenes/lfrfid_scene_config.h @@ -16,6 +16,7 @@ ADD_SCENE(lfrfid, save_data, SaveData) ADD_SCENE(lfrfid, save_type, SaveType) ADD_SCENE(lfrfid, saved_info, SavedInfo) ADD_SCENE(lfrfid, clear_t5577, ClearT5577) +ADD_SCENE(lfrfid, clear_t5577_confirm, ClearT5577Confirm) ADD_SCENE(lfrfid, delete_success, DeleteSuccess) ADD_SCENE(lfrfid, extra_actions, ExtraActions) ADD_SCENE(lfrfid, raw_info, RawInfo) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index bf73eccd3..d2bf20680 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -80,7 +80,7 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexClearT5577) { - scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577); + scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577Confirm); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); From a2b2e486eacc89c57cee56f386544b6521379db1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 04:02:00 +0300 Subject: [PATCH 20/30] Update changelog, docs, readme --- CHANGELOG.md | 59 +++++++++++---------------------- ReadMe.md | 4 +-- documentation/SubGHzSettings.md | 2 ++ 3 files changed, 24 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc2bdc13..f0fa58cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,44 +1,25 @@ ### New changes -* Note: Due to latest official changes this release was delayed - release was tested by group of users, and some of them reported getting one crash on "furi_check failed", but this can not be reproduced in any way we tried, so, please if you found any issues with BLE (+ Mobile app) that results in crash, create issue with all details how you got it and how many times, and detailed steps on repeating such issue, if you got one crash and can't get it again, collect information how it happened and create issue with as much details as possible -> Thanks! -* SubGHz: New option to use timestamps + protocol name when you saving file, instead of random name - Enable in `Radio Settings -> Time in names = ON` -* SubGHz: Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk | PR #429) -* SubGHz: New options to ignore Magellan, Cars(ScherKhan, Kia)(no you can't send that signals) -* SubGHz: Fix keeloq custom buttons bugs -* SubGhz: Nero Radio 57bit **experimental** support + encoder improvements and decoder changes -* SubGhz: Fix RAW recording and reading, (bug where raw file plays endlessly) (Fixes issue #431) -* SubGHz Remote: Add Alutech AT4N Support, fix some issues -* Power GUI: Changing battery style doesnt require reboot (Added API to trigger UI change from different place) (Inspired by @ESurge work) -* Plugins: BLE Remote -> Keynote with vertical layout (by @Kami-no | PR #428) -* Plugins: Improve wifi marauder keyboard (added extra symbols!) (Port uart terminal keyboard into wifi marauder) -* Infrared: Update universal remote assets (by @amec0e | PR #421) -* Docs: Update build docs (by @PhoenixSheppy | PR #425) -* OFW: cubewb: updated to v1.16.0 -> **Part 2 of "Various stop mode fixes"** -* OFW: github: testing SDK with ufbt action -* OFW: Raw RFID documentation -* OFW: Introduce stealth mode and auto-selective lock -* OFW: Active RPC session icon -> **Breaking API change, api was changed from 22.x to 23.x** -* OFW: Various stop mode fixes -> **Should fix known issues with BLE (Random freezes, menu freeze, BT Remote plugin freeze) and other similar issues** -* OFW: Picopass: Correctly aborts when correct key is found -> Fixes Bug (Picopass app not reading elite keyed cards anymore. #413) -### Pre-release changes -* If you have copied apps into `apps` folder - remove `apps` folder on your microSD before installing this release to avoid issues! -* SubGHz: (Bug that I decided to keep as a feature) You can change default button (Ok) for remote by holding custom button and pressing back at same time (same can be used to restore your button if you changed it accidentally) - Be careful, it might be unstable, I will make proper option to change button in next releases -* SubGHz: Fixes for custom button bugs in SubGHz Remote app -* SubGHz: Add alutech table to enviroment alloc and free -* Docs: Fix and update docs - thanks to @lesterrry -* Plugins: Bluetooth Remote - implemented YouTube Shorts Remote (may be unstable) -* Plugins: Bluetooth Remote - improvements and fixes for TikTok remote (by @krolchonok | PR #420 and #432) -* Plugins: Implement an array for baudrates on GPS UART app (+ add 19200 baud) (by @p0ns | PR #416) -* Plugins: Remove UART Echo from releases since it is locked on 115200 baud, and we have **UART Terminal** with ability to set baudrate +* Power + BLE: DeepSleep + required ble stack upgrade added back, all known issues was fixed in OFW, no issues was found during our tests +* Desktop: Allow locking without pin using Up menu on desktop (Short click on `Lock` = Without PIN / Long = With PIN) +* RFID: Add confirmation message before running `Clear T5577 Password` +* RFID: Add more user friendly RAW emulation via UI [(by Dan Caprita)](https://forum.flipperzero.one/t/electra-intercom/6368/43) +* SubGHz: Fixed `Frequency Analyzer` issues, fixed `Read` mode issues +* SubGHz: Fix NFC crash when using external CC1101 radio module +* SubGHz: Fix multiple external CC1101 radio module issues, (int callbacks, SPI handlers init/reinit) +* SubGHz: Using scene manager function in add manually (by @gid9798 | PR #437) +* Plugins: ESP32: WiFi Marauder - add icon for log files in logs browser +* Plugins: Update **ESP32: WiFi Marauder companion** plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) merged [PR by @tcpassos](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/11) * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) -* Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) -* OFW: Deep Sleep Idle - **Improves battery usage!!!** -> **Breaking API change, api was changed from 21.x to 22.x** -* OFW: FuriHal: pwr pulls for some pins -* OFW: Bugfix: ISP Programmer and SubGhz -* OFW: AVR_ISP: fix NULL pointer dereference -* OFW: Fix gpio state isp programmer -* OFW: ufbt: project & debugging updates -* OFW: FuriHal: fix gpio naming and add explicit pulls for vibro, speaker and ir_tx -> **Breaking API change, api was changed from 20.x to 21.x** -**(this will make your manually copied plugins not work, update them in same way you installed them, or delete `apps` folder and then install firmware, if you using extra pack builds (with `e` in version) all apps in _Extra will be updated automatically)** +* Plugins: Fix RFID Fuzzer and iButton Fuzzer crashes +* Infrared: Updated infrared assets (by @amec0e | PR #441) +* Docs: Update **How To Install** images (by @krolchonok | PR #436) +* OFW PR 2620: NFC: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys (by Astrrra) +* OFW PR 2616: Picopass: remove spaces in CSN (by bettse) +* OFW PR 2604: WS: add protocol "Wendox W6726" (by Skorpionm) +* OFW PR 2607: BadUSB: command parser fix (by nminaylov) +* OFW: FuriHal: use proper divider for core2 when transition to sleep, remove extra stop mode transition checks, cleanup code. Furi: proper assert and check messages. +* OFW: Don't reboot on crash in debug builds +* OFW: cubewb: downgraded to v1.15.0 #### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) diff --git a/ReadMe.md b/ReadMe.md index fea644f92..1d42e52a3 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -69,12 +69,12 @@ Our Discord Community: Also check the changelog in releases for latest updates! ### Current modified and new Sub-GHz protocols list: -Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols. +Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols in OFW. Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b) Encoders or sending made by @xMasterX: -- Nero Radio 57bit (experimental) (+ 56bit encoder improvements) +- Nero Radio 57bit (+ 56bit encoder improvements) - Keeloq: HCS101 - Keeloq: AN-Motors - Keeloq: JCM Tech diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md index 41cf0d6bc..adf72067b 100644 --- a/documentation/SubGHzSettings.md +++ b/documentation/SubGHzSettings.md @@ -76,6 +76,8 @@ Change that line to `Add_standard_frequencies: false` +**You need to have custom frequencies added in both lists! in main frequency list and in hopping list! Replacing only hopping freqs will not work with that setting set on false, you need to add something in main list since it will be empty** + ### To add your own frequency to user list Just add new line `Frequency: 928000000` - where `928000000` is your frequency, keep it in that format! it should be 9 digits! From 408edb3e99751b5374571340c9517027845a65fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 27 Apr 2023 23:01:13 +0900 Subject: [PATCH 21/30] Keep HSI16 working in stop mode. (#2621) --- firmware/targets/f7/furi_hal/furi_hal_clock.c | 3 ++- firmware/targets/f7/furi_hal/furi_hal_power.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index a4df4877e..a76fbfbca 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -144,6 +144,7 @@ void furi_hal_clock_init() { LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1); LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_PLLSAI1); + LL_RCC_HSI_EnableInStopMode(); // Ensure that MR is capable of work in STOP0 LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1); LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); @@ -207,8 +208,8 @@ void furi_hal_clock_switch_to_hsi() { while(!LL_RCC_HSI_IsReady()) ; - LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 7d9334c2c..e380de7fa 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -94,6 +94,7 @@ void furi_hal_power_init() { LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); + LL_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); LL_C2_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); From 1afa3916acb465e6f68929f0a9b7b259b28ef148 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 20:21:04 +0300 Subject: [PATCH 22/30] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0fa58cac..197bd29f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * OFW PR 2616: Picopass: remove spaces in CSN (by bettse) * OFW PR 2604: WS: add protocol "Wendox W6726" (by Skorpionm) * OFW PR 2607: BadUSB: command parser fix (by nminaylov) +* OFW: Keep HSI16 working in stop mode. * OFW: FuriHal: use proper divider for core2 when transition to sleep, remove extra stop mode transition checks, cleanup code. Furi: proper assert and check messages. * OFW: Don't reboot on crash in debug builds * OFW: cubewb: downgraded to v1.15.0 From 6ed182013d42bfc46789fd25820fc80838e0d0cf Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 21:12:02 +0300 Subject: [PATCH 23/30] Sync ibutton fuzzer code PR with key updates from @team-orangeBlue --- CHANGELOG.md | 1 + .../scene/ibtnfuzzer_scene_run_attack.c | 19 +++++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 197bd29f0..f613f6eda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Plugins: Update **ESP32: WiFi Marauder companion** plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) merged [PR by @tcpassos](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/11) * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) * Plugins: Fix RFID Fuzzer and iButton Fuzzer crashes +* Plugins: iButton Fuzzer default keys update (by @team-orangeBlue) * Infrared: Updated infrared assets (by @amec0e | PR #441) * Docs: Update **How To Install** images (by @krolchonok | PR #436) * OFW PR 2620: NFC: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys (by Astrrra) diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c index 1cab8b04e..92975a427 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c @@ -3,7 +3,7 @@ uint8_t counter = 0; -uint8_t id_list_ds1990[25][8] = { +uint8_t id_list_ds1990[18][8] = { {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает @@ -19,16 +19,9 @@ uint8_t id_list_ds1990[25][8] = { {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF + {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 + {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni }; uint8_t id_list_metakom[17][4] = { @@ -51,7 +44,7 @@ uint8_t id_list_metakom[17][4] = { {0xCA, 0xCA, 0xCA, 0xCA}, // ?? }; -uint8_t id_list_cyfral[14][2] = { +uint8_t id_list_cyfral[16][2] = { {0x00, 0x00}, // Null bytes {0xFF, 0xFF}, // Only FF {0x11, 0x11}, // Only 11 @@ -66,6 +59,8 @@ uint8_t id_list_cyfral[14][2] = { {0x12, 0x34}, // Incremental UID {0x56, 0x34}, // Decremental UID {0xCA, 0xCA}, // ?? + {0x8E, 0xC9}, // Elevator code + {0x6A, 0x50}, // VERY fresh code from smartkey }; void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) { From d85e097ee5c9bcaf70f78519eeefbc72477fedb8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 21:45:30 +0300 Subject: [PATCH 24/30] Fix ibutton fuzzer stop values --- .../external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c index 92975a427..13ec6e6be 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c @@ -125,7 +125,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { context->payload[6] = id_list_ds1990[context->attack_step][6]; context->payload[7] = id_list_ds1990[context->attack_step][7]; - if(context->attack_step == 24) { + if(context->attack_step == 17) { context->attack_step = 0; counter = 0; context->is_attacking = false; @@ -155,7 +155,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { context->payload[0] = id_list_cyfral[context->attack_step][0]; context->payload[1] = id_list_cyfral[context->attack_step][1]; - if(context->attack_step == 13) { + if(context->attack_step == 15) { context->attack_step = 0; counter = 0; context->is_attacking = false; From 41895118bd5a73f5358d60ce89f337bec57f1d4d Mon Sep 17 00:00:00 2001 From: ushastoe <40743392+krolchonok@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:28:40 +0300 Subject: [PATCH 25/30] Update HowToInstall.md 46 version --- documentation/HowToInstall.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index a650d97ee..abbca0830 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -23,7 +23,7 @@ after that on web updater page - press `Connect` button - And wait, if all flashed successfully - you will have all needed assets pre installed - Done -![web](https://user-images.githubusercontent.com/40743392/233779040-596ad9a9-87cd-4b19-a831-16fdc678ba35.png) +![web](https://user-images.githubusercontent.com/40743392/235004244-30a59649-fa52-4db0-bf8f-ef5290951aa0.png)
@@ -40,7 +40,7 @@ after that on web updater page - press `Connect` button - Error in ios app will show up, but flipper will be updated successfully - And if all flashed successfully - you will have all needed assets pre installed - Done -![ios](https://user-images.githubusercontent.com/40743392/233779071-10e37af5-a7d9-41d6-81cd-9bb660352b4d.png) +![ios](https://user-images.githubusercontent.com/40743392/235004287-bf739fb9-ca77-4843-a235-4614832e9ae5.png)

@@ -56,7 +56,7 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done -![andro_tgz](https://user-images.githubusercontent.com/40743392/233782806-e81c9634-4694-4faf-88ce-08b89a8b6fa0.png) +![andro_tgz](https://user-images.githubusercontent.com/40743392/235004340-325a97da-7e3d-4821-892c-705f661bfddc.png)

@@ -71,7 +71,7 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done -![androweb](https://user-images.githubusercontent.com/40743392/233782906-1f8f1ebf-c488-4d9f-9a6f-67a4e693cbda.png) +![androweb](https://user-images.githubusercontent.com/40743392/235004374-bef68a66-1b10-4688-a506-8753660c2ebc.png)
@@ -90,7 +90,7 @@ after that on web updater page - press `Connect` button - And wait, if all flashed successfully - you will have all needed assets pre installed - Done -![qflip](https://user-images.githubusercontent.com/40743392/233779331-3f21c662-6e77-42e5-a928-f5441bd85bd4.png) +![qflip](https://user-images.githubusercontent.com/40743392/235004392-8e824589-d0fa-444c-a592-098ff629f8c6.png) @@ -111,7 +111,7 @@ after that on web updater page - press `Connect` button - Update will start, wait for all stages - Done -![manual](https://user-images.githubusercontent.com/40743392/233779363-faf5b35b-e136-4dca-b1fb-15188b26eb6a.png) +![manual](https://user-images.githubusercontent.com/40743392/235004403-cc82a02b-06a3-4cbe-9420-8eb0ad1f4659.png) From b31e955744d329601b087c0099d7c1734f0980cc Mon Sep 17 00:00:00 2001 From: ushastoe <40743392+krolchonok@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:39:58 +0300 Subject: [PATCH 26/30] Update HowToInstall.md --- documentation/HowToInstall.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index abbca0830..9b762ea11 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -23,7 +23,7 @@ after that on web updater page - press `Connect` button - And wait, if all flashed successfully - you will have all needed assets pre installed - Done -![web](https://user-images.githubusercontent.com/40743392/235004244-30a59649-fa52-4db0-bf8f-ef5290951aa0.png) +![web](https://user-images.githubusercontent.com/40743392/235005830-98ceda39-a143-47ef-ad4d-5489bc3df98b.png)
@@ -40,7 +40,7 @@ after that on web updater page - press `Connect` button - Error in ios app will show up, but flipper will be updated successfully - And if all flashed successfully - you will have all needed assets pre installed - Done -![ios](https://user-images.githubusercontent.com/40743392/235004287-bf739fb9-ca77-4843-a235-4614832e9ae5.png) +![ios](https://user-images.githubusercontent.com/40743392/235005844-bea8f2fd-f50d-41b1-9191-e3842d8658d2.png)

@@ -56,7 +56,8 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done -![andro_tgz](https://user-images.githubusercontent.com/40743392/235004340-325a97da-7e3d-4821-892c-705f661bfddc.png) +![andro_tgz](https://user-images.githubusercontent.com/40743392/235005877-d4f5f73c-241c-4a7b-a51d-b8407983856c.png) +

@@ -71,7 +72,7 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done -![androweb](https://user-images.githubusercontent.com/40743392/235004374-bef68a66-1b10-4688-a506-8753660c2ebc.png) +![androweb](https://user-images.githubusercontent.com/40743392/235005891-19ef6bb6-094f-437d-afcd-75d60921e3c4.png)
@@ -89,8 +90,7 @@ after that on web updater page - press `Connect` button - Update will start - And wait, if all flashed successfully - you will have all needed assets pre installed - Done - -![qflip](https://user-images.githubusercontent.com/40743392/235004392-8e824589-d0fa-444c-a592-098ff629f8c6.png) +![qflip](https://user-images.githubusercontent.com/40743392/235005910-819abd34-65d4-4aaa-a11c-9c28bea737e9.png) @@ -110,8 +110,8 @@ after that on web updater page - press `Connect` button `update/f7-update-(CURRENT VERSION)/update.fuf` - Update will start, wait for all stages - Done +![manual](https://user-images.githubusercontent.com/40743392/235005942-c4debf85-b251-41c1-ad0b-c7bd94b999fb.png) -![manual](https://user-images.githubusercontent.com/40743392/235004403-cc82a02b-06a3-4cbe-9420-8eb0ad1f4659.png) From 7b21dd7082fddf58542e1a06ebcd1a0ad711e845 Mon Sep 17 00:00:00 2001 From: ushastoe <40743392+krolchonok@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:41:03 +0300 Subject: [PATCH 27/30] Update HowToInstall.md --- documentation/HowToInstall.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index 9b762ea11..d4dcc3bbc 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -110,7 +110,8 @@ after that on web updater page - press `Connect` button `update/f7-update-(CURRENT VERSION)/update.fuf` - Update will start, wait for all stages - Done -![manual](https://user-images.githubusercontent.com/40743392/235005942-c4debf85-b251-41c1-ad0b-c7bd94b999fb.png) +![manual](https://user-images.githubusercontent.com/40743392/235006093-5f76f28c-6159-4785-a7ca-a06354dffab6.png) + From c0de75367f7b4209585648a2506aaeec165c0b38 Mon Sep 17 00:00:00 2001 From: ushastoe <40743392+krolchonok@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:43:27 +0300 Subject: [PATCH 28/30] Update HowToInstall.md --- documentation/HowToInstall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index d4dcc3bbc..656d4cc4b 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -110,7 +110,7 @@ after that on web updater page - press `Connect` button `update/f7-update-(CURRENT VERSION)/update.fuf` - Update will start, wait for all stages - Done -![manual](https://user-images.githubusercontent.com/40743392/235006093-5f76f28c-6159-4785-a7ca-a06354dffab6.png) +![manual](https://user-images.githubusercontent.com/40743392/235006410-19eaf58e-2425-4e8e-8ec9-973bda362c47.png) From 96375e8244a1e90bf6359e040677425dbc36968d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 28 Apr 2023 14:00:40 +0300 Subject: [PATCH 29/30] Version instead of branch --- firmware/targets/f7/ble_glue/dev_info_service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c index 8bdb2eea8..d24058632 100644 --- a/firmware/targets/f7/ble_glue/dev_info_service.c +++ b/firmware/targets/f7/ble_glue/dev_info_service.c @@ -33,7 +33,7 @@ void dev_info_svc_start() { dev_info_svc->version_string = furi_string_alloc_printf( "%s %s %s %s", version_get_githash(NULL), - version_get_gitbranch(NULL), + version_get_version(NULL), version_get_gitbranchnum(NULL), version_get_builddate(NULL)); snprintf( From e87256e01f3e289c6eb60badd4ae88e190369da5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 28 Apr 2023 14:04:16 +0300 Subject: [PATCH 30/30] Update TOTP --- .../services/config/token_info_iterator.c | 16 ++++++-------- .../external/totp/services/hmac/hmac_common.h | 1 - .../external/totp/services/hmac/hmac_sha256.c | 1 + .../external/totp/services/hmac/sha1.c | 8 +++---- .../external/totp/services/hmac/sha256.c | 7 ++----- .../external/totp/services/hmac/sha512.c | 7 ++----- .../totp/services/hmac/sha_pad_buffer.c | 11 ++++++++++ .../totp/services/hmac/sha_pad_buffer.h | 4 ++++ applications/external/totp/types/token_info.c | 21 +++++++++++++++++++ applications/external/totp/types/token_info.h | 12 +++++++++-- 10 files changed, 60 insertions(+), 28 deletions(-) create mode 100644 applications/external/totp/services/hmac/sha_pad_buffer.c create mode 100644 applications/external/totp/services/hmac/sha_pad_buffer.h diff --git a/applications/external/totp/services/config/token_info_iterator.c b/applications/external/totp/services/config/token_info_iterator.c index 9b7dd5550..f8cd3c64e 100644 --- a/applications/external/totp/services/config/token_info_iterator.c +++ b/applications/external/totp/services/config/token_info_iterator.c @@ -68,7 +68,9 @@ static bool seek_to_token(size_t token_index, TokenInfoIteratorContext* context) direction = StreamDirectionBackward; } - stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart); + if(!stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart)) { + return false; + } if(token_index_diff != 0) { long i = 0; @@ -89,10 +91,6 @@ static bool seek_to_token(size_t token_index, TokenInfoIteratorContext* context) context->last_seek_offset = stream_tell(stream); context->last_seek_index = token_index; - } else { - if(!stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart)) { - return false; - } } return true; @@ -495,11 +493,9 @@ bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t to } uint32_t temp_data32; - if(flipper_format_read_uint32( - context->config_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &temp_data32, 1) && - temp_data32 <= STEAM) { - tokenInfo->algo = (TokenHashAlgo)temp_data32; - } else { + if(!flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &temp_data32, 1) || + !token_info_set_algo_from_int(tokenInfo, temp_data32)) { tokenInfo->algo = SHA1; } diff --git a/applications/external/totp/services/hmac/hmac_common.h b/applications/external/totp/services/hmac/hmac_common.h index 0cd56ed99..3499cb800 100644 --- a/applications/external/totp/services/hmac/hmac_common.h +++ b/applications/external/totp/services/hmac/hmac_common.h @@ -1,5 +1,4 @@ #include -#include "sha256.h" #include "memxor.h" #define IPAD 0x36 diff --git a/applications/external/totp/services/hmac/hmac_sha256.c b/applications/external/totp/services/hmac/hmac_sha256.c index c51f24b4d..00ac2a177 100644 --- a/applications/external/totp/services/hmac/hmac_sha256.c +++ b/applications/external/totp/services/hmac/hmac_sha256.c @@ -15,6 +15,7 @@ along with this program. If not, see . */ #include "hmac_sha256.h" +#include "sha256.h" #define GL_HMAC_NAME 256 #define GL_HMAC_BLOCKSIZE 64 diff --git a/applications/external/totp/services/hmac/sha1.c b/applications/external/totp/services/hmac/sha1.c index ecf22fc97..29f22e3c3 100644 --- a/applications/external/totp/services/hmac/sha1.c +++ b/applications/external/totp/services/hmac/sha1.c @@ -27,6 +27,8 @@ #include #include +#include "sha_pad_buffer.h" + #ifdef WORDS_BIGENDIAN #define SWAP(n) (n) #else @@ -34,10 +36,6 @@ #define SWAP(n) swap_uint32(n) #endif -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. (RFC 1321, 3.1: Step 1) */ -static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */}; - /* Take a pointer to a 160 bit block of data (five 32 bit ints) and initialize it to the start constants of the SHA1 algorithm. This must be called before using hash in the call to sha1_hash. */ @@ -87,7 +85,7 @@ void* sha1_finish_ctx(struct sha1_ctx* ctx, void* resbuf) { ctx->buffer[size - 2] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)); ctx->buffer[size - 1] = SWAP(ctx->total[0] << 3); - memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); /* Process last bytes. */ sha1_process_block(ctx->buffer, size * 4, ctx); diff --git a/applications/external/totp/services/hmac/sha256.c b/applications/external/totp/services/hmac/sha256.c index 89ca67c2b..09ba272e7 100644 --- a/applications/external/totp/services/hmac/sha256.c +++ b/applications/external/totp/services/hmac/sha256.c @@ -25,6 +25,7 @@ #include #include +#include "sha_pad_buffer.h" #ifdef WORDS_BIGENDIAN #define SWAP(n) (n) @@ -33,10 +34,6 @@ #define SWAP(n) swap_uint32(n) #endif -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. */ -static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */}; - /* Takes a pointer to a 256 bit block of data (eight 32 bit ints) and initializes it to the start constants of the SHA256 algorithm. This @@ -91,7 +88,7 @@ static void sha256_conclude_ctx(struct sha256_ctx* ctx) { set_uint32((char*)&ctx->buffer[size - 2], SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29))); set_uint32((char*)&ctx->buffer[size - 1], SWAP(ctx->total[0] << 3)); - memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); /* Process last bytes. */ sha256_process_block(ctx->buffer, size * 4, ctx); diff --git a/applications/external/totp/services/hmac/sha512.c b/applications/external/totp/services/hmac/sha512.c index b56dd0f2e..ffe2864fb 100644 --- a/applications/external/totp/services/hmac/sha512.c +++ b/applications/external/totp/services/hmac/sha512.c @@ -27,13 +27,10 @@ #include #include "byteswap.h" +#include "sha_pad_buffer.h" #define SWAP(n) swap_uint64(n) -/* This array contains the bytes used to pad the buffer to the next - 128-byte boundary. */ -static const unsigned char fillbuf[128] = {0x80, 0 /* , 0, 0, ... */}; - /* Takes a pointer to a 512 bit block of data (eight 64 bit ints) and initializes it to the start constants of the SHA512 algorithm. This @@ -90,7 +87,7 @@ static void sha512_conclude_ctx(struct sha512_ctx* ctx) { SWAP(u64or(u64shl(ctx->total[1], 3), u64shr(ctx->total[0], 61)))); set_uint64((char*)&ctx->buffer[size - 1], SWAP(u64shl(ctx->total[0], 3))); - memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 8 - bytes); + sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 8 - bytes); /* Process last bytes. */ sha512_process_block(ctx->buffer, size * 8, ctx); diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.c b/applications/external/totp/services/hmac/sha_pad_buffer.c new file mode 100644 index 000000000..badedbcc7 --- /dev/null +++ b/applications/external/totp/services/hmac/sha_pad_buffer.c @@ -0,0 +1,11 @@ +#include "sha_pad_buffer.h" +#include + +void sha_pad_buffer(uint8_t* buffer, size_t size) { + if(size > 0) { + buffer[0] = 0x80; + if(size > 1) { + memset(&buffer[1], 0, size - 1); + } + } +} \ No newline at end of file diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.h b/applications/external/totp/services/hmac/sha_pad_buffer.h new file mode 100644 index 000000000..7dba40fa9 --- /dev/null +++ b/applications/external/totp/services/hmac/sha_pad_buffer.h @@ -0,0 +1,4 @@ +#include +#include + +void sha_pad_buffer(uint8_t* buffer, size_t size); \ No newline at end of file diff --git a/applications/external/totp/types/token_info.c b/applications/external/totp/types/token_info.c index 2f108033b..6810d0211 100644 --- a/applications/external/totp/types/token_info.c +++ b/applications/external/totp/types/token_info.c @@ -117,6 +117,27 @@ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) return false; } +bool token_info_set_algo_from_int(TokenInfo* token_info, uint8_t algo_code) { + switch(algo_code) { + case SHA1: + token_info->algo = SHA1; + break; + case SHA256: + token_info->algo = SHA256; + break; + case SHA512: + token_info->algo = SHA512; + break; + case STEAM: + token_info->algo = STEAM; + break; + default: + return false; + } + + return true; +} + char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { switch(token_info->algo) { case SHA1: diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index 138ad32b1..0d73dd061 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -168,7 +168,7 @@ void token_info_free(TokenInfo* token_info); /** * @brief Encrypts & sets plain token secret to the given instance of \c TokenInfo * @param token_info instance where secret should be updated - * @param base32_token_secret plain token secret in Base32 format + * @param plain_token_secret plain token secret * @param token_secret_length plain token secret length * @param plain_token_secret_encoding plain token secret encoding * @param iv initialization vecor (IV) to be used for encryption @@ -201,10 +201,18 @@ bool token_info_set_duration_from_int(TokenInfo* token_info, uint8_t duration); * @brief Sets token hashing algorithm from \c str value * @param token_info instance whichs token hashing algorithm should be updated * @param str desired token algorithm - * @return \c true if token hahsing algorithm has been updated; \c false otherwise + * @return \c true if token hashing algorithm has been updated; \c false otherwise */ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str); +/** + * @brief Sets token hashing algorithm from \c algo_code code + * @param token_info instance whichs token hashing algorithm should be updated + * @param algo_code desired token algorithm code + * @return \c true if token hashing algorithm has been updated; \c false otherwise + */ +bool token_info_set_algo_from_int(TokenInfo* token_info, uint8_t algo_code); + /** * @brief Gets token hahsing algorithm name as C-string * @param token_info instance which token hahsing algorithm name should be returned