diff --git a/applications/plugins/passgen/LICENSE b/applications/plugins/passgen/LICENSE new file mode 100644 index 000000000..85e363072 --- /dev/null +++ b/applications/plugins/passgen/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Skurydin Alexey + +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. diff --git a/applications/plugins/passgen/README.md b/applications/plugins/passgen/README.md new file mode 100644 index 000000000..fbeb41680 --- /dev/null +++ b/applications/plugins/passgen/README.md @@ -0,0 +1,4 @@ +# flipper_passgen +This is a simple Password Generator plugin (**fap**) for the [Flipper Zero](https://www.flipperzero.one). + +![preview](images/preview.png) \ No newline at end of file diff --git a/applications/plugins/passgen/application.fam b/applications/plugins/passgen/application.fam new file mode 100644 index 000000000..89b7b9d33 --- /dev/null +++ b/applications/plugins/passgen/application.fam @@ -0,0 +1,12 @@ +App( + appid="passgen", + name="Password Generator", + apptype=FlipperAppType.PLUGIN, + entry_point="passgenapp", + requires=[ + "gui", + ], + fap_category="Tools", + fap_icon="icons/passgen_icon.png", + fap_icon_assets="icons", +) \ No newline at end of file diff --git a/applications/plugins/passgen/icons/Horizontal_arrow_9x7.png b/applications/plugins/passgen/icons/Horizontal_arrow_9x7.png new file mode 100644 index 000000000..caca88718 Binary files /dev/null and b/applications/plugins/passgen/icons/Horizontal_arrow_9x7.png differ diff --git a/applications/plugins/passgen/icons/Ok_btn_9x9.png b/applications/plugins/passgen/icons/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/applications/plugins/passgen/icons/Ok_btn_9x9.png differ diff --git a/applications/plugins/passgen/icons/Pin_back_arrow_10x8.png b/applications/plugins/passgen/icons/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/applications/plugins/passgen/icons/Pin_back_arrow_10x8.png differ diff --git a/applications/plugins/passgen/icons/Vertical_arrow_7x9.png b/applications/plugins/passgen/icons/Vertical_arrow_7x9.png new file mode 100644 index 000000000..b889fc8f3 Binary files /dev/null and b/applications/plugins/passgen/icons/Vertical_arrow_7x9.png differ diff --git a/applications/plugins/passgen/icons/passgen_icon.png b/applications/plugins/passgen/icons/passgen_icon.png new file mode 100644 index 000000000..1ed4f77fc Binary files /dev/null and b/applications/plugins/passgen/icons/passgen_icon.png differ diff --git a/applications/plugins/passgen/images/preview.png b/applications/plugins/passgen/images/preview.png new file mode 100644 index 000000000..3224b0a8d Binary files /dev/null and b/applications/plugins/passgen/images/preview.png differ diff --git a/applications/plugins/passgen/passgen.c b/applications/plugins/passgen/passgen.c new file mode 100644 index 000000000..aed42466f --- /dev/null +++ b/applications/plugins/passgen/passgen.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include +#include +#include + +#define PASSGEN_MAX_LENGTH 16 +#define PASSGEN_CHARACTERS_LENGTH (26*4) + +#define PASSGEN_DIGITS "0123456789" +#define PASSGEN_LETTERS_LOW "abcdefghijklmnopqrstuvwxyz" +#define PASSGEN_LETTERS_UP "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define PASSGEN_SPECIAL "!#$%^&*.-_" + +typedef enum PassGen_Alphabet +{ + Digits = 1, + Lowercase = 2, + + Uppercase = 4, + Special = 8, + + DigitsLower = Digits | Lowercase, + DigitsAllLetters = Digits | Lowercase | Uppercase, + Mixed = DigitsAllLetters | Special +} PassGen_Alphabet; + +const int AlphabetLevels[] = { Digits, Lowercase, DigitsLower, DigitsAllLetters, Mixed }; +const char* AlphabetLevelNames[] = { "1234", "abcd", "ab12", "Ab12", "Ab1#" }; +const int AlphabetLevelsCount = sizeof(AlphabetLevels) / sizeof(int); + +const NotificationSequence PassGen_Alert_vibro = { + &message_vibro_on, + &message_blue_255, + &message_delay_50, + &message_vibro_off, + NULL, +}; + +typedef struct { + FuriMessageQueue* input_queue; + ViewPort* view_port; + Gui* gui; + FuriMutex** mutex; + NotificationApp* notify; + char password[PASSGEN_MAX_LENGTH+1]; + char alphabet[PASSGEN_CHARACTERS_LENGTH+1]; + int length; + int level; +} PassGen; + +void state_free(PassGen* app) { + gui_remove_view_port(app->gui, app->view_port); + furi_record_close(RECORD_GUI); + view_port_free(app->view_port); + furi_message_queue_free(app->input_queue); + furi_mutex_free(app->mutex); + furi_record_close(RECORD_NOTIFICATION); + free(app); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + PassGen* app = ctx; + if(input_event->type == InputTypeShort) { + furi_message_queue_put(app->input_queue, input_event, 0); + } +} + +static void render_callback(Canvas* canvas, void* ctx) { + char str_length[8]; + PassGen* app = ctx; + furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk); + + canvas_clear(canvas); + canvas_draw_box(canvas, 0, 0, 128, 14); + canvas_set_color(canvas, ColorWhite); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 11, "Password Generator"); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignCenter, app->password); + + // Navigation menu: + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 96, 52, &I_Pin_back_arrow_10x8); + canvas_draw_str(canvas, 108, 60, "Exit"); + + canvas_draw_icon(canvas, 54, 52, &I_Vertical_arrow_7x9); + canvas_draw_str(canvas, 64, 60, AlphabetLevelNames[app->level]); + + snprintf(str_length, sizeof(str_length), "Len: %d", app->length); + canvas_draw_icon(canvas, 4, 53, &I_Horizontal_arrow_9x7); + canvas_draw_str(canvas, 15, 60, str_length); + + furi_mutex_release(app->mutex); +} + +void build_alphabet(PassGen* app) +{ + PassGen_Alphabet mode = AlphabetLevels[app->level]; + app->alphabet[0] = '\0'; + if ((mode & Digits) != 0) + strcat(app->alphabet, PASSGEN_DIGITS); + if ((mode & Lowercase) != 0) + strcat(app->alphabet, PASSGEN_LETTERS_LOW); + if ((mode & Uppercase) != 0) + strcat(app->alphabet, PASSGEN_LETTERS_UP); + if ((mode & Special) != 0) + strcat(app->alphabet, PASSGEN_SPECIAL); +} + +PassGen* state_init() { + PassGen* app = malloc(sizeof(PassGen)); + app->length = 8; + app->level = 2; + build_alphabet(app); + app->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + app->view_port = view_port_alloc(); + app->gui = furi_record_open(RECORD_GUI); + app->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + view_port_input_callback_set(app->view_port, input_callback, app); + view_port_draw_callback_set(app->view_port, render_callback, app); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + + app->notify = furi_record_open(RECORD_NOTIFICATION); + + return app; +} + +void generate(PassGen* app) +{ + int hi = strlen(app->alphabet); + for (int i=0; ilength; i++) + { + int x = rand() % hi; + app->password[i]=app->alphabet[x]; + } + app->password[app->length] = '\0'; +} + +void update_password(PassGen* app, bool vibro) +{ + generate(app); + + if (vibro) + notification_message(app->notify, &PassGen_Alert_vibro); + else + notification_message(app->notify, &sequence_blink_blue_100); + view_port_update(app->view_port); +} + +int32_t passgenapp(void) { + PassGen* app = state_init(); + generate(app); + + while(1) { + InputEvent input; + while(furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk) { + furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk); + + if (input.type == InputTypeShort) + { + switch (input.key) { + case InputKeyBack: + furi_mutex_release(app->mutex); + state_free(app); + return 0; + case InputKeyDown: + if (app->level > 0) + { + app->level--; + build_alphabet(app); + update_password(app, false); + } + else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyUp: + if (app->level < AlphabetLevelsCount - 1) + { + app->level++; + build_alphabet(app); + update_password(app, false); + } + else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyLeft: + if (app->length > 1) + { + app->length--; + update_password(app, false); + } + else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyRight: + if (app->length < PASSGEN_MAX_LENGTH) + { + app->length++; + update_password(app, false); + } + else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyOk: + update_password(app, true); + break; + default: + break; + } + } + furi_mutex_release(app->mutex); + } + } + state_free(app); + return 0; +}