using System; using System.Security.Cryptography; using System.Threading; [Serializable] public class JitterCacheRng : RandomNumberGenerator { private const int ReSecureThreshold = 10; private readonly SHA3ModInt _algorithm; private readonly JitterEx _jit; private readonly int _moveSize; private int _availableCacheBytes; private byte[] _cache; private byte[] _cacheBuffer; private int _cacheSize; private int _ptr; public int cacheFills; public JitterCacheRng() : this(1048576, 256, 256, 4) { } public JitterCacheRng(int cacheSize) : this(cacheSize, 256, 256, 4) { } public JitterCacheRng(int cacheSize, int seedSize) : this(cacheSize, seedSize, 256, 4) { } public JitterCacheRng(int cacheSize, int seedSize, int sha3Size) : this(cacheSize, seedSize, sha3Size, 4) { } public JitterCacheRng(int cacheSize, int seedSize, int sha3Size, int sha3Rounds) { if (cacheSize == 0) throw new ArgumentException("Cache Size cannot be zero"); if (seedSize < 256) throw new ArgumentException("The seed size should be 256 or more bytes."); if (sha3Size < 64 && sha3Size % 64 != 0) throw new ArgumentException("The bitWidth of the SHA3 hash algorithm need to be at least 64 bits and a multiple of 64."); if (sha3Rounds < 4) throw new ArgumentException("Sha3 rounds of less than 4 might produce some short run repetitive sequences."); _cacheSize = cacheSize; _jit = new JitterEx(seedSize); _cacheBuffer = _jit.GetBuffer(); _cache = new byte[_cacheSize]; _algorithm = new SHA3ModInt(sha3Size, sha3Rounds); _moveSize = _algorithm.ComputeHash(2.GetBytes()).Length; FillCache(); } public bool Protect { get; set; } protected override void Dispose(bool disposing) { _cache.Fill(0); _availableCacheBytes = 0; _algorithm.Dispose(); } public override void GetBytes(byte[] data) { if (data.Length > _cacheSize) { _cacheSize = data.Length; _cache = new byte[_cacheSize]; FillCache(); } if (_availableCacheBytes == 0 || _availableCacheBytes < data.Length) { if (_cacheSize < data.Length) { _cacheSize = data.Length; _cache = new byte[_cacheSize]; } FillCache(); } if (_ptr + data.Length > _cacheSize) FillCache(); if (Protect) ProtectedMemory.Unprotect(_cache, MemoryProtectionScope.SameLogon); Buffer.BlockCopy(_cache, _ptr, data, 0, data.Length); if (Protect) ProtectedMemory.Protect(_cache, MemoryProtectionScope.SameLogon); _ptr += data.Length; _availableCacheBytes -= data.Length; } private void FillCache() { var btrd = new Thread(() => { _availableCacheBytes = 0; _ptr = 0; var p = 0; var moveSize = _moveSize; cacheFills++; if (cacheFills % ReSecureThreshold == 0) _cacheBuffer = _jit.GetBuffer(); if (Protect) ProtectedMemory.Unprotect(_cache, MemoryProtectionScope.SameLogon); while (true) { var remainingBytesToMove = _cacheSize - _availableCacheBytes; if (remainingBytesToMove < moveSize) moveSize = remainingBytesToMove; if (remainingBytesToMove <= 0) break; _cacheBuffer = _algorithm.ComputeHash(_cacheBuffer); Buffer.BlockCopy(_cacheBuffer, 0, _cache, p, moveSize); p += moveSize; _availableCacheBytes += moveSize; } if (Protect) ProtectedMemory.Protect(_cache, MemoryProtectionScope.SameLogon); }) {Priority = ThreadPriority.Highest}; btrd.Start(); btrd.Join(); } }
One thought on “JitterCacheRng.cs”