Skip to content

Commit 1e29fe8

Browse files
authored
Merge pull request #385 from matheusmoreira/totp-hot-patch
TOTP hotfix: reduce memory usage
2 parents 955ac94 + 10701f3 commit 1e29fe8

File tree

2 files changed

+95
-32
lines changed

2 files changed

+95
-32
lines changed

movement/watch_faces/complication/totp_face.c

Lines changed: 93 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,22 @@
3939
#include "TOTP.h"
4040
#include "base32.h"
4141

42+
#ifndef TOTP_FACE_MAX_KEY_LENGTH
43+
#define TOTP_FACE_MAX_KEY_LENGTH 128
44+
#endif
45+
4246
typedef struct {
4347
unsigned char labels[2];
4448
hmac_alg algorithm;
4549
uint32_t period;
46-
size_t key_length;
47-
unsigned char *key;
50+
size_t encoded_key_length;
51+
unsigned char *encoded_key;
4852
} totp_t;
4953

5054
#define CREDENTIAL(label, key_array, algo, timestep) \
5155
(const totp_t) { \
52-
.key = ((unsigned char *) key_array), \
53-
.key_length = sizeof(key_array) - 1, \
56+
.encoded_key = ((unsigned char *) key_array), \
57+
.encoded_key_length = sizeof(key_array) - 1, \
5458
.period = (timestep), \
5559
.labels = (#label), \
5660
.algorithm = (algo), \
@@ -67,15 +71,63 @@ static totp_t credentials[] = {
6771
// END OF KEY DATA.
6872
////////////////////////////////////////////////////////////////////////////////
6973

74+
static inline totp_t *totp_at(size_t i) {
75+
return &credentials[i];
76+
}
77+
7078
static inline totp_t *totp_current(totp_state_t *totp_state) {
71-
return &credentials[totp_state->current_index];
79+
return totp_at(totp_state->current_index);
7280
}
7381

7482
static inline size_t totp_total(void) {
7583
return sizeof(credentials) / sizeof(*credentials);
7684
}
7785

78-
static void totp_display(totp_state_t *totp_state) {
86+
static void totp_validate_key_lengths(void) {
87+
for (size_t n = totp_total(), i = 0; i < n; ++i) {
88+
totp_t *totp = totp_at(i);
89+
90+
if (UNBASE32_LEN(totp->encoded_key_length) > TOTP_FACE_MAX_KEY_LENGTH) {
91+
// Key exceeds static limits, turn it off by zeroing the length
92+
totp->encoded_key_length = 0;
93+
}
94+
}
95+
}
96+
97+
static void totp_generate(totp_state_t *totp_state) {
98+
totp_t *totp = totp_current(totp_state);
99+
100+
if (totp->encoded_key_length <= 0) {
101+
// Key exceeded static limits and was turned off
102+
totp_state->current_decoded_key_length = 0;
103+
return;
104+
}
105+
106+
totp_state->current_decoded_key_length = base32_decode(totp->encoded_key, totp_state->current_decoded_key);
107+
108+
if (totp_state->current_decoded_key_length == 0) {
109+
// Decoding failed for some reason
110+
// Not a base 32 string?
111+
return;
112+
}
113+
114+
TOTP(
115+
totp_state->current_decoded_key,
116+
totp_state->current_decoded_key_length,
117+
totp->period,
118+
totp->algorithm
119+
);
120+
}
121+
122+
static void totp_display_error(totp_state_t *totp_state) {
123+
char buf[10 + 1];
124+
totp_t *totp = totp_current(totp_state);
125+
126+
snprintf(buf, sizeof(buf), "%c%c ERROR ", totp->labels[0], totp->labels[1]);
127+
watch_display_string(buf, 0);
128+
}
129+
130+
static void totp_display_code(totp_state_t *totp_state) {
79131
char buf[14];
80132
div_t result;
81133
uint8_t valid_for;
@@ -92,46 +144,55 @@ static void totp_display(totp_state_t *totp_state) {
92144
watch_display_string(buf, 0);
93145
}
94146

95-
static void totp_face_decode_secrets(void) {
96-
for (size_t n = totp_total(), i = 0; i < n; ++i) {
97-
totp_t *totp = &credentials[i];
98-
unsigned char *key = totp->key;
147+
static void totp_display(totp_state_t *totp_state) {
148+
if (totp_state->current_decoded_key_length > 0) {
149+
totp_display_code(totp_state);
150+
} else {
151+
totp_display_error(totp_state);
152+
}
153+
}
99154

100-
totp->key = malloc(UNBASE32_LEN(totp->key_length));
101-
totp->key_length = base32_decode(key, totp->key);
155+
static void totp_generate_and_display(totp_state_t *totp_state) {
156+
totp_generate(totp_state);
157+
totp_display(totp_state);
158+
}
102159

103-
if (totp->key_length == 0) {
104-
free(totp->key);
105-
continue;
106-
}
107-
}
160+
static inline uint32_t totp_compute_base_timestamp(movement_settings_t *settings) {
161+
return watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60);
108162
}
109163

110164
void totp_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
111165
(void) settings;
112166
(void) watch_face_index;
113167

168+
totp_validate_key_lengths();
169+
114170
if (*context_ptr == NULL) {
115171
totp_state_t *totp = malloc(sizeof(totp_state_t));
116-
totp_face_decode_secrets();
172+
totp->current_decoded_key = malloc(TOTP_FACE_MAX_KEY_LENGTH);
117173
*context_ptr = totp;
118174
}
119175
}
120176

121177
void totp_face_activate(movement_settings_t *settings, void *context) {
122178
(void) settings;
123-
memset(context, 0, sizeof(totp_state_t));
124-
totp_state_t *totp_state = (totp_state_t *)context;
125-
totp_t *totp = totp_current(totp_state);
126-
TOTP(totp->key, totp->key_length, totp->period, totp->algorithm);
127-
totp_state->timestamp = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60);
128-
totp_state->current_code = getCodeFromTimestamp(totp_state->timestamp);
179+
180+
totp_state_t *totp = (totp_state_t *) context;
181+
182+
totp->timestamp = totp_compute_base_timestamp(settings);
183+
totp->steps = 0;
184+
totp->current_code = 0;
185+
totp->current_index = 0;
186+
totp->current_decoded_key_length = 0;
187+
// totp->current_decoded_key is already initialized in setup
188+
189+
totp_generate_and_display(totp);
129190
}
130191

131192
bool totp_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
132193
(void) settings;
133-
totp_state_t *totp_state = (totp_state_t *)context;
134-
totp_t *totp;
194+
195+
totp_state_t *totp_state = (totp_state_t *) context;
135196

136197
switch (event.event_type) {
137198
case EVENT_TICK:
@@ -150,9 +211,9 @@ bool totp_face_loop(movement_event_t event, movement_settings_t *settings, void
150211
// wrap around to first key
151212
totp_state->current_index = 0;
152213
}
153-
totp = totp_current(totp_state);
154-
TOTP(totp->key, totp->key_length, totp->period, totp->algorithm);
155-
totp_display(totp_state);
214+
215+
totp_generate_and_display(totp_state);
216+
156217
break;
157218
case EVENT_LIGHT_BUTTON_UP:
158219
if (totp_state->current_index == 0) {
@@ -161,9 +222,9 @@ bool totp_face_loop(movement_event_t event, movement_settings_t *settings, void
161222
} else {
162223
totp_state->current_index--;
163224
}
164-
totp = totp_current(totp_state);
165-
TOTP(totp->key, totp->key_length, totp->period, totp->algorithm);
166-
totp_display(totp_state);
225+
226+
totp_generate_and_display(totp_state);
227+
167228
break;
168229
case EVENT_ALARM_BUTTON_DOWN:
169230
case EVENT_ALARM_LONG_PRESS:

movement/watch_faces/complication/totp_face.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ typedef struct {
7070
uint8_t steps;
7171
uint32_t current_code;
7272
uint8_t current_index;
73+
uint8_t *current_decoded_key;
74+
size_t current_decoded_key_length;
7375
} totp_state_t;
7476

7577
void totp_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

0 commit comments

Comments
 (0)