[git commit] libbb/yescrypt: accept longer salts (up to 84 chars)
Denys Vlasenko
vda.linux at googlemail.com
Mon Jul 7 15:08:32 UTC 2025
commit: https://git.busybox.net/busybox/commit/?id=75758c73608f3c6be9ea2d338a199a8aa11c51e2
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master
function old new delta
cryptpw_main 214 223 +9
chpasswd_main 347 356 +9
passwd_main 931 934 +3
yescrypt_r 1084 1056 -28
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/1 up/down: 21/-28) Total: -7 bytes
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
include/libbb.h | 5 ++-
libbb/yescrypt/alg-yescrypt-common.c | 76 +++++++++++++++++++++++++++++++++++-
libbb/yescrypt/alg-yescrypt.h | 5 +++
testsuite/cryptpw.tests | 4 ++
4 files changed, 86 insertions(+), 4 deletions(-)
diff --git a/include/libbb.h b/include/libbb.h
index cbf723f7e..544ca3155 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1819,9 +1819,10 @@ extern int crypt_make_rand64encoded(char *p, int cnt /*, int rnd*/) FAST_FUNC;
* "$6$<sha_salt_16_chars><NUL>"
* #define MAX_PW_SALT_LEN (3 + 16 + 1)
* yescrypt:
- * "$y$j9T$<yescrypt_salt_24_chars><NUL>"
+ * "$y$" <up to 8 params of up to 6 chars each> "$" <up to 84 chars salt><NUL>
+ * (84 chars are ascii64-encoded 64 binary bytes)
*/
-#define MAX_PW_SALT_LEN (7 + 24 + 1)
+#define MAX_PW_SALT_LEN (3 + 8*6 + 1 + 84 + 1)
extern char* crypt_make_pw_salt(char p[MAX_PW_SALT_LEN], const char *algo) FAST_FUNC;
/* Returns number of lines changed, or -1 on error */
diff --git a/libbb/yescrypt/alg-yescrypt-common.c b/libbb/yescrypt/alg-yescrypt-common.c
index db6e098c7..1e896df64 100644
--- a/libbb/yescrypt/alg-yescrypt-common.c
+++ b/libbb/yescrypt/alg-yescrypt-common.c
@@ -63,6 +63,75 @@ fail:
return NULL;
}
+#if 1
+static const uint8_t *decode64(
+ uint8_t *dst, size_t *dstlen,
+ const uint8_t *src, size_t srclen)
+{
+ size_t dstpos = 0;
+
+ dbg_dec64("src:'%s' len:%d", src, (int)srclen);
+ for (;;) {
+ uint32_t c, value = 0;
+ int bits = 0;
+ while (srclen != 0) {
+ srclen--;
+ c = a2i64(*src);
+ if (c > 63) { /* bad ascii64 char, stop decoding at it */
+ srclen = 0;
+ 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 v:%08x", bits, (int)SWAP_BE32(value)); //BE to see lsb first
+ while (dstpos < *dstlen) {
+ if (srclen == 0 && value == 0) {
+ /* 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;
+ }
+ dstpos++;
+ *dst++ = value;
+ value >>= 8;
+ bits -= 8;
+ if (bits <= 0) /* can get negative, if we e.g. had 6 bits */
+ goto next;
+ }
+ dbg_dec64(" ERR: bits:%d dst[] is too small", bits);
+ goto fail;
+ next:
+ if (srclen == 0)
+ break;
+ }
+ end:
+ /* here, srclen is 0, no need to check */
+ *dstlen = dstpos;
+ dbg_dec64("dec64: OK, dst[%d]", (int)dstpos);
+ return src;
+fail:
+ *dstlen = 0;
+ 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)
@@ -87,6 +156,7 @@ static const uint8_t *decode64(
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;
@@ -104,12 +174,14 @@ static const uint8_t *decode64(
if (!srclen && dstpos <= *dstlen) {
*dstlen = dstpos;
+ dbg_dec64("dec64: OK, dst[%d]", (int)dstpos);
return src;
}
fail:
- *dstlen = 0;
+ /* *dstlen = 0; - not needed, caller detects error by seeing NULL */
return NULL;
}
+#endif
static char *encode64(
char *dst, size_t dstlen,
@@ -189,7 +261,7 @@ char *yescrypt_r(
goto fail;
if (*src != '$') {
src = decode64_uint32(&u32, src, 1);
- dbg("yescrypt has extended params:0x%x", (unsigned)have);
+ dbg("yescrypt has extended params:0x%x", (unsigned)u32);
if (u32 & 1)
src = decode64_uint32(&yctx->param.p, src, 2);
if (u32 & 2)
diff --git a/libbb/yescrypt/alg-yescrypt.h b/libbb/yescrypt/alg-yescrypt.h
index 97475d89f..4554e3de3 100644
--- a/libbb/yescrypt/alg-yescrypt.h
+++ b/libbb/yescrypt/alg-yescrypt.h
@@ -33,6 +33,11 @@
# else
# define dbg(...) bb_error_msg(__VA_ARGS__)
# endif
+# if 1
+# define dbg_dec64(...) ((void)0)
+# else
+# define dbg_dec64(...) bb_error_msg(__VA_ARGS__)
+# endif
#endif
/**
diff --git a/testsuite/cryptpw.tests b/testsuite/cryptpw.tests
index 739fb4e9f..ef04e20d7 100755
--- a/testsuite/cryptpw.tests
+++ b/testsuite/cryptpw.tests
@@ -73,6 +73,10 @@ testing 'cryptpw yescrypt with 3-char salt' \
'cryptpw -m yescrypt qweRTY123 at -+ j9T\$123' \
'$y$j9T$123$A34DMIGUbUIo3bjx66Wtk2IFoREMIw6d49it25KQh2D\n' \
'' ''
+testing 'cryptpw yescrypt with 84-char salt (max size)' \
+ 'cryptpw -m yescrypt qweRTY123 at -+ j9T\$123456789012345678901234567890123456789012345678901234567890123456789012345678901234' \
+ '$y$j9T$123456789012345678901234567890123456789012345678901234567890123456789012345678901234$ubrUuPCpI97LIMlVMt/A0Mhs/kBK2UBJYcQSxEZSlz4\n' \
+ '' ''
testing 'cryptpw yescrypt implicit' \
'cryptpw qweRTY123 at -+ \$y\$j9T\$123456789012345678901234' \
'$y$j9T$123456789012345678901234$AKxw5OX/T4jD.v./IW.5tE/j7izNjw06fg3OvH1LsN9\n' \
More information about the busybox-cvs
mailing list