public void CheapEntropyIsDifferentOnSubsequentCalls_32Bytes() { var e1 = CheapEntropy.Get32(); var e2 = CheapEntropy.Get32(); CollectionAssert.AreNotEqual(e1, e2); }
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." ); } }
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"); }