/* CryptoNight Step 2: Iteratively encrypt the results from Keccak * to fill the large scratchpad */ private static byte[] FillScratchpad(CNState cnState, ICryptoNight cnParams) { /* Expand our initial key into many for each round of pseudo aes */ byte[] expandedKeys = AES.AES.ExpandKey(cnState.GetAESKey()); /* Our large scratchpad, 2MB in default CN */ byte[] scratchpad = new byte[cnParams.Memory()]; byte[] text = cnState.GetText(); /* Fill the scratchpad with AES encryption of text */ for (int i = 0; i < cnParams.Memory() / Constants.InitSizeByte; i++) { for (int j = 0; j < Constants.InitSizeBlock; j++) { /* Need to pass the array with an offset because we manip * it in place */ AES.AES.AESBPseudoRound(expandedKeys, text, j * AES.Constants.BlockSize); } /* Write text to the scratchpad, at the offset * i * InitSizeByte */ Buffer.BlockCopy(text, 0, scratchpad, i * Constants.InitSizeByte, text.Length); } return(scratchpad); }
/* CryptoNight Step 4: Sequentially pass through the mixing buffer * and use 10 rounds of AES encryption to mix the random data back * into the 'text' buffer. */ private static void EncryptScratchpadToText(CNState cnState, ICryptoNight cnParams, byte[] scratchpad) { /* Reinitialize text from state */ byte[] text = cnState.GetText(); /* Expand our initial key into many for each round of pseudo aes */ byte[] expandedKeys = AES.AES.ExpandKey(cnState.GetAESKey2()); for (int i = 0; i < cnParams.Memory() / Constants.InitSizeByte; i++) { for (int j = 0; j < Constants.InitSizeBlock; j++) { int offsetA = j * AES.Constants.BlockSize; int offsetB = (i * Constants.InitSizeByte) + offsetA; XORBlocks(text, scratchpad, offsetA, offsetB); /* Need to pass the array with an offset because we manip * it in place */ AES.AES.AESBPseudoRound(expandedKeys, text, offsetA); } } cnState.SetText(text); }
/* 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 void MixScratchpadV0(CNState cnState, ICryptoNight cnParams, byte[] scratchpad) { MixScratchpadState mixingState = new MixScratchpadState(cnState); for (int i = 0; i < cnParams.Iterations() / 2; i++) { for (int iteration = 1; iteration < 3; iteration++) { /* Get our 'memory' address we're using for this round */ int j = E2I(mixingState.a, cnParams.Memory()); /* Load c from the scratchpad */ CopyBlockFromScratchpad(scratchpad, mixingState.c, j); /* Perform the mixing function */ if (iteration == 1) { MixScratchpadIterationOne(mixingState); } else { MixScratchpadIterationTwo(mixingState); } /* Write c back to the scratchpad */ CopyBlockToScratchpad(scratchpad, mixingState.c, j); SwapBlocks(ref mixingState.a, ref mixingState.b); } } }
private static byte[] VariantOneInit(CNState state, byte[] input) { byte[] tweak = state.GetTweak(); XOR64(tweak, input, 0, 35); return(tweak); }
public MixScratchpadState(CNState cnState) { k = cnState.GetK(); for (int i = 0; i < AES.Constants.BlockSize; i++) { a[i] = (byte)(k[i] ^ k[i + 32]); b[i] = (byte)(k[i + 16] ^ k[i + 48]); } }
private static byte[] CryptoNightV0(byte[] input, ICryptoNight cnParams) { /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' * buffer, encapsulated in cnState */ CNState cnState = new CNState(Keccak.Keccak.keccak1600(input)); byte[] scratchpad = FillScratchpad(cnState, cnParams); MixScratchpadV0(cnState, cnParams, scratchpad); EncryptScratchpadToText(cnState, cnParams, scratchpad); return(HashFinalState(cnState)); }
/* 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. */ private static byte[] HashFinalState(CNState cnState) { /* Get the state buffer as an array of ulongs rather than bytes */ ulong[] hashState = cnState.GetHashState(); Keccak.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(); IHashProvider p; /* Choose the final hashing function to use based on the value of * state[0] */ switch (state[0] % 4) { case 0: { p = new Blake.Blake(); return(p.Hash(state)); } case 1: { p = new Groestl.Groestl(); return(p.Hash(state)); } case 2: { p = new JH.JH(); return(p.Hash(state)); } default: { p = new Skein.Skein(); return(p.Hash(state)); } } }
private static byte[] CryptoNightV1(byte[] input, ICryptoNight cnParams) { if (input.Length < 43) { throw new ArgumentException( "Input to CryptoNightV1 must be at least 43 bytes!" ); } /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' * buffer, encapsulated in cnState */ CNState cnState = new CNState(Keccak.Keccak.keccak1600(input)); byte[] scratchpad = FillScratchpad(cnState, cnParams); byte[] tweak = VariantOneInit(cnState, input); MixScratchpadV1(cnState, cnParams, scratchpad, tweak); EncryptScratchpadToText(cnState, cnParams, scratchpad); return(HashFinalState(cnState)); }