//#define TEST using System; using System.Diagnostics; using System.Management; using System.Runtime.CompilerServices; using System.Threading; [Serializable] public class JitterEx { private const int DesaturationLoopLimit = 500; private const int UpperCpuLoadLimit = 90; private const int LowerCpuLoadLimit = 10; private static bool _scaleSet; private readonly int _bufferSize; private readonly object _lock = new object(); private readonly Random _prng; private byte[] _rngCache; private int _tscLoopLimitCpu = 10; private int _tscSampleSizeRam = 18; public JitterEx(int buffersize, bool randomize = false) { _bufferSize = buffersize; _rngCache = new byte[_bufferSize]; _prng = new Random(); Randomize = randomize; SetScale(); } public bool Randomize { get; set; } #if TEST public void TestListF() { var rlst = new List<List<(byte[], int)>>(); for (var i = 0; i < TestList.Count; i++) { var f = TestList[i]; for (var j = i + 1; j < TestList.Count; j++) { var s = TestList[j]; rlst.Add(new BMPartialPatternSearch(3).SearchPartialAll(f, s)); } } } #endif [MethodImpl(MethodImplOptions.NoOptimization)] private float GetCpuSpeed() { void Loop() { var i = 0; while (true) i = i + 1 - 1; } var cpuCounter = new PerformanceCounter("Processor Information", "% Processor Performance", "_Total"); var cpuValue = cpuCounter.NextValue(); var loop = new Thread(() => Loop()) {Priority = ThreadPriority.Highest}; var Speed = 0f; lock (_lock) { loop.Start(); Thread.Sleep(60); cpuValue = cpuCounter.NextValue(); loop.Abort(); } foreach (ManagementObject obj in new ManagementObjectSearcher("SELECT *, Name FROM Win32_Processor").Get()) { var v = Convert.ToSingle(obj["MaxClockSpeed"]); Speed = v / 1000 * cpuValue / 100; } return Speed; } /// <summary> /// This is a fairly arbitrary value which is based on the CPU speed. 4.7 is the speed on which the tests are based. /// </summary> private void SetScale() { if (_scaleSet) return; _scaleSet = true; const float baseFreq = 4.7f; var thisFreq = GetCpuSpeed(); var rat = baseFreq / thisFreq; _tscLoopLimitCpu = (int) Math.Ceiling(_tscLoopLimitCpu * rat); _tscSampleSizeRam = (int) Math.Ceiling(_tscSampleSizeRam * rat); } private void LoadBlock() { var DesaturationLoops = 0; var dump = CpuTotalPc.CPULoad; do { Thread.Sleep(0); DesaturationLoops++; } while (DesaturationLoops < DesaturationLoopLimit && ((int) CpuTotalPc.CPULoad > UpperCpuLoadLimit || (int) CpuTotalPc.CPULoad < LowerCpuLoadLimit)); } [MethodImpl(MethodImplOptions.NoInlining)] private byte[] GetBufferCpu() { void Loop() { var x = 0; for (var i = 0; i < _tscLoopLimitCpu; i++) x = x + 1 - 1; } var jitterBuffer = new byte[_bufferSize]; var ptr = 0; LoadBlock(); lock (_lock) { var start = Rdtsc.TimestampP(); do { var loop = new Thread(() => Loop()) {Priority = ThreadPriority.Highest}; loop.Start(); loop.Join(); var stop = Rdtsc.TimestampP(); Buffer.BlockCopy((100000.0 / ((stop - start) * 100000.0)).GetBytes(), 0, jitterBuffer, ptr, 4); start = stop; ptr += 4; } while (ptr < _bufferSize); return jitterBuffer; } } [MethodImpl(MethodImplOptions.NoInlining)] private unsafe byte[] GetBufferRam() { void Loop() { byte tempByte; fixed (byte* p1 = _rngCache) { var x1 = p1; for (var j = 0; j < _tscSampleSizeRam; ++j, ++x1) tempByte = *x1; } } LoadBlock(); var jitterBuffer = new byte[_bufferSize]; lock (_lock) { if (_rngCache.Length != _bufferSize) _rngCache = new byte[_bufferSize]; _prng.NextBytes(_rngCache); var ptr = 0; var start = Rdtsc.TimestampP(); while (true) { do { var loop = new Thread(() => Loop()) {Priority = ThreadPriority.Highest}; loop.Start(); loop.Join(); var stop = Rdtsc.TimestampP(); Buffer.BlockCopy((100000.0 / ((stop - start) * 100000.0)).GetBytes(), 0, jitterBuffer, ptr, 4); start = stop; ptr += 4; if (ptr < _bufferSize) { if (_rngCache.Length != _bufferSize) _rngCache = new byte[_bufferSize]; _prng.NextBytes(_rngCache); } } while (ptr < _bufferSize); return jitterBuffer; } } } public byte[] GetBuffer() { var firstBuffer = GetBufferCpu(); #if TEST ///Weighting add 15%, perfection is not what we are looking for here. var lim = _bufferSize <= 256 ? _bufferSize / 256.0 * 85.0 : 6.8; if (ent.Entropy(firstBuffer) < lim) throw new Exception("Error CPU Jitter Buffer Contains only zeros."); #endif var secondBuffer = GetBufferRam(); #if TEST if (ent.Entropy(secondBuffer) < lim) throw new Exception("Error CPU Jitter Buffer Contains only zeros."); #endif var finalBuffer = new byte[_bufferSize]; for (var j = 0; j < _bufferSize; ++j) finalBuffer[j] = (byte) (firstBuffer[j] ^ secondBuffer[j]); #if TEST if (ent.Entropy(finalBuffer) < lim) throw new Exception("Error CPU Jitter Buffer Contains only zeros."); #endif if (Randomize) { var ula = CreateNoiseArrays(finalBuffer); var rBlock = new byte[ula.Length * 8]; Buffer.BlockCopy(ula, 0, rBlock, 0, ula.Length * 8); #if TEST if (_bufferSize >= 256) { var loop = new Thread(() => { if (!rBlock.ChiSquaredTest().isRandom) MessageBoxEx.Show("Warning", "Block not random.", MessageBoxExButtons.OK, MessageBoxExIcon.Warning); }) {Priority = ThreadPriority.Highest}; loop.Start(); } TestList.Add(rBlock); #endif return rBlock; } #if TEST if (_bufferSize >= 256) { var loop = new Thread(() => { if (!finalBuffer.ChiSquaredTest().isRandom) MessageBoxEx.Show("Warning", "Block not random.", MessageBoxExButtons.OK, MessageBoxExIcon.Warning); }) {Priority = ThreadPriority.Highest}; loop.Start(); } TestList.Add(finalBuffer); #endif return finalBuffer; } private ulong[] ByteArrayToULongArray(byte[] ba, int finalSize) { var minSize = ba.Length / 8; if (finalSize < minSize) finalSize = minSize; ba = PadULong(ba); var result = new ulong[finalSize]; for (var i = 0; i < ba.Length; i += 8) Buffer.BlockCopy(ba, i, result, i, 8); return result; } private static byte[] PadULong(byte[] ba) { var s = ba.Length % 8; switch (s) { case 0: break; case 1: Array.Resize(ref ba, ba.Length + 7); ba[ba.Length - 1] = 0x80; ba[ba.Length - 2] = 0x80; ba[ba.Length - 3] = 0x80; ba[ba.Length - 4] = 0x80; ba[ba.Length - 5] = 0x80; ba[ba.Length - 6] = 0x80; ba[ba.Length - 7] = 0x80; break; case 2: Array.Resize(ref ba, ba.Length + 6); ba[ba.Length - 1] = 0x80; ba[ba.Length - 2] = 0x80; ba[ba.Length - 3] = 0x80; ba[ba.Length - 4] = 0x80; ba[ba.Length - 5] = 0x80; ba[ba.Length - 6] = 0x80; break; case 3: Array.Resize(ref ba, ba.Length + 5); ba[ba.Length - 1] = 0x80; ba[ba.Length - 2] = 0x80; ba[ba.Length - 3] = 0x80; ba[ba.Length - 4] = 0x80; ba[ba.Length - 5] = 0x80; break; case 4: Array.Resize(ref ba, ba.Length + 4); ba[ba.Length - 1] = 0x80; ba[ba.Length - 2] = 0x80; ba[ba.Length - 3] = 0x80; ba[ba.Length - 4] = 0x80; break; case 5: Array.Resize(ref ba, ba.Length + 3); ba[ba.Length - 1] = 0x80; ba[ba.Length - 2] = 0x80; ba[ba.Length - 3] = 0x80; break; case 6: Array.Resize(ref ba, ba.Length + 2); ba[ba.Length - 1] = 0x80; ba[ba.Length - 2] = 0x80; break; case 7: Array.Resize(ref ba, ba.Length + 1); ba[ba.Length - 1] = 0x80; break; } return ba; } private static void Extrude(ulong[] x) { var size = x.Length; for (var i = 0; i < size; i++) { ulong n = 0; var j = 0; while (j < size) { n ^= x[j]; ++j; } x[i] = (n << 1) | (n >> 56); } } private ulong[] CreateNoiseArrays(byte[] ba) { var ula = ByteArrayToULongArray(ba, ba.Length / 8); Extrude(ula); return ula; } internal class EntropyI { private const double NaturalLogOfTwo = 0.69314718055994530941723212145818; //Math.Log(2); /// <summary> /// Get the Entropy from 1 to 100% 1 being very ordered data, /// 100% being very disordered data. /// </summary> public double Entropy(byte[] a) { var h = new int[256]; var l = a.Length; for (var i = 0; i < l; ++i) h[a[i]]++; var e = 0.0; for (var i = 0; i < 256; ++i) { var v = h[i]; if (v <= 0) continue; var r = v / (double) l; e -= r * (Math.Log(r) / NaturalLogOfTwo); } return e / 8.0 * 100.0; } } #if TEST private EntropyI ent = new EntropyI(); private List<byte[]> TestList = new List<byte[]>(); #endif }