11#include <furi.h>
2+ #include <furi_hal_random.h>
23#include <gui/gui.h>
34#include <gui/elements.h>
45#include <input/input.h>
@@ -27,6 +28,25 @@ typedef enum PassGen_Alphabet
2728 Mixed = DigitsAllLetters | Special
2829} PassGen_Alphabet ;
2930
31+ const char * const PassGen_AlphabetChars [16 ] = {
32+ "0" , // invalid value
33+ /* PASSGEN_SPECIAL PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW */ PASSGEN_DIGITS ,
34+ /* PASSGEN_SPECIAL PASSGEN_LETTERS_UP */ PASSGEN_LETTERS_LOW /* PASSGEN_DIGITS */ ,
35+ /* PASSGEN_SPECIAL PASSGEN_LETTERS_UP */ PASSGEN_LETTERS_LOW PASSGEN_DIGITS ,
36+ /* PASSGEN_SPECIAL */ PASSGEN_LETTERS_UP /* PASSGEN_LETTERS_LOW PASSGEN_DIGITS */ ,
37+ /* PASSGEN_SPECIAL */ PASSGEN_LETTERS_UP /* PASSGEN_LETTERS_LOW */ PASSGEN_DIGITS ,
38+ /* PASSGEN_SPECIAL */ PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW /* PASSGEN_DIGITS */ ,
39+ /* PASSGEN_SPECIAL */ PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW PASSGEN_DIGITS ,
40+ PASSGEN_SPECIAL /* PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW PASSGEN_DIGITS */ ,
41+ PASSGEN_SPECIAL /* PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW */ PASSGEN_DIGITS ,
42+ PASSGEN_SPECIAL /* PASSGEN_LETTERS_UP */ PASSGEN_LETTERS_LOW /* PASSGEN_DIGITS */ ,
43+ PASSGEN_SPECIAL /* PASSGEN_LETTERS_UP */ PASSGEN_LETTERS_LOW PASSGEN_DIGITS ,
44+ PASSGEN_SPECIAL PASSGEN_LETTERS_UP /* PASSGEN_LETTERS_LOW PASSGEN_DIGITS */ ,
45+ PASSGEN_SPECIAL PASSGEN_LETTERS_UP /* PASSGEN_LETTERS_LOW */ PASSGEN_DIGITS ,
46+ PASSGEN_SPECIAL PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW /* PASSGEN_DIGITS */ ,
47+ PASSGEN_SPECIAL PASSGEN_LETTERS_UP PASSGEN_LETTERS_LOW PASSGEN_DIGITS ,
48+ };
49+
3050const int AlphabetLevels [] = { Digits , Lowercase , DigitsLower , DigitsAllLetters , Mixed };
3151const char * AlphabetLevelNames [] = { "1234" , "abcd" , "ab12" , "Ab12" , "Ab1#" };
3252const int AlphabetLevelsCount = sizeof (AlphabetLevels ) / sizeof (int );
@@ -45,13 +65,18 @@ typedef struct {
4565 Gui * gui ;
4666 FuriMutex * * mutex ;
4767 NotificationApp * notify ;
68+ const char * alphabet ;
4869 char password [PASSGEN_MAX_LENGTH + 1 ];
49- char alphabet [PASSGEN_CHARACTERS_LENGTH + 1 ];
50- int length ;
70+ int length ; // must be <= PASSGEN_MAX_LENGTH
5171 int level ;
5272} PassGen ;
5373
5474void state_free (PassGen * app ) {
75+ // NOTE: would have preferred if a "safe" memset() was available...
76+ // but, since cannot prevent optimization from removing
77+ // memset(), fill with random data instead.
78+ furi_hal_random_fill_buf ((void * )(app -> password ), PASSGEN_MAX_LENGTH );
79+
5580 gui_remove_view_port (app -> gui , app -> view_port );
5681 furi_record_close (RECORD_GUI );
5782 view_port_free (app -> view_port );
@@ -100,19 +125,16 @@ static void render_callback(Canvas* canvas, void* ctx) {
100125void build_alphabet (PassGen * app )
101126{
102127 PassGen_Alphabet mode = AlphabetLevels [app -> level ];
103- app -> alphabet [0 ] = '\0' ;
104- if ((mode & Digits ) != 0 )
105- strcat (app -> alphabet , PASSGEN_DIGITS );
106- if ((mode & Lowercase ) != 0 )
107- strcat (app -> alphabet , PASSGEN_LETTERS_LOW );
108- if ((mode & Uppercase ) != 0 )
109- strcat (app -> alphabet , PASSGEN_LETTERS_UP );
110- if ((mode & Special ) != 0 )
111- strcat (app -> alphabet , PASSGEN_SPECIAL );
128+ if (mode > 0 && mode < 16 ) {
129+ app -> alphabet = PassGen_AlphabetChars [mode ];
130+ } else {
131+ app -> alphabet = PassGen_AlphabetChars [0 ]; // Invalid mode ... password will be all zero digits
132+ }
112133}
113134
114135PassGen * state_init () {
115136 PassGen * app = malloc (sizeof (PassGen ));
137+ _Static_assert (8 <= PASSGEN_MAX_LENGTH , "app->length must be set <= PASSGEN_MAX_LENGTH" );
116138 app -> length = 8 ;
117139 app -> level = 2 ;
118140 build_alphabet (app );
@@ -131,13 +153,46 @@ PassGen* state_init() {
131153
132154void generate (PassGen * app )
133155{
134- int hi = strlen (app -> alphabet );
135- for (int i = 0 ; i < app -> length ; i ++ )
136- {
137- int x = rand () % hi ;
138- app -> password [i ]= app -> alphabet [x ];
156+ memset (app -> password , 0 , PASSGEN_MAX_LENGTH + 1 );
157+
158+ int char_option_count = strlen (app -> alphabet );
159+ if (char_option_count < 0 ) {
160+ return ;
161+ }
162+
163+ // determine largest character value that avoids bias
164+ char ceil = CHAR_MAX - (CHAR_MAX % char_option_count ) - 1 ;
165+
166+ // iteratively fill the password buffer with random values
167+ // then keep only values that are in-range (no bias)
168+ void * remaining_buffer = app -> password ;
169+ size_t remaining_length = (app -> length * sizeof (char ));
170+
171+ while (remaining_length != 0 ) {
172+ // fewer calls to hardware TRNG is more efficient
173+ furi_hal_random_fill_buf (remaining_buffer , remaining_length );
174+
175+ // keep only values that are in-range (no bias)
176+ char * target = remaining_buffer ;
177+ char * source = remaining_buffer ;
178+ size_t valid_count = 0 ;
179+
180+ for (size_t i = 0 ; i < remaining_length ; i ++ ) {
181+ int v = * source ;
182+ // if the generated random value is in range, keep it
183+ if (v < ceil ) {
184+ v %= char_option_count ;
185+ * target = app -> alphabet [v ];
186+ // increment target pointer and count of valid items found
187+ target ++ ;
188+ valid_count ++ ;
189+ }
190+ // always increment the source pointer
191+ source ++ ;
192+ }
193+ remaining_length -= valid_count ;
194+ remaining_buffer = target ;
139195 }
140- app -> password [app -> length ] = '\0' ;
141196}
142197
143198void update_password (PassGen * app , bool vibro )
0 commit comments