Skip to content

Instantly share code, notes, and snippets.

@mjs3339
Created January 12, 2021 14:19
Show Gist options
  • Save mjs3339/808c4a3bfa2aa48a33868940da052f14 to your computer and use it in GitHub Desktop.
Save mjs3339/808c4a3bfa2aa48a33868940da052f14 to your computer and use it in GitHub Desktop.
Jitter Cache
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();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment