Skip to content

Commit 7725397

Browse files
Merge pull request #70 from paolostivanin/fixes
security: Add memory sanitization and improve type safety in OTP module
2 parents 78a3783 + fec3fb3 commit 7725397

File tree

3 files changed

+46
-15
lines changed

3 files changed

+46
-15
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.16)
2-
project(cotp VERSION "3.1.0" LANGUAGES "C")
2+
project(cotp VERSION "3.1.1" LANGUAGES "C")
33

44
set(CMAKE_C_STANDARD 11)
55

src/otp.c

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@
66
#include "whmac.h"
77
#include "cotp.h"
88

9+
static void secure_memzero(void *p, size_t n) {
10+
volatile unsigned char *vp = (volatile unsigned char *)p;
11+
while (n--) {
12+
*vp++ = 0;
13+
}
14+
}
15+
16+
static size_t b32_decoded_len_from_str(const char *s) {
17+
if (!s) return 0;
18+
size_t chars = 0;
19+
for (const char *p = s; *p; ++p) {
20+
if (*p != '=' && *p != ' ') {
21+
++chars;
22+
}
23+
}
24+
return (chars * 5) / 8; // floor
25+
}
26+
927
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1028
#define REVERSE_BYTES(C, C_reverse_byte_order) \
1129
for (int j = 0, i = 7; j < 8; j++, i--) { \
@@ -83,9 +101,11 @@ get_hotp (const char *secret,
83101
return NULL;
84102
}
85103

104+
size_t dlen = whmac_getlen(hd);
86105
int tk = truncate (hmac, digits, hd);
87106
whmac_freehandle (hd);
88107

108+
secure_memzero(hmac, dlen);
89109
free (hmac);
90110

91111
*err_code = NO_ERROR;
@@ -183,10 +203,12 @@ get_steam_totp_at (const char *secret,
183203

184204
char *totp = get_steam_code (hmac, hd);
185205

206+
size_t dlen = whmac_getlen(hd);
186207
whmac_freehandle (hd);
187208

188209
*err_code = NO_ERROR;
189210

211+
secure_memzero(hmac, dlen);
190212
free(hmac);
191213

192214
return totp;
@@ -234,18 +256,19 @@ static char *
234256
get_steam_code (const unsigned char *hmac,
235257
whmac_handle_t *hd)
236258
{
237-
int offset = (hmac[whmac_getlen(hd)-1] & 0x0f);
259+
size_t hlen = whmac_getlen(hd);
260+
int offset = (hmac[hlen-1] & 0x0f);
238261

239262
// Starting from the offset, take the successive 4 bytes while stripping the topmost bit to prevent it being handled as a signed integer
240-
int bin_code = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | ((hmac[offset + 3] & 0xff));
263+
uint32_t bin_code = ((uint32_t)(hmac[offset] & 0x7f) << 24) | ((uint32_t)(hmac[offset + 1] & 0xff) << 16) | ((uint32_t)(hmac[offset + 2] & 0xff) << 8) | ((uint32_t)(hmac[offset + 3] & 0xff));
241264

242265
const char steam_alphabet[] = "23456789BCDFGHJKMNPQRTVWXY";
243266

244267
char code[6];
245268
size_t steam_alphabet_len = strlen (steam_alphabet);
246269
for (int i = 0; i < 5; i++) {
247-
int mod = (int)(bin_code % steam_alphabet_len);
248-
bin_code = (int)(bin_code / steam_alphabet_len);
270+
uint32_t mod = bin_code % (uint32_t)steam_alphabet_len;
271+
bin_code = bin_code / (uint32_t)steam_alphabet_len;
249272
code[i] = steam_alphabet[mod];
250273
}
251274
code[5] = '\0';
@@ -260,13 +283,17 @@ truncate (const unsigned char *hmac,
260283
whmac_handle_t *hd)
261284
{
262285
// take the lower four bits of the last byte
263-
int offset = hmac[whmac_getlen(hd) - 1] & 0x0f;
286+
size_t hlen = whmac_getlen(hd);
287+
int offset = hmac[hlen - 1] & 0x0f;
264288

265289
// Starting from the offset, take the successive 4 bytes while stripping the topmost bit to prevent it being handled as a signed integer
266-
int bin_code = ((hmac[offset] & 0x7f) << 24) | ((hmac[offset + 1] & 0xff) << 16) | ((hmac[offset + 2] & 0xff) << 8) | ((hmac[offset + 3] & 0xff));
290+
uint32_t bin_code = ((uint32_t)(hmac[offset] & 0x7f) << 24) | ((uint32_t)(hmac[offset + 1] & 0xff) << 16) | ((uint32_t)(hmac[offset + 2] & 0xff) << 8) | ((uint32_t)(hmac[offset + 3] & 0xff));
267291

268-
long long int DIGITS_POWER[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000};
269-
int token = (int)(bin_code % DIGITS_POWER[digits_length]);
292+
uint64_t mod = 1;
293+
for (int i = 0; i < digits_length; ++i) {
294+
mod *= 10ULL;
295+
}
296+
int token = (int)(((uint64_t)bin_code) % mod);
270297

271298
return token;
272299
}
@@ -277,13 +304,13 @@ compute_hmac (const char *K,
277304
long C,
278305
whmac_handle_t *hd)
279306
{
280-
size_t secret_len = (size_t)((strlen(K) + 1.6 - 1) / 1.6);
281-
282307
char *normalized_K = normalize_secret (K);
283308
if (normalized_K == NULL) {
284309
return NULL;
285310
}
286311

312+
size_t secret_len = b32_decoded_len_from_str(normalized_K);
313+
287314
cotp_error_t err;
288315
unsigned char *secret = base32_decode (normalized_K, strlen(normalized_K), &err);
289316
free (normalized_K);
@@ -297,6 +324,7 @@ compute_hmac (const char *K,
297324
err = whmac_setkey (hd, secret, secret_len);
298325
if (err) {
299326
fprintf (stderr, "Error while setting the cipher key.\n");
327+
secure_memzero(secret, secret_len);
300328
free (secret);
301329
return NULL;
302330
}
@@ -306,17 +334,21 @@ compute_hmac (const char *K,
306334
unsigned char *hmac = calloc (dlen, 1);
307335
if (hmac == NULL) {
308336
fprintf (stderr, "Error allocating memory");
337+
secure_memzero(secret, secret_len);
309338
free (secret);
310339
return NULL;
311340
}
312341

313342
ssize_t flen = whmac_finalize (hd, hmac, dlen);
314343
if (flen < 0) {
315344
fprintf (stderr, "Error getting digest\n");
345+
secure_memzero(hmac, dlen);
316346
free (hmac);
347+
secure_memzero(secret, secret_len);
317348
free (secret);
318349
return NULL;
319350
}
351+
secure_memzero(secret, secret_len);
320352
free (secret);
321353

322354
return hmac;
@@ -331,9 +363,8 @@ finalize (int digits_length,
331363
if (token == NULL) {
332364
return NULL;
333365
}
334-
char fmt[6];
335-
sprintf (fmt, "%%0%dd", digits_length);
336-
snprintf (token, digits_length + 1, fmt, tk);
366+
// Print with leading zeros without building an intermediate format string
367+
snprintf (token, digits_length + 1, "%0*d", digits_length, tk);
337368
return token;
338369
}
339370

src/utils/base32.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ base32_decode (const char *user_data_untrimmed,
153153
bits_left += BITS_PER_BYTE - BITS_PER_B32_BLOCK;
154154
}
155155
}
156-
decoded_data[output_length-1] = '\0';
156+
decoded_data[output_length] = '\0';
157157

158158
free (user_data);
159159

0 commit comments

Comments
 (0)