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
17namespace crypto {
18
19namespace entropy {
20
21zx_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.
71JitterentropyCollector::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
85size_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