libbb: add yescrypt password hashing support
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)
No binary (build failed)
—
(log yok)
| @@ -476,6 +476,7 @@ libs-y := \ | |
| findutils/ \ | |
| init/ \ | |
| libbb/ \ | |
| + | libbb/yescrypt/ \ |
| libpwdgrp/ \ | |
| loginutils/ \ | |
| mailutils/ \ | |
| @@ -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) \ | |
| @@ -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(¶ms, 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); | |
| @@ -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 |
| @@ -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. |
| @@ -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)); |
| + | } |
| @@ -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); |
| @@ -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(¶ms.r, 30, src); |
| + | if (!src) |
| + | return NULL; |
| + | |
| + | src = decode64_uint32_fixed(¶ms.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(¶ms.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(¶ms.p, src, 2); |
| + | if (!src) |
| + | return NULL; |
| + | } |
| + | |
| + | if (have & 2) { |
| + | src = decode64_uint32(¶ms.t, src, 1); |
| + | if (!src) |
| + | return NULL; |
| + | } |
| + | |
| + | if (have & 4) { |
| + | src = decode64_uint32(¶ms.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, |
| + | ¶ms, 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, ¶ms, buf, buflen); |
| + | if (yescrypt_free_local(&local)) |
| + | return -1; |
| + | return retval; |
| + | } |
| + | #endif //UNUSED |
| @@ -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; |
| + | } |
| @@ -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; |
| + | } |
| @@ -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); |
| @@ -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" |
| @@ -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 | |
| @@ -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; | |
| } | |
| @@ -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 | |