#define SCRYPT_HASH "Skein-512" #define SCRYPT_HASH_BLOCK_SIZE 64 #define SCRYPT_HASH_DIGEST_SIZE 64 typedef uint8_t scrypt_hash_digest[SCRYPT_HASH_DIGEST_SIZE]; typedef struct scrypt_hash_state_t { uint64_t X[8], T[2]; uint32_t leftover; uint8_t buffer[SCRYPT_HASH_BLOCK_SIZE]; } scrypt_hash_state; #include static void skein512_blocks(scrypt_hash_state *S, const uint8_t *in, size_t blocks, size_t add) { uint64_t X[8], key[8], Xt[9+18], T[3+1]; size_t r; while (blocks--) { T[0] = S->T[0] + add; T[1] = S->T[1]; T[2] = T[0] ^ T[1]; key[0] = U8TO64_LE(in + 0); Xt[0] = S->X[0]; X[0] = key[0] + Xt[0]; key[1] = U8TO64_LE(in + 8); Xt[1] = S->X[1]; X[1] = key[1] + Xt[1]; key[2] = U8TO64_LE(in + 16); Xt[2] = S->X[2]; X[2] = key[2] + Xt[2]; key[3] = U8TO64_LE(in + 24); Xt[3] = S->X[3]; X[3] = key[3] + Xt[3]; key[4] = U8TO64_LE(in + 32); Xt[4] = S->X[4]; X[4] = key[4] + Xt[4]; key[5] = U8TO64_LE(in + 40); Xt[5] = S->X[5]; X[5] = key[5] + Xt[5] + T[0]; key[6] = U8TO64_LE(in + 48); Xt[6] = S->X[6]; X[6] = key[6] + Xt[6] + T[1]; key[7] = U8TO64_LE(in + 56); Xt[7] = S->X[7]; X[7] = key[7] + Xt[7]; Xt[8] = 0x1BD11BDAA9FC1A22ull ^ Xt[0] ^ Xt[1] ^ Xt[2] ^ Xt[3] ^ Xt[4] ^ Xt[5] ^ Xt[6] ^ Xt[7]; in += SCRYPT_HASH_BLOCK_SIZE; for (r = 0; r < 18; r++) Xt[r + 9] = Xt[r + 0]; for (r = 0; r < 18; r += 2) { X[0] += X[1]; X[1] = ROTL64(X[1], 46) ^ X[0]; X[2] += X[3]; X[3] = ROTL64(X[3], 36) ^ X[2]; X[4] += X[5]; X[5] = ROTL64(X[5], 19) ^ X[4]; X[6] += X[7]; X[7] = ROTL64(X[7], 37) ^ X[6]; X[2] += X[1]; X[1] = ROTL64(X[1], 33) ^ X[2]; X[0] += X[3]; X[3] = ROTL64(X[3], 42) ^ X[0]; X[6] += X[5]; X[5] = ROTL64(X[5], 14) ^ X[6]; X[4] += X[7]; X[7] = ROTL64(X[7], 27) ^ X[4]; X[4] += X[1]; X[1] = ROTL64(X[1], 17) ^ X[4]; X[6] += X[3]; X[3] = ROTL64(X[3], 49) ^ X[6]; X[0] += X[5]; X[5] = ROTL64(X[5], 36) ^ X[0]; X[2] += X[7]; X[7] = ROTL64(X[7], 39) ^ X[2]; X[6] += X[1]; X[1] = ROTL64(X[1], 44) ^ X[6]; X[4] += X[3]; X[3] = ROTL64(X[3], 56) ^ X[4]; X[2] += X[5]; X[5] = ROTL64(X[5], 54) ^ X[2]; X[0] += X[7]; X[7] = ROTL64(X[7], 9) ^ X[0]; X[0] += Xt[r + 1]; X[1] += Xt[r + 2]; X[2] += Xt[r + 3]; X[3] += Xt[r + 4]; X[4] += Xt[r + 5]; X[5] += Xt[r + 6] + T[1]; X[6] += Xt[r + 7] + T[2]; X[7] += Xt[r + 8] + r + 1; T[3] = T[0]; T[0] = T[1]; T[1] = T[2]; T[2] = T[3]; X[0] += X[1]; X[1] = ROTL64(X[1], 39) ^ X[0]; X[2] += X[3]; X[3] = ROTL64(X[3], 30) ^ X[2]; X[4] += X[5]; X[5] = ROTL64(X[5], 34) ^ X[4]; X[6] += X[7]; X[7] = ROTL64(X[7], 24) ^ X[6]; X[2] += X[1]; X[1] = ROTL64(X[1], 13) ^ X[2]; X[0] += X[3]; X[3] = ROTL64(X[3], 17) ^ X[0]; X[6] += X[5]; X[5] = ROTL64(X[5], 10) ^ X[6]; X[4] += X[7]; X[7] = ROTL64(X[7], 50) ^ X[4]; X[4] += X[1]; X[1] = ROTL64(X[1], 25) ^ X[4]; X[6] += X[3]; X[3] = ROTL64(X[3], 29) ^ X[6]; X[0] += X[5]; X[5] = ROTL64(X[5], 39) ^ X[0]; X[2] += X[7]; X[7] = ROTL64(X[7], 43) ^ X[2]; X[6] += X[1]; X[1] = ROTL64(X[1], 8) ^ X[6]; X[4] += X[3]; X[3] = ROTL64(X[3], 22) ^ X[4]; X[2] += X[5]; X[5] = ROTL64(X[5], 56) ^ X[2]; X[0] += X[7]; X[7] = ROTL64(X[7], 35) ^ X[0]; X[0] += Xt[r + 2]; X[1] += Xt[r + 3]; X[2] += Xt[r + 4]; X[3] += Xt[r + 5]; X[4] += Xt[r + 6]; X[5] += Xt[r + 7] + T[1]; X[6] += Xt[r + 8] + T[2]; X[7] += Xt[r + 9] + r + 2; T[3] = T[0]; T[0] = T[1]; T[1] = T[2]; T[2] = T[3]; } S->X[0] = key[0] ^ X[0]; S->X[1] = key[1] ^ X[1]; S->X[2] = key[2] ^ X[2]; S->X[3] = key[3] ^ X[3]; S->X[4] = key[4] ^ X[4]; S->X[5] = key[5] ^ X[5]; S->X[6] = key[6] ^ X[6]; S->X[7] = key[7] ^ X[7]; S->T[0] = T[0]; S->T[1] = T[1] & ~0x4000000000000000ull; } } static void scrypt_hash_init(scrypt_hash_state *S) { S->X[0] = 0x4903ADFF749C51CEull; S->X[1] = 0x0D95DE399746DF03ull; S->X[2] = 0x8FD1934127C79BCEull; S->X[3] = 0x9A255629FF352CB1ull; S->X[4] = 0x5DB62599DF6CA7B0ull; S->X[5] = 0xEABE394CA9D5C3F4ull; S->X[6] = 0x991112C71A75B523ull; S->X[7] = 0xAE18A40B660FCC33ull; S->T[0] = 0x0000000000000000ull; S->T[1] = 0x7000000000000000ull; S->leftover = 0; } static void scrypt_hash_update(scrypt_hash_state *S, const uint8_t *in, size_t inlen) { size_t blocks, want; /* skein processes the final <=64 bytes raw, so we can only update if there are at least 64+1 bytes available */ if ((S->leftover + inlen) > SCRYPT_HASH_BLOCK_SIZE) { /* handle the previous data, we know there is enough for at least one block */ if (S->leftover) { want = (SCRYPT_HASH_BLOCK_SIZE - S->leftover); memcpy(S->buffer + S->leftover, in, want); in += want; inlen -= want; S->leftover = 0; skein512_blocks(S, S->buffer, 1, SCRYPT_HASH_BLOCK_SIZE); } /* handle the current data if there's more than one block */ if (inlen > SCRYPT_HASH_BLOCK_SIZE) { blocks = ((inlen - 1) & ~(SCRYPT_HASH_BLOCK_SIZE - 1)); skein512_blocks(S, in, blocks / SCRYPT_HASH_BLOCK_SIZE, SCRYPT_HASH_BLOCK_SIZE); inlen -= blocks; in += blocks; } } /* handle leftover data */ memcpy(S->buffer + S->leftover, in, inlen); S->leftover += inlen; } static void scrypt_hash_finish(scrypt_hash_state *S, uint8_t *hash) { memset(S->buffer + S->leftover, 0, SCRYPT_HASH_BLOCK_SIZE - S->leftover); S->T[1] |= 0x8000000000000000ull; skein512_blocks(S, S->buffer, 1, S->leftover); memset(S->buffer, 0, SCRYPT_HASH_BLOCK_SIZE); S->T[0] = 0; S->T[1] = 0xff00000000000000ull; skein512_blocks(S, S->buffer, 1, 8); U64TO8_LE(&hash[ 0], S->X[0]); U64TO8_LE(&hash[ 8], S->X[1]); U64TO8_LE(&hash[16], S->X[2]); U64TO8_LE(&hash[24], S->X[3]); U64TO8_LE(&hash[32], S->X[4]); U64TO8_LE(&hash[40], S->X[5]); U64TO8_LE(&hash[48], S->X[6]); U64TO8_LE(&hash[56], S->X[7]); } static const uint8_t scrypt_test_hash_expected[SCRYPT_HASH_DIGEST_SIZE] = { 0x4d,0x52,0x29,0xff,0x10,0xbc,0xd2,0x62,0xd1,0x61,0x83,0xc8,0xe6,0xf0,0x83,0xc4, 0x9f,0xf5,0x6a,0x42,0x75,0x2a,0x26,0x4e,0xf0,0x28,0x72,0x28,0x47,0xe8,0x23,0xdf, 0x1e,0x64,0xf1,0x51,0x38,0x35,0x9d,0xc2,0x83,0xfc,0x35,0x4e,0xc0,0x52,0x5f,0x41, 0x6a,0x0b,0x7d,0xf5,0xce,0x98,0xde,0x6f,0x36,0xd8,0x51,0x15,0x78,0x78,0x93,0x67, };