public void CheapEntropyIsDifferentOnSubsequentCalls_16Bytes() { var e1 = CheapEntropy.Get16(); var e2 = CheapEntropy.Get16(); 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); }
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); }