This repository was archived by the owner on Jul 16, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathlp.h
More file actions
342 lines (284 loc) · 8.61 KB
/
lp.h
File metadata and controls
342 lines (284 loc) · 8.61 KB
1
2
3
4
5
6
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
60
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#ifndef LP_INCLUDE
#define LP_INCLUDE
#include "pbkdf2_sha256.h"
#include <stdint.h>
#ifndef LP_STATIC
#define LP_DEF extern
#else
#define LP_DEF static
#endif
#define LPMAXSTRLEN 2048
#define LP_VER 2 // default version
#ifndef LP_KEYLEN
#define LP_KEYLEN 32 // pbkdf2 keylen for version 2
#endif
#ifndef LP_ITERS
#define LP_ITERS 100000 // pbkdf2 iterations for version 2
#endif
#define LP_NUM_CHARSETS 4
#define LP_CSF_LOWERCASE 0x01
#define LP_CSF_UPPERCASE 0x02
#define LP_CSF_DIGITS 0x04
#define LP_CSF_SYMBOLS 0x08
#define LP_CSF_ALL 0x0F
typedef enum
{
LP_COUNTER_DEF = 1, LP_COUNTER_MIN = 1, LP_COUNTER_MAX = 0x0FFFFFFF,
LP_LENGTH_DEF = 16, LP_LENGTH_MIN = 5, LP_LENGTH_MAX = 35,
LP_CSF_DEF = LP_CSF_ALL,
} lp_options;
#define ENT_LEN 10 // >= LP_LENGTH_MAX / sizeof(uint32_t) + 1
typedef struct lp_ctx_st
{
unsigned version;
unsigned keylen;
unsigned iterations;
unsigned counter;
unsigned length;
unsigned charsets;
uint32_t entropy[ENT_LEN];
HMAC_SHA256_CTX hmac;
unsigned buflen;
char buffer[LPMAXSTRLEN];
uint8_t keybuf[LP_KEYLEN];
} LP_CTX;
typedef enum
{
LP_ERR_GENERIC = -64,
LP_ERR_VERSION, // version is not 2 (internal)
LP_ERR_KEYLEN, // keylen is not 32 (internal)
LP_ERR_ITER, // iterations is not 100000 (internal)
//LP_ERR_DIGEST, // digest is not sha256 (internal)
LP_ERR_LENGTH, // passlen out of range
LP_ERR_COUNTER, // counter out of range
LP_ERR_FLAGS, // no charsets flags selected
LP_ERR_INIT, // LP_CTX is not initialized
LP_ERR_NULL_SITE,
LP_ERR_NULL_LOGIN,
LP_ERR_LONG_SALT, // generated salt too long (>=LPMAXSTRLEN)
LP_ERR_NULL_SECRET,
LP_ERR_LONG_SECRET, // (>=LPMAXSTRLEN)
LP_ERR_NULL_PASS
} lp_error;
LP_DEF void LP_CTX_init(LP_CTX *ctx);
// returns the value if valid, 0 (which is invalid) otherwise
LP_DEF unsigned LP_check_counter(unsigned);
LP_DEF unsigned LP_check_length(unsigned);
LP_DEF unsigned LP_check_charsets(unsigned);
// returns ctx->length on success,
// returns negative LP_ERR_xxx value on failure
LP_DEF int LP_generate(LP_CTX *ctx, const char *site, const char *login, const char *secret);
#endif // LP_INCLUDE
//------------------------------------------------------------------------------
#ifdef LP_IMPLEMENTATION
#define PBKDF2_SHA256_STATIC
#define PBKDF2_SHA256_IMPLEMENTATION
#include "pbkdf2_sha256.h"
typedef struct charset_s
{
const char *value;
unsigned char length; // set length
unsigned char numsets; // number of sets used
unsigned char lensets[4]; // lengths of sets used
} charset_t;
static const charset_t cslist[] =
{
{"", 0, 0, {0, 0, 0, 0}},
{"abcdefghijklmnopqrstuvwxyz", 26, 1, {26, 0, 0, 0}},
{"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26, 1, {26, 0, 0, 0}},
{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 52, 2, {26, 26, 0, 0}},
{"0123456789", 10, 1, {10, 0, 0, 0}},
{"abcdefghijklmnopqrstuvwxyz0123456789", 36, 2, {26, 10, 0, 0}},
{"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 36, 2, {26, 10, 0, 0}},
{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 62, 3, {26, 26, 10, 0}},
{"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", 32, 1, {32, 0, 0, 0}},
{"abcdefghijklmnopqrstuvwxyz!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", 58, 2, {26, 32, 0, 0}},
{"ABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", 58, 2, {26, 32, 0, 0}},
{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", 84, 3, {26, 26, 32, 0}},
{"0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", 42, 2, {10, 32, 0, 0}},
{"abcdefghijklmnopqrstuvwxyz0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", 68, 3, {26, 10, 32, 0}},
{"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", 68, 3, {26, 10, 32, 0}},
{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", 94, 4, {26, 26, 10, 32}}
};
static void init_entropy(uint32_t *ent, uint8_t *buffer, uint32_t buflen)
{
// NOTE: assumes buflen is a multiple of 4
unsigned l = 0;
for (uint8_t *s = buffer + buflen - 4; s >= buffer; s -= 4)
{
ent[l++] = (uint32_t) s[0] << 24 | (uint32_t) s[1] << 16 |
(uint32_t) s[2] << 8 | (uint32_t) s[3];
}
for (l = buflen / 4; l < ENT_LEN; l++)
{
ent[l] = 0;
}
}
static uint32_t div_entropy(uint32_t *ent, uint32_t d)
{
int i;
for (i = ENT_LEN - 1; i >= 0; i--)
{
if (ent[i] != 0)
break;
}
if (i == -1)
return 0;
uint64_t qt = 0;
uint64_t r = 0;
for (; i >= 0; i--)
{
qt = (r << 32) | ent[i];
r = qt % d;
ent[i] = qt / d;
}
return r;
}
static void generate_chars(LP_CTX *ctx, char *dst, unsigned dstlen, const char *set, unsigned setlen)
{
for (unsigned i = 0; i < dstlen; i++)
{
dst[i] = set[div_entropy(ctx->entropy, setlen)];
}
}
static char generate_char(LP_CTX *ctx, const char *set, unsigned setlen)
{
return set[div_entropy(ctx->entropy, setlen)];
}
static unsigned generate_int(LP_CTX *ctx, unsigned setlen)
{
return div_entropy(ctx->entropy, setlen);
}
static unsigned mystrnlen(const char *s, unsigned max)
{
if (!s || !*s)
return 0;
unsigned i;
for (i = 0; (i < max) && s[i]; i++);
return i;
}
/*
static unsigned mystrlen(const char *s)
{
return mystrnlen(s, LPMAXSTRLEN);
}
*/
static unsigned myhexlen(unsigned u)
{
if (u == 0)
return 1;
unsigned d;
for (d = 0; u; d++)
{
u >>= 4;
}
return d;
}
static void mysprinthex(char *dst, unsigned dlen, unsigned u)
{
static const char hexchars[] = "0123456789abcdef"; //version 2 uses small letters
for (unsigned d = dlen; d > 0; d--)
{
dst[d - 1] = hexchars[u & 0xF];
u >>= 4;
}
}
static void mymemcpy(char *dst, const char *src, unsigned count)
{
for (; count > 0; count--)
{
dst[count - 1] = src[count - 1];
}
}
static void mypushchar(char *dst, unsigned len, unsigned pos, char c)
{
mymemcpy(dst + pos + 1, dst + pos, len - pos - 1);
dst[pos] = c;
}
static unsigned generate(LP_CTX *ctx, const char *secret, unsigned secretlen)
{
// Create entropy number from PBKDF2
pbkdf2_sha256(&ctx->hmac, (uint8_t *) secret, secretlen,
(uint8_t *) ctx->buffer, ctx->buflen, ctx->iterations, ctx->keybuf, LP_KEYLEN);
init_entropy(ctx->entropy, ctx->keybuf, LP_KEYLEN);
// Select len (= length - numsets) characters from the merged charset
const charset_t *charset = &cslist[ctx->charsets & LP_CSF_ALL];
unsigned len = ctx->length - charset->numsets;
generate_chars(ctx, ctx->buffer, len, charset->value, charset->length);
// Select numsets characters (one from each subset of charset)
unsigned offset = 0;
for (unsigned i = 0; i < charset->numsets; i++)
{
ctx->buffer[len + i] = generate_char(ctx, charset->value + offset, charset->lensets[i]);
offset += charset->lensets[i];
}
// Combine last numsets characters into the first len characters
for (; len < ctx->length; len++)
{
mypushchar(ctx->buffer, len + 1, generate_int(ctx, len), ctx->buffer[len]);
}
ctx->buffer[len] = 0;
ctx->buflen = len;
return len;
}
LP_DEF void LP_CTX_init(LP_CTX *ctx)
{
ctx->version = LP_VER;
ctx->keylen = LP_KEYLEN;
ctx->iterations = LP_ITERS;
ctx->counter = LP_COUNTER_DEF;
ctx->length = LP_LENGTH_DEF;
ctx->charsets = LP_CSF_DEF;
}
LP_DEF unsigned LP_check_counter(unsigned counter)
{
if (counter >= LP_COUNTER_MIN && counter <= LP_COUNTER_MAX)
{
return counter;
}
return 0;
}
LP_DEF unsigned LP_check_length(unsigned length)
{
if (length >= LP_LENGTH_MIN && length <= LP_LENGTH_MAX)
{
return length;
}
return 0;
}
LP_DEF unsigned LP_check_charsets(unsigned charsets)
{
return charsets & LP_CSF_ALL;
}
#define LP_ASSERT(COND,VAL) if (!(COND)) {return LP_ERR_##VAL;}
LP_DEF int LP_generate(LP_CTX *ctx, const char *site, const char *login, const char *secret)
{
LP_ASSERT(ctx, INIT);
LP_ASSERT(ctx->version == 2, VERSION);
LP_ASSERT(ctx->keylen == LP_KEYLEN, KEYLEN);
LP_ASSERT(ctx->iterations == LP_ITERS, ITER);
LP_ASSERT(site, NULL_SITE);
LP_ASSERT(login, NULL_LOGIN);
LP_ASSERT(secret, NULL_SECRET);
LP_ASSERT(LP_check_length(ctx->length), LENGTH);
LP_ASSERT(LP_check_counter(ctx->counter), COUNTER);
LP_ASSERT(LP_check_charsets(ctx->charsets), FLAGS);
unsigned sitelen = mystrnlen(site, LPMAXSTRLEN);
unsigned loginlen = mystrnlen(login, LPMAXSTRLEN);
unsigned ctrlen = myhexlen(ctx->counter);
unsigned saltlen = sitelen + loginlen + ctrlen;
LP_ASSERT(saltlen < LPMAXSTRLEN, LONG_SALT);
unsigned secretlen = mystrnlen(secret, LPMAXSTRLEN);
LP_ASSERT(secretlen < LPMAXSTRLEN, LONG_SECRET);
// Create salt string in ctx->buffer: site|login|hex(counter)
char *p = ctx->buffer;
mymemcpy(p, site, sitelen);
p += sitelen;
mymemcpy(p, login, loginlen);
p += loginlen;
mysprinthex(p, ctrlen, ctx->counter);
ctx->buflen = saltlen;
return (int) generate(ctx, secret, secretlen);
}
#undef LP_ASSERT
#endif // LP_IMPLEMENTATION