Skip to content

Commit 6324f81

Browse files
authored
Merge pull request #6 from henrygab/rand_clarity
Remove Bias and clarify use of TRNG
2 parents 6c1121e + b6a7db8 commit 6324f81

File tree

3 files changed

+110
-19
lines changed

3 files changed

+110
-19
lines changed

.github/workflows/build.yml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: "FAP: Build and lint"
2+
on: [push, pull_request]
3+
jobs:
4+
ufbt-build-action:
5+
runs-on: ubuntu-latest
6+
name: 'ufbt: Build for Release branch'
7+
steps:
8+
- name: Checkout
9+
uses: actions/checkout@v3
10+
11+
- name: Setup ufbt
12+
uses: flipperdevices/flipperzero-ufbt-action@v0.1.2
13+
with:
14+
task: setup
15+
sdk-channel: release
16+
17+
- name: Build with ufbt
18+
uses: flipperdevices/flipperzero-ufbt-action@v0.1.2
19+
id: build-app
20+
with:
21+
skip-setup: true
22+
sdk-channel: release
23+
24+
- name: Upload app artifacts
25+
uses: actions/upload-artifact@v3
26+
with:
27+
name: ${{ github.event.repository.name }}-${{ steps.build-app.outputs.suffix }}
28+
path: ${{ steps.build-app.outputs.fap-artifacts }}
29+
30+
# # You can remove this step if you don't want to check source code formatting
31+
# - name: Lint sources
32+
# uses: flipperdevices/flipperzero-ufbt-action@v0.1.2
33+
# with:
34+
# # skip SDK setup, we already did it in previous step
35+
# skip-setup: true
36+
# task: lint

application.fam

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
App(
22
appid="passgen",
33
name="Password Generator",
4-
apptype=FlipperAppType.PLUGIN,
4+
apptype=FlipperAppType.EXTERNAL,
55
entry_point="passgenapp",
66
requires=[
77
"gui",
88
],
99
fap_category="Tools",
1010
fap_icon="icons/passgen_icon.png",
1111
fap_icon_assets="icons",
12-
)
12+
)

passgen.c

+72-17
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>
@@ -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+
3050
const int AlphabetLevels[] = { Digits, Lowercase, DigitsLower, DigitsAllLetters, Mixed };
3151
const char* AlphabetLevelNames[] = { "1234", "abcd", "ab12", "Ab12", "Ab1#" };
3252
const 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

5474
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+
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) {
100125
void 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

114135
PassGen* 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

132154
void 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

143198
void update_password(PassGen* app, bool vibro)

0 commit comments

Comments
 (0)