/* 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.ExpandKey(cnState.GetAESKey2(), false); for (int i = 0; i < cnParams.InitRounds; i++) { for (int j = 0; j < Constants.INIT_SIZE_BLOCK; j++) { int offsetA = j * AES.BLOCK_SIZE; int offsetB = (i * Constants.INIT_SIZE_BYTE) + offsetA; XORBlocks(text, scratchpad, offsetA, offsetB); /* Need to pass the array with an offset because we manip * it in place */ AES.AESBPseudoRound(expandedKeys, text, offsetA); } } cnState.SetText(text); }
public static byte[] SlowHash(byte[] input, ICryptoNight cnParams) { bool useX64Version = Aes.IsSupported && Bmi2.X64.IsSupported && Sse2.IsSupported && cnParams.Intrinsics; switch (cnParams.Variant) { case 0: { if (useX64Version) { return(X64.CryptoNightV0(input, cnParams)); } else { return(Portable.CryptoNightV0(input, cnParams)); } } case 1: { if (useX64Version) { return(X64.CryptoNightV1(input, cnParams)); } else { return(Portable.CryptoNightV1(input, cnParams)); } } case 2: { if (useX64Version) { return(X64.CryptoNightV2(input, cnParams)); } else { return(Portable.CryptoNightV2(input, cnParams)); } } default: { throw new ArgumentException( "Variants other than 0, 1, and 2 are not supported!" ); } } }
public static byte[] CryptoNightV2(byte[] input, ICryptoNight cnParams) { /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' * buffer, encapsulated in cnState */ CNState cnState = new CNState(Keccak.Keccak1600(input)); byte[] scratchpad = CryptoNight.FillScratchpad(cnState, cnParams); MixScratchpadV2(cnState, cnParams, scratchpad); EncryptScratchpadToText(cnState, cnParams, scratchpad); return(CryptoNight.HashFinalState(cnState)); }
/* 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.ExpandKey(cnState.GetAESKey2(), true); for (int i = 0; i < cnParams.InitRounds; i++) { AES.AESPseudoRoundXOR( expandedKeys, text, scratchpad, i * Constants.INIT_SIZE_BYTE ); } cnState.SetText(text); }
/* CryptoNight Step 2: Iteratively encrypt the results from Keccak * to fill the large scratchpad */ public static byte[] FillScratchpad(CNState cnState, ICryptoNight cnParams) { /* Expand our initial key into many for each round of pseudo aes */ byte[] expandedKeys = AES.ExpandKey(cnState.GetAESKey(), cnParams.Intrinsics); /* Our large scratchpad, 2MB in default CN */ byte[] scratchpad = new byte[cnParams.Memory]; byte[] text = cnState.GetText(); if (cnParams.Intrinsics && Aes.IsSupported && Sse2.IsSupported) { /* Fill the scratchpad with AES encryption of text */ for (int i = 0; i < cnParams.InitRounds; i++) { AES.AESPseudoRoundNative(expandedKeys, text); /* Write text to the scratchpad, at the offset * i * InitSizeByte */ Buffer.BlockCopy(text, 0, scratchpad, i * Constants.INIT_SIZE_BYTE, text.Length); } } else { /* Fill the scratchpad with AES encryption of text */ for (int i = 0; i < cnParams.InitRounds; i++) { AES.AESPseudoRound(expandedKeys, text, cnParams.Intrinsics); /* Write text to the scratchpad, at the offset * i * InitSizeByte */ Buffer.BlockCopy(text, 0, scratchpad, i * Constants.INIT_SIZE_BYTE, text.Length); } } return(scratchpad); }
public 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.Keccak1600(input)); byte[] scratchpad = CryptoNight.FillScratchpad(cnState, cnParams); byte[] tweak = CryptoNight.VariantOneInit(cnState, input); MixScratchpadV1(cnState, cnParams, scratchpad, tweak); EncryptScratchpadToText(cnState, cnParams, scratchpad); return(CryptoNight.HashFinalState(cnState)); }
/* 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 MixScratchpadV0( 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()) { ulong lo; ulong hi; ulong *loPtr = &lo; ulong *p; ulong *a = (ulong *)aByte; ulong *b = (ulong *)bByte; ulong *c = (ulong *)cByte; CryptoNight.InitMixingState(a, b, (ulong *)k); Vector128 <byte> _a; Vector128 <byte> _b = Sse2.LoadVector128(bByte); Vector128 <byte> _c; int j; 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); /* 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]; /* 64 bit multiply the low parts of C and B */ hi = Bmi2.X64.MultiplyNoFlags(c[0], b[0], loPtr); /* 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]; /* Store C in B */ _b = _c; } } }
/* 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 MixScratchpadV0( 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()) { CryptoNight.InitMixingState((ulong *)a, (ulong *)b, (ulong *)k); 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); 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); Multiply64((ulong *)c1, (ulong *)c, (ulong *)d); /* 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 a block from c1 to b */ CopyBlock(b, c1); } } }
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); } } }