Skip to content

Commit b6a7db8

Browse files
committed
remove bias
1 parent d1630b5 commit b6a7db8

File tree

1 file changed

+47
-7
lines changed

1 file changed

+47
-7
lines changed

passgen.c

+47-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
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>
@@ -66,11 +67,16 @@ typedef struct {
6667
NotificationApp* notify;
6768
const char* alphabet;
6869
char password[PASSGEN_MAX_LENGTH+1];
69-
int length;
70+
int length; // must be <= PASSGEN_MAX_LENGTH
7071
int level;
7172
} PassGen;
7273

7374
void 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+
7480
gui_remove_view_port(app->gui, app->view_port);
7581
furi_record_close(RECORD_GUI);
7682
view_port_free(app->view_port);
@@ -128,6 +134,7 @@ void build_alphabet(PassGen* app)
128134

129135
PassGen* state_init() {
130136
PassGen* app = malloc(sizeof(PassGen));
137+
_Static_assert(8 <= PASSGEN_MAX_LENGTH, "app->length must be set <= PASSGEN_MAX_LENGTH");
131138
app->length = 8;
132139
app->level = 2;
133140
build_alphabet(app);
@@ -146,13 +153,46 @@ PassGen* state_init() {
146153

147154
void generate(PassGen* app)
148155
{
149-
int hi = strlen(app->alphabet);
150-
for (int i=0; i<app->length; i++)
151-
{
152-
int x = rand() % hi;
153-
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;
154195
}
155-
app->password[app->length] = '\0';
156196
}
157197

158198
void update_password(PassGen* app, bool vibro)

0 commit comments

Comments
 (0)