RngJitterSource.cs

CPU RAM Jitter Driven RNG Data Source

using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Threading;
public class RngJitterSource : RandomNumberGenerator
{
    /// <summary>
    ///     Recalculate buffer every 1000 milliseconds
    /// </summary>
    private const int ReSecureThresholdBf = 1000;
    private readonly SHA3ModInt _algorithm;
    private readonly object     _cacheLock = new object();
    private readonly int        _moveSize;
    /// <summary>
    ///     Just in case you need access to the underlying data
    /// </summary>
    public readonly JitterEx Jitter;
    private readonly Stopwatch tmr;
    private          byte[]    _cacheBuffer;
    private          int       _cacheSize;
    private volatile int       _ptr;
    private volatile int       BytesRemainingInCache;
    /// <summary>
    ///     If you want to use this as a flat memory pool
    ///     Use in conjunction with DataReady -- DataReady.WaitOne(); -- use data
    /// </summary>
    public volatile byte[] Cache;
    public  int    cacheFills;
    private Thread CacheThread;
    /// <summary>
    ///     See Cache above
    /// </summary>
    public AutoResetEvent DataReady = new AutoResetEvent(false);
    private bool Filled;
    public  int  ResecureCount;
    public RngJitterSource() : this(1024 * 1024, 1024, 256, 4)
    {
    }
    public RngJitterSource(int cacheSize) : this(cacheSize, 1024, 256, 4)
    {
    }
    public RngJitterSource(int cacheSize, int seedSize) : this(cacheSize, seedSize, 256, 4)
    {
    }
    public RngJitterSource(int cacheSize, int seedSize, int sha3Size) : this(cacheSize, seedSize, sha3Size, 4)
    {
    }
    public RngJitterSource(int cacheSize, int seedSize, int sha3Size, int sha3Rounds)
    {
        _cacheSize   = cacheSize;
        Jitter       = new JitterEx();
        _cacheBuffer = new byte[seedSize];
        Jitter.GetBytes(_cacheBuffer);
        Cache                 = new byte[_cacheSize];
        _ptr                  = 0;
        BytesRemainingInCache = 0;
        _algorithm            = new SHA3ModInt(sha3Size, sha3Rounds);
        _moveSize             = _algorithm.ComputeHash(2.GetBytes()).Length;
        Filled                = false;
        tmr                   = new Stopwatch();
        CacheThread           = new Thread(KeepFullCache) {Priority = ThreadPriority.Highest};
        CacheThread.Start();
    }
    public void Abort()
    {
        CacheThread.Abort();
    }
    protected override void Dispose(bool disposing)
    {
        _algorithm.Dispose();
    }
    public override void GetBytes(byte[] data)
    {
        if (data.Length > _cacheSize)
        {
            _cacheSize            = data.Length;
            Cache                 = new byte[_cacheSize];
            _ptr                  = 0;
            BytesRemainingInCache = 0;
            if (CacheThread.IsAlive)
                CacheThread.Abort();
            DataReady   = new AutoResetEvent(false);
            CacheThread = new Thread(KeepFullCache) {Priority = ThreadPriority.Highest};
            CacheThread.Start();
        }
        while (_ptr < data.Length)
            Thread.Sleep(10);
        DataReady.WaitOne();
        Buffer.BlockCopy(Cache, _ptr - data.Length, data, 0, data.Length);
        _ptr                  -= data.Length;
        BytesRemainingInCache -= data.Length;
    }
    private void KeepFullCache()
    {
        while (true)
            if (BytesRemainingInCache < _moveSize)
                lock (_cacheLock)
                {
                    var moveSize = _moveSize;
                    cacheFills++;
                    var bufferFills = 0;
                    tmr.Start();
                    while (true)
                    {
                        bufferFills++;
                        if (tmr.Elapsed.TotalMilliseconds >= ReSecureThresholdBf)
                        {
                            Jitter.GetBytes(_cacheBuffer);
                            tmr.Restart();
                            ResecureCount++;
                        }
                        var remainingBytesToMove = _cacheSize - _ptr;
                        if (remainingBytesToMove < moveSize)
                            moveSize = remainingBytesToMove;
                        if (remainingBytesToMove <= 0 || _ptr >= _cacheSize)
                            break;
                        _cacheBuffer = _algorithm.ComputeHash(_cacheBuffer);
                        Buffer.BlockCopy(_cacheBuffer, 0, Cache, _ptr, moveSize);
                        _ptr += moveSize;
                    }
                    tmr.Stop();
                    _ptr                  = _cacheSize;
                    BytesRemainingInCache = _cacheSize;
                    DataReady.Set();
                }
            else
                DataReady.Set();
    }
}