diff options
Diffstat (limited to '')
-rw-r--r-- | libbb/yescrypt/alg-yescrypt-common.c | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/libbb/yescrypt/alg-yescrypt-common.c b/libbb/yescrypt/alg-yescrypt-common.c new file mode 100644 index 000000000..c51823787 --- /dev/null +++ b/libbb/yescrypt/alg-yescrypt-common.c | |||
@@ -0,0 +1,408 @@ | |||
1 | /*- | ||
2 | * Copyright 2013-2018 Alexander Peslyak | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted. | ||
7 | * | ||
8 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||
9 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
10 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
11 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||
12 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
13 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
14 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
15 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
16 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
17 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
18 | * SUCH DAMAGE. | ||
19 | */ | ||
20 | |||
21 | #if RESTRICTED_PARAMS | ||
22 | |||
23 | #define decode64_uint32(dst, src, min) \ | ||
24 | ({ \ | ||
25 | uint32_t d32 = a2i64(*(src)); \ | ||
26 | if (d32 > 47) \ | ||
27 | goto fail; \ | ||
28 | *(dst) = d32 + (min); \ | ||
29 | ++src; \ | ||
30 | }) | ||
31 | #define test_decode64_uint32() ((void)0) | ||
32 | #define FULL_PARAMS(...) | ||
33 | |||
34 | #else | ||
35 | |||
36 | #define FULL_PARAMS(...) __VA_ARGS__ | ||
37 | |||
38 | /* Not inlining: | ||
39 | * de/encode64 functions are only used to read | ||
40 | * yescrypt_params_t field, and convert salt to binary - | ||
41 | * both of these are negligible compared to main hashing operation | ||
42 | */ | ||
43 | static NOINLINE const uint8_t *decode64_uint32( | ||
44 | uint32_t *dst, | ||
45 | const uint8_t *src, uint32_t val) | ||
46 | { | ||
47 | uint32_t start = 0, end = 47, bits = 0; | ||
48 | uint32_t c; | ||
49 | |||
50 | if (!src) /* previous decode failed already? */ | ||
51 | goto fail; | ||
52 | |||
53 | c = a2i64(*src++); | ||
54 | if (c > 63) | ||
55 | goto fail; | ||
56 | |||
57 | // The encoding of number N: | ||
58 | // start = 0 end = 47 | ||
59 | // If N < 48, it is encoded verbatim, else | ||
60 | // N -= 48 | ||
61 | // start = end+1 = 48 | ||
62 | // end += (64-end)/2 = 55 | ||
63 | // If N < (end+1-start)<<6 = 8<<6, it is encoded as 48+(N>>6)|low6bits (that is, 48...55|<6bit>), else | ||
64 | // N -= 8<<6 | ||
65 | // start = end+1 = 56 | ||
66 | // end += (64-end)/2 = 59 | ||
67 | // If N < (end+1-start)<<2*6 = 4<<12, it is encoded as 56+(N>>2*6)|low12bits (that is, 56...59|<6bit>|<6bit>), else | ||
68 | // ...same for 60..61|<6bit>|<6bit>|<6bit> | ||
69 | // .......same for 62|<6bit>|<6bit>|<6bit>|<6bit> | ||
70 | // .......same for 63|<6bit>|<6bit>|<6bit>|<6bit>|<6bit> | ||
71 | dbg_dec64("c:%d val:0x%08x", (int)c, (unsigned)val); | ||
72 | while (c > end) { | ||
73 | dbg_dec64("c:%d > end:%d", (int)c, (int)end); | ||
74 | val += (end + 1 - start) << bits; | ||
75 | dbg_dec64("val+=0x%08x", (int)((end + 1 - start) << bits)); | ||
76 | dbg_dec64(" val:0x%08x", (unsigned)val); | ||
77 | start = end + 1; | ||
78 | end += (64 - end) / 2; | ||
79 | bits += 6; | ||
80 | dbg_dec64("start=%d", (int)start); | ||
81 | dbg_dec64("end=%d", (int)end); | ||
82 | dbg_dec64("bits=%d", (int)bits); | ||
83 | } | ||
84 | |||
85 | val += (c - start) << bits; | ||
86 | dbg_dec64("final val+=0x%08x", (int)((c - start) << bits)); | ||
87 | dbg_dec64(" val:0x%08x", (unsigned)val); | ||
88 | |||
89 | while (bits != 0) { | ||
90 | c = a2i64(*src++); | ||
91 | if (c > 63) | ||
92 | goto fail; | ||
93 | bits -= 6; | ||
94 | val += c << bits; | ||
95 | dbg_dec64("low bits val+=0x%08x", (int)(c << bits)); | ||
96 | dbg_dec64(" val:0x%08x", (unsigned)val); | ||
97 | } | ||
98 | ret: | ||
99 | *dst = val; | ||
100 | return src; | ||
101 | fail: | ||
102 | val = 0; | ||
103 | src = NULL; | ||
104 | goto ret; | ||
105 | } | ||
106 | |||
107 | #if TEST_DECODE64 | ||
108 | static void test_decode64_uint32(void) | ||
109 | { | ||
110 | const uint8_t *src, *end; | ||
111 | uint32_t u32; | ||
112 | int a = 48; | ||
113 | int b = 8<<6; // 0x0200 | ||
114 | int c = 4<<12; // 0x04000 | ||
115 | int d = 2<<18; // 0x080000 | ||
116 | int e = 1<<24; // 0x1000000 | ||
117 | |||
118 | src = (void*)"wzzz"; | ||
119 | end = decode64_uint32(&u32, src, 0); | ||
120 | if (u32 != 0x0003ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32); | ||
121 | if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end); | ||
122 | src = (void*)"xzzz"; | ||
123 | end = decode64_uint32(&u32, src, 0); | ||
124 | if (u32 != 0x0007ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32); | ||
125 | if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end); | ||
126 | // Note how the last representable "x---" encoding, 0x7ffff, is exactly d-1! | ||
127 | // And if we now increment it, we get: | ||
128 | src = (void*)"y...."; | ||
129 | end = decode64_uint32(&u32, src, 0); | ||
130 | if (u32 != 0x00000000+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32); | ||
131 | if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end); | ||
132 | src = (void*)"yzzzz"; | ||
133 | end = decode64_uint32(&u32, src, 0); | ||
134 | if (u32 != 0x00ffffff+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32); | ||
135 | if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end); | ||
136 | |||
137 | src = (void*)"zzzzzz"; | ||
138 | end = decode64_uint32(&u32, src, 0); | ||
139 | if (u32 != 0x3fffffff+e+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32); | ||
140 | if (end != src + 6) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end); | ||
141 | |||
142 | bb_error_msg("test_decode64_uint32() OK"); | ||
143 | } | ||
144 | #else | ||
145 | # define test_decode64_uint32() ((void)0) | ||
146 | #endif | ||
147 | |||
148 | #endif /* !RESTRICTED_PARAMS */ | ||
149 | |||
150 | #if 1 | ||
151 | static const uint8_t *decode64( | ||
152 | uint8_t *dst, size_t *dstlen, | ||
153 | const uint8_t *src) | ||
154 | { | ||
155 | unsigned dstpos = 0; | ||
156 | |||
157 | dbg_dec64("src:'%s'", src); | ||
158 | for (;;) { | ||
159 | uint32_t c, value = 0; | ||
160 | int bits = 0; | ||
161 | while (*src != '\0' && *src != '$') { | ||
162 | c = a2i64(*src); | ||
163 | if (c > 63) { /* bad ascii64 char, stop decoding at it */ | ||
164 | break; | ||
165 | } | ||
166 | src++; | ||
167 | value |= c << bits; | ||
168 | bits += 6; | ||
169 | if (bits == 24) /* got 4 chars */ | ||
170 | goto store; | ||
171 | } | ||
172 | /* we read entire src, or met a non-ascii64 char (such as "$") */ | ||
173 | if (bits == 0) | ||
174 | break; | ||
175 | /* else: we got last, partial bit block - store it */ | ||
176 | store: | ||
177 | dbg_dec64(" storing bits:%d dstpos:%u v:%08x", bits, dstpos, (int)SWAP_BE32(value)); //BE to see lsb first | ||
178 | for (;;) { | ||
179 | if ((*src == '\0' || *src == '$') | ||
180 | && value == 0 && bits < 8 | ||
181 | ) { | ||
182 | /* Example: mkpasswd PWD '$y$j9T$123': | ||
183 | * the "123" is bits:18 value:03,51,00 | ||
184 | * is considered to be 2 bytes, not 3! | ||
185 | * | ||
186 | * '$y$j9T$zzz' in upstream fails outright (3rd byte isn't zero). | ||
187 | * IOW: for upstream, validity of salt depends on VALUE, | ||
188 | * not just size of salt. Which is a bug. | ||
189 | * The '$y$j9T$zzz.' salt is the same | ||
190 | * (it adds 6 zero msbits) but upstream works with it, | ||
191 | * thus '$y$j9T$zzz' should work too and give the same result. | ||
192 | */ | ||
193 | goto end; | ||
194 | } | ||
195 | if (dstpos >= *dstlen) { | ||
196 | dbg_dec64(" ERR: bits:%d dstpos:%u dst[] is too small", bits, dstpos); | ||
197 | goto fail; | ||
198 | } | ||
199 | *dst++ = value; | ||
200 | dstpos++; | ||
201 | value >>= 8; | ||
202 | bits -= 8; | ||
203 | if (bits <= 0) /* can get negative, if we e.g. had 6 bits */ | ||
204 | break; | ||
205 | } | ||
206 | if (*src == '\0' || *src == '$') | ||
207 | break; | ||
208 | } | ||
209 | end: | ||
210 | *dstlen = dstpos; | ||
211 | dbg_dec64("dec64: OK, dst[%d]", (int)dstpos); | ||
212 | return src; | ||
213 | fail: | ||
214 | /* *dstlen = 0; - not needed, caller detects error by seeing NULL */ | ||
215 | return NULL; | ||
216 | } | ||
217 | #else | ||
218 | /* Buggy (and larger) original code */ | ||
219 | static const uint8_t *decode64( | ||
220 | uint8_t *dst, size_t *dstlen, | ||
221 | const uint8_t *src, size_t srclen) | ||
222 | { | ||
223 | size_t dstpos = 0; | ||
224 | |||
225 | while (dstpos <= *dstlen && srclen) { | ||
226 | uint32_t value = 0, bits = 0; | ||
227 | while (srclen--) { | ||
228 | uint32_t c = a2i64(*src); | ||
229 | if (c > 63) { | ||
230 | srclen = 0; | ||
231 | break; | ||
232 | } | ||
233 | src++; | ||
234 | value |= c << bits; | ||
235 | bits += 6; | ||
236 | if (bits >= 24) | ||
237 | break; | ||
238 | } | ||
239 | if (!bits) | ||
240 | break; | ||
241 | if (bits < 12) /* must have at least one full byte */ | ||
242 | goto fail; | ||
243 | dbg_dec64(" storing bits:%d v:%08x", (int)bits, (int)SWAP_BE32(value)); //BE to see lsb first | ||
244 | while (dstpos++ < *dstlen) { | ||
245 | *dst++ = value; | ||
246 | value >>= 8; | ||
247 | bits -= 8; | ||
248 | if (bits < 8) { /* 2 or 4 */ | ||
249 | if (value) /* must be 0 */ | ||
250 | goto fail; | ||
251 | bits = 0; | ||
252 | break; | ||
253 | } | ||
254 | } | ||
255 | if (bits) | ||
256 | goto fail; | ||
257 | } | ||
258 | |||
259 | if (!srclen && dstpos <= *dstlen) { | ||
260 | *dstlen = dstpos; | ||
261 | dbg_dec64("dec64: OK, dst[%d]", (int)dstpos); | ||
262 | return src; | ||
263 | } | ||
264 | fail: | ||
265 | /* *dstlen = 0; - not needed, caller detects error by seeing NULL */ | ||
266 | return NULL; | ||
267 | } | ||
268 | #endif | ||
269 | |||
270 | static char *encode64( | ||
271 | char *dst, size_t dstlen, | ||
272 | const uint8_t *src, size_t srclen) | ||
273 | { | ||
274 | while (srclen) { | ||
275 | uint32_t value = 0, b = 0; | ||
276 | do { | ||
277 | value |= (uint32_t)(*src++ << b); | ||
278 | b += 8; | ||
279 | srclen--; | ||
280 | } while (srclen && b < 24); | ||
281 | |||
282 | b >>= 3; /* number of bits to number of bytes */ | ||
283 | b++; /* 1, 2 or 3 bytes will become 2, 3 or 4 ascii64 chars */ | ||
284 | dstlen -= b; | ||
285 | if ((ssize_t)dstlen <= 0) | ||
286 | return NULL; | ||
287 | dst = num2str64_lsb_first(dst, value, b); | ||
288 | } | ||
289 | *dst = '\0'; | ||
290 | return dst; | ||
291 | } | ||
292 | |||
293 | char *yescrypt_r( | ||
294 | const uint8_t *passwd, size_t passwdlen, | ||
295 | const uint8_t *setting, | ||
296 | char *buf, size_t buflen) | ||
297 | { | ||
298 | struct { | ||
299 | yescrypt_ctx_t yctx[1]; | ||
300 | unsigned char hashbin32[32]; | ||
301 | } u; | ||
302 | #define yctx u.yctx | ||
303 | #define hashbin32 u.hashbin32 | ||
304 | char *dst; | ||
305 | const uint8_t *src, *saltend; | ||
306 | size_t need, prefixlen; | ||
307 | uint32_t u32; | ||
308 | |||
309 | test_decode64_uint32(); | ||
310 | |||
311 | memset(yctx, 0, sizeof(yctx)); | ||
312 | FULL_PARAMS(yctx->param.p = 1;) | ||
313 | |||
314 | /* we assume setting starts with "$y$" (caller must ensure this) */ | ||
315 | src = setting + 3; | ||
316 | |||
317 | src = decode64_uint32(&yctx->param.flags, src, 0); | ||
318 | /* "j9T" returns: 0x2f */ | ||
319 | //if (!src) | ||
320 | // goto fail; | ||
321 | |||
322 | if (yctx->param.flags < YESCRYPT_RW) { | ||
323 | dbg("yctx->param.flags=0x%x", (unsigned)yctx->param.flags); | ||
324 | goto fail; // bbox: we don't support scrypt - only yescrypt | ||
325 | } else if (yctx->param.flags <= YESCRYPT_RW + (YESCRYPT_RW_FLAVOR_MASK >> 2)) { | ||
326 | /* "j9T" sets flags to 0xb6 */ | ||
327 | yctx->param.flags = YESCRYPT_RW + ((yctx->param.flags - YESCRYPT_RW) << 2); | ||
328 | dbg("yctx->param.flags=0x%x", (unsigned)yctx->param.flags); | ||
329 | dbg(" YESCRYPT_RW:%u", !!(yctx->param.flags & YESCRYPT_RW)); | ||
330 | dbg((yctx->param.flags & YESCRYPT_RW_FLAVOR_MASK) == | ||
331 | (YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K) | ||
332 | ? " YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K" | ||
333 | : " flags are not standard" | ||
334 | ); | ||
335 | } else { | ||
336 | goto fail; | ||
337 | } | ||
338 | |||
339 | src = decode64_uint32(&u32, src, 1); | ||
340 | if (/*!src ||*/ u32 > 63) | ||
341 | goto fail; | ||
342 | yctx->param.N = (uint64_t)1 << u32; | ||
343 | /* "j9T" sets to 4096 (1<<12) */ | ||
344 | dbg("yctx->param.N=%llu (1<<%u)", (unsigned long long)yctx->param.N, (unsigned)u32); | ||
345 | |||
346 | src = decode64_uint32(&yctx->param.r, src, 1); | ||
347 | /* "j9T" sets to 32 */ | ||
348 | dbg("yctx->param.r=%u", yctx->param.r); | ||
349 | |||
350 | if (!src) | ||
351 | goto fail; | ||
352 | if (*src != '$') { | ||
353 | #if RESTRICTED_PARAMS | ||
354 | goto fail; | ||
355 | #else | ||
356 | src = decode64_uint32(&u32, src, 1); | ||
357 | dbg("yescrypt has extended params:0x%x", (unsigned)u32); | ||
358 | if (u32 & 1) | ||
359 | src = decode64_uint32(&yctx->param.p, src, 2); | ||
360 | if (u32 & 2) | ||
361 | src = decode64_uint32(&yctx->param.t, src, 1); | ||
362 | if (u32 & 4) | ||
363 | src = decode64_uint32(&yctx->param.g, src, 1); | ||
364 | if (u32 & 8) { | ||
365 | src = decode64_uint32(&u32, src, 1); | ||
366 | if (/*!src ||*/ u32 > 63) | ||
367 | goto fail; | ||
368 | yctx->param.NROM = (uint64_t)1 << u32; | ||
369 | } | ||
370 | if (!src) | ||
371 | goto fail; | ||
372 | if (*src != '$') | ||
373 | goto fail; | ||
374 | #endif | ||
375 | } | ||
376 | |||
377 | yctx->saltlen = sizeof(yctx->salt); | ||
378 | src++; /* now points to salt */ | ||
379 | saltend = decode64(yctx->salt, &yctx->saltlen, src); | ||
380 | if (!saltend || (*saltend != '\0' && *saltend != '$')) | ||
381 | goto fail; /* salt[] is too small, or bad char during decode */ | ||
382 | dbg_dec64("salt is %d ascii64 chars -> %d bytes (in binary)", (int)(saltend - src), (int)yctx->saltlen); | ||
383 | |||
384 | prefixlen = saltend - setting; | ||
385 | need = prefixlen + 1 + YESCRYPT_HASH_LEN + 1; | ||
386 | if (need > buflen /*overflow is quite unlikely: || need < prefixlen*/) | ||
387 | goto fail; | ||
388 | |||
389 | if (yescrypt_kdf32(yctx, passwd, passwdlen, hashbin32)) { | ||
390 | dbg("error in yescrypt_kdf32"); | ||
391 | goto fail; | ||
392 | } | ||
393 | |||
394 | dst = mempcpy(buf, setting, prefixlen); | ||
395 | *dst++ = '$'; | ||
396 | dst = encode64(dst, buflen - (dst - buf), hashbin32, sizeof(hashbin32)); | ||
397 | if (!dst) | ||
398 | goto fail; | ||
399 | ret: | ||
400 | free_region(yctx->local); | ||
401 | explicit_bzero(&u, sizeof(u)); | ||
402 | return buf; | ||
403 | fail: | ||
404 | buf = NULL; | ||
405 | goto ret; | ||
406 | #undef yctx | ||
407 | #undef hashbin32 | ||
408 | } | ||