1 // Copyright 2017 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 #include <lib/crypto/entropy/jitterentropy_collector.h>
8
9 #include <kernel/cmdline.h>
10 #include <zircon/errors.h>
11 #include <fbl/atomic.h>
12
13 #ifndef JITTERENTROPY_MEM_SIZE
14 #define JITTERENTROPY_MEM_SIZE (64u * 1024u)
15 #endif
16
17 namespace crypto {
18
19 namespace entropy {
20
GetInstance(Collector ** ptr)21 zx_status_t JitterentropyCollector::GetInstance(Collector** ptr) {
22 static JitterentropyCollector* instance = nullptr;
23 // Note: this would be fbl::atomic<bool>, except that fbl doesn't support
24 // that specialization.
25 static fbl::atomic<int> initialized = {0};
26
27 // Release-acquire ordering guarantees that, once a thread has stored a
28 // value in |initialized| with |memory_order_release|, any other thread that
29 // later loads |initialized| with |memory_order_acquire| will see the result
30 // that the first thread wrote to |instance|. In particular, any thread that
31 // reads |initialized| as 1 will see |instance| as having been initialized.
32 //
33 // Note that this doesn't protect against concurrent access: if two threads
34 // both enter GetInstance while |initialized| is still 0, they might both
35 // try to run the initialization code. That's why the comment in
36 // jitterentropy_collector.h requires that GetInstance() runs to completion
37 // first before concurrent calls are allowed.
38 if (!initialized.load(fbl::memory_order_acquire)) {
39 if (jent_entropy_init() != 0) {
40 // Initialization failed; keep instance == nullptr
41 instance = nullptr;
42 } else {
43 // TODO(andrewkrieger): after optimizing jitterentropy parameters
44 // (see ZX-1022), replace JITTERENTROPY_MEM_SIZE by the optimal
45 // size.
46 static uint8_t mem[JITTERENTROPY_MEM_SIZE];
47 static JitterentropyCollector collector(mem, sizeof(mem));
48 instance = &collector;
49 }
50 initialized.store(1, fbl::memory_order_release);
51 }
52
53 if (instance) {
54 *ptr = instance;
55 return ZX_OK;
56 } else {
57 *ptr = nullptr;
58 return ZX_ERR_NOT_SUPPORTED;
59 }
60 }
61
62 // TODO(ZX-1024): Test jitterentropy in different environments (especially on
63 // different platforms/architectures, and in multi-threaded mode). Ensure
64 // entropy estimate is safe enough.
65
66 // Testing shows that, with the default parameters below (bs=64, bc=512,
67 // ml=32, ll=1, raw=true), each byte of data contributes approximately
68 // 0.58 bits of min-entropy on the rpi3 and 0.5 bits on qemu-arm64. A safety
69 // factor of 0.9 gives us 0.50 * 0.9 * 1000 == 450 bits of entropy per 1000
70 // bytes of random data.
JitterentropyCollector(uint8_t * mem,size_t len)71 JitterentropyCollector::JitterentropyCollector(uint8_t* mem, size_t len)
72 : Collector("jitterentropy", /* entropy_per_1000_bytes */ 450) {
73 // TODO(ZX-1022): optimize default jitterentropy parameters, then update
74 // values here and in docs/kernel_cmdline.md.
75 uint32_t bs = cmdline_get_uint32("kernel.jitterentropy.bs", 64);
76 uint32_t bc = cmdline_get_uint32("kernel.jitterentropy.bc", 512);
77 mem_loops_ = cmdline_get_uint32("kernel.jitterentropy.ml", 32);
78 lfsr_loops_ = cmdline_get_uint32("kernel.jitterentropy.ll", 1);
79 use_raw_samples_ = cmdline_get_bool("kernel.jitterentropy.raw", true);
80
81 jent_entropy_collector_init(&ec_, mem, len, bs, bc, mem_loops_,
82 /* stir */ true);
83 }
84
DrawEntropy(uint8_t * buf,size_t len)85 size_t JitterentropyCollector::DrawEntropy(uint8_t* buf, size_t len) {
86 // TODO(ZX-1024): Test jitterentropy in multi-CPU environment. Disable
87 // interrupts, or otherwise ensure that jitterentropy still performs well in
88 // multi-threaded systems.
89 fbl::AutoLock guard(&lock_);
90
91 if (use_raw_samples_) {
92 for (size_t i = 0; i < len; i++) {
93 buf[i] = static_cast<uint8_t>(jent_lfsr_var_stat(&ec_, lfsr_loops_,
94 mem_loops_));
95 }
96 return len;
97 } else {
98 ssize_t err =jent_read_entropy(&ec_, reinterpret_cast<char*>(buf),
99 len);
100 return (err < 0) ? 0 : static_cast<size_t>(err);
101 }
102 }
103
104 } // namespace entropy
105
106 } // namespace crypto
107