| // 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 inline uint64_t GetTimestamp() { return _rdtsc(); } |
| #elif defined(__aarch64__) |
| static inline 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 inline 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_; |
| }; |
| |
| 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 |