1 // Copyright 2020 The Pigweed Authors
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
7 // https://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
20 #include "pw_bytes/span.h"
21 #include "pw_random/random.h"
22 #include "pw_status/status_with_size.h"
24 namespace pw::random {
26 // This is the "xorshift*" algorithm which is a bit stronger than plain XOR
27 // shift thanks to the nonlinear transformation at the end (multiplication).
29 // See: https://en.wikipedia.org/wiki/Xorshift
31 // This random generator is NOT cryptographically secure, and incorporates
32 // pseudo-random generation to extrapolate any true injected entropy. The
33 // distribution is not guaranteed to be uniform.
34 class XorShiftStarRng64 : public RandomGenerator {
36 XorShiftStarRng64(uint64_t initial_seed) : state_(initial_seed) {}
38 // This generator uses entropy-seeded PRNG to never exhaust its random number
40 StatusWithSize Get(ByteSpan dest) final {
41 const size_t bytes_written = dest.size_bytes();
42 while (!dest.empty()) {
43 uint64_t random = Regenerate();
44 size_t copy_size = std::min(dest.size_bytes(), sizeof(state_));
45 std::memcpy(dest.data(), &random, copy_size);
46 dest = dest.subspan(copy_size);
49 return StatusWithSize(bytes_written);
52 // Entropy is injected by rotating the state by the number of entropy bits
53 // before xoring the entropy with the current state. This ensures seeding
54 // the random value with single bits will progressively fill the state with
56 void InjectEntropyBits(uint32_t data, uint_fast8_t num_bits) final {
59 } else if (num_bits > 32) {
64 uint64_t untouched_state = state_ >> (kNumStateBits - num_bits);
65 state_ = untouched_state | (state_ << num_bits);
66 // Zero-out all irrelevant bits, then XOR entropy into state.
67 uint32_t mask = (1 << num_bits) - 1;
68 state_ ^= (data & mask);
72 // Calculate and return the next value based on the "xorshift*" algorithm
73 uint64_t Regenerate() {
74 // State must be nonzero, or the algorithm will get stuck and always return
79 state_ ^= state_ >> 12;
80 state_ ^= state_ << 25;
81 state_ ^= state_ >> 27;
82 return state_ * kMultConst;
85 static constexpr uint8_t kNumStateBits = sizeof(state_) * 8;
87 // For information on why this constant was selected, see:
88 // https://www.jstatsoft.org/article/view/v008i14
89 // http://vigna.di.unimi.it/ftp/papers/xorshift.pdf
90 static constexpr uint64_t kMultConst = 0x2545F4914F6CDD1D;
93 } // namespace pw::random