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