Пример #1
0
        public void CheapEntropyIsDifferentOnSubsequentCalls_32Bytes()
        {
            var e1 = CheapEntropy.Get32();
            var e2 = CheapEntropy.Get32();

            CollectionAssert.AreNotEqual(e1, e2);
        }
Пример #2
0
        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);
        }
Пример #3
0
        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."
                    );
            }
        }
Пример #4
0
        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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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");
        }