/* CryptoNight Step 5: Apply Keccak to the state again, and then * use the resulting data to select which of four finalizer * hash functions to apply to the data (Blake, Groestl, JH, * or Skein). Use this hash to squeeze the state array down * to the final 32 byte hash output. */ public static byte[] HashFinalState(CNState cnState) { /* Get the state buffer as an array of ulongs rather than bytes */ ulong[] hashState = cnState.GetHashState(); Keccak.Keccakf(hashState, 24); /* Set the state buffer from the coerced hash state */ cnState.SetHashState(hashState); /* Get the actual state buffer finally */ byte[] state = cnState.GetState(); /* Choose the final hashing function to use based on the value of * state[0] */ switch (state[0] % 4) { case 0: { return(new Blake().Hash(state)); } case 1: { return(new Groestl().Hash(state)); } case 2: { return(new JH().Hash(state)); } default: { return(new Skein().Hash(state)); } } }
/* CryptoNight Step 3: Bounce randomly 1,048,576 times (1<<20) * through the mixing scratchpad, using 524,288 iterations of the * following mixing function. Each execution performs two reads * and writes from the mixing scratchpad. */ private static unsafe void MixScratchpadV2( CNState cnState, ICryptoNight cnParams, byte[] scratchpad) { byte[] aArray = new byte[AES.BLOCK_SIZE]; byte[] bArray = new byte[AES.BLOCK_SIZE * 2]; byte[] cArray = new byte[AES.BLOCK_SIZE]; fixed(byte *scratchpadPtr = scratchpad, aByte = aArray, bByte = bArray, cByte = cArray, k = cnState.GetK(), hashState = cnState.GetState()) { ulong lo; ulong hi; ulong *loPtr = &lo; ulong *p; ulong *a = (ulong *)aByte; ulong *b = (ulong *)bByte; ulong *c = (ulong *)cByte; CryptoNight.InitMixingState((ulong *)a, (ulong *)b, (ulong *)k); int j; ulong *w = (ulong *)hashState; b[2] = w[8] ^ w[10]; b[3] = w[9] ^ w[11]; ulong divisionResult = w[12]; ulong sqrtResult = w[13]; Vector128 <byte> _a; Vector128 <byte> _b = Sse2.LoadVector128(bByte); Vector128 <byte> _b1 = Sse2.LoadVector128(bByte + 16); Vector128 <byte> _c; for (int i = 0; i < cnParams.AesRounds; i++) { /* Get our 'memory' address we're using for this round */ j = CryptoNight.StateIndex(*a, cnParams.ScratchModulus); /* Load C from the memory address in the scratchpad */ _c = Sse2.LoadVector128(scratchpadPtr + j); /* Reload A from the buffer */ _a = Sse2.LoadVector128(aByte); /* Use AES to mix scratchpad into c */ _c = Aes.Encrypt(_c, _a); VariantTwoShuffleAdd( scratchpadPtr, j, _b1, _b, _a ); /* Store C back in it's original spot */ Sse2.Store(cByte, _c); /* XOR b and C, and store back at the 'memory' address * in the scratchpad */ Sse2.Store(scratchpadPtr + j, Sse2.Xor(_b, _c)); /* Get the new 'memory' address we're using for this round */ j = CryptoNight.StateIndex(*c, cnParams.ScratchModulus); /* Grab a pointer to the spot of memory we're interested */ p = (ulong *)(scratchpadPtr + j); /* Load b from the memory address in the scratchpad */ b[0] = p[0]; b[1] = p[1]; VariantTwoIntegerMath(b, c, ref divisionResult, ref sqrtResult); /* 64 bit multiply the low parts of C and B */ hi = Bmi2.X64.MultiplyNoFlags(c[0], b[0], loPtr); *(ulong *)(scratchpadPtr + (j ^ 0x10)) ^= hi; *((ulong *)(scratchpadPtr + (j ^ 0x10)) + 1) ^= lo; hi ^= *(ulong *)(scratchpadPtr + (j ^ 0x20)); lo ^= *((ulong *)(scratchpadPtr + (j ^ 0x20)) + 1); VariantTwoShuffleAdd( scratchpadPtr, j, _b1, _b, _a ); /* Sum the result halves of the multiplication with each half of A */ a[0] += hi; a[1] += lo; /* Write the modified A back to the scratchpad */ p[0] = a[0]; p[1] = a[1]; /* Xor the two 64 bit halves of A and B */ a[0] ^= b[0]; a[1] ^= b[1]; _b1 = _b; /* Store C in B */ _b = _c; } } }
private static unsafe void MixScratchpadV2( CNState cnState, ICryptoNight cnParams, byte[] scratchpad) { byte[] aArray = new byte[AES.BLOCK_SIZE]; byte[] bArray = new byte[AES.BLOCK_SIZE * 2]; byte[] cArray = new byte[AES.BLOCK_SIZE]; byte[] c1Array = new byte[AES.BLOCK_SIZE]; byte[] dArray = new byte[AES.BLOCK_SIZE]; fixed(byte *scratchpadPtr = scratchpad, a = aArray, b = bArray, c = cArray, c1 = c1Array, d = dArray, k = cnState.GetK(), hashState = cnState.GetState()) { ulong divisionResult; ulong sqrtResult; CryptoNight.InitMixingState((ulong *)a, (ulong *)b, (ulong *)k); /* Init */ { ulong *_b = (ulong *)b; ulong *w = (ulong *)hashState; _b[2] = w[8] ^ w[10]; _b[3] = w[9] ^ w[11]; divisionResult = w[12]; sqrtResult = w[13]; } for (int i = 0; i < cnParams.AesRounds; i++) { /* ITERATION ONE */ /* Get the memory address */ int j = CryptoNight.StateIndex(*(ulong *)a, cnParams.ScratchModulus); /* Get a pointer to the memory address in the scratchpad */ ulong *p = (ulong *)(scratchpadPtr + j); /* Perform AES round from/to scratchpad, with A as the * expanded key */ AES.AESBSingleRound((uint *)p, (uint *)a); /* Copy a block from p to c1 */ CopyBlock(c1, p); VariantTwoShuffleAdd(scratchpadPtr, j, (ulong *)b, (ulong *)a); XORBlocks(p, (ulong *)b); /* ITERATION TWO */ /* Get new memory address */ j = CryptoNight.StateIndex(*(ulong *)c1, cnParams.ScratchModulus); /* Update pointer to scratchpad */ p = (ulong *)(scratchpadPtr + j); /* Copy a block from p to c */ CopyBlock(c, p); VariantTwoIntegerMath( (ulong *)c, (ulong *)c1, ref divisionResult, ref sqrtResult ); Multiply64((ulong *)c1, (ulong *)c, (ulong *)d); /* Variant 2 */ XORBlocks((ulong *)(scratchpadPtr + (j ^ 0x10)), (ulong *)d); XORBlocks((ulong *)d, (ulong *)(scratchpadPtr + (j ^ 0x20))); VariantTwoShuffleAdd(scratchpadPtr, j, (ulong *)b, (ulong *)a); /* Sum half blocks */ ((ulong *)a)[0] += ((ulong *)d)[0]; ((ulong *)a)[1] += ((ulong *)d)[1]; SwapBlocks((ulong *)a, (ulong *)c); XORBlocks((ulong *)a, (ulong *)c); /* Copy a block from c to p */ CopyBlock(p, c); /* Copy first half of b to second half */ CopyBlock(b + AES.BLOCK_SIZE, b); /* Copy a block from c1 to b */ CopyBlock(b, c1); } } }