private static double GetSolveAttemptsPerSecond(byte[] payload, EstimationAccuracy accuracy) { CashPack pack = Packer.Pack(payload, (ushort)(payload.Length * 8)); Stopwatch watch = new Stopwatch(); int decrypts = 0; watch.Start(); byte[] currentKey = new byte[pack.Key.Length]; Array.Copy(pack.Key, 0, currentKey, 0, pack.Key.Length); byte[] solveWorkspace = new byte[pack.Payload.Length]; while (watch.ElapsedMilliseconds < ((uint)accuracy * 1000)) { for (int i = 0; i < pack.Payload.Length; i++) { solveWorkspace[i] = (byte)(pack.Payload[i] ^ currentKey[i]); } // IMPORTANT: Use HMAC instead of Checksum // This prevents you to do attacks where you can correlate known payloads // And a CashPack without ever solving it. using (HMACSHA512 sha = new HMACSHA512(currentKey)) { byte[] hash = sha.ComputeHash(solveWorkspace); for (int i = 0; i < pack.Hash.Length; i++) { if (pack.Hash[i] != hash[i]) { break; } } } // Increment key ulong keyAsNumber = BitHelper.KeyToNumber(currentKey, pack.BitDifficulty); BitHelper.SetLastBitNumberBytes(currentKey, keyAsNumber + 1, pack.BitDifficulty); decrypts++; } watch.Stop(); return(decrypts / (watch.ElapsedMilliseconds / 1000d)); }
/// <summary> /// Solves a CashPack in a single go. Only recommended to be used for very easy CashPacks that takes no more than a few minutes. /// </summary> /// <param name="pack">The CashPack to solve.</param> /// <returns>The solved plaintext payload.</returns> public static byte[] Solve(CashPack pack) { byte[] currentKey = new byte[pack.Key.Length]; Array.Copy(pack.Key, 0, currentKey, 0, pack.Key.Length); byte[] solveWorkspace = new byte[pack.Payload.Length]; while (true) { for (int i = 0; i < pack.Payload.Length; i++) { solveWorkspace[i] = (byte)(pack.Payload[i] ^ currentKey[i]); } // IMPORTANT: Use HMAC instead of Checksum // This prevents you to do attacks where you can correlate known payloads // And a CashPack without ever solving it. using (HMACSHA512 sha = new HMACSHA512(currentKey)) { byte[] hash = sha.ComputeHash(solveWorkspace); bool failed = false; for (int i = 0; i < pack.Hash.Length; i++) { if (pack.Hash[i] != hash[i]) { failed = true; break; } } if (!failed) { return(solveWorkspace); } } // Increment key ulong keyAsNumber = BitHelper.KeyToNumber(currentKey, pack.BitDifficulty); BitHelper.SetLastBitNumberBytes(currentKey, keyAsNumber + 1, pack.BitDifficulty); } }
/// <summary> /// Packs a payload into a CashPack. /// </summary> /// <param name="payload">The plaintext payload to CashPack.</param> /// <param name="bitDifficulty">The amount of bits to omit from the packed key. Calculator class can calculate this.</param> /// <returns>The packed CashPack ready to distribute. It only contains the encrypted payload and reduced key.</returns> public static CashPack Pack(byte[] payload, ushort bitDifficulty) { CashPack pack = new CashPack() { BitDifficulty = bitDifficulty, }; // Alloc key byte[] fullKey = new byte[payload.Length]; // Create key using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) { rng.GetBytes(fullKey); } // HMAC Key using (HMACSHA512 sha = new HMACSHA512(fullKey)) { pack.Hash = sha.ComputeHash(payload); } // Alloc payload pack.Payload = new byte[payload.Length]; // Encrypt payload for (int i = 0; i < pack.Payload.Length; i++) { pack.Payload[i] = (byte)(payload[i] ^ fullKey[i]); } // Reduce key byte[] reducedKey = fullKey; BitHelper.ReduceKey(reducedKey, bitDifficulty); pack.Key = reducedKey; return(pack); }