public void CheapEntropyIsDifferentOnSubsequentCalls_32Bytes() { var e1 = CheapEntropy.Get32(); var e2 = CheapEntropy.Get32(); CollectionAssert.AreNotEqual(e1, e2); }
protected override async Task <byte[]> GetInternalEntropyAsync(EntropyPriority priority) { // Note that many of these servers will have similar content and it is publicly accessible. // We must mix in some local entropy to ensure differnt computers end up with different entropy. // Yes, this reduces the effectiveness of this source, but it will still contribute over time. var localEntropy = (await StaticLocalEntropy.Get32()).Concat(CheapEntropy.Get16()).ToArray(); // Select the servers we will fetch from. var serversToSample = new List <ServerFetcher>(_ServersPerSample); for (int i = 0; i < _ServersPerSample; i++) { if (_NextSource >= _Sources.Count) { _NextSource = 0; } serversToSample.Add(new ServerFetcher(_Sources[_NextSource], _UserAgent, localEntropy)); _NextSource = _NextSource + 1; } if (!serversToSample.Any()) { return(null); } byte[] response; if (!_UseRandomSourceForUnitTest) { // Now fetch from the servers and use the contents, and time to derive entropy. var responses = await Task.WhenAll(serversToSample.Select(x => x.ResetAndRun())); response = responses.SelectMany(x => x ?? new byte[0]).ToArray(); } else { // For unit tests, we just get random bytes. response = _Rng.GetRandomBytes(_ServersPerSample * 32); } return(response); }
private static (byte[] seed, string description) DeriveSeed() { var seedLength = GetKeysizeBytesForCryptoPrimitive(); var sha512 = SHA512.Create(); if (String.IsNullOrEmpty(seed)) { // No seed provided: generate one! return( sha512.ComputeHash( StaticLocalEntropy.Get32().GetAwaiter().GetResult().Concat(CheapEntropy.Get32()).ToArray() ).EnsureArraySize(seedLength) , "System environment." ); } if (seed.IsHexString() && seed.Length == seedLength * 2) { // A hex string of required bytes. return(seed.ParseFromHexString(), $"{seedLength} byte hex seed."); } else if (File.Exists(seed)) { // A file reference: get the SHA512 hash of it as a seed. using (var stream = new FileStream(seed, FileMode.Open, FileAccess.Read, FileShare.Read, 64 * 1024)) return( sha512.ComputeHash(stream).EnsureArraySize(seedLength), "SHA512 hash of file." ); } else { // Assume a random set of characters: get the SHA512 hash of the UTF8 string as a seed. return( sha512.ComputeHash(Encoding.UTF8.GetBytes(seed)).EnsureArraySize(seedLength), "SHA512 hash of random string / password / passphrase." ); } }
protected override async Task <byte[]> GetInternalEntropyAsync(EntropyPriority priority) { // https://beacon.nist.gov/ // Note that this will return the same result for 60 second period. // We must mix in some local entropy to ensure differnt computers end up with different entropy. // Yes, this reduces the effectiveness of this source, but it will still contribute over time. Log.Trace("Beginning to gather entropy."); // Fetch data. var response = ""; var sw = Stopwatch.StartNew(); if (!_UseDiskSourceForUnitTests) { var apiUri = new Uri("https://beacon.nist.gov/rest/record/last"); var hc = HttpClientHelpers.Create(userAgent: _UserAgent); try { response = await hc.GetStringAsync(apiUri); } catch (Exception ex) { Log.Warn(ex, "Unable to GET from {0}", apiUri); return(null); } Log.Trace("Read {0:N0} characters of html in {1:N2}ms.", response.Length, sw.Elapsed.TotalMilliseconds); } else { using (var stream = File.OpenRead(HttpClientHelpers._BasePathToUnitTestData + "beacon.nist.gov-last.xml")) { response = await new StreamReader(stream).ReadToEndAsync(); } } sw.Stop(); var localEntropy = (await StaticLocalEntropy.Get32()).Concat(CheapEntropy.Get16()) .Concat(BitConverter.GetBytes((uint)sw.Elapsed.Ticks)) .ToArray(); Log.Trace("Got {0:N0} bytes of local entropy to mix.", localEntropy.Length); // Parse out the useful parts of the response. // Keeping away from XML parsing to minimise dependencies. At least for now. // The first two return 64 random bytes each, the signature is 256 bytes. All are hashed to 64 bytes when combined with local entropy. var lastOutputBytes = GetWithinXmlTags(response, "previousOutputValue").ParseFromHexString(); var outputValueBytes = GetWithinXmlTags(response, "outputValue").ParseFromHexString(); var signatureBytes = GetWithinXmlTags(response, "signatureValue").ParseFromHexString(); Log.Trace("Got {0:N0} output bytes, {1:N0} last output bytes, {2:N0} signature bytes.", outputValueBytes.Length, lastOutputBytes.Length, signatureBytes.Length); // Mix in some local entropy. var hasher = SHA512.Create(); var result = hasher.ComputeHash(lastOutputBytes.Concat(localEntropy).ToArray()) .Concat(hasher.ComputeHash(outputValueBytes.Concat(localEntropy).ToArray())) .Concat(hasher.ComputeHash(signatureBytes.Concat(localEntropy).ToArray())) .Concat(BitConverter.GetBytes(unchecked ((uint)sw.Elapsed.Ticks))) // Don't forget to include network timing! .ToArray(); Log.Trace("Read {0:N0} bytes of entropy.", result.Length); return(result); }
private static GeneratorAndDescription CreateRandomGenerator() { var result = new GeneratorAndDescription(); if (generatorType == Generator.StockRandom) { result.Description = "deterministic PRNG - " + typeof(Rand).Namespace + "." + typeof(Rand).Name; var(seed, description) = DeriveSeed(); result.SeedDescription = description; result.Generator = new StandardRandomWrapperGenerator(new Rand(BitConverter.ToInt32(seed, 0))); result.WaitForGeneratorReady = () => { }; result.WaitForGeneratorStopped = () => { }; } else if (generatorType == Generator.CryptoRandom) { result.Description = "non-deterministic CPRNG - " + typeof(RandomNumberGenerator).Namespace + "." + typeof(RandomNumberGenerator).Name; result.SeedDescription = "No seed required"; result.Generator = new CryptoRandomWrapperGenerator(); result.WaitForGeneratorReady = () => { }; result.WaitForGeneratorStopped = () => { }; } else if (generatorType == Generator.TerningerCypher) { var primitive = GetCryptoPrimitive(); var hash = GetHashAlgorithm(); var counter = new CypherCounter(primitive.BlockSizeBytes); var entropyGetter = GetEntropyGetter(); var(seed, description) = DeriveSeed(); result.Description = $"{(nonDeterministic ? "non-" : "")}deterministic PRNG - " + typeof(CypherBasedPrngGenerator).Namespace + "." + typeof(CypherBasedPrngGenerator).Name; result.ExtraDescription = $"Using crypto primitive: {cryptoPrimitive}, hash: {hashAlgorithm}"; result.SeedDescription = description; result.Generator = CypherBasedPrngGenerator.Create(key: seed, cryptoPrimitive: primitive, hashAlgorithm: hash, initialCounter: counter, additionalEntropyGetter: entropyGetter); result.WaitForGeneratorReady = () => { }; result.WaitForGeneratorStopped = () => { }; } else if (generatorType == Generator.TerningerPooled) { var(seed, description) = DeriveSeed(); result.SeedDescription = description; // Accumulator. var accKey = SHA512.Create().ComputeHash( StaticLocalEntropy.Get32().GetAwaiter().GetResult().Concat(CheapEntropy.Get32()).ToArray() ).EnsureArraySize(32); var accPrng = CypherBasedPrngGenerator.Create(accKey, CryptoPrimitive.Aes256(), SHA512.Create()); var acc = new EntropyAccumulator(linearPools, randomPools, accPrng, SHA512.Create); // Generator. var primitive = GetCryptoPrimitive(); var hash = GetHashAlgorithm(); var genPrng = CypherBasedPrngGenerator.Create(new byte[32], primitive, hash); IEnumerable <IEntropySource> sources = new IEntropySource[] { new UserSuppliedSource(seed), new CurrentTimeSource(), new TimerSource(), new GCMemorySource(), new CryptoRandomSource(), new NetworkStatsSource(), new ProcessStatsSource(), }; if (includeNetworkSources) { sources = sources.Concat(new IEntropySource[] { new PingStatsSource(), new ExternalWebContentSource(), new AnuExternalRandomSource(), new BeaconNistExternalRandomSource(), new HotbitsExternalRandomSource(), new RandomNumbersInfoExternalRandomSource(), new RandomOrgExternalRandomSource(), }); } // As the pooled generator will be churning out entropy as fast as it can, we increase the reseed rate by polling faster and forcing reseeds more frequently. var config = new PooledEntropyCprngGenerator.PooledGeneratorConfig() { MaximumBytesGeneratedBeforeReseed = Int32.MaxValue, PollWaitTimeInNormalPriority = TimeSpan.FromSeconds(1), EntropyToTriggerReseedInNormalPriority = 64, }; var generator = new PooledEntropyCprngGenerator(sources, acc, genPrng, config); result.Generator = generator; result.Description = $"non-deterministic CPRNG - " + typeof(PooledEntropyCprngGenerator).Namespace + "." + typeof(PooledEntropyCprngGenerator).Name; result.ExtraDescription = $"Using {linearPools}+{randomPools} pools (linear+random), {sources.Count()} entropy sources, crypto primitive: {cryptoPrimitive}, hash: {hashAlgorithm}"; result.WaitForGeneratorReady = () => { generator.StartAndWaitForFirstSeed().Wait(TimeSpan.FromSeconds(60)); }; result.WaitForGeneratorStopped = () => { generator.Stop().Wait(TimeSpan.FromSeconds(60)); }; } else { throw new Exception("Unexpected Generator type: " + generatorType); } return(result); }
public async Task CypherGenerator_LocalAndCheapKeyWithExtraEntropy() { var key = SHA256.Create().ComputeHash((await StaticLocalEntropy.Get32()).Concat(CheapEntropy.Get32()).ToArray()); var rng = CypherBasedPrngGenerator.Create(key, additionalEntropyGetter: CheapEntropy.Get16); await FuzzGenerator(10000, 1, 64, rng, nameof(CypherBasedPrngGenerator) + "_LocalAndCheapKeyAndExtraEntropy"); }