mirror of
https://git.busybox.net/busybox
synced 2025-08-28 21:14:54 +02:00
function old new delta yescrypt_r 767 756 -11 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
408 lines
11 KiB
C
408 lines
11 KiB
C
/*-
|
|
* Copyright 2013-2018 Alexander Peslyak
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#if RESTRICTED_PARAMS
|
|
|
|
#define decode64_uint32(dst, src, min) \
|
|
({ \
|
|
uint32_t d32 = a2i64(*(src)); \
|
|
if (d32 > 47) \
|
|
goto fail; \
|
|
*(dst) = d32 + (min); \
|
|
++src; \
|
|
})
|
|
#define test_decode64_uint32() ((void)0)
|
|
#define FULL_PARAMS(...)
|
|
|
|
#else
|
|
|
|
#define FULL_PARAMS(...) __VA_ARGS__
|
|
|
|
/* Not inlining:
|
|
* de/encode64 functions are only used to read
|
|
* yescrypt_params_t field, and convert salt to binary -
|
|
* both of these are negligible compared to main hashing operation
|
|
*/
|
|
static NOINLINE const uint8_t *decode64_uint32(
|
|
uint32_t *dst,
|
|
const uint8_t *src, uint32_t val)
|
|
{
|
|
uint32_t start = 0, end = 47, bits = 0;
|
|
uint32_t c;
|
|
|
|
if (!src) /* previous decode failed already? */
|
|
goto fail;
|
|
|
|
c = a2i64(*src++);
|
|
if (c > 63)
|
|
goto fail;
|
|
|
|
// The encoding of number N:
|
|
// start = 0 end = 47
|
|
// If N < 48, it is encoded verbatim, else
|
|
// N -= 48
|
|
// start = end+1 = 48
|
|
// end += (64-end)/2 = 55
|
|
// If N < (end+1-start)<<6 = 8<<6, it is encoded as 48+(N>>6)|low6bits (that is, 48...55|<6bit>), else
|
|
// N -= 8<<6
|
|
// start = end+1 = 56
|
|
// end += (64-end)/2 = 59
|
|
// 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
|
|
// ...same for 60..61|<6bit>|<6bit>|<6bit>
|
|
// .......same for 62|<6bit>|<6bit>|<6bit>|<6bit>
|
|
// .......same for 63|<6bit>|<6bit>|<6bit>|<6bit>|<6bit>
|
|
dbg_dec64("c:%d val:0x%08x", (int)c, (unsigned)val);
|
|
while (c > end) {
|
|
dbg_dec64("c:%d > end:%d", (int)c, (int)end);
|
|
val += (end + 1 - start) << bits;
|
|
dbg_dec64("val+=0x%08x", (int)((end + 1 - start) << bits));
|
|
dbg_dec64(" val:0x%08x", (unsigned)val);
|
|
start = end + 1;
|
|
end += (64 - end) / 2;
|
|
bits += 6;
|
|
dbg_dec64("start=%d", (int)start);
|
|
dbg_dec64("end=%d", (int)end);
|
|
dbg_dec64("bits=%d", (int)bits);
|
|
}
|
|
|
|
val += (c - start) << bits;
|
|
dbg_dec64("final val+=0x%08x", (int)((c - start) << bits));
|
|
dbg_dec64(" val:0x%08x", (unsigned)val);
|
|
|
|
while (bits != 0) {
|
|
c = a2i64(*src++);
|
|
if (c > 63)
|
|
goto fail;
|
|
bits -= 6;
|
|
val += c << bits;
|
|
dbg_dec64("low bits val+=0x%08x", (int)(c << bits));
|
|
dbg_dec64(" val:0x%08x", (unsigned)val);
|
|
}
|
|
ret:
|
|
*dst = val;
|
|
return src;
|
|
fail:
|
|
val = 0;
|
|
src = NULL;
|
|
goto ret;
|
|
}
|
|
|
|
#if TEST_DECODE64
|
|
static void test_decode64_uint32(void)
|
|
{
|
|
const uint8_t *src, *end;
|
|
uint32_t u32;
|
|
int a = 48;
|
|
int b = 8<<6; // 0x0200
|
|
int c = 4<<12; // 0x04000
|
|
int d = 2<<18; // 0x080000
|
|
int e = 1<<24; // 0x1000000
|
|
|
|
src = (void*)"wzzz";
|
|
end = decode64_uint32(&u32, src, 0);
|
|
if (u32 != 0x0003ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
|
|
if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
|
|
src = (void*)"xzzz";
|
|
end = decode64_uint32(&u32, src, 0);
|
|
if (u32 != 0x0007ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
|
|
if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
|
|
// Note how the last representable "x---" encoding, 0x7ffff, is exactly d-1!
|
|
// And if we now increment it, we get:
|
|
src = (void*)"y....";
|
|
end = decode64_uint32(&u32, src, 0);
|
|
if (u32 != 0x00000000+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
|
|
if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
|
|
src = (void*)"yzzzz";
|
|
end = decode64_uint32(&u32, src, 0);
|
|
if (u32 != 0x00ffffff+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
|
|
if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
|
|
|
|
src = (void*)"zzzzzz";
|
|
end = decode64_uint32(&u32, src, 0);
|
|
if (u32 != 0x3fffffff+e+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
|
|
if (end != src + 6) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
|
|
|
|
bb_error_msg("test_decode64_uint32() OK");
|
|
}
|
|
#else
|
|
# define test_decode64_uint32() ((void)0)
|
|
#endif
|
|
|
|
#endif /* !RESTRICTED_PARAMS */
|
|
|
|
#if 1
|
|
static const uint8_t *decode64(
|
|
uint8_t *dst, size_t *dstlen,
|
|
const uint8_t *src)
|
|
{
|
|
unsigned dstpos = 0;
|
|
|
|
dbg_dec64("src:'%s'", src);
|
|
for (;;) {
|
|
uint32_t c, value = 0;
|
|
int bits = 0;
|
|
while (*src != '\0' && *src != '$') {
|
|
c = a2i64(*src);
|
|
if (c > 63) { /* bad ascii64 char, stop decoding at it */
|
|
break;
|
|
}
|
|
src++;
|
|
value |= c << bits;
|
|
bits += 6;
|
|
if (bits == 24) /* got 4 chars */
|
|
goto store;
|
|
}
|
|
/* we read entire src, or met a non-ascii64 char (such as "$") */
|
|
if (bits == 0)
|
|
break;
|
|
/* else: we got last, partial bit block - store it */
|
|
store:
|
|
dbg_dec64(" storing bits:%d dstpos:%u v:%08x", bits, dstpos, (int)SWAP_BE32(value)); //BE to see lsb first
|
|
for (;;) {
|
|
if ((*src == '\0' || *src == '$')
|
|
&& value == 0 && bits < 8
|
|
) {
|
|
/* Example: mkpasswd PWD '$y$j9T$123':
|
|
* the "123" is bits:18 value:03,51,00
|
|
* is considered to be 2 bytes, not 3!
|
|
*
|
|
* '$y$j9T$zzz' in upstream fails outright (3rd byte isn't zero).
|
|
* IOW: for upstream, validity of salt depends on VALUE,
|
|
* not just size of salt. Which is a bug.
|
|
* The '$y$j9T$zzz.' salt is the same
|
|
* (it adds 6 zero msbits) but upstream works with it,
|
|
* thus '$y$j9T$zzz' should work too and give the same result.
|
|
*/
|
|
goto end;
|
|
}
|
|
if (dstpos >= *dstlen) {
|
|
dbg_dec64(" ERR: bits:%d dstpos:%u dst[] is too small", bits, dstpos);
|
|
goto fail;
|
|
}
|
|
*dst++ = value;
|
|
dstpos++;
|
|
value >>= 8;
|
|
bits -= 8;
|
|
if (bits <= 0) /* can get negative, if we e.g. had 6 bits */
|
|
break;
|
|
}
|
|
if (*src == '\0' || *src == '$')
|
|
break;
|
|
}
|
|
end:
|
|
*dstlen = dstpos;
|
|
dbg_dec64("dec64: OK, dst[%d]", (int)dstpos);
|
|
return src;
|
|
fail:
|
|
/* *dstlen = 0; - not needed, caller detects error by seeing NULL */
|
|
return NULL;
|
|
}
|
|
#else
|
|
/* Buggy (and larger) original code */
|
|
static const uint8_t *decode64(
|
|
uint8_t *dst, size_t *dstlen,
|
|
const uint8_t *src, size_t srclen)
|
|
{
|
|
size_t dstpos = 0;
|
|
|
|
while (dstpos <= *dstlen && srclen) {
|
|
uint32_t value = 0, bits = 0;
|
|
while (srclen--) {
|
|
uint32_t c = a2i64(*src);
|
|
if (c > 63) {
|
|
srclen = 0;
|
|
break;
|
|
}
|
|
src++;
|
|
value |= c << bits;
|
|
bits += 6;
|
|
if (bits >= 24)
|
|
break;
|
|
}
|
|
if (!bits)
|
|
break;
|
|
if (bits < 12) /* must have at least one full byte */
|
|
goto fail;
|
|
dbg_dec64(" storing bits:%d v:%08x", (int)bits, (int)SWAP_BE32(value)); //BE to see lsb first
|
|
while (dstpos++ < *dstlen) {
|
|
*dst++ = value;
|
|
value >>= 8;
|
|
bits -= 8;
|
|
if (bits < 8) { /* 2 or 4 */
|
|
if (value) /* must be 0 */
|
|
goto fail;
|
|
bits = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (bits)
|
|
goto fail;
|
|
}
|
|
|
|
if (!srclen && dstpos <= *dstlen) {
|
|
*dstlen = dstpos;
|
|
dbg_dec64("dec64: OK, dst[%d]", (int)dstpos);
|
|
return src;
|
|
}
|
|
fail:
|
|
/* *dstlen = 0; - not needed, caller detects error by seeing NULL */
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static char *encode64(
|
|
char *dst, size_t dstlen,
|
|
const uint8_t *src, size_t srclen)
|
|
{
|
|
while (srclen) {
|
|
uint32_t value = 0, b = 0;
|
|
do {
|
|
value |= (uint32_t)(*src++ << b);
|
|
b += 8;
|
|
srclen--;
|
|
} while (srclen && b < 24);
|
|
|
|
b >>= 3; /* number of bits to number of bytes */
|
|
b++; /* 1, 2 or 3 bytes will become 2, 3 or 4 ascii64 chars */
|
|
dstlen -= b;
|
|
if ((ssize_t)dstlen <= 0)
|
|
return NULL;
|
|
dst = num2str64_lsb_first(dst, value, b);
|
|
}
|
|
*dst = '\0';
|
|
return dst;
|
|
}
|
|
|
|
char *yescrypt_r(
|
|
const uint8_t *passwd, size_t passwdlen,
|
|
const uint8_t *setting,
|
|
char *buf, size_t buflen)
|
|
{
|
|
struct {
|
|
yescrypt_ctx_t yctx[1];
|
|
unsigned char hashbin32[32];
|
|
} u;
|
|
#define yctx u.yctx
|
|
#define hashbin32 u.hashbin32
|
|
char *dst;
|
|
const uint8_t *src, *saltend;
|
|
size_t need, prefixlen;
|
|
uint32_t u32;
|
|
|
|
test_decode64_uint32();
|
|
|
|
memset(yctx, 0, sizeof(yctx));
|
|
FULL_PARAMS(yctx->param.p = 1;)
|
|
|
|
/* we assume setting starts with "$y$" (caller must ensure this) */
|
|
src = setting + 3;
|
|
|
|
src = decode64_uint32(&yctx->param.flags, src, 0);
|
|
/* "j9T" returns: 0x2f */
|
|
//if (!src)
|
|
// goto fail;
|
|
|
|
if (yctx->param.flags < YESCRYPT_RW) {
|
|
dbg("yctx->param.flags=0x%x", (unsigned)yctx->param.flags);
|
|
goto fail; // bbox: we don't support scrypt - only yescrypt
|
|
} else if (yctx->param.flags <= YESCRYPT_RW + (YESCRYPT_RW_FLAVOR_MASK >> 2)) {
|
|
/* "j9T" sets flags to 0xb6 */
|
|
yctx->param.flags = YESCRYPT_RW + ((yctx->param.flags - YESCRYPT_RW) << 2);
|
|
dbg("yctx->param.flags=0x%x", (unsigned)yctx->param.flags);
|
|
dbg(" YESCRYPT_RW:%u", !!(yctx->param.flags & YESCRYPT_RW));
|
|
dbg((yctx->param.flags & YESCRYPT_RW_FLAVOR_MASK) ==
|
|
(YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K)
|
|
? " YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K"
|
|
: " flags are not standard"
|
|
);
|
|
} else {
|
|
goto fail;
|
|
}
|
|
|
|
src = decode64_uint32(&u32, src, 1);
|
|
if (/*!src ||*/ u32 > 63)
|
|
goto fail;
|
|
yctx->param.N = (uint64_t)1 << u32;
|
|
/* "j9T" sets to 4096 (1<<12) */
|
|
dbg("yctx->param.N=%llu (1<<%u)", (unsigned long long)yctx->param.N, (unsigned)u32);
|
|
|
|
src = decode64_uint32(&yctx->param.r, src, 1);
|
|
/* "j9T" sets to 32 */
|
|
dbg("yctx->param.r=%u", yctx->param.r);
|
|
|
|
if (!src)
|
|
goto fail;
|
|
if (*src != '$') {
|
|
#if RESTRICTED_PARAMS
|
|
goto fail;
|
|
#else
|
|
src = decode64_uint32(&u32, src, 1);
|
|
dbg("yescrypt has extended params:0x%x", (unsigned)u32);
|
|
if (u32 & 1)
|
|
src = decode64_uint32(&yctx->param.p, src, 2);
|
|
if (u32 & 2)
|
|
src = decode64_uint32(&yctx->param.t, src, 1);
|
|
if (u32 & 4)
|
|
src = decode64_uint32(&yctx->param.g, src, 1);
|
|
if (u32 & 8) {
|
|
src = decode64_uint32(&u32, src, 1);
|
|
if (/*!src ||*/ u32 > 63)
|
|
goto fail;
|
|
yctx->param.NROM = (uint64_t)1 << u32;
|
|
}
|
|
if (!src)
|
|
goto fail;
|
|
if (*src != '$')
|
|
goto fail;
|
|
#endif
|
|
}
|
|
|
|
yctx->saltlen = sizeof(yctx->salt);
|
|
src++; /* now points to salt */
|
|
saltend = decode64(yctx->salt, &yctx->saltlen, src);
|
|
if (!saltend || (*saltend != '\0' && *saltend != '$'))
|
|
goto fail; /* salt[] is too small, or bad char during decode */
|
|
dbg_dec64("salt is %d ascii64 chars -> %d bytes (in binary)", (int)(saltend - src), (int)yctx->saltlen);
|
|
|
|
prefixlen = saltend - setting;
|
|
need = prefixlen + 1 + YESCRYPT_HASH_LEN + 1;
|
|
if (need > buflen /*overflow is quite unlikely: || need < prefixlen*/)
|
|
goto fail;
|
|
|
|
if (yescrypt_kdf32(yctx, passwd, passwdlen, hashbin32)) {
|
|
dbg("error in yescrypt_kdf32");
|
|
goto fail;
|
|
}
|
|
|
|
dst = mempcpy(buf, setting, prefixlen);
|
|
*dst++ = '$';
|
|
dst = encode64(dst, buflen - (dst - buf), hashbin32, sizeof(hashbin32));
|
|
if (!dst)
|
|
goto fail;
|
|
ret:
|
|
free_region(yctx->local);
|
|
explicit_bzero(&u, sizeof(u));
|
|
return buf;
|
|
fail:
|
|
buf = NULL;
|
|
goto ret;
|
|
#undef yctx
|
|
#undef hashbin32
|
|
}
|