← Commit list

80e676664

Denys Vlasenko
Denys Vlasenko
2025-07-06
SOURCE BUG Duration: 113s

Subject

libbb: add yescrypt password hashing support

SOURCE BUG

This commit was genuinely broken at the time (not related to our build system): libbb/yescrypt eklenirken (2025-07-06 - 2025-07-07 arasi) once unutulan kaynak dosyalari (pw_encrypt_yes.c, hash_sha256_block.c, pw_ascii64.c), ardindan 64-bit'te derlenmeyen bir 'buflen' degiskeni (alg-yescrypt-kdf.c) vardi - busybox'in kendi kodundaki gercek hatalar (e5d3a8763 ile duzeltildi)

→ Bu hatanin tam hikayesini ve duzeltme diffini gor

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

Makefile+1 -0
@@ -476,6 +476,7 @@ libs-y := \
findutils/ \
init/ \
libbb/ \
+ libbb/yescrypt/ \
libpwdgrp/ \
loginutils/ \
mailutils/ \
include/libbb.h+14 -8
@@ -1806,18 +1806,24 @@ extern char *pw_encrypt(const char *clear, const char *salt, int cleanup) FAST_F
extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) FAST_FUNC;
/*
* rnd is additional random input. New one is returned.
- * Useful if you call crypt_make_salt many times in a row:
- * rnd = crypt_make_salt(buf1, 4, 0);
- * rnd = crypt_make_salt(buf2, 4, rnd);
- * rnd = crypt_make_salt(buf3, 4, rnd);
+ * Useful if you call crypt_make_rand64encoded many times in a row:
+ * rnd = crypt_make_rand64encoded(buf1, 4, 0);
+ * rnd = crypt_make_rand64encoded(buf2, 4, rnd);
+ * rnd = crypt_make_rand64encoded(buf3, 4, rnd);
* (otherwise we risk having same salt generated)
*/
-extern int crypt_make_salt(char *p, int cnt /*, int rnd*/) FAST_FUNC;
-/* "$N$" + sha_salt_16_bytes + NUL */
-#define MAX_PW_SALT_LEN (3 + 16 + 1)
+extern int crypt_make_rand64encoded(char *p, int cnt /*, int rnd*/) FAST_FUNC;
+/* Size of char salt[] to hold randomly-generated salt string
+ * sha256/512:
+ * "$5$<sha_salt_16_chars><NUL>"
+ * "$6$<sha_salt_16_chars><NUL>"
+ * #define MAX_PW_SALT_LEN (3 + 16 + 1)
+ * yescrypt:
+ * "$y$j9T$<yescrypt_salt_24_chars><NUL>"
+ */
+#define MAX_PW_SALT_LEN (7 + 24 + 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 */
#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
#define update_passwd(filename, username, data, member) \
libbb/pw_encrypt.c+49 -4
@@ -31,7 +31,7 @@ static int i64c(int i)
return ('a' - 38 + i);
}
-int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */)
+int FAST_FUNC crypt_make_rand64encoded(char *p, int cnt /*, int x */)
{
/* was: x += ... */
unsigned x = getpid() + monotonic_us();
@@ -68,11 +68,49 @@ char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo)
#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
if ((algo[0]|0x20) == 's') { /* sha */
salt[1] = '5' + (strcasecmp(algo, "sha512") == 0);
- len = 16/2;
+ len = 16 / 2;
+ }
+#endif
+#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_YES
+ if ((algo[0]|0x20) == 'y') { /* yescrypt */
+ salt[1] = 'y';
+ len = 24 / 2;
+// The "j9T$" below is the default "yescrypt parameters" encoded by yescrypt_encode_params_r():
+//
+//shadow-4.17.4/src/passwd.c
+// salt = crypt_make_rand64encoded(NULL, NULL);
+//shadow-4.17.4/lib/salt.c
+//const char *crypt_make_rand64encoded(const char *meth, void *arg)
+// if (streq(method, "YESCRYPT")) {
+// MAGNUM(result, 'y');
+// salt_len = YESCRYPT_SALT_SIZE; // 24
+// rounds = YESCRYPT_get_salt_cost(arg); // always Y_COST_DEFAULT == 5 for NULL arg
+// YESCRYPT_salt_cost_to_buf(result, rounds); // always "j9T$"
+// char *retval = crypt_gensalt(result, rounds, NULL, 0);
+//libxcrypt-4.4.38/lib/crypt-yescrypt.c
+//void gensalt_yescrypt_rn (unsigned long count,
+// const uint8_t *rbytes, size_t nrbytes,
+// uint8_t *output, size_t o_size)
+// yescrypt_params_t params = {
+// .flags = YESCRYPT_DEFAULTS,
+// .p = 1,
+// };
+// if (count < 3) ... else
+// params.r = 32; // N in 4KiB
+// params.N = 1ULL << (count + 7); // 3 -> 1024, 4 -> 2048, ... 11 -> 262144
+// yescrypt_encode_params_r(&params, rbytes, nrbytes, outbuf, o_size) // always "$y$j9T$<random>"
+ salt_ptr = stpcpy(salt_ptr, "j9T$");
+ crypt_make_rand64encoded(salt_ptr, len); /* appends 2*len random chars */
+ /* For "mkpasswd -m yescrypt PASS j9T$<salt>" use case,
+ * "j9T$" is considered part of salt,
+ * need to return pointer to 'j'. Without -4,
+ * we'd end up using "j9T$j9T$<salt>" as salt.
+ */
+ return salt_ptr - 4;
}
#endif
}
- crypt_make_salt(salt_ptr, len);
+ crypt_make_rand64encoded(salt_ptr, len); /* appends 2*len random chars */
return salt_ptr;
}
@@ -99,6 +137,9 @@ to64(char *s, unsigned v, int n)
#if ENABLE_USE_BB_CRYPT_SHA
#include "pw_encrypt_sha.c"
#endif
+#if ENABLE_USE_BB_CRYPT_YES
+#include "pw_encrypt_yes.c"
+#endif
/* Other advanced crypt ids (TODO?): */
/* $2$ or $2a$: Blowfish */
@@ -109,10 +150,14 @@ static struct des_ctx *des_ctx;
/* my_crypt returns malloc'ed data */
static char *my_crypt(const char *key, const char *salt)
{
- /* MD5 or SHA? */
+ /* "$x$...." string? */
if (salt[0] == '$' && salt[1] && salt[2] == '$') {
if (salt[1] == '1')
return md5_crypt(xzalloc(MD5_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt);
+#if ENABLE_USE_BB_CRYPT_YES
+ if (salt[1] == 'y')
+ return yes_crypt(key, salt);
+#endif
#if ENABLE_USE_BB_CRYPT_SHA
if (salt[1] == '5' || salt[1] == '6')
return sha_crypt((char*)key, (char*)salt);
libbb/yescrypt/Kbuild.src+9 -0
@@ -0,0 +1,9 @@
+# Makefile for busybox
+#
+# Copyright (C) 2025 by Denys Vlasenko <[email protected]>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
libbb/yescrypt/README+4 -0
@@ -0,0 +1,4 @@
+The yescrypt code in this directory is adapted from libxcrypt-4.4.38
+with minimal edits, hopefully making it easier to track
+backports by resetting the tree to the commit which created this file,
+then comparing changes in upstream libxcrypt to the tree.
libbb/yescrypt/alg-sha256.c+608 -0
@@ -0,0 +1,608 @@
+/*-
+ * Copyright 2005-2016 Colin Percival
+ * Copyright 2016-2018,2021 Alexander Peslyak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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 defined(__GNUC__)
+#define restrict __restrict
+#else
+#define restrict
+#endif
+
+/* SHA256 round constants. */
+static const uint32_t Krnd[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/* Elementary functions used by SHA256 */
+#define Ch(x, y, z) ((x & (y ^ z)) ^ z)
+#if 1 /* Explicit caching/reuse of common subexpression between rounds */
+#define Maj(x, y, z) (y ^ ((x_xor_y = x ^ y) & y_xor_z))
+#else /* Let the compiler cache/reuse or not */
+#define Maj(x, y, z) (y ^ ((x ^ y) & (y ^ z)))
+#endif
+#define SHR(x, n) (x >> n)
+#define ROTR(x, n) ((x >> n) | (x << (32 - n)))
+#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
+#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
+
+/* SHA256 round function */
+#define RND(a, b, c, d, e, f, g, h, k) \
+ h += S1(e) + Ch(e, f, g) + k; \
+ d += h; \
+ h += S0(a) + Maj(a, b, c); \
+ y_xor_z = x_xor_y;
+
+/* Adjusted round function for rotating state */
+#define RNDr(S, W, i, ii) \
+ RND(S[(64 - i) % 8], S[(65 - i) % 8], \
+ S[(66 - i) % 8], S[(67 - i) % 8], \
+ S[(68 - i) % 8], S[(69 - i) % 8], \
+ S[(70 - i) % 8], S[(71 - i) % 8], \
+ W[i + ii] + Krnd[i + ii])
+
+/* Message schedule computation */
+#define MSCH(W, ii, i) \
+ W[i + ii + 16] = s1(W[i + ii + 14]) + W[i + ii + 9] + s0(W[i + ii + 1]) + W[i + ii]
+
+/*
+ * SHA256 block compression function. The 256-bit state is transformed via
+ * the 512-bit input block to produce a new state.
+ */
+static void
+SHA256_Transform(uint32_t state[static restrict 8],
+ const uint8_t block[static restrict 64],
+ uint32_t W[static restrict 64], uint32_t S[static restrict 8])
+{
+ int i;
+
+ /* 1. Prepare the first part of the message schedule W. */
+ be32dec_vect(W, block, 16);
+
+ /* 2. Initialize working variables. */
+ memcpy(S, state, 32);
+
+ /* 3. Mix. */
+ for (i = 0; i <= 48; i += 16) {
+ uint32_t x_xor_y, y_xor_z = S[(65 - i) % 8] ^ S[(66 - i) % 8];
+ RNDr(S, W, 0, i);
+ RNDr(S, W, 1, i);
+ RNDr(S, W, 2, i);
+ RNDr(S, W, 3, i);
+ RNDr(S, W, 4, i);
+ RNDr(S, W, 5, i);
+ RNDr(S, W, 6, i);
+ RNDr(S, W, 7, i);
+ RNDr(S, W, 8, i);
+ RNDr(S, W, 9, i);
+ RNDr(S, W, 10, i);
+ RNDr(S, W, 11, i);
+ RNDr(S, W, 12, i);
+ RNDr(S, W, 13, i);
+ RNDr(S, W, 14, i);
+ RNDr(S, W, 15, i);
+
+ if (i == 48)
+ break;
+
+ MSCH(W, 0, i);
+ MSCH(W, 1, i);
+ MSCH(W, 2, i);
+ MSCH(W, 3, i);
+ MSCH(W, 4, i);
+ MSCH(W, 5, i);
+ MSCH(W, 6, i);
+ MSCH(W, 7, i);
+ MSCH(W, 8, i);
+ MSCH(W, 9, i);
+ MSCH(W, 10, i);
+ MSCH(W, 11, i);
+ MSCH(W, 12, i);
+ MSCH(W, 13, i);
+ MSCH(W, 14, i);
+ MSCH(W, 15, i);
+ }
+
+ /* 4. Mix local working variables into global state. */
+ state[0] += S[0];
+ state[1] += S[1];
+ state[2] += S[2];
+ state[3] += S[3];
+ state[4] += S[4];
+ state[5] += S[5];
+ state[6] += S[6];
+ state[7] += S[7];
+}
+
+static const uint8_t PAD[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* Add padding and terminating bit-count. */
+static void
+SHA256_Pad(SHA256_CTX * ctx, uint32_t tmp32[static restrict 72])
+{
+ size_t r;
+
+ /* Figure out how many bytes we have buffered. */
+ r = (ctx->count >> 3) & 0x3f;
+
+ /* Pad to 56 mod 64, transforming if we finish a block en route. */
+ if (r < 56) {
+ /* Pad to 56 mod 64. */
+ memcpy(&ctx->buf[r], PAD, 56 - r);
+ } else {
+ /* Finish the current block and mix. */
+ memcpy(&ctx->buf[r], PAD, 64 - r);
+ SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]);
+
+ /* The start of the final block is all zeroes. */
+ memset(&ctx->buf[0], 0, 56);
+ }
+
+ /* Add the terminating bit-count. */
+ be64enc(&ctx->buf[56], ctx->count);
+
+ /* Mix in the final block. */
+ SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]);
+}
+
+/* Magic initialization constants. */
+static const uint32_t initial_state[8] = {
+ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
+ 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
+};
+
+/**
+ * SHA256_Init(ctx):
+ * Initialize the SHA256 context ${ctx}.
+ */
+void
+SHA256_Init(SHA256_CTX * ctx)
+{
+
+ /* Zero bits processed so far. */
+ ctx->count = 0;
+
+ /* Initialize state. */
+ memcpy(ctx->state, initial_state, sizeof(initial_state));
+}
+
+/**
+ * SHA256_Update(ctx, in, len):
+ * Input ${len} bytes from ${in} into the SHA256 context ${ctx}.
+ */
+static void
+_SHA256_Update(SHA256_CTX * ctx, const void * in, size_t len,
+ uint32_t tmp32[static restrict 72])
+{
+ uint32_t r;
+ const uint8_t * src = in;
+
+ /* Return immediately if we have nothing to do. */
+ if (len == 0)
+ return;
+
+ /* Number of bytes left in the buffer from previous updates. */
+ r = (ctx->count >> 3) & 0x3f;
+
+ /* Update number of bits. */
+ ctx->count += (uint64_t)(len) << 3;
+
+ /* Handle the case where we don't need to perform any transforms. */
+ if (len < 64 - r) {
+ memcpy(&ctx->buf[r], src, len);
+ return;
+ }
+
+ /* Finish the current block. */
+ memcpy(&ctx->buf[r], src, 64 - r);
+ SHA256_Transform(ctx->state, ctx->buf, &tmp32[0], &tmp32[64]);
+ src += 64 - r;
+ len -= 64 - r;
+
+ /* Perform complete blocks. */
+ while (len >= 64) {
+ SHA256_Transform(ctx->state, src, &tmp32[0], &tmp32[64]);
+ src += 64;
+ len -= 64;
+ }
+
+ /* Copy left over data into buffer. */
+ memcpy(ctx->buf, src, len);
+}
+
+/* Wrapper function for intermediate-values sanitization. */
+void
+SHA256_Update(SHA256_CTX * ctx, const void * in, size_t len)
+{
+ uint32_t tmp32[72];
+
+ /* Call the real function. */
+ _SHA256_Update(ctx, in, len, tmp32);
+
+ /* Clean the stack. */
+ explicit_bzero(tmp32, 288);
+}
+
+/**
+ * SHA256_Final(digest, ctx):
+ * Output the SHA256 hash of the data input to the context ${ctx} into the
+ * buffer ${digest}.
+ */
+static void
+_SHA256_Final(uint8_t digest[32], SHA256_CTX * ctx,
+ uint32_t tmp32[static restrict 72])
+{
+
+ /* Add padding. */
+ SHA256_Pad(ctx, tmp32);
+
+ /* Write the hash. */
+ be32enc_vect(digest, ctx->state, 8);
+}
+
+/* Wrapper function for intermediate-values sanitization. */
+void
+SHA256_Final(uint8_t digest[32], SHA256_CTX * ctx)
+{
+ uint32_t tmp32[72];
+
+ /* Call the real function. */
+ _SHA256_Final(digest, ctx, tmp32);
+
+ /* Clear the context state. */
+ explicit_bzero(ctx, sizeof(SHA256_CTX));
+
+ /* Clean the stack. */
+ explicit_bzero(tmp32, 288);
+}
+
+/**
+ * SHA256_Buf(in, len, digest):
+ * Compute the SHA256 hash of ${len} bytes from ${in} and write it to ${digest}.
+ */
+void
+SHA256_Buf(const void * in, size_t len, uint8_t digest[32])
+{
+ SHA256_CTX ctx;
+ uint32_t tmp32[72];
+
+ SHA256_Init(&ctx);
+ _SHA256_Update(&ctx, in, len, tmp32);
+ _SHA256_Final(digest, &ctx, tmp32);
+
+ /* Clean the stack. */
+ explicit_bzero(&ctx, sizeof(SHA256_CTX));
+ explicit_bzero(tmp32, 288);
+}
+
+/**
+ * HMAC_SHA256_Init(ctx, K, Klen):
+ * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from
+ * ${K}.
+ */
+static void
+_HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen,
+ uint32_t tmp32[static restrict 72], uint8_t pad[static restrict 64],
+ uint8_t khash[static restrict 32])
+{
+ const uint8_t * K = _K;
+ size_t i;
+
+ /* If Klen > 64, the key is really SHA256(K). */
+ if (Klen > 64) {
+ SHA256_Init(&ctx->ictx);
+ _SHA256_Update(&ctx->ictx, K, Klen, tmp32);
+ _SHA256_Final(khash, &ctx->ictx, tmp32);
+ K = khash;
+ Klen = 32;
+ }
+
+ /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */
+ SHA256_Init(&ctx->ictx);
+ memset(pad, 0x36, 64);
+ for (i = 0; i < Klen; i++)
+ pad[i] ^= K[i];
+ _SHA256_Update(&ctx->ictx, pad, 64, tmp32);
+
+ /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */
+ SHA256_Init(&ctx->octx);
+ memset(pad, 0x5c, 64);
+ for (i = 0; i < Klen; i++)
+ pad[i] ^= K[i];
+ _SHA256_Update(&ctx->octx, pad, 64, tmp32);
+}
+
+/* Wrapper function for intermediate-values sanitization. */
+void
+HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen)
+{
+ uint32_t tmp32[72];
+ uint8_t pad[64];
+ uint8_t khash[32];
+
+ /* Call the real function. */
+ _HMAC_SHA256_Init(ctx, _K, Klen, tmp32, pad, khash);
+
+ /* Clean the stack. */
+ explicit_bzero(tmp32, 288);
+ explicit_bzero(khash, 32);
+ explicit_bzero(pad, 64);
+}
+
+/**
+ * HMAC_SHA256_Update(ctx, in, len):
+ * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}.
+ */
+static void
+_HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void * in, size_t len,
+ uint32_t tmp32[static restrict 72])
+{
+
+ /* Feed data to the inner SHA256 operation. */
+ _SHA256_Update(&ctx->ictx, in, len, tmp32);
+}
+
+/* Wrapper function for intermediate-values sanitization. */
+void
+HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void * in, size_t len)
+{
+ uint32_t tmp32[72];
+
+ /* Call the real function. */
+ _HMAC_SHA256_Update(ctx, in, len, tmp32);
+
+ /* Clean the stack. */
+ explicit_bzero(tmp32, 288);
+}
+
+/**
+ * HMAC_SHA256_Final(digest, ctx):
+ * Output the HMAC-SHA256 of the data input to the context ${ctx} into the
+ * buffer ${digest}.
+ */
+static void
+_HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx,
+ uint32_t tmp32[static restrict 72], uint8_t ihash[static restrict 32])
+{
+
+ /* Finish the inner SHA256 operation. */
+ _SHA256_Final(ihash, &ctx->ictx, tmp32);
+
+ /* Feed the inner hash to the outer SHA256 operation. */
+ _SHA256_Update(&ctx->octx, ihash, 32, tmp32);
+
+ /* Finish the outer SHA256 operation. */
+ _SHA256_Final(digest, &ctx->octx, tmp32);
+}
+
+/* Wrapper function for intermediate-values sanitization. */
+void
+HMAC_SHA256_Final(uint8_t digest[32], HMAC_SHA256_CTX * ctx)
+{
+ uint32_t tmp32[72];
+ uint8_t ihash[32];
+
+ /* Call the real function. */
+ _HMAC_SHA256_Final(digest, ctx, tmp32, ihash);
+
+ /* Clear the context state. */
+ explicit_bzero(ctx, sizeof(HMAC_SHA256_CTX));
+
+ /* Clean the stack. */
+ explicit_bzero(tmp32, 288);
+ explicit_bzero(ihash, 32);
+}
+
+/**
+ * HMAC_SHA256_Buf(K, Klen, in, len, digest):
+ * Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of
+ * length ${Klen}, and write the result to ${digest}.
+ */
+void
+HMAC_SHA256_Buf(const void * K, size_t Klen, const void * in, size_t len,
+ uint8_t digest[32])
+{
+ HMAC_SHA256_CTX ctx;
+ uint32_t tmp32[72];
+ uint8_t tmp8[96];
+
+ _HMAC_SHA256_Init(&ctx, K, Klen, tmp32, &tmp8[0], &tmp8[64]);
+ _HMAC_SHA256_Update(&ctx, in, len, tmp32);
+ _HMAC_SHA256_Final(digest, &ctx, tmp32, &tmp8[0]);
+
+ /* Clean the stack. */
+ explicit_bzero(&ctx, sizeof(HMAC_SHA256_CTX));
+ explicit_bzero(tmp32, 288);
+ explicit_bzero(tmp8, 96);
+}
+
+/* Add padding and terminating bit-count, but don't invoke Transform yet. */
+static int
+SHA256_Pad_Almost(SHA256_CTX * ctx, uint8_t len[static restrict 8],
+ uint32_t tmp32[static restrict 72])
+{
+ uint32_t r;
+
+ r = (ctx->count >> 3) & 0x3f;
+ if (r >= 56)
+ return -1;
+
+ /*
+ * Convert length to a vector of bytes -- we do this now rather
+ * than later because the length will change after we pad.
+ */
+ be64enc(len, ctx->count);
+
+ /* Add 1--56 bytes so that the resulting length is 56 mod 64. */
+ _SHA256_Update(ctx, PAD, 56 - r, tmp32);
+
+ /* Add the terminating bit-count. */
+ ctx->buf[63] = len[7];
+ _SHA256_Update(ctx, len, 7, tmp32);
+
+ return 0;
+}
+
+/**
+ * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
+ * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
+ * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
+ */
+void
+PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt,
+ size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen)
+{
+ HMAC_SHA256_CTX Phctx, PShctx, hctx;
+ uint32_t tmp32[72];
+ union {
+ uint8_t tmp8[96];
+ uint32_t state[8];
+ } u;
+ size_t i;
+ uint8_t ivec[4];
+ uint8_t U[32];
+ uint8_t T[32];
+ uint64_t j;
+ int k;
+ size_t clen;
+
+ /* Sanity-check. */
+ assert(dkLen <= 32 * (size_t)(UINT32_MAX));
+
+ if (c == 1 && (dkLen & 31) == 0 && (saltlen & 63) <= 51) {
+ uint32_t oldcount;
+ uint8_t * ivecp;
+
+ /* Compute HMAC state after processing P and S. */
+ _HMAC_SHA256_Init(&hctx, passwd, passwdlen,
+ tmp32, &u.tmp8[0], &u.tmp8[64]);
+ _HMAC_SHA256_Update(&hctx, salt, saltlen, tmp32);
+
+ /* Prepare ictx padding. */
+ oldcount = hctx.ictx.count & (0x3f << 3);
+ _HMAC_SHA256_Update(&hctx, "\0\0\0", 4, tmp32);
+ if ((hctx.ictx.count & (0x3f << 3)) < oldcount ||
+ SHA256_Pad_Almost(&hctx.ictx, u.tmp8, tmp32))
+ goto generic; /* Can't happen due to saltlen check */
+ ivecp = hctx.ictx.buf + (oldcount >> 3);
+
+ /* Prepare octx padding. */
+ hctx.octx.count += 32 << 3;
+ SHA256_Pad_Almost(&hctx.octx, u.tmp8, tmp32);
+
+ /* Iterate through the blocks. */
+ for (i = 0; i * 32 < dkLen; i++) {
+ /* Generate INT(i + 1). */
+ be32enc(ivecp, (uint32_t)(i + 1));
+
+ /* Compute U_1 = PRF(P, S || INT(i)). */
+ memcpy(u.state, hctx.ictx.state, sizeof(u.state));
+ SHA256_Transform(u.state, hctx.ictx.buf,
+ &tmp32[0], &tmp32[64]);
+ be32enc_vect(hctx.octx.buf, u.state, 8);
+ memcpy(u.state, hctx.octx.state, sizeof(u.state));
+ SHA256_Transform(u.state, hctx.octx.buf,
+ &tmp32[0], &tmp32[64]);
+ be32enc_vect(&buf[i * 32], u.state, 8);
+ }
+
+ goto cleanup;
+ }
+
+generic:
+ /* Compute HMAC state after processing P. */
+ _HMAC_SHA256_Init(&Phctx, passwd, passwdlen,
+ tmp32, &u.tmp8[0], &u.tmp8[64]);
+
+ /* Compute HMAC state after processing P and S. */
+ memcpy(&PShctx, &Phctx, sizeof(HMAC_SHA256_CTX));
+ _HMAC_SHA256_Update(&PShctx, salt, saltlen, tmp32);
+
+ /* Iterate through the blocks. */
+ for (i = 0; i * 32 < dkLen; i++) {
+ /* Generate INT(i + 1). */
+ be32enc(ivec, (uint32_t)(i + 1));
+
+ /* Compute U_1 = PRF(P, S || INT(i)). */
+ memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX));
+ _HMAC_SHA256_Update(&hctx, ivec, 4, tmp32);
+ _HMAC_SHA256_Final(T, &hctx, tmp32, u.tmp8);
+
+ if (c > 1) {
+ /* T_i = U_1 ... */
+ memcpy(U, T, 32);
+
+ for (j = 2; j <= c; j++) {
+ /* Compute U_j. */
+ memcpy(&hctx, &Phctx, sizeof(HMAC_SHA256_CTX));
+ _HMAC_SHA256_Update(&hctx, U, 32, tmp32);
+ _HMAC_SHA256_Final(U, &hctx, tmp32, u.tmp8);
+
+ /* ... xor U_j ... */
+ for (k = 0; k < 32; k++)
+ T[k] ^= U[k];
+ }
+ }
+
+ /* Copy as many bytes as necessary into buf. */
+ clen = dkLen - i * 32;
+ if (clen > 32)
+ clen = 32;
+ memcpy(&buf[i * 32], T, clen);
+ }
+
+ /* Clean the stack. */
+ explicit_bzero(&Phctx, sizeof(HMAC_SHA256_CTX));
+ explicit_bzero(&PShctx, sizeof(HMAC_SHA256_CTX));
+ explicit_bzero(U, 32);
+ explicit_bzero(T, 32);
+
+cleanup:
+ explicit_bzero(&hctx, sizeof(HMAC_SHA256_CTX));
+ explicit_bzero(tmp32, 288);
+ explicit_bzero(&u, sizeof(u));
+}
libbb/yescrypt/alg-sha256.h+113 -0
@@ -0,0 +1,113 @@
+/*-
+ * Copyright 2005-2016 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ */
+
+/*
+ * Use #defines in order to avoid namespace collisions with anyone else's
+ * SHA256 code (e.g., the code in OpenSSL).
+ */
+#define SHA256_Init libcperciva_SHA256_Init
+#define SHA256_Update libcperciva_SHA256_Update
+#define SHA256_Final libcperciva_SHA256_Final
+#define SHA256_Buf libcperciva_SHA256_Buf
+#define SHA256_CTX libcperciva_SHA256_CTX
+#define HMAC_SHA256_Init libcperciva_HMAC_SHA256_Init
+#define HMAC_SHA256_Update libcperciva_HMAC_SHA256_Update
+#define HMAC_SHA256_Final libcperciva_HMAC_SHA256_Final
+#define HMAC_SHA256_Buf libcperciva_HMAC_SHA256_Buf
+#define HMAC_SHA256_CTX libcperciva_HMAC_SHA256_CTX
+
+/* Context structure for SHA256 operations. */
+typedef struct {
+ uint32_t state[8];
+ uint64_t count;
+ uint8_t buf[64];
+} SHA256_CTX;
+
+/**
+ * SHA256_Init(ctx):
+ * Initialize the SHA256 context ${ctx}.
+ */
+extern void SHA256_Init(SHA256_CTX *);
+
+/**
+ * SHA256_Update(ctx, in, len):
+ * Input ${len} bytes from ${in} into the SHA256 context ${ctx}.
+ */
+extern void SHA256_Update(SHA256_CTX *, const void *, size_t);
+
+/**
+ * SHA256_Final(digest, ctx):
+ * Output the SHA256 hash of the data input to the context ${ctx} into the
+ * buffer ${digest}.
+ */
+extern void SHA256_Final(uint8_t[32], SHA256_CTX *);
+
+/**
+ * SHA256_Buf(in, len, digest):
+ * Compute the SHA256 hash of ${len} bytes from ${in} and write it to ${digest}.
+ */
+extern void SHA256_Buf(const void *, size_t, uint8_t[32]);
+
+/* Context structure for HMAC-SHA256 operations. */
+typedef struct {
+ SHA256_CTX ictx;
+ SHA256_CTX octx;
+} HMAC_SHA256_CTX;
+
+/**
+ * HMAC_SHA256_Init(ctx, K, Klen):
+ * Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from
+ * ${K}.
+ */
+extern void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t);
+
+/**
+ * HMAC_SHA256_Update(ctx, in, len):
+ * Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}.
+ */
+extern void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t);
+
+/**
+ * HMAC_SHA256_Final(digest, ctx):
+ * Output the HMAC-SHA256 of the data input to the context ${ctx} into the
+ * buffer ${digest}.
+ */
+extern void HMAC_SHA256_Final(uint8_t[32], HMAC_SHA256_CTX *);
+
+/**
+ * HMAC_SHA256_Buf(K, Klen, in, len, digest):
+ * Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of
+ * length ${Klen}, and write the result to ${digest}.
+ */
+extern void HMAC_SHA256_Buf(const void *, size_t, const void *, size_t, uint8_t[32]);
+
+/**
+ * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
+ * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
+ * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
+ */
+extern void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t,
+ uint64_t, uint8_t *, size_t);
libbb/yescrypt/alg-yescrypt-common.c+714 -0
@@ -0,0 +1,714 @@
+/*-
+ * 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 0 //UNUSED
+static uint8_t *encode64_uint32(uint8_t *dst, size_t dstlen,
+ uint32_t src, uint32_t min)
+{
+ uint32_t start = 0, end = 47, chars = 1, bits = 0;
+
+ if (src < min)
+ return NULL;
+ src -= min;
+
+ do {
+ uint32_t count = (end + 1 - start) << bits;
+ if (src < count)
+ break;
+ if (start >= 63)
+ return NULL;
+ start = end + 1;
+ end = start + (62 - end) / 2;
+ src -= count;
+ chars++;
+ bits += 6;
+ } while (1);
+
+ if (dstlen <= chars) /* require room for a NUL terminator */
+ return NULL;
+
+ *dst++ = itoa64[start + (src >> bits)];
+
+ while (--chars) {
+ bits -= 6;
+ *dst++ = itoa64[(src >> bits) & 0x3f];
+ }
+
+ *dst = 0; /* NUL terminate just in case */
+
+ return dst;
+}
+#endif //UNUSED
+
+static inline uint32_t atoi64(uint8_t src)
+{
+ static const uint8_t atoi64_partial[77] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 64, 64, 64, 64, 64, 64, 64,
+ 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,
+ 64, 64, 64, 64, 64, 64,
+ 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
+ };
+ if (src >= '.' && src <= 'z')
+ return atoi64_partial[src - '.'];
+ return 64;
+}
+
+static const uint8_t *decode64_uint32(uint32_t *dst,
+ const uint8_t *src, uint32_t min)
+{
+ uint32_t start = 0, end = 47, chars = 1, bits = 0;
+ uint32_t c;
+
+ c = atoi64(*src++);
+ if (c > 63)
+ goto fail;
+
+ *dst = min;
+ while (c > end) {
+ *dst += (end + 1 - start) << bits;
+ start = end + 1;
+ end = start + (62 - end) / 2;
+ chars++;
+ bits += 6;
+ }
+
+ *dst += (c - start) << bits;
+
+ while (--chars) {
+ c = atoi64(*src++);
+ if (c > 63)
+ goto fail;
+ bits -= 6;
+ *dst += c << bits;
+ }
+
+ return src;
+
+fail:
+ *dst = 0;
+ return NULL;
+}
+
+static uint8_t *encode64_uint32_fixed(uint8_t *dst, size_t dstlen,
+ uint32_t src, uint32_t srcbits)
+{
+ uint32_t bits;
+
+ for (bits = 0; bits < srcbits; bits += 6) {
+ if (dstlen < 2)
+ return NULL;
+ *dst++ = itoa64[src & 0x3f];
+ dstlen--;
+ src >>= 6;
+ }
+
+ if (src || dstlen < 1)
+ return NULL;
+
+ *dst = 0; /* NUL terminate just in case */
+
+ return dst;
+}
+
+uint8_t *encode64(uint8_t *dst, size_t dstlen,
+ const uint8_t *src, size_t srclen)
+{
+ size_t i;
+
+ for (i = 0; i < srclen; ) {
+ uint8_t *dnext;
+ uint32_t value = 0, bits = 0;
+ do {
+ value |= (uint32_t)src[i++] << bits;
+ bits += 8;
+ } while (bits < 24 && i < srclen);
+ dnext = encode64_uint32_fixed(dst, dstlen, value, bits);
+ if (!dnext)
+ return NULL;
+ dstlen -= dnext - dst;
+ dst = dnext;
+ }
+
+ if (dstlen < 1)
+ return NULL;
+
+ *dst = 0; /* NUL terminate just in case */
+
+ return dst;
+}
+
+static const uint8_t *decode64_uint32_fixed(uint32_t *dst, uint32_t dstbits,
+ const uint8_t *src)
+{
+ uint32_t bits;
+
+ *dst = 0;
+ for (bits = 0; bits < dstbits; bits += 6) {
+ uint32_t c = atoi64(*src++);
+ if (c > 63) {
+ *dst = 0;
+ return NULL;
+ }
+ *dst |= c << bits;
+ }
+
+ return src;
+}
+
+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 = atoi64(*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;
+ 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;
+ return src;
+ }
+
+fail:
+ *dstlen = 0;
+ return NULL;
+}
+
+#if 0 //UNUSED //KEY:
+typedef enum { ENC = 1, DEC = -1 } encrypt_dir_t;
+
+static void memxor(unsigned char *dst, unsigned char *src, size_t size)
+{
+ while (size--)
+ *dst++ ^= *src++;
+}
+
+static void yescrypt_sha256_cipher(unsigned char *data, size_t datalen,
+ const yescrypt_binary_t *key, encrypt_dir_t dir)
+{
+ SHA256_CTX ctx;
+ unsigned char f[32 + 4];
+ size_t halflen, which;
+ unsigned char mask, round, target;
+
+ if (!datalen)
+ return;
+ if (datalen > 64)
+ datalen = 64;
+
+ halflen = datalen >> 1;
+
+ which = 0; /* offset to half we are working on (0 or halflen) */
+ mask = 0x0f; /* current half's extra nibble mask if datalen is odd */
+
+ round = 0;
+ target = 5; /* 6 rounds due to Jacques Patarin's CRYPTO 2004 paper */
+
+ if (dir == DEC) {
+ which = halflen; /* even round count, so swap the halves */
+ mask ^= 0xff;
+
+ round = target;
+ target = 0;
+ }
+
+ f[32] = 0;
+ f[33] = sizeof(*key);
+ f[34] = datalen;
+
+ do {
+ SHA256_Init(&ctx);
+ f[35] = round;
+ SHA256_Update(&ctx, &f[32], 4);
+ SHA256_Update(&ctx, key, sizeof(*key));
+ SHA256_Update(&ctx, &data[which], halflen);
+ if (datalen & 1) {
+ f[0] = data[datalen - 1] & mask;
+ SHA256_Update(&ctx, f, 1);
+ }
+ SHA256_Final(f, &ctx);
+ which ^= halflen;
+ memxor(&data[which], f, halflen);
+ if (datalen & 1) {
+ mask ^= 0xff;
+ data[datalen - 1] ^= f[halflen] & mask;
+ }
+ if (round == target)
+ break;
+ round += dir;
+ } while (1);
+
+ /* ctx is presumably zeroized by SHA256_Final() */
+ explicit_bzero(f, sizeof(f));
+}
+#endif //UNUSED //KEY:
+
+uint8_t *yescrypt_r(
+ const yescrypt_shared_t *shared,
+ yescrypt_local_t *local,
+ const uint8_t *passwd, size_t passwdlen,
+ const uint8_t *setting,
+ //KEY const yescrypt_binary_t *key,
+ uint8_t *buf, size_t buflen)
+{
+ unsigned char saltbin[64], hashbin[32];
+ const uint8_t *src, *saltstr, *salt;
+ uint8_t *dst;
+ size_t need, prefixlen, saltstrlen, saltlen;
+ yescrypt_params_t params = { .p = 1 };
+
+ if (setting[0] != '$' ||
+ (setting[1] != '7' && setting[1] != 'y') ||
+ setting[2] != '$')
+ return NULL;
+ src = setting + 3;
+
+ if (setting[1] == '7') {
+ uint32_t N_log2 = atoi64(*src++);
+ if (N_log2 < 1 || N_log2 > 63)
+ return NULL;
+ params.N = (uint64_t)1 << N_log2;
+
+ src = decode64_uint32_fixed(&params.r, 30, src);
+ if (!src)
+ return NULL;
+
+ src = decode64_uint32_fixed(&params.p, 30, src);
+ if (!src)
+ return NULL;
+
+ //KEY:if (key)
+ //KEY: return NULL;
+ } else {
+ uint32_t flavor, N_log2;
+
+ src = decode64_uint32(&flavor, src, 0);
+ if (!src)
+ return NULL;
+
+ if (flavor < YESCRYPT_RW) {
+ params.flags = flavor;
+ } else if (flavor <= YESCRYPT_RW + (YESCRYPT_RW_FLAVOR_MASK >> 2)) {
+ params.flags = YESCRYPT_RW + ((flavor - YESCRYPT_RW) << 2);
+ } else {
+ return NULL;
+ }
+
+ src = decode64_uint32(&N_log2, src, 1);
+ if (!src || N_log2 > 63)
+ return NULL;
+ params.N = (uint64_t)1 << N_log2;
+
+ src = decode64_uint32(&params.r, src, 1);
+ if (!src)
+ return NULL;
+
+ if (*src != '$') {
+ uint32_t have;
+
+ src = decode64_uint32(&have, src, 1);
+ if (!src)
+ return NULL;
+
+ if (have & 1) {
+ src = decode64_uint32(&params.p, src, 2);
+ if (!src)
+ return NULL;
+ }
+
+ if (have & 2) {
+ src = decode64_uint32(&params.t, src, 1);
+ if (!src)
+ return NULL;
+ }
+
+ if (have & 4) {
+ src = decode64_uint32(&params.g, src, 1);
+ if (!src)
+ return NULL;
+ }
+
+ if (have & 8) {
+ uint32_t NROM_log2;
+ src = decode64_uint32(&NROM_log2, src, 1);
+ if (!src || NROM_log2 > 63)
+ return NULL;
+ params.NROM = (uint64_t)1 << NROM_log2;
+ }
+ }
+
+ if (*src++ != '$')
+ return NULL;
+ }
+
+ prefixlen = src - setting;
+
+ saltstr = src;
+ src = (uint8_t *)strrchr((char *)saltstr, '$');
+ if (src)
+ saltstrlen = src - saltstr;
+ else
+ saltstrlen = strlen((char *)saltstr);
+
+ if (setting[1] == '7') {
+ salt = saltstr;
+ saltlen = saltstrlen;
+ } else {
+ const uint8_t *saltend;
+
+ saltlen = sizeof(saltbin);
+ saltend = decode64(saltbin, &saltlen, saltstr, saltstrlen);
+
+ if (!saltend || (size_t)(saltend - saltstr) != saltstrlen)
+ goto fail;
+
+ salt = saltbin;
+
+ //KEY:if (key)
+ //KEY: yescrypt_sha256_cipher(saltbin, saltlen, key, ENC);
+ }
+
+ need = prefixlen + saltstrlen + 1 + HASH_LEN + 1;
+ if (need > buflen || need < saltstrlen)
+ goto fail;
+
+ if (yescrypt_kdf(shared, local, passwd, passwdlen, salt, saltlen,
+ &params, hashbin, sizeof(hashbin)))
+ goto fail;
+
+ //KEY:if (key) {
+ //KEY: explicit_bzero(saltbin, sizeof(saltbin));
+ //KEY: yescrypt_sha256_cipher(hashbin, sizeof(hashbin), key, ENC);
+ //KEY:}
+
+ dst = buf;
+ memcpy(dst, setting, prefixlen + saltstrlen);
+ dst += prefixlen + saltstrlen;
+ *dst++ = '$';
+
+ dst = encode64(dst, buflen - (dst - buf), hashbin, sizeof(hashbin));
+ explicit_bzero(hashbin, sizeof(hashbin));
+ if (!dst || dst >= buf + buflen)
+ return NULL;
+
+ *dst = 0; /* NUL termination */
+
+ return buf;
+
+fail:
+ explicit_bzero(saltbin, sizeof(saltbin));
+ explicit_bzero(hashbin, sizeof(hashbin));
+ return NULL;
+}
+
+
+
+
+
+
+
+
+
+int yescrypt_free_shared(yescrypt_shared_t *shared)
+{
+ return free_region(shared);
+}
+
+int yescrypt_init_local(yescrypt_local_t *local)
+{
+ init_region(local);
+ return 0;
+}
+
+int yescrypt_free_local(yescrypt_local_t *local)
+{
+ return free_region(local);
+}
+
+#if 0 //UNUSED
+uint8_t *yescrypt(const uint8_t *passwd, const uint8_t *setting)
+{
+ /* prefix, '$', hash, NUL */
+ static uint8_t buf[PREFIX_LEN + 1 + HASH_LEN + 1];
+ yescrypt_local_t local;
+ uint8_t *retval;
+
+ if (yescrypt_init_local(&local))
+ return NULL;
+ retval = yescrypt_r(
+ /* const yescrypt_shared_t *shared */ NULL,
+ &local,
+ passwd, strlen((char *)passwd),
+ setting,
+ //KEY:/* const yescrypt_binary_t *key */ NULL,
+ buf, sizeof(buf));
+ if (yescrypt_free_local(&local))
+ return NULL;
+ return retval;
+}
+
+uint8_t *yescrypt_reencrypt(uint8_t *hash,
+ const yescrypt_binary_t *from_key,
+ const yescrypt_binary_t *to_key)
+{
+ uint8_t *retval = NULL, *saltstart, *hashstart;
+ const uint8_t *hashend;
+ unsigned char saltbin[64], hashbin[32];
+ size_t saltstrlen, saltlen = 0, hashlen;
+
+ if (strncmp((char *)hash, "$y$", 3))
+ return NULL;
+
+ saltstart = NULL;
+ hashstart = (uint8_t *)strrchr((char *)hash, '$');
+ if (hashstart) {
+ if (hashstart > (uint8_t *)hash) {
+ saltstart = hashstart - 1;
+ while (*saltstart != '$' && saltstart > hash)
+ saltstart--;
+ if (*saltstart == '$')
+ saltstart++;
+ }
+ hashstart++;
+ } else {
+ hashstart = hash;
+ }
+ saltstrlen = saltstart ? (hashstart - 1 - saltstart) : 0;
+ if (saltstrlen > BYTES2CHARS(64) ||
+ strlen((char *)hashstart) != HASH_LEN)
+ return NULL;
+
+ if (saltstrlen) {
+ const uint8_t *saltend;
+ saltlen = sizeof(saltbin);
+ saltend = decode64(saltbin, &saltlen, saltstart, saltstrlen);
+ if (!saltend || *saltend != '$' || saltlen < 1 || saltlen > 64)
+ goto out;
+
+ if (from_key)
+ yescrypt_sha256_cipher(saltbin, saltlen, from_key, ENC);
+ if (to_key)
+ yescrypt_sha256_cipher(saltbin, saltlen, to_key, DEC);
+ }
+
+ hashlen = sizeof(hashbin);
+ hashend = decode64(hashbin, &hashlen, hashstart, HASH_LEN);
+ if (!hashend || *hashend || hashlen != sizeof(hashbin))
+ goto out;
+
+ if (from_key)
+ yescrypt_sha256_cipher(hashbin, hashlen, from_key, DEC);
+ if (to_key)
+ yescrypt_sha256_cipher(hashbin, hashlen, to_key, ENC);
+
+ if (saltstrlen) {
+ if (!encode64(saltstart, saltstrlen + 1, saltbin, saltlen))
+ goto out; /* can't happen */
+ *(saltstart + saltstrlen) = '$';
+ }
+
+ if (!encode64(hashstart, HASH_LEN + 1, hashbin, hashlen))
+ goto out; /* can't happen */
+
+ retval = hash;
+
+out:
+ explicit_bzero(saltbin, sizeof(saltbin));
+ explicit_bzero(hashbin, sizeof(hashbin));
+
+ return retval;
+}
+
+static uint32_t N2log2(uint64_t N)
+{
+ uint32_t N_log2;
+
+ if (N < 2)
+ return 0;
+
+ N_log2 = 2;
+ while (N >> N_log2 != 0)
+ N_log2++;
+ N_log2--;
+
+ if (N >> N_log2 != 1)
+ return 0;
+
+ return N_log2;
+}
+
+uint8_t *yescrypt_encode_params_r(const yescrypt_params_t *params,
+ const uint8_t *src, size_t srclen,
+ uint8_t *buf, size_t buflen)
+{
+ uint32_t flavor, N_log2, NROM_log2, have;
+ uint8_t *dst;
+
+ if (srclen > SIZE_MAX / 16)
+ return NULL;
+
+ if (params->flags < YESCRYPT_RW) {
+ flavor = params->flags;
+ } else if ((params->flags & YESCRYPT_MODE_MASK) == YESCRYPT_RW &&
+ params->flags <= (YESCRYPT_RW | YESCRYPT_RW_FLAVOR_MASK)) {
+ flavor = YESCRYPT_RW + (params->flags >> 2);
+ } else {
+ return NULL;
+ }
+
+ N_log2 = N2log2(params->N);
+ if (!N_log2)
+ return NULL;
+
+ NROM_log2 = N2log2(params->NROM);
+ if (params->NROM && !NROM_log2)
+ return NULL;
+
+ if ((uint64_t)params->r * (uint64_t)params->p >= (1U << 30))
+ return NULL;
+
+ dst = buf;
+ *dst++ = '$';
+ *dst++ = 'y';
+ *dst++ = '$';
+
+ dst = encode64_uint32(dst, buflen - (dst - buf), flavor, 0);
+ if (!dst)
+ return NULL;
+
+ dst = encode64_uint32(dst, buflen - (dst - buf), N_log2, 1);
+ if (!dst)
+ return NULL;
+
+ dst = encode64_uint32(dst, buflen - (dst - buf), params->r, 1);
+ if (!dst)
+ return NULL;
+
+ have = 0;
+ if (params->p != 1)
+ have |= 1;
+ if (params->t)
+ have |= 2;
+ if (params->g)
+ have |= 4;
+ if (NROM_log2)
+ have |= 8;
+
+ if (have) {
+ dst = encode64_uint32(dst, buflen - (dst - buf), have, 1);
+ if (!dst)
+ return NULL;
+ }
+
+ if (params->p != 1) {
+ dst = encode64_uint32(dst, buflen - (dst - buf), params->p, 2);
+ if (!dst)
+ return NULL;
+ }
+
+ if (params->t) {
+ dst = encode64_uint32(dst, buflen - (dst - buf), params->t, 1);
+ if (!dst)
+ return NULL;
+ }
+
+ if (params->g) {
+ dst = encode64_uint32(dst, buflen - (dst - buf), params->g, 1);
+ if (!dst)
+ return NULL;
+ }
+
+ if (NROM_log2) {
+ dst = encode64_uint32(dst, buflen - (dst - buf), NROM_log2, 1);
+ if (!dst)
+ return NULL;
+ }
+
+ if (dst >= buf + buflen)
+ return NULL;
+
+ *dst++ = '$';
+
+ dst = encode64(dst, buflen - (dst - buf), src, srclen);
+ if (!dst || dst >= buf + buflen)
+ return NULL;
+
+ *dst = 0; /* NUL termination */
+
+ return buf;
+}
+
+uint8_t *yescrypt_encode_params(const yescrypt_params_t *params,
+ const uint8_t *src, size_t srclen)
+{
+ /* prefix, NUL */
+ static uint8_t buf[PREFIX_LEN + 1];
+ return yescrypt_encode_params_r(params, src, srclen, buf, sizeof(buf));
+}
+
+int crypto_scrypt(const uint8_t *passwd, size_t passwdlen,
+ const uint8_t *salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,
+ uint8_t *buf, size_t buflen)
+{
+ yescrypt_local_t local;
+ yescrypt_params_t params = { .flags = 0, .N = N, .r = r, .p = p };
+ int retval;
+
+ if (yescrypt_init_local(&local))
+ return -1;
+ retval = yescrypt_kdf(NULL, &local,
+ passwd, passwdlen, salt, saltlen, &params, buf, buflen);
+ if (yescrypt_free_local(&local))
+ return -1;
+ return retval;
+}
+#endif //UNUSED
libbb/yescrypt/alg-yescrypt-kdf.c+1070 -0
@@ -0,0 +1,1070 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * Copyright 2012-2018 Alexander Peslyak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+
+#if __STDC_VERSION__ >= 199901L
+/* Have restrict */
+#elif defined(__GNUC__)
+#define restrict __restrict
+#else
+#define restrict
+#endif
+
+#ifdef __GNUC__
+#define unlikely(exp) __builtin_expect(exp, 0)
+#else
+#define unlikely(exp) (exp)
+#endif
+
+#if 0 //def __SSE__
+...
+#else /* !defined(__SSE2__) */
+
+typedef union {
+ uint32_t w[16];
+ uint64_t d[8];
+} salsa20_blk_t;
+
+static inline void salsa20_simd_shuffle(const salsa20_blk_t *Bin,
+ salsa20_blk_t *Bout)
+{
+#define COMBINE(out, in1, in2) \
+ Bout->d[out] = Bin->w[in1 * 2] | ((uint64_t)Bin->w[in2 * 2 + 1] << 32);
+ COMBINE(0, 0, 2)
+ COMBINE(1, 5, 7)
+ COMBINE(2, 2, 4)
+ COMBINE(3, 7, 1)
+ COMBINE(4, 4, 6)
+ COMBINE(5, 1, 3)
+ COMBINE(6, 6, 0)
+ COMBINE(7, 3, 5)
+#undef COMBINE
+}
+
+static inline void salsa20_simd_unshuffle(const salsa20_blk_t *Bin,
+ salsa20_blk_t *Bout)
+{
+#define UNCOMBINE(out, in1, in2) \
+ Bout->w[out * 2] = Bin->d[in1]; \
+ Bout->w[out * 2 + 1] = Bin->d[in2] >> 32;
+ UNCOMBINE(0, 0, 6)
+ UNCOMBINE(1, 5, 3)
+ UNCOMBINE(2, 2, 0)
+ UNCOMBINE(3, 7, 5)
+ UNCOMBINE(4, 4, 2)
+ UNCOMBINE(5, 1, 7)
+ UNCOMBINE(6, 6, 4)
+ UNCOMBINE(7, 3, 1)
+#undef UNCOMBINE
+}
+#define DECL_X \
+ salsa20_blk_t X;
+#define DECL_Y \
+ salsa20_blk_t Y;
+
+#define COPY(out, in) \
+ (out).d[0] = (in).d[0]; \
+ (out).d[1] = (in).d[1]; \
+ (out).d[2] = (in).d[2]; \
+ (out).d[3] = (in).d[3]; \
+ (out).d[4] = (in).d[4]; \
+ (out).d[5] = (in).d[5]; \
+ (out).d[6] = (in).d[6]; \
+ (out).d[7] = (in).d[7];
+
+#define READ_X(in) COPY(X, in)
+#define WRITE_X(out) COPY(out, X)
+
+/**
+ * salsa20(B):
+ * Apply the Salsa20 core to the provided block.
+ */
+static inline void salsa20(salsa20_blk_t *restrict B,
+ salsa20_blk_t *restrict Bout, uint32_t doublerounds)
+{
+ salsa20_blk_t X;
+#define x X.w
+
+ salsa20_simd_unshuffle(B, &X);
+
+ do {
+#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+ /* Operate on columns */
+ x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9);
+ x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18);
+
+ x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9);
+ x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18);
+
+ x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9);
+ x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18);
+
+ x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9);
+ x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18);
+
+ /* Operate on rows */
+ x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9);
+ x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18);
+
+ x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9);
+ x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18);
+
+ x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9);
+ x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18);
+
+ x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9);
+ x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18);
+#undef R
+ } while (--doublerounds);
+#undef x
+
+ {
+ uint32_t i;
+ salsa20_simd_shuffle(&X, Bout);
+ for (i = 0; i < 16; i += 4) {
+ B->w[i] = Bout->w[i] += B->w[i];
+ B->w[i + 1] = Bout->w[i + 1] += B->w[i + 1];
+ B->w[i + 2] = Bout->w[i + 2] += B->w[i + 2];
+ B->w[i + 3] = Bout->w[i + 3] += B->w[i + 3];
+ }
+ }
+
+#if 0
+ /* Too expensive */
+ explicit_bzero(&X, sizeof(X));
+#endif
+}
+
+/**
+ * Apply the Salsa20/2 core to the block provided in X.
+ */
+#define SALSA20_2(out) \
+ salsa20(&X, &out, 1);
+
+#define XOR(out, in1, in2) \
+ (out).d[0] = (in1).d[0] ^ (in2).d[0]; \
+ (out).d[1] = (in1).d[1] ^ (in2).d[1]; \
+ (out).d[2] = (in1).d[2] ^ (in2).d[2]; \
+ (out).d[3] = (in1).d[3] ^ (in2).d[3]; \
+ (out).d[4] = (in1).d[4] ^ (in2).d[4]; \
+ (out).d[5] = (in1).d[5] ^ (in2).d[5]; \
+ (out).d[6] = (in1).d[6] ^ (in2).d[6]; \
+ (out).d[7] = (in1).d[7] ^ (in2).d[7];
+
+#define XOR_X(in) XOR(X, X, in)
+#define XOR_X_2(in1, in2) XOR(X, in1, in2)
+#define XOR_X_WRITE_XOR_Y_2(out, in) \
+ XOR(Y, out, in) \
+ COPY(out, Y) \
+ XOR(X, X, Y)
+
+/**
+ * Apply the Salsa20/8 core to the block provided in X ^ in.
+ */
+#define SALSA20_8_XOR_MEM(in, out) \
+ XOR_X(in); \
+ salsa20(&X, &out, 4);
+
+#define INTEGERIFY (uint32_t)X.d[0]
+
+#endif /* !defined(__SSE2__) */
+
+/**
+ * blockmix_salsa8(Bin, Bout, r):
+ * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r
+ * bytes in length; the output Bout must also be the same size.
+ */
+static void blockmix_salsa8(const salsa20_blk_t *restrict Bin,
+ salsa20_blk_t *restrict Bout, size_t r)
+{
+ size_t i;
+ DECL_X
+
+ READ_X(Bin[r * 2 - 1])
+ for (i = 0; i < r; i++) {
+ SALSA20_8_XOR_MEM(Bin[i * 2], Bout[i])
+ SALSA20_8_XOR_MEM(Bin[i * 2 + 1], Bout[r + i])
+ }
+}
+
+static uint32_t blockmix_salsa8_xor(const salsa20_blk_t *restrict Bin1,
+ const salsa20_blk_t *restrict Bin2, salsa20_blk_t *restrict Bout,
+ size_t r)
+{
+ size_t i;
+ DECL_X
+
+#ifdef PREFETCH
+ PREFETCH(&Bin2[r * 2 - 1], _MM_HINT_T0)
+ for (i = 0; i < r - 1; i++) {
+ PREFETCH(&Bin2[i * 2], _MM_HINT_T0)
+ PREFETCH(&Bin2[i * 2 + 1], _MM_HINT_T0)
+ }
+ PREFETCH(&Bin2[i * 2], _MM_HINT_T0)
+#endif
+
+ XOR_X_2(Bin1[r * 2 - 1], Bin2[r * 2 - 1])
+ for (i = 0; i < r; i++) {
+ XOR_X(Bin1[i * 2])
+ SALSA20_8_XOR_MEM(Bin2[i * 2], Bout[i])
+ XOR_X(Bin1[i * 2 + 1])
+ SALSA20_8_XOR_MEM(Bin2[i * 2 + 1], Bout[r + i])
+ }
+
+ return INTEGERIFY;
+}
+
+/* This is tunable */
+#define Swidth 8
+
+/* Not tunable in this implementation, hard-coded in a few places */
+#define PWXsimple 2
+#define PWXgather 4
+
+/* Derived values. Not tunable except via Swidth above. */
+#define PWXbytes (PWXgather * PWXsimple * 8)
+#define Sbytes (3 * (1 << Swidth) * PWXsimple * 8)
+#define Smask (((1 << Swidth) - 1) * PWXsimple * 8)
+#define Smask2 (((uint64_t)Smask << 32) | Smask)
+
+#define DECL_SMASK2REG /* empty */
+#define FORCE_REGALLOC_3 /* empty */
+#define MAYBE_MEMORY_BARRIER /* empty */
+
+#if 0 //def __SSE2__
+...
+#else /* !defined(__SSE2__) */
+
+#define PWXFORM_SIMD(x0, x1) { \
+ uint64_t x = x0 & Smask2; \
+ uint64_t *p0 = (uint64_t *)(S0 + (uint32_t)x); \
+ uint64_t *p1 = (uint64_t *)(S1 + (x >> 32)); \
+ x0 = ((x0 >> 32) * (uint32_t)x0 + p0[0]) ^ p1[0]; \
+ x1 = ((x1 >> 32) * (uint32_t)x1 + p0[1]) ^ p1[1]; \
+}
+
+#define PWXFORM_ROUND \
+ PWXFORM_SIMD(X.d[0], X.d[1]) \
+ PWXFORM_SIMD(X.d[2], X.d[3]) \
+ PWXFORM_SIMD(X.d[4], X.d[5]) \
+ PWXFORM_SIMD(X.d[6], X.d[7])
+#endif
+
+/*
+ * This offset helps address the 256-byte write block via the single-byte
+ * displacements encodable in x86(-64) instructions. It is needed because the
+ * displacements are signed. Without it, we'd get 4-byte displacements for
+ * half of the writes. Setting it to 0x80 instead of 0x7c would avoid needing
+ * a displacement for one of the writes, but then the LEA instruction would
+ * need a 4-byte displacement.
+ */
+#define PWXFORM_WRITE_OFFSET 0x7c
+
+#define PWXFORM_WRITE \
+ WRITE_X(*(salsa20_blk_t *)(Sw - PWXFORM_WRITE_OFFSET)) \
+ Sw += 64;
+
+#define PWXFORM { \
+ uint8_t *Sw = S2 + w + PWXFORM_WRITE_OFFSET; \
+ FORCE_REGALLOC_3 \
+ MAYBE_MEMORY_BARRIER \
+ PWXFORM_ROUND \
+ PWXFORM_ROUND PWXFORM_WRITE \
+ PWXFORM_ROUND PWXFORM_WRITE \
+ PWXFORM_ROUND PWXFORM_WRITE \
+ PWXFORM_ROUND PWXFORM_WRITE \
+ PWXFORM_ROUND \
+ w = (w + 64 * 4) & Smask2; \
+ { \
+ uint8_t *Stmp = S2; \
+ S2 = S1; \
+ S1 = S0; \
+ S0 = Stmp; \
+ } \
+}
+
+typedef struct {
+ uint8_t *S0, *S1, *S2;
+ size_t w;
+} pwxform_ctx_t;
+
+#define Salloc (Sbytes + ((sizeof(pwxform_ctx_t) + 63) & ~63U))
+
+/**
+ * blockmix_pwxform(Bin, Bout, r, S):
+ * Compute Bout = BlockMix_pwxform{salsa20/2, r, S}(Bin). The input Bin must
+ * be 128r bytes in length; the output Bout must also be the same size.
+ */
+static void blockmix(const salsa20_blk_t *restrict Bin,
+ salsa20_blk_t *restrict Bout, size_t r, pwxform_ctx_t *restrict ctx)
+{
+ uint8_t *S0 = ctx->S0, *S1 = ctx->S1, *S2 = ctx->S2;
+ size_t w = ctx->w;
+ size_t i;
+ DECL_X
+
+ /* Convert count of 128-byte blocks to max index of 64-byte block */
+ r = r * 2 - 1;
+
+ READ_X(Bin[r])
+
+ DECL_SMASK2REG
+
+ i = 0;
+ do {
+ XOR_X(Bin[i])
+ PWXFORM
+ if (unlikely(i >= r))
+ break;
+ WRITE_X(Bout[i])
+ i++;
+ } while (1);
+
+ ctx->S0 = S0; ctx->S1 = S1; ctx->S2 = S2;
+ ctx->w = w;
+
+ SALSA20_2(Bout[i])
+}
+
+static uint32_t blockmix_xor(const salsa20_blk_t *Bin1,
+ const salsa20_blk_t *restrict Bin2, salsa20_blk_t *Bout,
+ size_t r, int Bin2_in_ROM, pwxform_ctx_t *restrict ctx)
+{
+ uint8_t *S0 = ctx->S0, *S1 = ctx->S1, *S2 = ctx->S2;
+ size_t w = ctx->w;
+ size_t i;
+ DECL_X
+
+ /* Convert count of 128-byte blocks to max index of 64-byte block */
+ r = r * 2 - 1;
+
+#ifdef PREFETCH
+ if (Bin2_in_ROM) {
+ PREFETCH(&Bin2[r], _MM_HINT_NTA)
+ for (i = 0; i < r; i++) {
+ PREFETCH(&Bin2[i], _MM_HINT_NTA)
+ }
+ } else {
+ PREFETCH(&Bin2[r], _MM_HINT_T0)
+ for (i = 0; i < r; i++) {
+ PREFETCH(&Bin2[i], _MM_HINT_T0)
+ }
+ }
+#else
+ (void)Bin2_in_ROM; /* unused */
+#endif
+
+ XOR_X_2(Bin1[r], Bin2[r])
+
+ DECL_SMASK2REG
+
+ i = 0;
+ r--;
+ do {
+ XOR_X(Bin1[i])
+ XOR_X(Bin2[i])
+ PWXFORM
+ WRITE_X(Bout[i])
+
+ XOR_X(Bin1[i + 1])
+ XOR_X(Bin2[i + 1])
+ PWXFORM
+
+ if (unlikely(i >= r))
+ break;
+
+ WRITE_X(Bout[i + 1])
+
+ i += 2;
+ } while (1);
+ i++;
+
+ ctx->S0 = S0; ctx->S1 = S1; ctx->S2 = S2;
+ ctx->w = w;
+
+ SALSA20_2(Bout[i])
+
+ return INTEGERIFY;
+}
+
+static uint32_t blockmix_xor_save(salsa20_blk_t *restrict Bin1out,
+ salsa20_blk_t *restrict Bin2,
+ size_t r, pwxform_ctx_t *restrict ctx)
+{
+ uint8_t *S0 = ctx->S0, *S1 = ctx->S1, *S2 = ctx->S2;
+ size_t w = ctx->w;
+ size_t i;
+ DECL_X
+ DECL_Y
+
+ /* Convert count of 128-byte blocks to max index of 64-byte block */
+ r = r * 2 - 1;
+
+#ifdef PREFETCH
+ PREFETCH(&Bin2[r], _MM_HINT_T0)
+ for (i = 0; i < r; i++) {
+ PREFETCH(&Bin2[i], _MM_HINT_T0)
+ }
+#endif
+
+ XOR_X_2(Bin1out[r], Bin2[r])
+
+ DECL_SMASK2REG
+
+ i = 0;
+ r--;
+ do {
+ XOR_X_WRITE_XOR_Y_2(Bin2[i], Bin1out[i])
+ PWXFORM
+ WRITE_X(Bin1out[i])
+
+ XOR_X_WRITE_XOR_Y_2(Bin2[i + 1], Bin1out[i + 1])
+ PWXFORM
+
+ if (unlikely(i >= r))
+ break;
+
+ WRITE_X(Bin1out[i + 1])
+
+ i += 2;
+ } while (1);
+ i++;
+
+ ctx->S0 = S0; ctx->S1 = S1; ctx->S2 = S2;
+ ctx->w = w;
+
+ SALSA20_2(Bin1out[i])
+
+ return INTEGERIFY;
+}
+
+/**
+ * integerify(B, r):
+ * Return the result of parsing B_{2r-1} as a little-endian integer.
+ */
+static inline uint32_t integerify(const salsa20_blk_t *B, size_t r)
+{
+/*
+ * Our 64-bit words are in host byte order, which is why we don't just read
+ * w[0] here (would be wrong on big-endian). Also, our 32-bit words are
+ * SIMD-shuffled (so the next 32 bits would be part of d[6]), but currently
+ * this does not matter as we only care about the least significant 32 bits.
+ */
+ return (uint32_t)B[2 * r - 1].d[0];
+}
+
+/**
+ * smix1(B, r, N, flags, V, NROM, VROM, XY, ctx):
+ * Compute first loop of B = SMix_r(B, N). The input B must be 128r bytes in
+ * length; the temporary storage V must be 128rN bytes in length; the temporary
+ * storage XY must be 128r+64 bytes in length. N must be even and at least 4.
+ * The array V must be aligned to a multiple of 64 bytes, and arrays B and XY
+ * to a multiple of at least 16 bytes.
+ */
+static void smix1(uint8_t *B, size_t r, uint32_t N, yescrypt_flags_t flags,
+ salsa20_blk_t *V, uint32_t NROM, const salsa20_blk_t *VROM,
+ salsa20_blk_t *XY, pwxform_ctx_t *ctx)
+{
+ size_t s = 2 * r;
+ salsa20_blk_t *X = V, *Y = &V[s];
+ uint32_t i, j;
+
+ for (i = 0; i < 2 * r; i++) {
+ const salsa20_blk_t *src = (salsa20_blk_t *)&B[i * 64];
+ salsa20_blk_t *tmp = Y;
+ salsa20_blk_t *dst = &X[i];
+ size_t k;
+ for (k = 0; k < 16; k++)
+ tmp->w[k] = le32dec((const uint8_t *) &src->w[k]);
+ salsa20_simd_shuffle(tmp, dst);
+ }
+
+ if (VROM) {
+ uint32_t n;
+ const salsa20_blk_t *V_j;
+
+ V_j = &VROM[(NROM - 1) * s];
+ j = blockmix_xor(X, V_j, Y, r, 1, ctx) & (NROM - 1);
+ V_j = &VROM[j * s];
+ X = Y + s;
+ j = blockmix_xor(Y, V_j, X, r, 1, ctx);
+
+ for (n = 2; n < N; n <<= 1) {
+ uint32_t m = (n < N / 2) ? n : (N - 1 - n);
+ for (i = 1; i < m; i += 2) {
+ j &= n - 1;
+ j += i - 1;
+ V_j = &V[j * s];
+ Y = X + s;
+ j = blockmix_xor(X, V_j, Y, r, 0, ctx) & (NROM - 1);
+ V_j = &VROM[j * s];
+ X = Y + s;
+ j = blockmix_xor(Y, V_j, X, r, 1, ctx);
+ }
+ }
+ n >>= 1;
+
+ j &= n - 1;
+ j += N - 2 - n;
+ V_j = &V[j * s];
+ Y = X + s;
+ j = blockmix_xor(X, V_j, Y, r, 0, ctx) & (NROM - 1);
+ V_j = &VROM[j * s];
+ blockmix_xor(Y, V_j, XY, r, 1, ctx);
+ } else if (flags & YESCRYPT_RW) {
+ uint32_t n;
+ salsa20_blk_t *V_j;
+
+ blockmix(X, Y, r, ctx);
+ X = Y + s;
+ blockmix(Y, X, r, ctx);
+ j = integerify(X, r);
+
+ for (n = 2; n < N; n <<= 1) {
+ uint32_t m = (n < N / 2) ? n : (N - 1 - n);
+ for (i = 1; i < m; i += 2) {
+ Y = X + s;
+ j &= n - 1;
+ j += i - 1;
+ V_j = &V[j * s];
+ j = blockmix_xor(X, V_j, Y, r, 0, ctx);
+ j &= n - 1;
+ j += i;
+ V_j = &V[j * s];
+ X = Y + s;
+ j = blockmix_xor(Y, V_j, X, r, 0, ctx);
+ }
+ }
+ n >>= 1;
+
+ j &= n - 1;
+ j += N - 2 - n;
+ V_j = &V[j * s];
+ Y = X + s;
+ j = blockmix_xor(X, V_j, Y, r, 0, ctx);
+ j &= n - 1;
+ j += N - 1 - n;
+ V_j = &V[j * s];
+ blockmix_xor(Y, V_j, XY, r, 0, ctx);
+ } else {
+ N -= 2;
+ do {
+ blockmix_salsa8(X, Y, r);
+ X = Y + s;
+ blockmix_salsa8(Y, X, r);
+ Y = X + s;
+ } while ((N -= 2));
+
+ blockmix_salsa8(X, Y, r);
+ blockmix_salsa8(Y, XY, r);
+ }
+
+ for (i = 0; i < 2 * r; i++) {
+ const salsa20_blk_t *src = &XY[i];
+ salsa20_blk_t *tmp = &XY[s];
+ salsa20_blk_t *dst = (salsa20_blk_t *)&B[i * 64];
+ size_t k;
+ for (k = 0; k < 16; k++)
+ le32enc((uint8_t *)&tmp->w[k], src->w[k]);
+ salsa20_simd_unshuffle(tmp, dst);
+ }
+}
+
+/**
+ * smix2(B, r, N, Nloop, flags, V, NROM, VROM, XY, ctx):
+ * Compute second loop of B = SMix_r(B, N). The input B must be 128r bytes in
+ * length; the temporary storage V must be 128rN bytes in length; the temporary
+ * storage XY must be 256r bytes in length. N must be a power of 2 and at
+ * least 2. Nloop must be even. The array V must be aligned to a multiple of
+ * 64 bytes, and arrays B and XY to a multiple of at least 16 bytes.
+ */
+static void smix2(uint8_t *B, size_t r, uint32_t N, uint64_t Nloop,
+ yescrypt_flags_t flags, salsa20_blk_t *V, uint32_t NROM,
+ const salsa20_blk_t *VROM, salsa20_blk_t *XY, pwxform_ctx_t *ctx)
+{
+ size_t s = 2 * r;
+ salsa20_blk_t *X = XY, *Y = &XY[s];
+ uint32_t i, j;
+
+ if (Nloop == 0)
+ return;
+
+ for (i = 0; i < 2 * r; i++) {
+ const salsa20_blk_t *src = (salsa20_blk_t *)&B[i * 64];
+ salsa20_blk_t *tmp = Y;
+ salsa20_blk_t *dst = &X[i];
+ size_t k;
+ for (k = 0; k < 16; k++)
+ tmp->w[k] = le32dec((const uint8_t *)&src->w[k]);
+ salsa20_simd_shuffle(tmp, dst);
+ }
+
+ j = integerify(X, r) & (N - 1);
+
+/*
+ * Normally, VROM implies YESCRYPT_RW, but we check for these separately
+ * because our SMix resets YESCRYPT_RW for the smix2() calls operating on the
+ * entire V when p > 1.
+ */
+ if (VROM && (flags & YESCRYPT_RW)) {
+ do {
+ salsa20_blk_t *V_j = &V[j * s];
+ const salsa20_blk_t *VROM_j;
+ j = blockmix_xor_save(X, V_j, r, ctx) & (NROM - 1);
+ VROM_j = &VROM[j * s];
+ j = blockmix_xor(X, VROM_j, X, r, 1, ctx) & (N - 1);
+ } while (Nloop -= 2);
+ } else if (VROM) {
+ do {
+ const salsa20_blk_t *V_j = &V[j * s];
+ j = blockmix_xor(X, V_j, X, r, 0, ctx) & (NROM - 1);
+ V_j = &VROM[j * s];
+ j = blockmix_xor(X, V_j, X, r, 1, ctx) & (N - 1);
+ } while (Nloop -= 2);
+ } else if (flags & YESCRYPT_RW) {
+ do {
+ salsa20_blk_t *V_j = &V[j * s];
+ j = blockmix_xor_save(X, V_j, r, ctx) & (N - 1);
+ V_j = &V[j * s];
+ j = blockmix_xor_save(X, V_j, r, ctx) & (N - 1);
+ } while (Nloop -= 2);
+ } else if (ctx) {
+ do {
+ const salsa20_blk_t *V_j = &V[j * s];
+ j = blockmix_xor(X, V_j, X, r, 0, ctx) & (N - 1);
+ V_j = &V[j * s];
+ j = blockmix_xor(X, V_j, X, r, 0, ctx) & (N - 1);
+ } while (Nloop -= 2);
+ } else {
+ do {
+ const salsa20_blk_t *V_j = &V[j * s];
+ j = blockmix_salsa8_xor(X, V_j, Y, r) & (N - 1);
+ V_j = &V[j * s];
+ j = blockmix_salsa8_xor(Y, V_j, X, r) & (N - 1);
+ } while (Nloop -= 2);
+ }
+
+ for (i = 0; i < 2 * r; i++) {
+ const salsa20_blk_t *src = &X[i];
+ salsa20_blk_t *tmp = Y;
+ salsa20_blk_t *dst = (salsa20_blk_t *)&B[i * 64];
+ size_t k;
+ for (k = 0; k < 16; k++)
+ le32enc((uint8_t *)&tmp->w[k], src->w[k]);
+ salsa20_simd_unshuffle(tmp, dst);
+ }
+}
+
+/**
+ * p2floor(x):
+ * Largest power of 2 not greater than argument.
+ */
+static uint64_t p2floor(uint64_t x)
+{
+ uint64_t y;
+ while ((y = x & (x - 1)))
+ x = y;
+ return x;
+}
+
+/**
+ * smix(B, r, N, p, t, flags, V, NROM, VROM, XY, S, passwd):
+ * Compute B = SMix_r(B, N). The input B must be 128rp bytes in length; the
+ * temporary storage V must be 128rN bytes in length; the temporary storage
+ * XY must be 256r or 256rp bytes in length (the larger size is required with
+ * OpenMP-enabled builds). N must be a power of 2 and at least 4. The array V
+ * must be aligned to a multiple of 64 bytes, and arrays B and XY to a multiple
+ * of at least 16 bytes (aligning them to 64 bytes as well saves cache lines
+ * and helps avoid false sharing in OpenMP-enabled builds when p > 1, but it
+ * might also result in cache bank conflicts).
+ */
+static void smix(uint8_t *B, size_t r, uint32_t N, uint32_t p, uint32_t t,
+ yescrypt_flags_t flags,
+ salsa20_blk_t *V, uint32_t NROM, const salsa20_blk_t *VROM,
+ salsa20_blk_t *XY, uint8_t *S, uint8_t *passwd)
+{
+ size_t s = 2 * r;
+ uint32_t Nchunk;
+ uint64_t Nloop_all, Nloop_rw;
+ uint32_t i;
+
+ Nchunk = N / p;
+ Nloop_all = Nchunk;
+ if (flags & YESCRYPT_RW) {
+ if (t <= 1) {
+ if (t)
+ Nloop_all *= 2; /* 2/3 */
+ Nloop_all = (Nloop_all + 2) / 3; /* 1/3, round up */
+ } else {
+ Nloop_all *= t - 1;
+ }
+ } else if (t) {
+ if (t == 1)
+ Nloop_all += (Nloop_all + 1) / 2; /* 1.5, round up */
+ Nloop_all *= t;
+ }
+
+ Nloop_rw = 0;
+ if (flags & YESCRYPT_INIT_SHARED)
+ Nloop_rw = Nloop_all;
+ else if (flags & YESCRYPT_RW)
+ Nloop_rw = Nloop_all / p;
+
+ Nchunk &= ~(uint32_t)1; /* round down to even */
+ Nloop_all++; Nloop_all &= ~(uint64_t)1; /* round up to even */
+ Nloop_rw++; Nloop_rw &= ~(uint64_t)1; /* round up to even */
+
+ for (i = 0; i < p; i++) {
+ uint32_t Vchunk = i * Nchunk;
+ uint32_t Np = (i < p - 1) ? Nchunk : (N - Vchunk);
+ uint8_t *Bp = &B[128 * r * i];
+ salsa20_blk_t *Vp = &V[Vchunk * s];
+ salsa20_blk_t *XYp = XY;
+ pwxform_ctx_t *ctx_i = NULL;
+ if (flags & YESCRYPT_RW) {
+ uint8_t *Si = S + i * Salloc;
+ smix1(Bp, 1, Sbytes / 128, 0 /* no flags */,
+ (salsa20_blk_t *)Si, 0, NULL, XYp, NULL);
+ ctx_i = (pwxform_ctx_t *)(Si + Sbytes);
+ ctx_i->S2 = Si;
+ ctx_i->S1 = Si + Sbytes / 3;
+ ctx_i->S0 = Si + Sbytes / 3 * 2;
+ ctx_i->w = 0;
+ if (i == 0)
+ HMAC_SHA256_Buf(Bp + (128 * r - 64), 64,
+ passwd, 32, passwd);
+ }
+ smix1(Bp, r, Np, flags, Vp, NROM, VROM, XYp, ctx_i);
+ smix2(Bp, r, p2floor(Np), Nloop_rw, flags, Vp,
+ NROM, VROM, XYp, ctx_i);
+ }
+
+ if (Nloop_all > Nloop_rw) {
+ for (i = 0; i < p; i++) {
+ uint8_t *Bp = &B[128 * r * i];
+ salsa20_blk_t *XYp = XY;
+ pwxform_ctx_t *ctx_i = NULL;
+ if (flags & YESCRYPT_RW) {
+ uint8_t *Si = S + i * Salloc;
+ ctx_i = (pwxform_ctx_t *)(Si + Sbytes);
+ }
+ smix2(Bp, r, N, Nloop_all - Nloop_rw,
+ flags & (yescrypt_flags_t)~YESCRYPT_RW,
+ V, NROM, VROM, XYp, ctx_i);
+ }
+ }
+}
+
+/**
+ * yescrypt_kdf_body(shared, local, passwd, passwdlen, salt, saltlen,
+ * flags, N, r, p, t, NROM, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen), or a revision of scrypt as requested by flags and shared, and
+ * write the result into buf.
+ *
+ * shared and flags may request special modes as described in yescrypt.h.
+ *
+ * local is the thread-local data structure, allowing to preserve and reuse a
+ * memory allocation across calls, thereby reducing its overhead.
+ *
+ * t controls computation time while not affecting peak memory usage.
+ *
+ * Return 0 on success; or -1 on error.
+ *
+ * This optimized implementation currently limits N to the range from 4 to
+ * 2^31, but other implementations might not.
+ */
+static int yescrypt_kdf_body(const yescrypt_shared_t *shared,
+ yescrypt_local_t *local,
+ const uint8_t *passwd, size_t passwdlen,
+ const uint8_t *salt, size_t saltlen,
+ yescrypt_flags_t flags, uint64_t N, uint32_t r, uint32_t p, uint32_t t,
+ uint64_t NROM,
+ uint8_t *buf, size_t buflen)
+{
+ yescrypt_region_t tmp;
+ const salsa20_blk_t *VROM;
+ size_t B_size, V_size, XY_size, need;
+ uint8_t *B, *S;
+ salsa20_blk_t *V, *XY;
+ uint8_t sha256[32];
+ uint8_t dk[sizeof(sha256)], *dkp = buf;
+
+ /* Sanity-check parameters */
+ switch (flags & YESCRYPT_MODE_MASK) {
+ case 0: /* classic scrypt - can't have anything non-standard */
+ if (flags || t || NROM)
+ goto out_EINVAL;
+ break;
+ case YESCRYPT_WORM:
+ if (flags != YESCRYPT_WORM || NROM)
+ goto out_EINVAL;
+ break;
+ case YESCRYPT_RW:
+ if (flags != (flags & YESCRYPT_KNOWN_FLAGS))
+ goto out_EINVAL;
+#if PWXsimple == 2 && PWXgather == 4 && Sbytes == 12288
+ if ((flags & YESCRYPT_RW_FLAVOR_MASK) ==
+ (YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 |
+ YESCRYPT_SIMPLE_2 | YESCRYPT_SBOX_12K))
+ break;
+#else
+#error "Unsupported pwxform settings"
+#endif
+ /* FALLTHRU */
+ default:
+ goto out_EINVAL;
+ }
+#if SIZE_MAX > UINT32_MAX
+ if (buflen > (((uint64_t)1 << 32) - 1) * 32)
+ goto out_EINVAL;
+#endif
+ if ((uint64_t)r * (uint64_t)p >= 1 << 30)
+ goto out_EINVAL;
+ if (N > UINT32_MAX)
+ goto out_EINVAL;
+ if ((N & (N - 1)) != 0 || N <= 3 || r < 1 || p < 1)
+ goto out_EINVAL;
+ if (r > SIZE_MAX / 256 / p ||
+ N > SIZE_MAX / 128 / r)
+ goto out_EINVAL;
+ if (flags & YESCRYPT_RW) {
+ /* p cannot be greater than SIZE_MAX/Salloc on 64-bit systems,
+ but it can on 32-bit systems. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wtype-limits"
+ if (N / p <= 3 || p > SIZE_MAX / Salloc)
+ goto out_EINVAL;
+#pragma GCC diagnostic pop
+ }
+
+ VROM = NULL;
+ if (shared) {
+ uint64_t expected_size = (size_t)128 * r * NROM;
+ if ((NROM & (NROM - 1)) != 0 ||
+ NROM <= 1 || NROM > UINT32_MAX ||
+ shared->aligned_size < expected_size)
+ goto out_EINVAL;
+ if (!(flags & YESCRYPT_INIT_SHARED)) {
+ uint64_t *tag = (uint64_t *)
+ ((uint8_t *)shared->aligned + expected_size - 48);
+ if (tag[0] != YESCRYPT_ROM_TAG1 || tag[1] != YESCRYPT_ROM_TAG2)
+ goto out_EINVAL;
+ }
+ VROM = shared->aligned;
+ } else {
+ if (NROM)
+ goto out_EINVAL;
+ }
+
+ /* Allocate memory */
+ V = NULL;
+ V_size = (size_t)128 * r * N;
+ need = V_size;
+ if (flags & YESCRYPT_INIT_SHARED) {
+ if (local->aligned_size < need) {
+ if (local->base || local->aligned ||
+ local->base_size || local->aligned_size)
+ goto out_EINVAL;
+ if (!alloc_region(local, need))
+ return -1;
+ }
+ if (flags & YESCRYPT_ALLOC_ONLY)
+ return -2; /* expected "failure" */
+ V = (salsa20_blk_t *)local->aligned;
+ need = 0;
+ }
+ B_size = (size_t)128 * r * p;
+ need += B_size;
+ if (need < B_size)
+ goto out_EINVAL;
+ XY_size = (size_t)256 * r;
+ need += XY_size;
+ if (need < XY_size)
+ goto out_EINVAL;
+ if (flags & YESCRYPT_RW) {
+ size_t S_size = (size_t)Salloc * p;
+ need += S_size;
+ if (need < S_size)
+ goto out_EINVAL;
+ }
+ if (flags & YESCRYPT_INIT_SHARED) {
+ if (!alloc_region(&tmp, need))
+ return -1;
+ B = (uint8_t *)tmp.aligned;
+ XY = (salsa20_blk_t *)((uint8_t *)B + B_size);
+ } else {
+ init_region(&tmp);
+ if (local->aligned_size < need) {
+ if (free_region(local))
+ return -1;
+ if (!alloc_region(local, need))
+ return -1;
+ }
+ if (flags & YESCRYPT_ALLOC_ONLY)
+ return -3; /* expected "failure" */
+ B = (uint8_t *)local->aligned;
+ V = (salsa20_blk_t *)((uint8_t *)B + B_size);
+ XY = (salsa20_blk_t *)((uint8_t *)V + V_size);
+ }
+ S = NULL;
+ if (flags & YESCRYPT_RW)
+ S = (uint8_t *)XY + XY_size;
+
+ if (flags) {
+ HMAC_SHA256_Buf("yescrypt-prehash",
+ (flags & YESCRYPT_PREHASH) ? 16 : 8,
+ passwd, passwdlen, sha256);
+ passwd = sha256;
+ passwdlen = sizeof(sha256);
+ }
+
+ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, B_size);
+
+ if (flags)
+ memcpy(sha256, B, sizeof(sha256));
+
+ if (p == 1 || (flags & YESCRYPT_RW)) {
+ smix(B, r, N, p, t, flags, V, NROM, VROM, XY, S, sha256);
+ } else {
+ uint32_t i;
+ for (i = 0; i < p; i++) {
+ smix(&B[(size_t)128 * r * i], r, N, 1, t, flags, V,
+ NROM, VROM, XY, NULL, NULL);
+ }
+ }
+
+ dkp = buf;
+ if (flags && buflen < sizeof(dk)) {
+ PBKDF2_SHA256(passwd, passwdlen, B, B_size, 1, dk, sizeof(dk));
+ dkp = dk;
+ }
+
+ PBKDF2_SHA256(passwd, passwdlen, B, B_size, 1, buf, buflen);
+
+ /*
+ * Except when computing classic scrypt, allow all computation so far
+ * to be performed on the client. The final steps below match those of
+ * SCRAM (RFC 5802), so that an extension of SCRAM (with the steps so
+ * far in place of SCRAM's use of PBKDF2 and with SHA-256 in place of
+ * SCRAM's use of SHA-1) would be usable with yescrypt hashes.
+ */
+ if (flags && !(flags & YESCRYPT_PREHASH)) {
+ /* Compute ClientKey */
+ HMAC_SHA256_Buf(dkp, sizeof(dk), "Client Key", 10, sha256);
+ /* Compute StoredKey */
+ {
+ size_t clen = buflen;
+ if (clen > sizeof(dk))
+ clen = sizeof(dk);
+ SHA256_Buf(sha256, sizeof(sha256), dk);
+ memcpy(buf, dk, clen);
+ }
+ }
+
+ if (flags) {
+ explicit_bzero(sha256, sizeof(sha256));
+ explicit_bzero(dk, sizeof(dk));
+ }
+
+ if (free_region(&tmp)) {
+ explicit_bzero(buf, buflen); /* must preserve errno */
+ return -1;
+ }
+
+ /* Success! */
+ return 0;
+
+out_EINVAL:
+ errno = EINVAL;
+ return -1;
+}
+
+/**
+ * yescrypt_kdf(shared, local, passwd, passwdlen, salt, saltlen, params,
+ * buf, buflen):
+ * Compute scrypt or its revision as requested by the parameters. The inputs
+ * to this function are the same as those for yescrypt_kdf_body() above, with
+ * the addition of g, which controls hash upgrades (0 for no upgrades so far).
+ */
+int yescrypt_kdf(const yescrypt_shared_t *shared,
+ yescrypt_local_t *local,
+ const uint8_t *passwd, size_t passwdlen,
+ const uint8_t *salt, size_t saltlen,
+ const yescrypt_params_t *params,
+ uint8_t *buf, size_t buflen)
+{
+ yescrypt_flags_t flags = params->flags;
+ uint64_t N = params->N;
+ uint32_t r = params->r;
+ uint32_t p = params->p;
+ uint32_t t = params->t;
+ uint32_t g = params->g;
+ uint64_t NROM = params->NROM;
+ uint8_t dk[32];
+ int retval;
+
+ /* Support for hash upgrades has been temporarily removed */
+ if (g) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((flags & (YESCRYPT_RW | YESCRYPT_INIT_SHARED)) == YESCRYPT_RW
+ && p >= 1
+ && N / p >= 0x100
+ && N / p * r >= 0x20000
+ ) {
+ if (yescrypt_kdf_body(shared, local,
+ passwd, passwdlen, salt, saltlen,
+ flags | YESCRYPT_ALLOC_ONLY, N, r, p, t, NROM,
+ buf, buflen) != -3
+ ) {
+ errno = EINVAL;
+ return -1;
+ }
+ retval = yescrypt_kdf_body(shared, local,
+ passwd, passwdlen, salt, saltlen,
+ flags | YESCRYPT_PREHASH, N >> 6, r, p, 0, NROM,
+ dk, sizeof(dk));
+ if (retval)
+ return retval;
+ passwd = dk;
+ passwdlen = sizeof(dk);
+ }
+
+ retval = yescrypt_kdf_body(shared, local,
+ passwd, passwdlen, salt, saltlen,
+ flags, N, r, p, t, NROM, buf, buflen);
+#ifndef SKIP_MEMZERO
+ if (passwd == dk)
+ explicit_bzero(dk, sizeof(dk));
+#endif
+ return retval;
+}
libbb/yescrypt/alg-yescrypt-platform.c+65 -0
@@ -0,0 +1,65 @@
+/*-
+ * Copyright 2013-2018,2022 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.
+ */
+
+static void *alloc_region(yescrypt_region_t *region, size_t size)
+{
+ size_t base_size = size;
+ uint8_t *base, *aligned;
+
+#if 0 //def MAP_ANON - use mmap, possibly
+//(if defined(MAP_HUGETLB) && defined(MAP_HUGE_2MB)) using 2MB pages
+#else /* mmap not available */
+ base = aligned = NULL;
+ if (size + 63 < size) {
+ errno = ENOMEM;
+ } else {
+ base = malloc(size + 63);
+ if (base) {
+ aligned = base + 63;
+ aligned -= (uintptr_t)aligned & 63;
+ }
+ }
+#endif
+ region->base = base;
+ region->aligned = aligned;
+ region->base_size = base ? base_size : 0;
+ region->aligned_size = base ? size : 0;
+ return aligned;
+}
+
+static inline void init_region(yescrypt_region_t *region)
+{
+ region->base = region->aligned = NULL;
+ region->base_size = region->aligned_size = 0;
+}
+
+static int free_region(yescrypt_region_t *region)
+{
+ if (region->base) {
+#if 0 //def MAP_ANON
+ if (munmap(region->base, region->base_size))
+ return -1;
+#else
+ free(region->base);
+#endif
+ }
+ init_region(region);
+ return 0;
+}
libbb/yescrypt/alg-yescrypt.h+355 -0
@@ -0,0 +1,355 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * Copyright 2013-2018 Alexander Peslyak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+
+#if 0 //UNUSED
+/**
+ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen) and write the result into buf. The parameters r, p, and buflen
+ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N
+ * must be a power of 2 greater than 1.
+ *
+ * Return 0 on success; or -1 on error.
+ *
+ * MT-safe as long as buf is local to the thread.
+ */
+extern int crypto_scrypt(const uint8_t *passwd, size_t passwdlen,
+ const uint8_t *salt, size_t saltlen,
+ uint64_t N, uint32_t r, uint32_t p, uint8_t *buf, size_t buflen);
+#endif
+
+/**
+ * Internal type used by the memory allocator. Please do not use it directly.
+ * Use yescrypt_shared_t and yescrypt_local_t as appropriate instead, since
+ * they might differ from each other in a future version.
+ */
+typedef struct {
+ void *base, *aligned;
+ size_t base_size, aligned_size;
+} yescrypt_region_t;
+
+/**
+ * Types for shared (ROM) and thread-local (RAM) data structures.
+ */
+typedef yescrypt_region_t yescrypt_shared_t;
+typedef yescrypt_region_t yescrypt_local_t;
+
+/**
+ * Two 64-bit tags placed 48 bytes to the end of a ROM in host byte endianness
+ * (and followed by 32 bytes of the ROM digest).
+ */
+#define YESCRYPT_ROM_TAG1 0x7470797263736579 /* "yescrypt" */
+#define YESCRYPT_ROM_TAG2 0x687361684d4f522d /* "-ROMhash" */
+
+/**
+ * Type and possible values for the flags argument of yescrypt_kdf(),
+ * yescrypt_encode_params_r(), yescrypt_encode_params(). Most of these may be
+ * OR'ed together, except that YESCRYPT_WORM stands on its own.
+ * Please refer to the description of yescrypt_kdf() below for the meaning of
+ * these flags.
+ */
+typedef uint32_t yescrypt_flags_t;
+/* Public */
+#define YESCRYPT_WORM 1
+#define YESCRYPT_RW 0x002
+#define YESCRYPT_ROUNDS_3 0x000
+#define YESCRYPT_ROUNDS_6 0x004
+#define YESCRYPT_GATHER_1 0x000
+#define YESCRYPT_GATHER_2 0x008
+#define YESCRYPT_GATHER_4 0x010
+#define YESCRYPT_GATHER_8 0x018
+#define YESCRYPT_SIMPLE_1 0x000
+#define YESCRYPT_SIMPLE_2 0x020
+#define YESCRYPT_SIMPLE_4 0x040
+#define YESCRYPT_SIMPLE_8 0x060
+#define YESCRYPT_SBOX_6K 0x000
+#define YESCRYPT_SBOX_12K 0x080
+#define YESCRYPT_SBOX_24K 0x100
+#define YESCRYPT_SBOX_48K 0x180
+#define YESCRYPT_SBOX_96K 0x200
+#define YESCRYPT_SBOX_192K 0x280
+#define YESCRYPT_SBOX_384K 0x300
+#define YESCRYPT_SBOX_768K 0x380
+/* Only valid for yescrypt_init_shared() */
+#define YESCRYPT_SHARED_PREALLOCATED 0x10000
+#ifdef YESCRYPT_INTERNAL
+/* Private */
+#define YESCRYPT_MODE_MASK 0x003
+#define YESCRYPT_RW_FLAVOR_MASK 0x3fc
+#define YESCRYPT_INIT_SHARED 0x01000000
+#define YESCRYPT_ALLOC_ONLY 0x08000000
+#define YESCRYPT_PREHASH 0x10000000
+#endif
+
+#define YESCRYPT_RW_DEFAULTS \
+ (YESCRYPT_RW | \
+ YESCRYPT_ROUNDS_6 | YESCRYPT_GATHER_4 | YESCRYPT_SIMPLE_2 | \
+ YESCRYPT_SBOX_12K)
+
+#define YESCRYPT_DEFAULTS YESCRYPT_RW_DEFAULTS
+
+#ifdef YESCRYPT_INTERNAL
+#define YESCRYPT_KNOWN_FLAGS \
+ (YESCRYPT_MODE_MASK | YESCRYPT_RW_FLAVOR_MASK | \
+ YESCRYPT_SHARED_PREALLOCATED | \
+ YESCRYPT_INIT_SHARED | YESCRYPT_ALLOC_ONLY | YESCRYPT_PREHASH)
+#endif
+
+/**
+ * yescrypt parameters combined into one struct. N, r, p are the same as in
+ * classic scrypt, except that the meaning of p changes when YESCRYPT_RW is
+ * set. flags, t, g, NROM are special to yescrypt.
+ */
+typedef struct {
+ yescrypt_flags_t flags;
+ uint64_t N;
+ uint32_t r, p, t, g;
+ uint64_t NROM;
+} yescrypt_params_t;
+
+/**
+ * A 256-bit yescrypt hash, or a hash encryption key (which may itself have
+ * been derived as a yescrypt hash of a human-specified key string).
+ */
+typedef union {
+ unsigned char uc[32];
+ uint64_t u64[4];
+} yescrypt_binary_t;
+
+/* How many chars base-64 encoded bytes require? */
+#define BYTES2CHARS(bytes) ((((bytes) * 8) + 5) / 6)
+/* The /etc/passwd-style hash is "<prefix>$<hash><NUL>" */
+/*
+ * "$y$", up to 8 params of up to 6 chars each, '$', salt
+ * Alternatively, but that's smaller:
+ * "$7$", 3 params encoded as 1+5+5 chars, salt
+ */
+#define PREFIX_LEN (3 + 8 * 6 + 1 + BYTES2CHARS(32))
+
+#define HASH_SIZE sizeof(yescrypt_binary_t) /* bytes */
+#define HASH_LEN BYTES2CHARS(HASH_SIZE)
+
+
+/**
+ * yescrypt_init_shared(shared, seed, seedlen, params):
+ * Optionally allocate memory for and initialize the shared (ROM) data
+ * structure. The parameters flags, NROM, r, p, and t specify how the ROM is
+ * to be initialized, and seed and seedlen specify the initial seed affecting
+ * the data with which the ROM is filled.
+ *
+ * Return 0 on success; or -1 on error.
+ *
+ * If bit YESCRYPT_SHARED_PREALLOCATED in flags is set, then memory for the
+ * ROM is assumed to have been preallocated by the caller, with shared->aligned
+ * being the start address of the ROM and shared->aligned_size being its size
+ * (which must be sufficient for NROM, r, p). This may be used e.g. when the
+ * ROM is to be placed in a SysV shared memory segment allocated by the caller.
+ *
+ * MT-safe as long as shared is local to the thread.
+ */
+extern int yescrypt_init_shared(yescrypt_shared_t *shared,
+ const uint8_t *seed, size_t seedlen, const yescrypt_params_t *params);
+
+/**
+ * yescrypt_digest_shared(shared):
+ * Extract the previously stored message digest of the provided yescrypt ROM.
+ *
+ * Return pointer to the message digest on success; or NULL on error.
+ *
+ * MT-unsafe.
+ */
+extern yescrypt_binary_t *yescrypt_digest_shared(yescrypt_shared_t *shared);
+
+/**
+ * yescrypt_free_shared(shared):
+ * Free memory that had been allocated with yescrypt_init_shared().
+ *
+ * Return 0 on success; or -1 on error.
+ *
+ * MT-safe as long as shared is local to the thread.
+ */
+extern int yescrypt_free_shared(yescrypt_shared_t *shared);
+
+/**
+ * yescrypt_init_local(local):
+ * Initialize the thread-local (RAM) data structure. Actual memory allocation
+ * is currently fully postponed until a call to yescrypt_kdf() or yescrypt_r().
+ *
+ * Return 0 on success; or -1 on error.
+ *
+ * MT-safe as long as local is local to the thread.
+ */
+extern int yescrypt_init_local(yescrypt_local_t *local);
+
+/**
+ * yescrypt_free_local(local):
+ * Free memory that may have been allocated for an initialized thread-local
+ * (RAM) data structure.
+ *
+ * Return 0 on success; or -1 on error.
+ *
+ * MT-safe as long as local is local to the thread.
+ */
+extern int yescrypt_free_local(yescrypt_local_t *local);
+
+/**
+ * yescrypt_kdf(shared, local, passwd, passwdlen, salt, saltlen, params,
+ * buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen), or a revision of scrypt as requested by flags and shared, and
+ * write the result into buf. The parameters N, r, p, and buflen must satisfy
+ * the same conditions as with crypto_scrypt(). t controls computation time
+ * while not affecting peak memory usage (t = 0 is optimal unless higher N*r
+ * is not affordable while higher t is). g controls hash upgrades (g = 0 for
+ * no upgrades so far). shared and flags may request special modes. local is
+ * the thread-local data structure, allowing to preserve and reuse a memory
+ * allocation across calls, thereby reducing processing overhead.
+ *
+ * Return 0 on success; or -1 on error.
+ *
+ * Classic scrypt is available by setting shared = NULL, flags = 0, and t = 0.
+ *
+ * Setting YESCRYPT_WORM enables only minimal deviations from classic scrypt:
+ * support for the t parameter, and pre- and post-hashing.
+ *
+ * Setting YESCRYPT_RW fully enables yescrypt. As a side effect of differences
+ * between the algorithms, it also prevents p > 1 from growing the threads'
+ * combined processing time and memory allocation (like it did with classic
+ * scrypt and YESCRYPT_WORM), treating p as a divider rather than a multiplier.
+ *
+ * Passing a shared structure, with ROM contents previously computed by
+ * yescrypt_init_shared(), enables the use of ROM and requires YESCRYPT_RW.
+ *
+ * In order to allow for initialization of the ROM to be split into a separate
+ * program (or separate invocation of the same program), the shared->aligned
+ * and shared->aligned_size fields may optionally be set by the caller directly
+ * (e.g., to a mapped SysV shm segment), without using yescrypt_init_shared().
+ *
+ * local must be initialized with yescrypt_init_local().
+ *
+ * MT-safe as long as local and buf are local to the thread.
+ */
+extern int yescrypt_kdf(const yescrypt_shared_t *shared,
+ yescrypt_local_t *local,
+ const uint8_t *passwd, size_t passwdlen,
+ const uint8_t *salt, size_t saltlen,
+ const yescrypt_params_t *params,
+ uint8_t *buf, size_t buflen);
+
+/**
+ * yescrypt_r(shared, local, passwd, passwdlen, setting, key, buf, buflen):
+ * Compute and encode an scrypt or enhanced scrypt hash of passwd given the
+ * parameters and salt value encoded in setting. If shared is not NULL, a ROM
+ * is used and YESCRYPT_RW is required. Otherwise, whether to compute classic
+ * scrypt, YESCRYPT_WORM (a slight deviation from classic scrypt), or
+ * YESCRYPT_RW (time-memory tradeoff discouraging modification) is determined
+ * by the setting string. shared (if not NULL) and local must be initialized
+ * as described above for yescrypt_kdf(). buf must be large enough (as
+ * indicated by buflen) to hold the encoded hash string.
+ *
+ * Return the encoded hash string on success; or NULL on error.
+ *
+ * MT-safe as long as local and buf are local to the thread.
+ */
+extern uint8_t *yescrypt_r(const yescrypt_shared_t *shared,
+ yescrypt_local_t *local,
+ const uint8_t *passwd, size_t passwdlen,
+ const uint8_t *setting,
+ //KEY: const yescrypt_binary_t *key,
+ uint8_t *buf, size_t buflen);
+
+#if 0 //UNUSED
+/**
+ * yescrypt(passwd, setting):
+ * Compute and encode an scrypt or enhanced scrypt hash of passwd given the
+ * parameters and salt value encoded in setting. Whether to compute classic
+ * scrypt, YESCRYPT_WORM (a slight deviation from classic scrypt), or
+ * YESCRYPT_RW (time-memory tradeoff discouraging modification) is determined
+ * by the setting string.
+ *
+ * Return the encoded hash string on success; or NULL on error.
+ *
+ * This is a crypt(3)-like interface, which is simpler to use than
+ * yescrypt_r(), but it is not MT-safe, it does not allow for the use of a ROM,
+ * and it is slower than yescrypt_r() for repeated calls because it allocates
+ * and frees memory on each call.
+ *
+ * MT-unsafe.
+ */
+extern uint8_t *yescrypt(const uint8_t *passwd, const uint8_t *setting);
+
+/**
+ * yescrypt_reencrypt(hash, from_key, to_key):
+ * Re-encrypt a yescrypt hash from one key to another. Either key may be NULL
+ * to indicate unencrypted hash. The encoded hash string is modified in-place.
+ *
+ * Return the hash pointer on success; or NULL on error (in which case the hash
+ * string is left unmodified).
+ *
+ * MT-safe as long as hash is local to the thread.
+ */
+extern uint8_t *yescrypt_reencrypt(uint8_t *hash,
+ const yescrypt_binary_t *from_key,
+ const yescrypt_binary_t *to_key);
+
+/**
+ * yescrypt_encode_params_r(params, src, srclen, buf, buflen):
+ * Generate a setting string for use with yescrypt_r() and yescrypt() by
+ * encoding into it the parameters flags, N, r, p, t, g, and a salt given by
+ * src (of srclen bytes). buf must be large enough (as indicated by buflen)
+ * to hold the setting string.
+ *
+ * Return the setting string on success; or NULL on error.
+ *
+ * MT-safe as long as buf is local to the thread.
+ */
+extern uint8_t *yescrypt_encode_params_r(const yescrypt_params_t *params,
+ const uint8_t *src, size_t srclen,
+ uint8_t *buf, size_t buflen);
+
+
+/**
+ * yescrypt_encode_params(params, src, srclen):
+ * Generate a setting string for use with yescrypt_r() and yescrypt(). This
+ * function is the same as yescrypt_encode_params_r() except that it uses a
+ * static buffer and thus is not MT-safe.
+ *
+ * Return the setting string on success; or NULL on error.
+ *
+ * MT-unsafe.
+ */
+extern uint8_t *yescrypt_encode_params(const yescrypt_params_t *params,
+ const uint8_t *src, size_t srclen);
+#endif
+
+extern const uint8_t *decode64(uint8_t *dst, size_t *dstlen,
+ const uint8_t *src, size_t srclen);
+extern uint8_t *encode64(uint8_t *dst, size_t dstlen,
+ const uint8_t *src, size_t srclen);
libbb/yescrypt/y.c+139 -0
@@ -0,0 +1,139 @@
+//kbuild:lib-$(CONFIG_USE_BB_CRYPT_YES) += y.o
+
+#include <libbb.h>
+
+#include <assert.h>
+
+static inline void
+cpu_to_le32 (unsigned char *buf, uint32_t n)
+{
+ buf[0] = (unsigned char)((n & 0x000000FFu) >> 0);
+ buf[1] = (unsigned char)((n & 0x0000FF00u) >> 8);
+ buf[2] = (unsigned char)((n & 0x00FF0000u) >> 16);
+ buf[3] = (unsigned char)((n & 0xFF000000u) >> 24);
+}
+static inline uint32_t
+le32_to_cpu (const unsigned char *buf)
+{
+ return ((((uint32_t)buf[0]) << 0) |
+ (((uint32_t)buf[1]) << 8) |
+ (((uint32_t)buf[2]) << 16) |
+ (((uint32_t)buf[3]) << 24) );
+}
+
+/* Alternative names used in code derived from Colin Percival's
+ cryptography libraries. */
+#define le32enc cpu_to_le32
+#define le32dec le32_to_cpu
+#define le64enc cpu_to_le64
+#define le64dec le64_to_cpu
+
+#define be32enc cpu_to_be32
+#define be32dec be32_to_cpu
+#define be64enc cpu_to_be64
+#define be64dec be64_to_cpu
+
+#define be32enc_vect cpu_to_be32_vect
+#define be32dec_vect be32_to_cpu_vect
+#define be64enc_vect cpu_to_be64_vect
+#define be64dec_vect be64_to_cpu_vect
+
+
+//USED ONY BY SHA256 for be32_to_cpu_vect():
+static inline void
+cpu_to_be32(unsigned char *buf, uint32_t n)
+{
+ buf[0] = (unsigned char)((n & 0xFF000000u) >> 24);
+ buf[1] = (unsigned char)((n & 0x00FF0000u) >> 16);
+ buf[2] = (unsigned char)((n & 0x0000FF00u) >> 8);
+ buf[3] = (unsigned char)((n & 0x000000FFu) >> 0);
+}
+static inline void
+cpu_to_be64 (unsigned char *buf, uint64_t n)
+{
+ buf[0] = (unsigned char)((n & 0xFF00000000000000ull) >> 56);
+ buf[1] = (unsigned char)((n & 0x00FF000000000000ull) >> 48);
+ buf[2] = (unsigned char)((n & 0x0000FF0000000000ull) >> 40);
+ buf[3] = (unsigned char)((n & 0x000000FF00000000ull) >> 32);
+ buf[4] = (unsigned char)((n & 0x00000000FF000000ull) >> 24);
+ buf[5] = (unsigned char)((n & 0x0000000000FF0000ull) >> 16);
+ buf[6] = (unsigned char)((n & 0x000000000000FF00ull) >> 8);
+ buf[7] = (unsigned char)((n & 0x00000000000000FFull) >> 0);
+}
+static inline uint32_t
+be32_to_cpu (const unsigned char *buf)
+{
+ return ((((uint32_t)buf[0]) << 24) |
+ (((uint32_t)buf[1]) << 16) |
+ (((uint32_t)buf[2]) << 8) |
+ (((uint32_t)buf[3]) << 0) );
+}
+static inline uint64_t
+be64_to_cpu (const unsigned char *buf)
+{
+ return ((((uint64_t)buf[0]) << 56) |
+ (((uint64_t)buf[1]) << 48) |
+ (((uint64_t)buf[2]) << 40) |
+ (((uint64_t)buf[3]) << 32) |
+ (((uint64_t)buf[4]) << 24) |
+ (((uint64_t)buf[5]) << 16) |
+ (((uint64_t)buf[6]) << 8) |
+ (((uint64_t)buf[7]) << 0) );
+}
+/* Template: Define a function named cpu_to_<END><BITS>_vect that
+ takes a vector SRC of LEN integers, each of type uint<BITS>_t, and
+ writes them to the buffer DST in the endianness defined by END.
+ Caution: LEN is the number of vector elements, not the total size
+ of the buffers. */
+#define VECTOR_CPU_TO(end, bits) VECTOR_CPU_TO_(end##bits, uint##bits##_t)
+#define VECTOR_CPU_TO_(prim, stype) \
+ static inline void \
+ cpu_to_##prim##_vect(uint8_t *dst, const stype *src, size_t len) \
+ { \
+ while (len) \
+ { \
+ cpu_to_##prim(dst, *src); \
+ src += 1; \
+ dst += sizeof(stype); \
+ len -= 1; \
+ } \
+ } struct _swallow_semicolon
+/* Template: Define a function named <END><BITS>_to_cpu_vect that
+ reads a vector of LEN integers, each of type uint<BITS>_t, from the
+ buffer SRC, in the endianness defined by END, and writes them to
+ the vector DST. Caution: LEN is the number of vector elements, not
+ the total size of the buffers. */
+#define VECTOR_TO_CPU(end, bits) VECTOR_TO_CPU_(end##bits, uint##bits##_t)
+#define VECTOR_TO_CPU_(prim, dtype) \
+ static inline void \
+ prim##_to_cpu_vect(dtype *dst, const uint8_t *src, size_t len) \
+ { \
+ while (len) \
+ { \
+ *dst = prim##_to_cpu(src); \
+ src += sizeof(dtype); \
+ dst += 1; \
+ len -= 1; \
+ } \
+ } struct _swallow_semicolon
+/* These are the vectorized endianness-conversion functions that are
+ presently used. Add more as necessary. */
+VECTOR_CPU_TO(be,32);
+VECTOR_CPU_TO(be,64);
+VECTOR_TO_CPU(be,32);
+VECTOR_TO_CPU(be,64);
+
+
+const unsigned char ascii64[65] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+/* 0000000000111111111122222222223333333333444444444455555555556666 */
+/* 0123456789012345678901234567890123456789012345678901234567890123 */
+#define itoa64 ascii64
+
+#define YESCRYPT_INTERNAL
+#include "alg-sha256.h"
+#include "alg-yescrypt.h"
+#include "alg-sha256.c"
+#include "alg-yescrypt-platform.c"
+#include "alg-yescrypt-kdf.c"
+#include "alg-yescrypt-common.c"
loginutils/Config.src+11 -0
@@ -91,6 +91,17 @@ config USE_BB_CRYPT_SHA
With this option off, login will fail password check for any
user which has password encrypted with these algorithms.
+config USE_BB_CRYPT_YES
+ bool "Enable yescrypt functions"
+ default y
+ depends on USE_BB_CRYPT
+ help
+ Enable this if you have passwords starting with "$y$" or
+ in your /etc/passwd or /etc/shadow files. These passwords
+ are hashed using yescrypt algorithms.
+ With this option off, login will fail password check for any
+ user which has password encrypted with these algorithms.
+
INSERT
endmenu
networking/httpd.c+1 -1
@@ -2826,7 +2826,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
salt[0] = '$';
salt[1] = '1';
salt[2] = '$';
- crypt_make_salt(salt + 3, 4);
+ crypt_make_rand64encoded(salt + 3, 8 / 2); /* 8 chars */
puts(pw_encrypt(pass, salt, /*cleanup:*/ 0));
return 0;
}
testsuite/cryptpw.tests+53 -13
@@ -22,21 +22,61 @@ testing "cryptpw des zz" \
#SKIP=
optional USE_BB_CRYPT_SHA
-testing "cryptpw sha256" \
- "cryptpw -m sha256 QWErty '123456789012345678901234567890'" \
- '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' "" ""
+# Note: mkpasswd-5.6.2 won't accept "-m sha256", wants "-m sha256crypt"
+testing 'cryptpw sha256' \
+ 'cryptpw -m sha256 QWErty 1234567890123456' \
+ '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \
+ '' ''
+# mkpasswd-5.6.2 does not allow overlong salts, we truncate (at 16 chars for sha256)
+testing 'cryptpw sha256 overlong' \
+ 'cryptpw -m sha256 QWErty 123456789012345678901234567890' \
+ '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \
+ '' ''
+testing 'cryptpw sha256 implicit' \
+ 'cryptpw QWErty \$5\$1234567890123456' \
+ '$5$1234567890123456$5DxfOCmU4vRhtzfsbdK.6wSGMwwVbac7ZkWwusb8Si7\n' \
+ '' ''
+testing 'cryptpw sha256 rounds=99999' \
+ 'cryptpw -m sha256 QWErty rounds=99999\$123456789012345678901234567890' \
+ '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' \
+ '' ''
+testing 'cryptpw sha256 rounds=99999 implicit' \
+ 'cryptpw QWErty \$5\$rounds=99999\$123456789012345678901234567890' \
+ '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' \
+ '' ''
-testing "cryptpw sha256 rounds=99999" \
- "cryptpw -m sha256 QWErty 'rounds=99999\$123456789012345678901234567890'" \
- '$5$rounds=99999$1234567890123456$aYellycJGZM6AKyVzaQsSrDBdTixubtMnM6J.MN0xM8\n' "" ""
-
-testing "cryptpw sha512" \
- "cryptpw -m sha512 QWErty '123456789012345678901234567890'" \
- '$6$1234567890123456$KB7QqxFyqmJSWyQYcCuGeFukgz1bPQoipWZf7.9L7z3k8UNTXa6UikbKcUGDc2ANn7DOGmDaroxDgpK16w/RE0\n' "" ""
+testing 'cryptpw sha512' \
+ 'cryptpw -m sha512 QWErty 123456789012345678901234567890' \
+ '$6$1234567890123456$KB7QqxFyqmJSWyQYcCuGeFukgz1bPQoipWZf7.9L7z3k8UNTXa6UikbKcUGDc2ANn7DOGmDaroxDgpK16w/RE0\n' \
+ '' ''
+testing 'cryptpw sha512 rounds=99999' \
+ 'cryptpw -m sha512 QWErty rounds=99999\$123456789012345678901234567890' \
+ '$6$rounds=99999$1234567890123456$BfF6gD6ZjUmwawH5QaAglYAxtU./yvsz0fcQ464l49aMI2DZW3j5ri28CrxK7riPWNpLuUpfaIdY751SBYKUH.\n' \
+ '' ''
+SKIP=
-testing "cryptpw sha512 rounds=99999" \
- "cryptpw -m sha512 QWErty 'rounds=99999\$123456789012345678901234567890'" \
- '$6$rounds=99999$1234567890123456$BfF6gD6ZjUmwawH5QaAglYAxtU./yvsz0fcQ464l49aMI2DZW3j5ri28CrxK7riPWNpLuUpfaIdY751SBYKUH.\n' "" ""
+optional USE_BB_CRYPT_YES
+testing 'cryptpw yescrypt' \
+ 'cryptpw -m yescrypt qweRTY123@-+ j9T\$123456789012345678901234' \
+ '$y$j9T$123456789012345678901234$AKxw5OX/T4jD.v./IW.5tE/j7izNjw06fg3OvH1LsN9\n' \
+ '' ''
+testing 'cryptpw yescrypt with non-standard cost 4 instead of 5 (j8T instead of j9T)' \
+ 'cryptpw -m yescrypt qweRTY123@-+ j8T\$123456789012345678901234' \
+ '$y$j8T$123456789012345678901234$JQUUfopCxlfZNE8f.THJwbOkhy.XtB3GIjo9HUVioWB\n' \
+ '' ''
+# mkpasswd-5.6.2 allows short salts for yescrypt
+testing 'cryptpw yescrypt with empty salt' \
+ 'cryptpw -m yescrypt qweRTY123@-+ j9T\$' \
+ '$y$j9T$$hpeksL94GXNRwnA00L3c8WFy0khFAUbCpBSak.N3Bp.\n' \
+ '' ''
+testing 'cryptpw yescrypt with 3-char salt' \
+ 'cryptpw -m yescrypt qweRTY123@-+ j9T\$123' \
+ '$y$j9T$123$A34DMIGUbUIo3bjx66Wtk2IFoREMIw6d49it25KQh2D\n' \
+ '' ''
+testing 'cryptpw yescrypt implicit' \
+ 'cryptpw qweRTY123@-+ \$y\$j9T\$123456789012345678901234' \
+ '$y$j9T$123456789012345678901234$AKxw5OX/T4jD.v./IW.5tE/j7izNjw06fg3OvH1LsN9\n' \
+ '' ''
SKIP=
exit $FAILCOUNT