/// <summary> /// Applies the Salsa20 hash function. /// It maps a 16 element input to an output of the same size. /// </summary> /// <param name="rounds">The number of rounds. SCrypt uses 8.</param> /// <param name="input">The input buffer.</param> /// <param name="inputOffset">The offset into the input buffer.</param> /// <param name="output">The output buffer.</param> /// <param name="outputOffset">The offset into the output buffer.</param> public static void Compute(int rounds, uint[] input, int inputOffset, uint[] output, int outputOffset) { if (rounds < 2 || rounds > 20 || (rounds & 1) == 1) { throw Exceptions.Argument("rounds", "Must be even and in the range 2 to 20."); } try { // .NET's bounds checking hurts performance in tight loops like this one. // So, I unroll the array to eliminate it - a 50% speed increase. uint x0 = input[inputOffset + 0]; uint x1 = input[inputOffset + 1]; uint x2 = input[inputOffset + 2]; uint x3 = input[inputOffset + 3]; uint x4 = input[inputOffset + 4]; uint x5 = input[inputOffset + 5]; uint x6 = input[inputOffset + 6]; uint x7 = input[inputOffset + 7]; uint x8 = input[inputOffset + 8]; uint x9 = input[inputOffset + 9]; uint x10 = input[inputOffset + 10]; uint x11 = input[inputOffset + 11]; uint x12 = input[inputOffset + 12]; uint x13 = input[inputOffset + 13]; uint x14 = input[inputOffset + 14]; uint x15 = input[inputOffset + 15]; for (int i = rounds; i > 0; i -= 2) { x4 ^= R(x0 + x12, 7); x8 ^= R(x4 + x0, 9); x12 ^= R(x8 + x4, 13); x0 ^= R(x12 + x8, 18); x9 ^= R(x5 + x1, 7); x13 ^= R(x9 + x5, 9); x1 ^= R(x13 + x9, 13); x5 ^= R(x1 + x13, 18); x14 ^= R(x10 + x6, 7); x2 ^= R(x14 + x10, 9); x6 ^= R(x2 + x14, 13); x10 ^= R(x6 + x2, 18); x3 ^= R(x15 + x11, 7); x7 ^= R(x3 + x15, 9); x11 ^= R(x7 + x3, 13); x15 ^= R(x11 + x7, 18); x1 ^= R(x0 + x3, 7); x2 ^= R(x1 + x0, 9); x3 ^= R(x2 + x1, 13); x0 ^= R(x3 + x2, 18); x6 ^= R(x5 + x4, 7); x7 ^= R(x6 + x5, 9); x4 ^= R(x7 + x6, 13); x5 ^= R(x4 + x7, 18); x11 ^= R(x10 + x9, 7); x8 ^= R(x11 + x10, 9); x9 ^= R(x8 + x11, 13); x10 ^= R(x9 + x8, 18); x12 ^= R(x15 + x14, 7); x13 ^= R(x12 + x15, 9); x14 ^= R(x13 + x12, 13); x15 ^= R(x14 + x13, 18); } output[outputOffset + 0] = input[inputOffset + 0] + x0; x0 = 0; output[outputOffset + 1] = input[inputOffset + 1] + x1; x1 = 0; output[outputOffset + 2] = input[inputOffset + 2] + x2; x2 = 0; output[outputOffset + 3] = input[inputOffset + 3] + x3; x3 = 0; output[outputOffset + 4] = input[inputOffset + 4] + x4; x4 = 0; output[outputOffset + 5] = input[inputOffset + 5] + x5; x5 = 0; output[outputOffset + 6] = input[inputOffset + 6] + x6; x6 = 0; output[outputOffset + 7] = input[inputOffset + 7] + x7; x7 = 0; output[outputOffset + 8] = input[inputOffset + 8] + x8; x8 = 0; output[outputOffset + 9] = input[inputOffset + 9] + x9; x9 = 0; output[outputOffset + 10] = input[inputOffset + 10] + x10; x10 = 0; output[outputOffset + 11] = input[inputOffset + 11] + x11; x11 = 0; output[outputOffset + 12] = input[inputOffset + 12] + x12; x12 = 0; output[outputOffset + 13] = input[inputOffset + 13] + x13; x13 = 0; output[outputOffset + 14] = input[inputOffset + 14] + x14; x14 = 0; output[outputOffset + 15] = input[inputOffset + 15] + x15; x15 = 0; } catch (IndexOutOfRangeException) { // For speed, don't bounds-check until .NET throws from a bounds error. Check.Null("input", input); Check.Bounds("input", input, inputOffset, 16); Check.Null("output", output); Check.Bounds("output", output, outputOffset, 16); throw; } }