/
otp.c
93 lines (77 loc) · 2.45 KB
1
2
3
4
5
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include "sha1.h"
6
#include "otp.h"
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
static int base32_decode(const char *src, const int srclen, uint8_t *dst, const int dstlen)
{
const int len = srclen == -1 ? strlen((const char *) src) : srclen;
int retval = 0;
uint32_t accum = 0;
int shifter = 0;
int i;
for (i = 0; i < len; i++) {
const uint8_t ch = (uint8_t) src[i];
uint8_t val;
if ((ch >= 'A') && (ch <= 'Z')) {
val = ch - 'A';
} else if ((ch >= '2') && (ch <= '7')) {
val = (ch - '2') + 26;
} else if (ch == '=') {
val = 0;
/* these are illegal in base32, but GAuth keys might have them. */
} else if (ch == ' ') {
continue; /* skip these. */
} else if ((ch >= 'a') && (ch <= 'z')) {
val = ch - 'a'; /* treat like uppercase. */
} else {
return -1; /* invalid string. */
}
accum = (accum << 5) | ((uint32_t) val);
shifter += 5;
if (shifter >= 8) {
if (retval > dstlen) {
return -1; /* dst too small */
}
dst[retval] = (uint8_t) ((accum >> (shifter - 8)) & 0xFF);
retval++;
shifter -= 8;
}
}
if (shifter > 0) {
if (retval > dstlen) {
return -1; /* dst too small */
}
dst[retval] = (uint8_t) (accum & 0xFF);
retval++;
}
return retval;
}
60
int totp(const char *base32_secret, char *dst, int dstlen)
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{
uint8_t decoded[64];
int decodedlen;
uint64_t secs;
uint8_t timebytes[8];
unsigned char digest[SHA1_DIGEST_LENGTH];
uint8_t *bytes;
uint32_t val;
decodedlen = base32_decode(base32_secret, -1, decoded, sizeof (decoded));
if (decodedlen == -1) {
return -1;
}
secs = ((uint64_t) time(NULL)) / 30;
for (int i = 0; i < 8; i++) {
timebytes[i] = (uint8_t) (secs >> ((7-i) * 8));
}
SHA1Hmac(decoded, decodedlen, timebytes, 8, digest);
bytes = (uint8_t *) (digest + (digest[SHA1_DIGEST_LENGTH-1] & 0xF));
val = (((uint32_t) bytes[0]) << 24) | (((uint32_t) bytes[1]) << 16) |
(((uint32_t) bytes[2]) << 8) | (((uint32_t) bytes[3]));
val &= 0x7FFFFFFF; /* drop most significant bit. */
val %= 1000000; /* make it six digits long. */
snprintf(dst, dstlen, "%06u", (unsigned int) val);
return 0;
}
/* end of otp.c ... */