[git commit] libbb/yescrypt: disable code which accepts unusual yescrypt parameters

Denys Vlasenko vda.linux at googlemail.com
Tue Jul 8 03:34:41 UTC 2025


commit: https://git.busybox.net/busybox/commit/?id=d0f0874d573f33f9f14372a4513f22f76c559479
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

Almost any reasonable yescrypt hashes in /etc/shadow should only ever use
"jXY" parameters which set N and r. Fancy multi-byte-encoded
wide integers are not needed for that.

function                                             old     new   delta
static.yescrypt_kdf32_body                             -     899    +899
static.PBKDF2_SHA256                                 213     219      +6
decode64_uint32                                      141       -    -141
yescrypt_r                                           990     805    -185
yescrypt_kdf32_body                                 1423       -   -1423
------------------------------------------------------------------------------
(add/remove: 1/2 grow/shrink: 1/1 up/down: 905/-1749)        Total: -844 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 libbb/yescrypt/alg-yescrypt-common.c |  25 +++++++-
 libbb/yescrypt/alg-yescrypt.h        | 120 ++++++++++++++++++++---------------
 2 files changed, 92 insertions(+), 53 deletions(-)

diff --git a/libbb/yescrypt/alg-yescrypt-common.c b/libbb/yescrypt/alg-yescrypt-common.c
index c85b2a0b9..e48be6581 100644
--- a/libbb/yescrypt/alg-yescrypt-common.c
+++ b/libbb/yescrypt/alg-yescrypt-common.c
@@ -18,6 +18,23 @@
  * 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 -
@@ -128,6 +145,8 @@ static void test_decode64_uint32(void)
 # define test_decode64_uint32() ((void)0)
 #endif
 
+#endif /* !RESTRICTED_PARAMS */
+
 #if 1
 static const uint8_t *decode64(
 		uint8_t *dst, size_t *dstlen,
@@ -283,7 +302,7 @@ char *yescrypt_r(
 	test_decode64_uint32();
 
 	memset(yctx, 0, sizeof(yctx));
-	yctx->param.p = 1;
+	FULL_PARAMS(yctx->param.p = 1;)
 
 	/* we assume setting starts with "$y$" (caller must ensure this) */
 	src = setting + 3;
@@ -324,6 +343,9 @@ char *yescrypt_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)
@@ -342,6 +364,7 @@ char *yescrypt_r(
 			goto fail;
 		if (*src != '$')
 			goto fail;
+#endif
 	}
 
 	yctx->saltlen = sizeof(yctx->salt);
diff --git a/libbb/yescrypt/alg-yescrypt.h b/libbb/yescrypt/alg-yescrypt.h
index e558cfdc5..a1d540c08 100644
--- a/libbb/yescrypt/alg-yescrypt.h
+++ b/libbb/yescrypt/alg-yescrypt.h
@@ -29,6 +29,8 @@
  */
 #ifdef YESCRYPT_INTERNAL
 
+// busybox debug and size-reduction configuration
+
 # if 1
 #  define dbg(...) ((void)0)
 # else
@@ -42,6 +44,68 @@
 #endif
 #define TEST_DECODE64 0
 
+/* Only accept one-char parameters in hash, and only first three?
+ * Almost any reasonable yescrypt hashes in /etc/shadow should
+ * only ever use "jXY" parameters which set N and r.
+ * Fancy multi-byte-encoded wide integers are not needed for that.
+ */
+#define RESTRICTED_PARAMS 1
+/* Note: if you enable the above, please also enable
+ * YCTX_param_p, YCTX_param_t, YCTX_param_g, YCTX_param_NROM
+ * optimizations.
+ */
+
+// How much we save by forcing "standard" value by commenting the next line:
+//  160 bytes
+//#define YCTX_param_flags           yctx->param.flags
+//  260 bytes
+//#define flags___YESCRYPT_RW        (flags & YESCRYPT_RW)
+//  140 bytes
+//#define flags___YESCRYPT_MODE_MASK (flags & YESCRYPT_MODE_MASK)
+// ^^^^ forcing the above since the code already requires (checks for) this
+//   50 bytes
+#define YCTX_param_N     yctx->param.N
+// -100 bytes (negative!!!)
+#define YCTX_param_r     yctx->param.r
+//  400 bytes
+//#define YCTX_param_p     yctx->param.p
+//  130 bytes
+//#define YCTX_param_t     yctx->param.t
+//    2 bytes
+//#define YCTX_param_g     yctx->param.g
+//    1 bytes
+// ^^^^ this looks wrong, compiler should be able to constant-propagate the fact that NROM code is dead
+//#define YCTX_param_NROM  yctx->param.NROM
+
+#ifndef YCTX_param_flags
+#define YCTX_param_flags (YESCRYPT_RW | YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K)
+#endif
+#ifndef flags___YESCRYPT_RW
+#define flags___YESCRYPT_RW ((void)flags, YESCRYPT_RW)
+#endif
+#ifndef flags___YESCRYPT_MODE_MASK
+#define flags___YESCRYPT_MODE_MASK ((void)flags, YESCRYPT_RW)
+#endif
+// standard ("j9T") values:
+#ifndef YCTX_param_N
+#define YCTX_param_N     4096
+#endif
+#ifndef YCTX_param_r
+#define YCTX_param_r     32
+#endif
+#ifndef YCTX_param_p
+#define YCTX_param_p     1
+#endif
+#ifndef YCTX_param_t
+#define YCTX_param_t     0
+#endif
+#ifndef YCTX_param_g
+#define YCTX_param_g     0
+#endif
+#ifndef YCTX_param_NROM
+#define YCTX_param_NROM  0
+#endif
+
 /**
  * Type and possible values for the flags argument of yescrypt_kdf(),
  * yescrypt_encode_params_r(), yescrypt_encode_params().  Most of these may be
@@ -129,9 +193,12 @@ typedef struct {
  */
 typedef struct {
 	uint32_t flags;
+	uint32_t r;
 	uint64_t N;
-	uint32_t r, p, t, g;
+#if !RESTRICTED_PARAMS
+	uint32_t p, t, g;
 	uint64_t NROM;
+#endif
 } yescrypt_params_t;
 
 typedef struct {
@@ -147,57 +214,6 @@ typedef struct {
 	yescrypt_region_t local[1];
 } yescrypt_ctx_t;
 
-// How much can save by forcing "standard" value by commenting the next line:
-//  160 bytes
-//#define YCTX_param_flags yctx->param.flags
-//  260 bytes
-//#define flags___YESCRYPT_RW (flags & YESCRYPT_RW)
-//  140 bytes
-//#define flags___YESCRYPT_MODE_MASK (flags & YESCRYPT_MODE_MASK)
-// ^^^^ forcing the above since the code already requires (checks for) this
-//   50 bytes
-#define YCTX_param_N     yctx->param.N
-// -100 bytes (negative!!!)
-#define YCTX_param_r     yctx->param.r
-//  400 bytes
-#define YCTX_param_p     yctx->param.p
-//  130 bytes
-#define YCTX_param_t     yctx->param.t
-//    2 bytes
-#define YCTX_param_g     yctx->param.g
-//    1 bytes
-// ^^^^ this looks wrong, compiler should be able to constant-propagate the fact that NROM code is dead
-#define YCTX_param_NROM  yctx->param.NROM
-
-// standard ("j9T") values:
-#ifndef YCTX_param_flags
-#define YCTX_param_flags (YESCRYPT_RW | YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K)
-#endif
-#ifndef flags___YESCRYPT_RW
-#define flags___YESCRYPT_RW ((void)flags, YESCRYPT_RW)
-#endif
-#ifndef flags___YESCRYPT_MODE_MASK
-#define flags___YESCRYPT_MODE_MASK ((void)flags, YESCRYPT_RW)
-#endif
-#ifndef YCTX_param_N
-#define YCTX_param_N     4096
-#endif
-#ifndef YCTX_param_r
-#define YCTX_param_r     32
-#endif
-#ifndef YCTX_param_p
-#define YCTX_param_p     1
-#endif
-#ifndef YCTX_param_t
-#define YCTX_param_t     0
-#endif
-#ifndef YCTX_param_g
-#define YCTX_param_g     0
-#endif
-#ifndef YCTX_param_NROM
-#define YCTX_param_NROM  0
-#endif
-
 /**
  * yescrypt_r(shared, local, passwd, passwdlen, setting, key, buf, buflen):
  * Compute and encode an scrypt or enhanced scrypt hash of passwd given the


More information about the busybox-cvs mailing list