1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #pragma once
8 
9 #include <stddef.h>
10 #include <stdint.h>
11 
12 #include <fbl/atomic.h>
13 #include <fbl/mutex.h>
14 #include <kernel/event.h>
15 #include <kernel/spinlock.h>
16 #include <zircon/thread_annotations.h>
17 
18 namespace crypto {
19 
20 typedef unsigned __int128 uint128_t;
21 
22 // This exposes a (optionally-)thread-safe cryptographically secure PRNG.
23 // This PRNG must be seeded with at least 256 bits of "real" entropy before
24 // being used for cryptographic applications.
25 class PRNG {
26 public:
27     // Tag object for constructing a non-thread-safe version.
28     struct NonThreadSafeTag {};
29 
30     // Construct a thread-safe instance of the PRNG with the byte array at
31     // |data| as the initial seed.  |size| is the length of |data| in bytes.
32     PRNG(const void* data, size_t size);
33 
34     // Construct a non-thread-safe instance of the PRNG with the byte array at
35     // |data| as the initial seed.  |size| is the length of |data| in bytes.
36     PRNG(const void* data, size_t size, NonThreadSafeTag);
37 
38     ~PRNG();
39 
40     // Re-seed the PRNG by mixing-in new entropy. |size| is in bytes.  |data|
41     // should be high-quality entropy, i.e. each bit should have equal
42     // probability of being 0 or 1. |size| MUST NOT be greater than kMaxEntropy.
43     void AddEntropy(const void* data, size_t size) TA_EXCL(mutex_)
44         TA_EXCL(spinlock_);
45 
46     // Get pseudo-random output of |size| bytes.  Blocks until at least
47     // kMinEntropy bytes of entropy have been added to this PRNG.  |size| MUST
48     // NOT be greater than kMaxDrawLen.  Identical PRNGs are only guaranteed to
49     // produce identical output when given identical inputs.
50     void Draw(void* out, size_t size) TA_EXCL(spinlock_);
51 
52     // Return an integer in the range [0, exclusive_upper_bound) chosen
53     // uniformly at random.  This is a wrapper for Draw(), and so has the same
54     // caveats.
55     uint64_t RandInt(uint64_t exclusive_upper_bound) TA_EXCL(spinlock_);
56 
57     // Transitions the PRNG to thread-safe mode.  This asserts that the
58     // instance is not yet thread-safe.
59     void BecomeThreadSafe();
60 
61     // Inspect if this PRNG is thread-safe.
62     bool is_thread_safe() const;
63 
64     // The minimum amount of entropy (in bytes) the generator requires before
65     // Draw will return data.
66     static constexpr uint64_t kMinEntropy = 32;
67 
68     // The maximum amount of entropy (in bytes) that can be submitted to
69     // AddEntropy.  Anything above this will panic.
70     static constexpr uint64_t kMaxEntropy = 1ULL << 30;
71 
72     // The maximum amount of pseudorandom data (in bytes) that can be drawn in
73     // one call to Draw. This the limit imposed by the maximum number of bytes
74     // that can be generated with a single key/nonce pair. Each request to Draw
75     // uses a different key/nonce pair.  Anything above this will panic.
76     static constexpr uint64_t kMaxDrawLen = 1ULL << 38;
77 
78 private:
79     PRNG(const PRNG&) = delete;
80     PRNG& operator=(const PRNG&) = delete;
81 
82     // Synchronizes calls to |AddEntropy|.
83     fbl::Mutex mutex_;
84 
85     // Controls access to |key_| |and nonce_|.
86     SpinLock spinlock_;
87 
88     // ChaCha20 key and nonce as described in RFC 7539.  The key length is
89     // enforced by a static assertion in the constructor.
90     uint8_t key_[32] TA_GUARDED(spinlock_);
91     uint128_t nonce_ TA_GUARDED(spinlock_);
92 
93     // Events used to signal when calls to |Draw| may proceed, if
94     // |BecomeThreadSafe| has been called.
95     event_t ready_;
96 
97     // Number of bytes of entropy added so far.
98     fbl::atomic<size_t> accumulated_;
99 };
100 
101 } // namespace crypto
102