| // Copyright 2025 The BoringSSL Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // BoringCrypto Jitter Entropy version 20250725. | |
| #include <openssl/base.h> | |
| #if defined(OPENSSL_LINUX) || defined(OPENSSL_MACOS) | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <time.h> | |
| #include <unistd.h> | |
| #include <array> | |
| #include <type_traits> | |
| #include <utility> | |
| #include <openssl/mem.h> | |
| #include "internal.h" | |
| #include "sha512.cc.inc" | |
| #if defined(__x86_64__) | |
| #include <x86intrin.h> | |
| #endif | |
| BSSL_NAMESPACE_BEGIN | |
| namespace entropy { | |
| namespace { | |
| #if defined(__x86_64__) | |
| static uint64_t GetTimestamp() { return _rdtsc(); } | |
| #elif defined(__aarch64__) | |
| static uint64_t GetTimestamp() { | |
| // Ideally this would use __arm_rsr64 from <arm_acle.h>. Clang has supported | |
| // it Clang 3.7 (2016), but GCC did not add it until GCC 14.1.0 (2024). See | |
| // https://crbug.com/440670941. When our minimum GCC is past that point, | |
| // switch this back to __arm_rsr64. | |
| uint64_t ret; | |
| __asm__ volatile("mrs %0, cntvct_el0" : "=r"(ret)); | |
| return ret; | |
| } | |
| #else | |
| static uint64_t GetTimestamp() { | |
| struct timespec ts; | |
| clock_gettime(CLOCK_MONOTONIC_RAW, &ts); | |
| return ts.tv_sec * 1000000000ULL + ts.tv_nsec; | |
| } | |
| #endif | |
| class MemoryOffsetLCG { | |
| public: | |
| MemoryOffsetLCG() : state(GetTimestamp() & 0xFFFFFFFF) {} | |
| uint32_t Next() { | |
| state = state * 1664525 + 1013904223; | |
| return state; | |
| } | |
| private: | |
| uint32_t state; | |
| }; | |
| class MemoryAccessSampler { | |
| public: | |
| MemoryAccessSampler(size_t array_size, unsigned num_samples) | |
| : array_size_(array_size), | |
| num_samples_(num_samples), | |
| array_(reinterpret_cast<uint8_t *>(OPENSSL_malloc(array_size_))) { | |
| if (array_ == nullptr || // | |
| array_size_ == 0 || // | |
| array_size_ > (1u << 26) || // | |
| array_size_ & (array_size_ - 1)) { | |
| abort(); | |
| } | |
| } | |
| ~MemoryAccessSampler() { OPENSSL_free(const_cast<uint8_t *>(array_)); } | |
| MemoryAccessSampler(const MemoryAccessSampler &) = delete; | |
| MemoryAccessSampler &operator=(const MemoryAccessSampler &) = delete; | |
| MemoryAccessSampler(MemoryAccessSampler &&other) | |
| : array_size_(other.array_size_), | |
| num_samples_(other.num_samples_), | |
| lcg_(other.lcg_), | |
| array_(other.array_) { | |
| other.array_ = nullptr; | |
| } | |
| bool Next(uint64_t *out) { | |
| // Perform some memory accesses and measure how long it took. The LCG is | |
| // intended to defeat any CPU predictors and thus expose this code to as | |
| // much system entropy as possible. | |
| for (unsigned i = 0; i < num_samples_; i++) { | |
| // The lower bits of an LCG tend to fall into short cycles and so are | |
| // discarded here. | |
| array_[(lcg_.Next() >> 6) & (array_size_ - 1)] += 1; | |
| } | |
| *out = GetTimestamp(); | |
| return true; | |
| } | |
| private: | |
| const size_t array_size_; | |
| const unsigned num_samples_; | |
| MemoryOffsetLCG lcg_; | |
| volatile uint8_t *array_; | |
| }; | |
| template <typename T> | |
| class DeltaSampler { | |
| public: | |
| explicit DeltaSampler(T &&sub_sampler) | |
| : sub_sampler_(std::forward<T>(sub_sampler)) {} | |
| // Next function to return the delta between two subsequent samples | |
| bool Next(uint64_t *out) { | |
| uint64_t sample; | |
| if (!sub_sampler_.Next(&sample)) { | |
| return false; | |
| } | |
| if (!initialized_) { | |
| last_sample_ = sample; | |
| if (!sub_sampler_.Next(&sample)) { | |
| return false; | |
| } | |
| initialized_ = true; | |
| } | |
| *out = sample - last_sample_; | |
| last_sample_ = sample; | |
| return true; | |
| } | |
| private: | |
| bool initialized_ = false; | |
| T sub_sampler_; | |
| uint64_t last_sample_ = 0; | |
| }; | |
| template <typename U> | |
| DeltaSampler(U &&sub_sampler) -> DeltaSampler<std::decay_t<U>>; | |
| template <typename T> | |
| class MaskSampler { | |
| public: | |
| explicit MaskSampler(uint8_t mask, T &&sub_sampler) | |
| : mask_(mask), sub_sampler_(std::forward<T>(sub_sampler)) {} | |
| bool Next(uint8_t *out) { | |
| uint64_t sample; | |
| if (!sub_sampler_.Next(&sample)) { | |
| return false; | |
| } | |
| *out = sample & mask_; | |
| return true; | |
| } | |
| private: | |
| const uint8_t mask_; | |
| T sub_sampler_; | |
| }; | |
| template <typename U> | |
| MaskSampler(uint8_t mask, U &&sub_sampler) -> MaskSampler<std::decay_t<U>>; | |
| // The estimated entropy per sample from MaskSampler. | |
| constexpr float kH = 0.8; | |
| // kAlphaLog2 is log_2(alpha), where alpha is the standard false-positive | |
| // probability from SP 800-90B. | |
| static constexpr float kAlphaLog2 = -20; | |
| // kAlpha is the variable of the same name from section 4.4.1 of SP 800-90B. | |
| constexpr float kAlpha = 1.0 / (1 << static_cast<unsigned>(-kAlphaLog2)); | |
| // Ceil rounds up its non-negative argument to the next integer. (std::ceil | |
| // isn't constexpr until C++23.) | |
| constexpr unsigned Ceil(float val) { | |
| auto truncated = static_cast<unsigned>(val); | |
| if (val == static_cast<float>(truncated)) { | |
| return truncated; | |
| } | |
| if (val > 0) { | |
| return truncated + 1; | |
| } | |
| __builtin_unreachable(); | |
| } | |
| template <typename T> | |
| class RepetitionCountTest { | |
| public: | |
| static constexpr unsigned kThreshold = 1 + Ceil(-kAlphaLog2 / kH); | |
| static_assert(kThreshold == 26); | |
| explicit RepetitionCountTest(T &&sub_sampler) | |
| : sub_sampler_(std::forward<T>(sub_sampler)) {} | |
| bool Next(uint8_t *out) { | |
| uint8_t sample; | |
| if (!sub_sampler_.Next(&sample)) { | |
| return false; | |
| } | |
| if (sample == last_sample_) { | |
| count_++; | |
| } else { | |
| count_ = 1; | |
| last_sample_ = sample; | |
| } | |
| if (count_ >= kThreshold) { | |
| return false; | |
| } | |
| *out = sample; | |
| return true; | |
| } | |
| private: | |
| T sub_sampler_; | |
| unsigned count_ = 0; | |
| uint8_t last_sample_ = 0; | |
| }; | |
| template <typename U> | |
| RepetitionCountTest(U &&sub_sampler) -> RepetitionCountTest<std::decay_t<U>>; | |
| constexpr double BinomialPMF(int64_t k, int64_t n, double p) { | |
| if (k < 0 || k > n) { | |
| return 0.0; | |
| } | |
| double result = 1.0; | |
| for (int64_t i = 0; i < k; ++i) { | |
| result *= (n - i); | |
| result /= (i + 1); | |
| } | |
| for (int64_t i = 0; i < k; ++i) { | |
| result *= p; | |
| } | |
| for (int64_t i = 0; i < n - k; ++i) { | |
| result *= (1 - p); | |
| } | |
| return result; | |
| } | |
| // CritBinom implements the Excel function of the same name. | |
| constexpr unsigned CritBinom(unsigned trials, double probability_s, | |
| double alpha) { | |
| if (probability_s < 0.0 || probability_s > 1.0 || alpha < 0.0 || | |
| alpha > 1.0) { | |
| __builtin_unreachable(); | |
| } | |
| double cumulative = 0.0; | |
| for (unsigned k = 0; k <= trials; ++k) { | |
| cumulative += BinomialPMF(k, trials, probability_s); | |
| if (cumulative >= alpha) { | |
| return k; | |
| } | |
| } | |
| return trials; | |
| } | |
| // ExpTaylor calculates e^x using the Taylor series: e^x = 1 + x + x²/2! + | |
| // x³/3! + ... | |
| constexpr double ExpTaylor(double x) { | |
| double sum = 1.0; | |
| double term = 1.0; | |
| for (int i = 1; i < 25; ++i) { | |
| term *= x / i; | |
| sum += term; | |
| } | |
| return sum; | |
| } | |
| // Power2 calculates 2^exp by calculating e^(ln(2) * exp) = e^(ln(2)) ^ exp = | |
| // 2^exp. (std::pow isn't constexpr until C++26.) | |
| constexpr double Power2(double exp) { | |
| constexpr double ln2 = 0.693147180559945309417232121458; | |
| return ExpTaylor(exp * ln2); | |
| } | |
| // AdaptiveProportionTestCutoff implements the function from the footnote on | |
| // page 27 of SP 800-90B. | |
| constexpr unsigned AdaptiveProportionTestCutoff(unsigned W, float H, | |
| float alpha) { | |
| return 1 + CritBinom(W, Power2(-H), 1.0 - alpha); | |
| } | |
| // These are the example values from table 2 of SP 800-90B, to show that | |
| // we're calculating the values correctly. | |
| static_assert(AdaptiveProportionTestCutoff(512, 0.5, kAlpha) == 410); | |
| static_assert(AdaptiveProportionTestCutoff(512, 1, kAlpha) == 311); | |
| static_assert(AdaptiveProportionTestCutoff(512, 2, kAlpha) == 177); | |
| static_assert(AdaptiveProportionTestCutoff(512, 4, kAlpha) == 62); | |
| static_assert(AdaptiveProportionTestCutoff(512, 8, kAlpha) == 13); | |
| template <typename T> | |
| class AdaptiveProportionTest { | |
| public: | |
| // The size of the sliding window, representing the number of recent samples | |
| // to analyze. | |
| static constexpr unsigned kWindowSize = 512; | |
| // The maximum number of times any single byte value is allowed to appear | |
| // within the sliding window. | |
| static constexpr unsigned kThreshold = | |
| AdaptiveProportionTestCutoff(kWindowSize, kH, kAlpha); | |
| static_assert(kThreshold == 348); | |
| explicit AdaptiveProportionTest(T &&sub_sampler) | |
| : sub_sampler_(std::forward<T>(sub_sampler)) { | |
| counts_.fill(0); | |
| } | |
| bool Next(uint8_t *out) { | |
| uint8_t sample; | |
| if (!sub_sampler_.Next(&sample)) { | |
| return false; | |
| } | |
| *out = sample; | |
| if (samples_processed_ >= kWindowSize) { | |
| const uint8_t evicted_sample = buffer_[buffer_idx_]; | |
| counts_[evicted_sample]--; | |
| } | |
| buffer_[buffer_idx_] = sample; | |
| const uint16_t new_count = ++counts_[sample]; | |
| if (new_count > kThreshold) { | |
| return false; | |
| } | |
| buffer_idx_ = (buffer_idx_ + 1) % kWindowSize; | |
| samples_processed_++; | |
| return true; | |
| } | |
| private: | |
| T sub_sampler_; | |
| // A circular buffer to store the most recent `kWindowSize` samples. | |
| std::array<uint8_t, kWindowSize> buffer_{}; | |
| // An array to store the frequency counts of each possible byte value (0-255) | |
| // within the current window. | |
| std::array<uint16_t, 256> counts_{}; | |
| // The current index for writing into the circular buffer. | |
| size_t buffer_idx_ = 0; | |
| // The total number of samples processed. Used to determine when the buffer | |
| // is full and eviction should begin. | |
| size_t samples_processed_ = 0; | |
| }; | |
| template <typename U> | |
| AdaptiveProportionTest(U &&sub_sampler) | |
| -> AdaptiveProportionTest<std::decay_t<U>>; | |
| template <typename T> | |
| class SeedSampler { | |
| public: | |
| // NIST requires 1024 samples at start-up time. This code is structured so | |
| // that the entropy generator is considered to be starting afresh for each | |
| // seed. | |
| static constexpr unsigned kNumSamples = 1024; | |
| explicit SeedSampler(T &&sub_sampler) | |
| : sub_sampler_(std::forward<T>(sub_sampler)) {} | |
| bool Next(uint8_t out_seed[48]) { | |
| // HMAC-SHA384 `kNumSamples` samples with an all-zero key: | |
| SHA512_CTX ctx; | |
| SHA384_Init(&ctx); | |
| uint8_t block[kSHA384Block]; | |
| memset(block, 0x36, sizeof(block)); | |
| SHA384_Update(&ctx, block, sizeof(block)); | |
| static_assert(kNumSamples % sizeof(block) == 0); | |
| for (unsigned i = 0; i < kNumSamples / sizeof(block); i++) { | |
| for (unsigned j = 0; j < sizeof(block); j++) { | |
| if (!sub_sampler_.Next(&block[j])) { | |
| return false; | |
| } | |
| } | |
| SHA384_Update(&ctx, block, sizeof(block)); | |
| } | |
| SHA384_Final(out_seed, &ctx); | |
| SHA384_Init(&ctx); | |
| memset(block, 0x5c, sizeof(block)); | |
| SHA384_Update(&ctx, block, sizeof(block)); | |
| SHA384_Update(&ctx, out_seed, kSHA384DigestLength); | |
| SHA384_Final(out_seed, &ctx); | |
| return true; | |
| } | |
| private: | |
| T sub_sampler_; | |
| }; | |
| template <typename U> | |
| SeedSampler(U &&sub_sampler) -> SeedSampler<std::decay_t<U>>; | |
| constexpr size_t kMemorySize = 1u << 25; | |
| constexpr size_t kMemoryAccessesPerSample = 16; | |
| constexpr size_t kBitsPerSample = 8; | |
| constexpr uint8_t kMask = (1u << kBitsPerSample) - 1; | |
| } // namespace | |
| int GetVersion() { return 20250725; } | |
| bool GetSeed(uint8_t out[48]) { | |
| auto sampler(SeedSampler(AdaptiveProportionTest(RepetitionCountTest( | |
| MaskSampler(kMask, DeltaSampler(MemoryAccessSampler( | |
| kMemorySize, kMemoryAccessesPerSample))))))); | |
| return sampler.Next(out); | |
| } | |
| bool GetSamples(uint64_t *out, size_t n) { | |
| auto sampler( | |
| DeltaSampler(MemoryAccessSampler(kMemorySize, kMemoryAccessesPerSample))); | |
| for (size_t i = 0; i < n; i++) { | |
| if (!sampler.Next(&out[i])) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| } // namespace entropy | |
| BSSL_NAMESPACE_END | |
| #endif // LINUX || MACOS |