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